1/* 2 * omap_vout_vrfb.c 3 * 4 * Copyright (C) 2010 Texas Instruments. 5 * 6 * This file is licensed under the terms of the GNU General Public License 7 * version 2. This program is licensed "as is" without any warranty of any 8 * kind, whether express or implied. 9 * 10 */ 11 12#include <linux/sched.h> 13#include <linux/platform_device.h> 14#include <linux/videodev2.h> 15 16#include <media/videobuf-dma-contig.h> 17#include <media/v4l2-device.h> 18 19#include <plat/dma.h> 20#include <plat/vrfb.h> 21 22#include "omap_voutdef.h" 23#include "omap_voutlib.h" 24 25/* 26 * Function for allocating video buffers 27 */ 28static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout, 29 unsigned int *count, int startindex) 30{ 31 int i, j; 32 33 for (i = 0; i < *count; i++) { 34 if (!vout->smsshado_virt_addr[i]) { 35 vout->smsshado_virt_addr[i] = 36 omap_vout_alloc_buffer(vout->smsshado_size, 37 &vout->smsshado_phy_addr[i]); 38 } 39 if (!vout->smsshado_virt_addr[i] && startindex != -1) { 40 if (V4L2_MEMORY_MMAP == vout->memory && i >= startindex) 41 break; 42 } 43 if (!vout->smsshado_virt_addr[i]) { 44 for (j = 0; j < i; j++) { 45 omap_vout_free_buffer( 46 vout->smsshado_virt_addr[j], 47 vout->smsshado_size); 48 vout->smsshado_virt_addr[j] = 0; 49 vout->smsshado_phy_addr[j] = 0; 50 } 51 *count = 0; 52 return -ENOMEM; 53 } 54 memset((void *) vout->smsshado_virt_addr[i], 0, 55 vout->smsshado_size); 56 } 57 return 0; 58} 59 60/* 61 * Wakes up the application once the DMA transfer to VRFB space is completed. 62 */ 63static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data) 64{ 65 struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data; 66 67 t->tx_status = 1; 68 wake_up_interruptible(&t->wait); 69} 70 71/* 72 * Free VRFB buffers 73 */ 74void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) 75{ 76 int j; 77 78 for (j = 0; j < VRFB_NUM_BUFS; j++) { 79 omap_vout_free_buffer(vout->smsshado_virt_addr[j], 80 vout->smsshado_size); 81 vout->smsshado_virt_addr[j] = 0; 82 vout->smsshado_phy_addr[j] = 0; 83 } 84} 85 86int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, 87 bool static_vrfb_allocation) 88{ 89 int ret = 0, i, j; 90 struct omap_vout_device *vout; 91 struct video_device *vfd; 92 int image_width, image_height; 93 int vrfb_num_bufs = VRFB_NUM_BUFS; 94 struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); 95 struct omap2video_device *vid_dev = 96 container_of(v4l2_dev, struct omap2video_device, v4l2_dev); 97 98 vout = vid_dev->vouts[vid_num]; 99 vfd = vout->vfd; 100 101 for (i = 0; i < VRFB_NUM_BUFS; i++) { 102 if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) { 103 dev_info(&pdev->dev, ": VRFB allocation failed\n"); 104 for (j = 0; j < i; j++) 105 omap_vrfb_release_ctx(&vout->vrfb_context[j]); 106 ret = -ENOMEM; 107 goto free_buffers; 108 } 109 } 110 111 /* Calculate VRFB memory size */ 112 /* allocate for worst case size */ 113 image_width = VID_MAX_WIDTH / TILE_SIZE; 114 if (VID_MAX_WIDTH % TILE_SIZE) 115 image_width++; 116 117 image_width = image_width * TILE_SIZE; 118 image_height = VID_MAX_HEIGHT / TILE_SIZE; 119 120 if (VID_MAX_HEIGHT % TILE_SIZE) 121 image_height++; 122 123 image_height = image_height * TILE_SIZE; 124 vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2); 125 126 /* 127 * Request and Initialize DMA, for DMA based VRFB transfer 128 */ 129 vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE; 130 vout->vrfb_dma_tx.dma_ch = -1; 131 vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED; 132 ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX", 133 omap_vout_vrfb_dma_tx_callback, 134 (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch); 135 if (ret < 0) { 136 vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; 137 dev_info(&pdev->dev, ": failed to allocate DMA Channel for" 138 " video%d\n", vfd->minor); 139 } 140 init_waitqueue_head(&vout->vrfb_dma_tx.wait); 141 142 /* statically allocated the VRFB buffer is done through 143 commands line aruments */ 144 if (static_vrfb_allocation) { 145 if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) { 146 ret = -ENOMEM; 147 goto release_vrfb_ctx; 148 } 149 vout->vrfb_static_allocation = 1; 150 } 151 return 0; 152 153release_vrfb_ctx: 154 for (j = 0; j < VRFB_NUM_BUFS; j++) 155 omap_vrfb_release_ctx(&vout->vrfb_context[j]); 156free_buffers: 157 omap_vout_free_buffers(vout); 158 159 return ret; 160} 161 162/* 163 * Release the VRFB context once the module exits 164 */ 165void omap_vout_release_vrfb(struct omap_vout_device *vout) 166{ 167 int i; 168 169 for (i = 0; i < VRFB_NUM_BUFS; i++) 170 omap_vrfb_release_ctx(&vout->vrfb_context[i]); 171 172 if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) { 173 vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; 174 omap_free_dma(vout->vrfb_dma_tx.dma_ch); 175 } 176} 177 178/* 179 * Allocate the buffers for the VRFB space. Data is copied from V4L2 180 * buffers to the VRFB buffers using the DMA engine. 181 */ 182int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, 183 unsigned int *count, unsigned int startindex) 184{ 185 int i; 186 bool yuv_mode; 187 188 if (!is_rotation_enabled(vout)) 189 return 0; 190 191 /* If rotation is enabled, allocate memory for VRFB space also */ 192 *count = *count > VRFB_NUM_BUFS ? VRFB_NUM_BUFS : *count; 193 194 /* Allocate the VRFB buffers only if the buffers are not 195 * allocated during init time. 196 */ 197 if (!vout->vrfb_static_allocation) 198 if (omap_vout_allocate_vrfb_buffers(vout, count, startindex)) 199 return -ENOMEM; 200 201 if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 || 202 vout->dss_mode == OMAP_DSS_COLOR_UYVY) 203 yuv_mode = true; 204 else 205 yuv_mode = false; 206 207 for (i = 0; i < *count; i++) 208 omap_vrfb_setup(&vout->vrfb_context[i], 209 vout->smsshado_phy_addr[i], vout->pix.width, 210 vout->pix.height, vout->bpp, yuv_mode); 211 212 return 0; 213} 214 215int omap_vout_prepare_vrfb(struct omap_vout_device *vout, 216 struct videobuf_buffer *vb) 217{ 218 dma_addr_t dmabuf; 219 struct vid_vrfb_dma *tx; 220 enum dss_rotation rotation; 221 u32 dest_frame_index = 0, src_element_index = 0; 222 u32 dest_element_index = 0, src_frame_index = 0; 223 u32 elem_count = 0, frame_count = 0, pixsize = 2; 224 225 if (!is_rotation_enabled(vout)) 226 return 0; 227 228 dmabuf = vout->buf_phy_addr[vb->i]; 229 /* If rotation is enabled, copy input buffer into VRFB 230 * memory space using DMA. We are copying input buffer 231 * into VRFB memory space of desired angle and DSS will 232 * read image VRFB memory for 0 degree angle 233 */ 234 pixsize = vout->bpp * vout->vrfb_bpp; 235 /* 236 * DMA transfer in double index mode 237 */ 238 239 /* Frame index */ 240 dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) - 241 (vout->pix.width * vout->bpp)) + 1; 242 243 /* Source and destination parameters */ 244 src_element_index = 0; 245 src_frame_index = 0; 246 dest_element_index = 1; 247 /* Number of elements per frame */ 248 elem_count = vout->pix.width * vout->bpp; 249 frame_count = vout->pix.height; 250 tx = &vout->vrfb_dma_tx; 251 tx->tx_status = 0; 252 omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32, 253 (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT, 254 tx->dev_id, 0x0); 255 /* src_port required only for OMAP1 */ 256 omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, 257 dmabuf, src_element_index, src_frame_index); 258 /*set dma source burst mode for VRFB */ 259 omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); 260 rotation = calc_rotation(vout); 261 262 /* dest_port required only for OMAP1 */ 263 omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX, 264 vout->vrfb_context[vb->i].paddr[0], dest_element_index, 265 dest_frame_index); 266 /*set dma dest burst mode for VRFB */ 267 omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); 268 omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0); 269 270 omap_start_dma(tx->dma_ch); 271 interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT); 272 273 if (tx->tx_status == 0) { 274 omap_stop_dma(tx->dma_ch); 275 return -EINVAL; 276 } 277 /* Store buffers physical address into an array. Addresses 278 * from this array will be used to configure DSS */ 279 vout->queued_buf_addr[vb->i] = (u8 *) 280 vout->vrfb_context[vb->i].paddr[rotation]; 281 return 0; 282} 283 284/* 285 * Calculate the buffer offsets from which the streaming should 286 * start. This offset calculation is mainly required because of 287 * the VRFB 32 pixels alignment with rotation. 288 */ 289void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) 290{ 291 enum dss_rotation rotation; 292 bool mirroring = vout->mirror; 293 struct v4l2_rect *crop = &vout->crop; 294 struct v4l2_pix_format *pix = &vout->pix; 295 int *cropped_offset = &vout->cropped_offset; 296 int vr_ps = 1, ps = 2, temp_ps = 2; 297 int offset = 0, ctop = 0, cleft = 0, line_length = 0; 298 299 rotation = calc_rotation(vout); 300 301 if (V4L2_PIX_FMT_YUYV == pix->pixelformat || 302 V4L2_PIX_FMT_UYVY == pix->pixelformat) { 303 if (is_rotation_enabled(vout)) { 304 /* 305 * ps - Actual pixel size for YUYV/UYVY for 306 * VRFB/Mirroring is 4 bytes 307 * vr_ps - Virtually pixel size for YUYV/UYVY is 308 * 2 bytes 309 */ 310 ps = 4; 311 vr_ps = 2; 312 } else { 313 ps = 2; /* otherwise the pixel size is 2 byte */ 314 } 315 } else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) { 316 ps = 4; 317 } else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) { 318 ps = 3; 319 } 320 vout->ps = ps; 321 vout->vr_ps = vr_ps; 322 323 if (is_rotation_enabled(vout)) { 324 line_length = MAX_PIXELS_PER_LINE; 325 ctop = (pix->height - crop->height) - crop->top; 326 cleft = (pix->width - crop->width) - crop->left; 327 } else { 328 line_length = pix->width; 329 } 330 vout->line_length = line_length; 331 switch (rotation) { 332 case dss_rotation_90_degree: 333 offset = vout->vrfb_context[0].yoffset * 334 vout->vrfb_context[0].bytespp; 335 temp_ps = ps / vr_ps; 336 if (mirroring == 0) { 337 *cropped_offset = offset + line_length * 338 temp_ps * cleft + crop->top * temp_ps; 339 } else { 340 *cropped_offset = offset + line_length * temp_ps * 341 cleft + crop->top * temp_ps + (line_length * 342 ((crop->width / (vr_ps)) - 1) * ps); 343 } 344 break; 345 case dss_rotation_180_degree: 346 offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset * 347 vout->vrfb_context[0].bytespp) + 348 (vout->vrfb_context[0].xoffset * 349 vout->vrfb_context[0].bytespp)); 350 if (mirroring == 0) { 351 *cropped_offset = offset + (line_length * ps * ctop) + 352 (cleft / vr_ps) * ps; 353 354 } else { 355 *cropped_offset = offset + (line_length * ps * ctop) + 356 (cleft / vr_ps) * ps + (line_length * 357 (crop->height - 1) * ps); 358 } 359 break; 360 case dss_rotation_270_degree: 361 offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset * 362 vout->vrfb_context[0].bytespp; 363 temp_ps = ps / vr_ps; 364 if (mirroring == 0) { 365 *cropped_offset = offset + line_length * 366 temp_ps * crop->left + ctop * ps; 367 } else { 368 *cropped_offset = offset + line_length * 369 temp_ps * crop->left + ctop * ps + 370 (line_length * ((crop->width / vr_ps) - 1) * 371 ps); 372 } 373 break; 374 case dss_rotation_0_degree: 375 if (mirroring == 0) { 376 *cropped_offset = (line_length * ps) * 377 crop->top + (crop->left / vr_ps) * ps; 378 } else { 379 *cropped_offset = (line_length * ps) * 380 crop->top + (crop->left / vr_ps) * ps + 381 (line_length * (crop->height - 1) * ps); 382 } 383 break; 384 default: 385 *cropped_offset = (line_length * ps * crop->top) / 386 vr_ps + (crop->left * ps) / vr_ps + 387 ((crop->width / vr_ps) - 1) * ps; 388 break; 389 } 390} 391