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