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