root/drivers/media/platform/omap/omap_vout_vrfb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. omap_vout_allocate_vrfb_buffers
  2. omap_vout_vrfb_dma_tx_callback
  3. omap_vout_free_vrfb_buffers
  4. omap_vout_setup_vrfb_bufs
  5. omap_vout_release_vrfb
  6. omap_vout_vrfb_buffer_setup
  7. omap_vout_prepare_vrfb
  8. omap_vout_calculate_vrfb_offset

   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 #include <linux/slab.h>
  16 
  17 #include <media/v4l2-device.h>
  18 
  19 #include <video/omapvrfb.h>
  20 
  21 #include "omap_voutdef.h"
  22 #include "omap_voutlib.h"
  23 #include "omap_vout_vrfb.h"
  24 
  25 #define OMAP_DMA_NO_DEVICE      0
  26 
  27 /*
  28  * Function for allocating video buffers
  29  */
  30 static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,
  31                 unsigned int *count, int startindex)
  32 {
  33         int i, j;
  34 
  35         for (i = 0; i < *count; i++) {
  36                 if (!vout->smsshado_virt_addr[i]) {
  37                         vout->smsshado_virt_addr[i] =
  38                                 omap_vout_alloc_buffer(vout->smsshado_size,
  39                                                 &vout->smsshado_phy_addr[i]);
  40                 }
  41                 if (!vout->smsshado_virt_addr[i] && startindex != -1) {
  42                         if (vout->vq.memory == V4L2_MEMORY_MMAP && i >= startindex)
  43                                 break;
  44                 }
  45                 if (!vout->smsshado_virt_addr[i]) {
  46                         for (j = 0; j < i; j++) {
  47                                 omap_vout_free_buffer(
  48                                                 vout->smsshado_virt_addr[j],
  49                                                 vout->smsshado_size);
  50                                 vout->smsshado_virt_addr[j] = 0;
  51                                 vout->smsshado_phy_addr[j] = 0;
  52                         }
  53                         *count = 0;
  54                         return -ENOMEM;
  55                 }
  56                 memset((void *)(long)vout->smsshado_virt_addr[i], 0,
  57                        vout->smsshado_size);
  58         }
  59         return 0;
  60 }
  61 
  62 /*
  63  * Wakes up the application once the DMA transfer to VRFB space is completed.
  64  */
  65 static void omap_vout_vrfb_dma_tx_callback(void *data)
  66 {
  67         struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data;
  68 
  69         t->tx_status = 1;
  70         wake_up_interruptible(&t->wait);
  71 }
  72 
  73 /*
  74  * Free VRFB buffers
  75  */
  76 void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout)
  77 {
  78         int j;
  79 
  80         for (j = 0; j < VRFB_NUM_BUFS; j++) {
  81                 if (vout->smsshado_virt_addr[j]) {
  82                         omap_vout_free_buffer(vout->smsshado_virt_addr[j],
  83                                               vout->smsshado_size);
  84                         vout->smsshado_virt_addr[j] = 0;
  85                         vout->smsshado_phy_addr[j] = 0;
  86                 }
  87         }
  88 }
  89 
  90 int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
  91                               bool static_vrfb_allocation)
  92 {
  93         int ret = 0, i, j;
  94         struct omap_vout_device *vout;
  95         struct video_device *vfd;
  96         dma_cap_mask_t mask;
  97         int image_width, image_height;
  98         int vrfb_num_bufs = VRFB_NUM_BUFS;
  99         struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
 100         struct omap2video_device *vid_dev =
 101                 container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
 102 
 103         vout = vid_dev->vouts[vid_num];
 104         vfd = vout->vfd;
 105 
 106         for (i = 0; i < VRFB_NUM_BUFS; i++) {
 107                 if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) {
 108                         dev_info(&pdev->dev, ": VRFB allocation failed\n");
 109                         for (j = 0; j < i; j++)
 110                                 omap_vrfb_release_ctx(&vout->vrfb_context[j]);
 111                         return -ENOMEM;
 112                 }
 113         }
 114 
 115         /* Calculate VRFB memory size */
 116         /* allocate for worst case size */
 117         image_width = VID_MAX_WIDTH / TILE_SIZE;
 118         if (VID_MAX_WIDTH % TILE_SIZE)
 119                 image_width++;
 120 
 121         image_width = image_width * TILE_SIZE;
 122         image_height = VID_MAX_HEIGHT / TILE_SIZE;
 123 
 124         if (VID_MAX_HEIGHT % TILE_SIZE)
 125                 image_height++;
 126 
 127         image_height = image_height * TILE_SIZE;
 128         vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2);
 129 
 130         /*
 131          * Request and Initialize DMA, for DMA based VRFB transfer
 132          */
 133         dma_cap_zero(mask);
 134         dma_cap_set(DMA_INTERLEAVE, mask);
 135         vout->vrfb_dma_tx.chan = dma_request_chan_by_mask(&mask);
 136         if (IS_ERR(vout->vrfb_dma_tx.chan)) {
 137                 vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
 138         } else {
 139                 size_t xt_size = sizeof(struct dma_interleaved_template) +
 140                                  sizeof(struct data_chunk);
 141 
 142                 vout->vrfb_dma_tx.xt = kzalloc(xt_size, GFP_KERNEL);
 143                 if (!vout->vrfb_dma_tx.xt) {
 144                         dma_release_channel(vout->vrfb_dma_tx.chan);
 145                         vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
 146                 }
 147         }
 148 
 149         if (vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED)
 150                 dev_info(&pdev->dev,
 151                          ": failed to allocate DMA Channel for video%d\n",
 152                          vfd->minor);
 153 
 154         init_waitqueue_head(&vout->vrfb_dma_tx.wait);
 155 
 156         /*
 157          * statically allocated the VRFB buffer is done through
 158          * command line arguments
 159          */
 160         if (static_vrfb_allocation) {
 161                 if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) {
 162                         ret =  -ENOMEM;
 163                         goto release_vrfb_ctx;
 164                 }
 165                 vout->vrfb_static_allocation = true;
 166         }
 167         return 0;
 168 
 169 release_vrfb_ctx:
 170         for (j = 0; j < VRFB_NUM_BUFS; j++)
 171                 omap_vrfb_release_ctx(&vout->vrfb_context[j]);
 172         return ret;
 173 }
 174 
 175 /*
 176  * Release the VRFB context once the module exits
 177  */
 178 void omap_vout_release_vrfb(struct omap_vout_device *vout)
 179 {
 180         int i;
 181 
 182         for (i = 0; i < VRFB_NUM_BUFS; i++)
 183                 omap_vrfb_release_ctx(&vout->vrfb_context[i]);
 184 
 185         if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) {
 186                 vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
 187                 kfree(vout->vrfb_dma_tx.xt);
 188                 dmaengine_terminate_sync(vout->vrfb_dma_tx.chan);
 189                 dma_release_channel(vout->vrfb_dma_tx.chan);
 190         }
 191 }
 192 
 193 /*
 194  * Allocate the buffers for the VRFB space.  Data is copied from V4L2
 195  * buffers to the VRFB buffers using the DMA engine.
 196  */
 197 int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
 198                           unsigned int *count, unsigned int startindex)
 199 {
 200         int i;
 201         bool yuv_mode;
 202 
 203         if (!is_rotation_enabled(vout))
 204                 return 0;
 205 
 206         /* If rotation is enabled, allocate memory for VRFB space also */
 207         *count = *count > VRFB_NUM_BUFS ? VRFB_NUM_BUFS : *count;
 208 
 209         /* Allocate the VRFB buffers only if the buffers are not
 210          * allocated during init time.
 211          */
 212         if (!vout->vrfb_static_allocation)
 213                 if (omap_vout_allocate_vrfb_buffers(vout, count, startindex))
 214                         return -ENOMEM;
 215 
 216         if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 ||
 217                         vout->dss_mode == OMAP_DSS_COLOR_UYVY)
 218                 yuv_mode = true;
 219         else
 220                 yuv_mode = false;
 221 
 222         for (i = 0; i < *count; i++)
 223                 omap_vrfb_setup(&vout->vrfb_context[i],
 224                                 vout->smsshado_phy_addr[i], vout->pix.width,
 225                                 vout->pix.height, vout->bpp, yuv_mode);
 226 
 227         return 0;
 228 }
 229 
 230 int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
 231                            struct vb2_buffer *vb)
 232 {
 233         struct dma_async_tx_descriptor *tx;
 234         enum dma_ctrl_flags flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
 235         struct dma_chan *chan = vout->vrfb_dma_tx.chan;
 236         struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt;
 237         dma_cookie_t cookie;
 238         dma_addr_t buf_phy_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
 239         enum dma_status status;
 240         enum dss_rotation rotation;
 241         size_t dst_icg;
 242         u32 pixsize;
 243 
 244         if (!is_rotation_enabled(vout))
 245                 return 0;
 246 
 247         /* If rotation is enabled, copy input buffer into VRFB
 248          * memory space using DMA. We are copying input buffer
 249          * into VRFB memory space of desired angle and DSS will
 250          * read image VRFB memory for 0 degree angle
 251          */
 252 
 253         pixsize = vout->bpp * vout->vrfb_bpp;
 254         dst_icg = MAX_PIXELS_PER_LINE * pixsize - vout->pix.width * vout->bpp;
 255 
 256         xt->src_start = buf_phy_addr;
 257         xt->dst_start = vout->vrfb_context[vb->index].paddr[0];
 258 
 259         xt->numf = vout->pix.height;
 260         xt->frame_size = 1;
 261         xt->sgl[0].size = vout->pix.width * vout->bpp;
 262         xt->sgl[0].icg = dst_icg;
 263 
 264         xt->dir = DMA_MEM_TO_MEM;
 265         xt->src_sgl = false;
 266         xt->src_inc = true;
 267         xt->dst_sgl = true;
 268         xt->dst_inc = true;
 269 
 270         tx = dmaengine_prep_interleaved_dma(chan, xt, flags);
 271         if (tx == NULL) {
 272                 pr_err("%s: DMA interleaved prep error\n", __func__);
 273                 return -EINVAL;
 274         }
 275 
 276         tx->callback = omap_vout_vrfb_dma_tx_callback;
 277         tx->callback_param = &vout->vrfb_dma_tx;
 278 
 279         cookie = dmaengine_submit(tx);
 280         if (dma_submit_error(cookie)) {
 281                 pr_err("%s: dmaengine_submit failed (%d)\n", __func__, cookie);
 282                 return -EINVAL;
 283         }
 284 
 285         vout->vrfb_dma_tx.tx_status = 0;
 286         dma_async_issue_pending(chan);
 287 
 288         wait_event_interruptible_timeout(vout->vrfb_dma_tx.wait,
 289                                          vout->vrfb_dma_tx.tx_status == 1,
 290                                          VRFB_TX_TIMEOUT);
 291 
 292         status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
 293 
 294         if (vout->vrfb_dma_tx.tx_status == 0) {
 295                 pr_err("%s: Timeout while waiting for DMA\n", __func__);
 296                 dmaengine_terminate_sync(chan);
 297                 return -EINVAL;
 298         } else if (status != DMA_COMPLETE) {
 299                 pr_err("%s: DMA completion %s status\n", __func__,
 300                        status == DMA_ERROR ? "error" : "busy");
 301                 dmaengine_terminate_sync(chan);
 302                 return -EINVAL;
 303         }
 304 
 305         /* Store buffers physical address into an array. Addresses
 306          * from this array will be used to configure DSS */
 307         rotation = calc_rotation(vout);
 308         vout->queued_buf_addr[vb->index] = (u8 *)
 309                 vout->vrfb_context[vb->index].paddr[rotation];
 310         return 0;
 311 }
 312 
 313 /*
 314  * Calculate the buffer offsets from which the streaming should
 315  * start. This offset calculation is mainly required because of
 316  * the VRFB 32 pixels alignment with rotation.
 317  */
 318 void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
 319 {
 320         enum dss_rotation rotation;
 321         bool mirroring = vout->mirror;
 322         struct v4l2_rect *crop = &vout->crop;
 323         struct v4l2_pix_format *pix = &vout->pix;
 324         int *cropped_offset = &vout->cropped_offset;
 325         int vr_ps = 1, ps = 2, temp_ps = 2;
 326         int offset = 0, ctop = 0, cleft = 0, line_length = 0;
 327 
 328         rotation = calc_rotation(vout);
 329 
 330         if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
 331                         V4L2_PIX_FMT_UYVY == pix->pixelformat) {
 332                 if (is_rotation_enabled(vout)) {
 333                         /*
 334                          * ps    - Actual pixel size for YUYV/UYVY for
 335                          *         VRFB/Mirroring is 4 bytes
 336                          * vr_ps - Virtually pixel size for YUYV/UYVY is
 337                          *         2 bytes
 338                          */
 339                         ps = 4;
 340                         vr_ps = 2;
 341                 } else {
 342                         ps = 2; /* otherwise the pixel size is 2 byte */
 343                 }
 344         } else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) {
 345                 ps = 4;
 346         } else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) {
 347                 ps = 3;
 348         }
 349         vout->ps = ps;
 350         vout->vr_ps = vr_ps;
 351 
 352         if (is_rotation_enabled(vout)) {
 353                 line_length = MAX_PIXELS_PER_LINE;
 354                 ctop = (pix->height - crop->height) - crop->top;
 355                 cleft = (pix->width - crop->width) - crop->left;
 356         } else {
 357                 line_length = pix->width;
 358         }
 359         vout->line_length = line_length;
 360         switch (rotation) {
 361         case dss_rotation_90_degree:
 362                 offset = vout->vrfb_context[0].yoffset *
 363                         vout->vrfb_context[0].bytespp;
 364                 temp_ps = ps / vr_ps;
 365                 if (!mirroring) {
 366                         *cropped_offset = offset + line_length *
 367                                 temp_ps * cleft + crop->top * temp_ps;
 368                 } else {
 369                         *cropped_offset = offset + line_length * temp_ps *
 370                                 cleft + crop->top * temp_ps + (line_length *
 371                                 ((crop->width / (vr_ps)) - 1) * ps);
 372                 }
 373                 break;
 374         case dss_rotation_180_degree:
 375                 offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset *
 376                         vout->vrfb_context[0].bytespp) +
 377                         (vout->vrfb_context[0].xoffset *
 378                         vout->vrfb_context[0].bytespp));
 379                 if (!mirroring) {
 380                         *cropped_offset = offset + (line_length * ps * ctop) +
 381                                 (cleft / vr_ps) * ps;
 382 
 383                 } else {
 384                         *cropped_offset = offset + (line_length * ps * ctop) +
 385                                 (cleft / vr_ps) * ps + (line_length *
 386                                 (crop->height - 1) * ps);
 387                 }
 388                 break;
 389         case dss_rotation_270_degree:
 390                 offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *
 391                         vout->vrfb_context[0].bytespp;
 392                 temp_ps = ps / vr_ps;
 393                 if (!mirroring) {
 394                         *cropped_offset = offset + line_length *
 395                             temp_ps * crop->left + ctop * ps;
 396                 } else {
 397                         *cropped_offset = offset + line_length *
 398                                 temp_ps * crop->left + ctop * ps +
 399                                 (line_length * ((crop->width / vr_ps) - 1) *
 400                                  ps);
 401                 }
 402                 break;
 403         case dss_rotation_0_degree:
 404                 if (!mirroring) {
 405                         *cropped_offset = (line_length * ps) *
 406                                 crop->top + (crop->left / vr_ps) * ps;
 407                 } else {
 408                         *cropped_offset = (line_length * ps) *
 409                                 crop->top + (crop->left / vr_ps) * ps +
 410                                 (line_length * (crop->height - 1) * ps);
 411                 }
 412                 break;
 413         default:
 414                 *cropped_offset = (line_length * ps * crop->top) /
 415                         vr_ps + (crop->left * ps) / vr_ps +
 416                         ((crop->width / vr_ps) - 1) * ps;
 417                 break;
 418         }
 419 }

/* [<][>][^][v][top][bottom][index][help] */