These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / media / platform / sh_vou.c
index dde1ccc..2231f89 100644 (file)
@@ -27,7 +27,8 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-mediabus.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
 
 /* Mirror addresses are not available for all registers */
 #define VOUER  0
@@ -57,31 +58,41 @@ enum sh_vou_status {
        SH_VOU_RUNNING,
 };
 
+#define VOU_MIN_IMAGE_WIDTH    16
 #define VOU_MAX_IMAGE_WIDTH    720
-#define VOU_MAX_IMAGE_HEIGHT   576
+#define VOU_MIN_IMAGE_HEIGHT   16
+
+struct sh_vou_buffer {
+       struct vb2_v4l2_buffer vb;
+       struct list_head list;
+};
+
+static inline struct
+sh_vou_buffer *to_sh_vou_buffer(struct vb2_v4l2_buffer *vb2)
+{
+       return container_of(vb2, struct sh_vou_buffer, vb);
+}
 
 struct sh_vou_device {
        struct v4l2_device v4l2_dev;
        struct video_device vdev;
-       atomic_t use_count;
        struct sh_vou_pdata *pdata;
        spinlock_t lock;
        void __iomem *base;
        /* State information */
        struct v4l2_pix_format pix;
        struct v4l2_rect rect;
-       struct list_head queue;
+       struct list_head buf_list;
        v4l2_std_id std;
        int pix_idx;
-       struct videobuf_buffer *active;
+       struct vb2_queue queue;
+       struct vb2_alloc_ctx *alloc_ctx;
+       struct sh_vou_buffer *active;
        enum sh_vou_status status;
+       unsigned sequence;
        struct mutex fop_lock;
 };
 
-struct sh_vou_file {
-       struct videobuf_queue vbq;
-};
-
 /* Register access routines for sides A, B and mirror addresses */
 static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg,
                               u32 value)
@@ -133,6 +144,7 @@ struct sh_vou_fmt {
        u32             pfmt;
        char            *desc;
        unsigned char   bpp;
+       unsigned char   bpl;
        unsigned char   rgb;
        unsigned char   yf;
        unsigned char   pkf;
@@ -143,6 +155,7 @@ static struct sh_vou_fmt vou_fmt[] = {
        {
                .pfmt   = V4L2_PIX_FMT_NV12,
                .bpp    = 12,
+               .bpl    = 1,
                .desc   = "YVU420 planar",
                .yf     = 0,
                .rgb    = 0,
@@ -150,6 +163,7 @@ static struct sh_vou_fmt vou_fmt[] = {
        {
                .pfmt   = V4L2_PIX_FMT_NV16,
                .bpp    = 16,
+               .bpl    = 1,
                .desc   = "YVYU planar",
                .yf     = 1,
                .rgb    = 0,
@@ -157,6 +171,7 @@ static struct sh_vou_fmt vou_fmt[] = {
        {
                .pfmt   = V4L2_PIX_FMT_RGB24,
                .bpp    = 24,
+               .bpl    = 3,
                .desc   = "RGB24",
                .pkf    = 2,
                .rgb    = 1,
@@ -164,6 +179,7 @@ static struct sh_vou_fmt vou_fmt[] = {
        {
                .pfmt   = V4L2_PIX_FMT_RGB565,
                .bpp    = 16,
+               .bpl    = 2,
                .desc   = "RGB565",
                .pkf    = 3,
                .rgb    = 1,
@@ -171,6 +187,7 @@ static struct sh_vou_fmt vou_fmt[] = {
        {
                .pfmt   = V4L2_PIX_FMT_RGB565X,
                .bpp    = 16,
+               .bpl    = 2,
                .desc   = "RGB565 byteswapped",
                .pkf    = 3,
                .rgb    = 1,
@@ -178,11 +195,11 @@ static struct sh_vou_fmt vou_fmt[] = {
 };
 
 static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,
-                                struct videobuf_buffer *vb)
+                                struct vb2_v4l2_buffer *vbuf)
 {
        dma_addr_t addr1, addr2;
 
-       addr1 = videobuf_to_dma_contig(vb);
+       addr1 = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
        switch (vou_dev->pix.pixelformat) {
        case V4L2_PIX_FMT_NV12:
        case V4L2_PIX_FMT_NV16:
@@ -196,8 +213,7 @@ static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,
        sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2);
 }
 
-static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
-                               struct videobuf_buffer *vb)
+static void sh_vou_stream_config(struct sh_vou_device *vou_dev)
 {
        unsigned int row_coeff;
 #ifdef __LITTLE_ENDIAN
@@ -224,167 +240,139 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
 
        sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap);
        sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff);
-       sh_vou_schedule_next(vou_dev, vb);
-}
-
-static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
-       BUG_ON(in_interrupt());
-
-       /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */
-       videobuf_waiton(vq, vb, 0, 0);
-       videobuf_dma_contig_free(vq, vb);
-       vb->state = VIDEOBUF_NEEDS_INIT;
 }
 
 /* Locking: caller holds fop_lock mutex */
-static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count,
-                           unsigned int *size)
+static int sh_vou_queue_setup(struct vb2_queue *vq, const void *parg,
+                      unsigned int *nbuffers, unsigned int *nplanes,
+                      unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct video_device *vdev = vq->priv_data;
-       struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
-       *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width *
-               vou_dev->pix.height / 8;
-
-       if (*count < 2)
-               *count = 2;
-
-       /* Taking into account maximum frame size, *count will stay >= 2 */
-       if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024)
-               *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size);
+       const struct v4l2_format *fmt = parg;
+       struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);
+       struct v4l2_pix_format *pix = &vou_dev->pix;
+       int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
 
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s(): count=%d, size=%d\n", __func__,
-               *count, *size);
+       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
 
+       if (fmt && fmt->fmt.pix.sizeimage < pix->height * bytes_per_line)
+               return -EINVAL;
+       *nplanes = 1;
+       sizes[0] = fmt ? fmt->fmt.pix.sizeimage : pix->height * bytes_per_line;
+       alloc_ctxs[0] = vou_dev->alloc_ctx;
        return 0;
 }
 
-/* Locking: caller holds fop_lock mutex */
-static int sh_vou_buf_prepare(struct videobuf_queue *vq,
-                             struct videobuf_buffer *vb,
-                             enum v4l2_field field)
+static int sh_vou_buf_prepare(struct vb2_buffer *vb)
 {
-       struct video_device *vdev = vq->priv_data;
-       struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+       struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue);
        struct v4l2_pix_format *pix = &vou_dev->pix;
-       int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
-       int ret;
+       unsigned bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
+       unsigned size = pix->height * bytes_per_line;
 
        dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
 
-       if (vb->width   != pix->width ||
-           vb->height  != pix->height ||
-           vb->field   != pix->field) {
-               vb->width       = pix->width;
-               vb->height      = pix->height;
-               vb->field       = field;
-               if (vb->state != VIDEOBUF_NEEDS_INIT)
-                       free_buffer(vq, vb);
-       }
-
-       vb->size = vb->height * bytes_per_line;
-       if (vb->baddr && vb->bsize < vb->size) {
+       if (vb2_plane_size(vb, 0) < size) {
                /* User buffer too small */
-               dev_warn(vq->dev, "User buffer too small: [%zu] @ %lx\n",
-                        vb->bsize, vb->baddr);
+               dev_warn(vou_dev->v4l2_dev.dev, "buffer too small (%lu < %u)\n",
+                        vb2_plane_size(vb, 0), size);
                return -EINVAL;
        }
 
-       if (vb->state == VIDEOBUF_NEEDS_INIT) {
-               ret = videobuf_iolock(vq, vb, NULL);
-               if (ret < 0) {
-                       dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n",
-                                vb->memory, ret);
-                       return ret;
-               }
-               vb->state = VIDEOBUF_PREPARED;
-       }
-
-       dev_dbg(vou_dev->v4l2_dev.dev,
-               "%s(): fmt #%d, %u bytes per line, phys %pad, type %d, state %d\n",
-               __func__, vou_dev->pix_idx, bytes_per_line,
-               ({ dma_addr_t addr = videobuf_to_dma_contig(vb); &addr; }),
-               vb->memory, vb->state);
-
+       vb2_set_plane_payload(vb, 0, size);
        return 0;
 }
 
 /* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */
-static void sh_vou_buf_queue(struct videobuf_queue *vq,
-                            struct videobuf_buffer *vb)
+static void sh_vou_buf_queue(struct vb2_buffer *vb)
 {
-       struct video_device *vdev = vq->priv_data;
-       struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+       struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+       struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct sh_vou_buffer *shbuf = to_sh_vou_buffer(vbuf);
+       unsigned long flags;
 
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+       spin_lock_irqsave(&vou_dev->lock, flags);
+       list_add_tail(&shbuf->list, &vou_dev->buf_list);
+       spin_unlock_irqrestore(&vou_dev->lock, flags);
+}
+
+static int sh_vou_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+       struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);
+       struct sh_vou_buffer *buf, *node;
+       int ret;
 
-       vb->state = VIDEOBUF_QUEUED;
-       list_add_tail(&vb->queue, &vou_dev->queue);
-
-       if (vou_dev->status == SH_VOU_RUNNING) {
-               return;
-       } else if (!vou_dev->active) {
-               vou_dev->active = vb;
-               /* Start from side A: we use mirror addresses, so, set B */
-               sh_vou_reg_a_write(vou_dev, VOURPR, 1);
-               dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n",
-                       __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));
-               sh_vou_schedule_next(vou_dev, vb);
-               /* Only activate VOU after the second buffer */
-       } else if (vou_dev->active->queue.next == &vb->queue) {
-               /* Second buffer - initialise register side B */
-               sh_vou_reg_a_write(vou_dev, VOURPR, 0);
-               sh_vou_stream_start(vou_dev, vb);
-
-               /* Register side switching with frame VSYNC */
-               sh_vou_reg_a_write(vou_dev, VOURCR, 5);
-               dev_dbg(vou_dev->v4l2_dev.dev, "%s: second buffer status 0x%x\n",
-                       __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));
-
-               /* Enable End-of-Frame (VSYNC) interrupts */
-               sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
-               /* Two buffers on the queue - activate the hardware */
-
-               vou_dev->status = SH_VOU_RUNNING;
-               sh_vou_reg_a_write(vou_dev, VOUER, 0x107);
+       vou_dev->sequence = 0;
+       ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
+                                        video, s_stream, 1);
+       if (ret < 0 && ret != -ENOIOCTLCMD) {
+               list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) {
+                       vb2_buffer_done(&buf->vb.vb2_buf,
+                                       VB2_BUF_STATE_QUEUED);
+                       list_del(&buf->list);
+               }
+               vou_dev->active = NULL;
+               return ret;
        }
+
+       buf = list_entry(vou_dev->buf_list.next, struct sh_vou_buffer, list);
+
+       vou_dev->active = buf;
+
+       /* Start from side A: we use mirror addresses, so, set B */
+       sh_vou_reg_a_write(vou_dev, VOURPR, 1);
+       dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n",
+               __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));
+       sh_vou_schedule_next(vou_dev, &buf->vb);
+
+       buf = list_entry(buf->list.next, struct sh_vou_buffer, list);
+
+       /* Second buffer - initialise register side B */
+       sh_vou_reg_a_write(vou_dev, VOURPR, 0);
+       sh_vou_schedule_next(vou_dev, &buf->vb);
+
+       /* Register side switching with frame VSYNC */
+       sh_vou_reg_a_write(vou_dev, VOURCR, 5);
+
+       sh_vou_stream_config(vou_dev);
+       /* Enable End-of-Frame (VSYNC) interrupts */
+       sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
+
+       /* Two buffers on the queue - activate the hardware */
+       vou_dev->status = SH_VOU_RUNNING;
+       sh_vou_reg_a_write(vou_dev, VOUER, 0x107);
+       return 0;
 }
 
-static void sh_vou_buf_release(struct videobuf_queue *vq,
-                              struct videobuf_buffer *vb)
+static void sh_vou_stop_streaming(struct vb2_queue *vq)
 {
-       struct video_device *vdev = vq->priv_data;
-       struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+       struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);
+       struct sh_vou_buffer *buf, *node;
        unsigned long flags;
 
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
+       v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
+                                        video, s_stream, 0);
+       /* disable output */
+       sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
+       /* ...but the current frame will complete */
+       sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
+       msleep(50);
        spin_lock_irqsave(&vou_dev->lock, flags);
-
-       if (vou_dev->active == vb) {
-               /* disable output */
-               sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
-               /* ...but the current frame will complete */
-               sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
-               vou_dev->active = NULL;
+       list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) {
+               vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+               list_del(&buf->list);
        }
-
-       if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) {
-               vb->state = VIDEOBUF_ERROR;
-               list_del(&vb->queue);
-       }
-
+       vou_dev->active = NULL;
        spin_unlock_irqrestore(&vou_dev->lock, flags);
-
-       free_buffer(vq, vb);
 }
 
-static struct videobuf_queue_ops sh_vou_video_qops = {
-       .buf_setup      = sh_vou_buf_setup,
-       .buf_prepare    = sh_vou_buf_prepare,
-       .buf_queue      = sh_vou_buf_queue,
-       .buf_release    = sh_vou_buf_release,
+static struct vb2_ops sh_vou_qops = {
+       .queue_setup            = sh_vou_queue_setup,
+       .buf_prepare            = sh_vou_buf_prepare,
+       .buf_queue              = sh_vou_buf_queue,
+       .start_streaming        = sh_vou_start_streaming,
+       .stop_streaming         = sh_vou_stop_streaming,
+       .wait_prepare           = vb2_ops_wait_prepare,
+       .wait_finish            = vb2_ops_wait_finish,
 };
 
 /* Video IOCTLs */
@@ -396,7 +384,10 @@ static int sh_vou_querycap(struct file *file, void  *priv,
        dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
 
        strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
-       cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+       strlcpy(cap->driver, "sh-vou", sizeof(cap->driver));
+       strlcpy(cap->bus_info, "platform:sh-vou", sizeof(cap->bus_info));
+       cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE |
+                          V4L2_CAP_STREAMING;
        cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
        return 0;
 }
@@ -540,8 +531,10 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std)
                img_height_max = 576;
 
        /* Image width must be a multiple of 4 */
-       v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2,
-                             &geo->in_height, 0, img_height_max, 1, 0);
+       v4l_bound_align_image(&geo->in_width,
+                             VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2,
+                             &geo->in_height,
+                             VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);
 
        /* Select scales to come as close as possible to the output image */
        for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) {
@@ -600,7 +593,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
 {
        unsigned int best_err = UINT_MAX, best = geo->in_width,
                width_max, height_max, img_height_max;
-       int i, idx = 0;
+       int i, idx_h = 0, idx_v = 0;
 
        if (std & V4L2_STD_525_60) {
                width_max = 858;
@@ -625,7 +618,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
                err = abs(found - geo->output.width);
                if (err < best_err) {
                        best_err = err;
-                       idx = i;
+                       idx_h = i;
                        best = found;
                }
                if (!err)
@@ -633,12 +626,12 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
        }
 
        geo->output.width = best;
-       geo->scale_idx_h = idx;
+       geo->scale_idx_h = idx_h;
        if (geo->output.left + best > width_max)
                geo->output.left = width_max - best;
 
        pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width,
-                vou_scale_h_num[idx], vou_scale_h_den[idx], best);
+                vou_scale_h_num[idx_h], vou_scale_h_den[idx_h], best);
 
        best_err = UINT_MAX;
 
@@ -655,7 +648,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
                err = abs(found - geo->output.height);
                if (err < best_err) {
                        best_err = err;
-                       idx = i;
+                       idx_v = i;
                        best = found;
                }
                if (!err)
@@ -663,40 +656,27 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
        }
 
        geo->output.height = best;
-       geo->scale_idx_v = idx;
+       geo->scale_idx_v = idx_v;
        if (geo->output.top + best > height_max)
                geo->output.top = height_max - best;
 
        pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height,
-                vou_scale_v_num[idx], vou_scale_v_den[idx], best);
+                vou_scale_v_num[idx_v], vou_scale_v_den[idx_v], best);
 }
 
-static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
-                               struct v4l2_format *fmt)
+static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
+                                 struct v4l2_format *fmt)
 {
        struct sh_vou_device *vou_dev = video_drvdata(file);
        struct v4l2_pix_format *pix = &fmt->fmt.pix;
        unsigned int img_height_max;
        int pix_idx;
-       struct sh_vou_geometry geo;
-       struct v4l2_mbus_framefmt mbfmt = {
-               /* Revisit: is this the correct code? */
-               .code = MEDIA_BUS_FMT_YUYV8_2X8,
-               .field = V4L2_FIELD_INTERLACED,
-               .colorspace = V4L2_COLORSPACE_SMPTE170M,
-       };
-       int ret;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
-               vou_dev->rect.width, vou_dev->rect.height,
-               pix->width, pix->height);
 
-       if (pix->field == V4L2_FIELD_ANY)
-               pix->field = V4L2_FIELD_NONE;
+       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
 
-       if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
-           pix->field != V4L2_FIELD_NONE)
-               return -EINVAL;
+       pix->field = V4L2_FIELD_INTERLACED;
+       pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+       pix->ycbcr_enc = pix->quantization = 0;
 
        for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)
                if (vou_fmt[pix_idx].pfmt == pix->pixelformat)
@@ -710,9 +690,38 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
        else
                img_height_max = 576;
 
-       /* Image width must be a multiple of 4 */
-       v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2,
-                             &pix->height, 0, img_height_max, 1, 0);
+       v4l_bound_align_image(&pix->width,
+                             VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2,
+                             &pix->height,
+                             VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);
+       pix->bytesperline = pix->width * vou_fmt[pix_idx].bpl;
+       pix->sizeimage = pix->height * ((pix->width * vou_fmt[pix_idx].bpp) >> 3);
+
+       return 0;
+}
+
+static int sh_vou_set_fmt_vid_out(struct sh_vou_device *vou_dev,
+                               struct v4l2_pix_format *pix)
+{
+       unsigned int img_height_max;
+       struct sh_vou_geometry geo;
+       struct v4l2_subdev_format format = {
+               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+               /* Revisit: is this the correct code? */
+               .format.code = MEDIA_BUS_FMT_YUYV8_2X8,
+               .format.field = V4L2_FIELD_INTERLACED,
+               .format.colorspace = V4L2_COLORSPACE_SMPTE170M,
+       };
+       struct v4l2_mbus_framefmt *mbfmt = &format.format;
+       int pix_idx;
+       int ret;
+
+       if (vb2_is_busy(&vou_dev->queue))
+               return -EBUSY;
+
+       for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)
+               if (vou_fmt[pix_idx].pfmt == pix->pixelformat)
+                       break;
 
        geo.in_width = pix->width;
        geo.in_height = pix->height;
@@ -720,27 +729,32 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
 
        vou_adjust_output(&geo, vou_dev->std);
 
-       mbfmt.width = geo.output.width;
-       mbfmt.height = geo.output.height;
-       ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
-                                        s_mbus_fmt, &mbfmt);
+       mbfmt->width = geo.output.width;
+       mbfmt->height = geo.output.height;
+       ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad,
+                                        set_fmt, NULL, &format);
        /* Must be implemented, so, don't check for -ENOIOCTLCMD */
        if (ret < 0)
                return ret;
 
        dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
-               geo.output.width, geo.output.height, mbfmt.width, mbfmt.height);
+               geo.output.width, geo.output.height, mbfmt->width, mbfmt->height);
+
+       if (vou_dev->std & V4L2_STD_525_60)
+               img_height_max = 480;
+       else
+               img_height_max = 576;
 
        /* Sanity checks */
-       if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
-           (unsigned)mbfmt.height > img_height_max ||
-           mbfmt.code != MEDIA_BUS_FMT_YUYV8_2X8)
+       if ((unsigned)mbfmt->width > VOU_MAX_IMAGE_WIDTH ||
+           (unsigned)mbfmt->height > img_height_max ||
+           mbfmt->code != MEDIA_BUS_FMT_YUYV8_2X8)
                return -EIO;
 
-       if (mbfmt.width != geo.output.width ||
-           mbfmt.height != geo.output.height) {
-               geo.output.width = mbfmt.width;
-               geo.output.height = mbfmt.height;
+       if (mbfmt->width != geo.output.width ||
+           mbfmt->height != geo.output.height) {
+               geo.output.width = mbfmt->width;
+               geo.output.height = mbfmt->height;
 
                vou_adjust_input(&geo, vou_dev->std);
        }
@@ -763,109 +777,39 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
        return 0;
 }
 
-static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
-                                 struct v4l2_format *fmt)
+static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
+                               struct v4l2_format *fmt)
 {
        struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-       int i;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-       pix->field = V4L2_FIELD_NONE;
+       int ret = sh_vou_try_fmt_vid_out(file, priv, fmt);
 
-       v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
-                             &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
-
-       for (i = 0; i < ARRAY_SIZE(vou_fmt); i++)
-               if (vou_fmt[i].pfmt == pix->pixelformat)
-                       return 0;
-
-       pix->pixelformat = vou_fmt[0].pfmt;
-
-       return 0;
+       if (ret)
+               return ret;
+       return sh_vou_set_fmt_vid_out(vou_dev, &fmt->fmt.pix);
 }
 
-static int sh_vou_reqbufs(struct file *file, void *priv,
-                         struct v4l2_requestbuffers *req)
+static int sh_vou_enum_output(struct file *file, void *fh,
+                             struct v4l2_output *a)
 {
        struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = priv;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
 
-       if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+       if (a->index)
                return -EINVAL;
-
-       return videobuf_reqbufs(&vou_file->vbq, req);
-}
-
-static int sh_vou_querybuf(struct file *file, void *priv,
-                          struct v4l2_buffer *b)
-{
-       struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = priv;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       return videobuf_querybuf(&vou_file->vbq, b);
+       strlcpy(a->name, "Video Out", sizeof(a->name));
+       a->type = V4L2_OUTPUT_TYPE_ANALOG;
+       a->std = vou_dev->vdev.tvnorms;
+       return 0;
 }
 
-static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+static int sh_vou_g_output(struct file *file, void *fh, unsigned int *i)
 {
-       struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = priv;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       return videobuf_qbuf(&vou_file->vbq, b);
+       *i = 0;
+       return 0;
 }
 
-static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+static int sh_vou_s_output(struct file *file, void *fh, unsigned int i)
 {
-       struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = priv;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK);
-}
-
-static int sh_vou_streamon(struct file *file, void *priv,
-                          enum v4l2_buf_type buftype)
-{
-       struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = priv;
-       int ret;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
-                                        video, s_stream, 1);
-       if (ret < 0 && ret != -ENOIOCTLCMD)
-               return ret;
-
-       /* This calls our .buf_queue() (== sh_vou_buf_queue) */
-       return videobuf_streamon(&vou_file->vbq);
-}
-
-static int sh_vou_streamoff(struct file *file, void *priv,
-                           enum v4l2_buf_type buftype)
-{
-       struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = priv;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       /*
-        * This calls buf_release from host driver's videobuf_queue_ops for all
-        * remaining buffers. When the last buffer is freed, stop streaming
-        */
-       videobuf_streamoff(&vou_file->vbq);
-       v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0);
-
-       return 0;
+       return i ? -EINVAL : 0;
 }
 
 static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)
@@ -890,8 +834,11 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id)
 
        dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id);
 
-       if (std_id & ~vou_dev->vdev.tvnorms)
-               return -EINVAL;
+       if (std_id == vou_dev->std)
+               return 0;
+
+       if (vb2_is_busy(&vou_dev->queue))
+               return -EBUSY;
 
        ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
                                         s_std_output, std_id);
@@ -899,13 +846,25 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id)
        if (ret < 0 && ret != -ENOIOCTLCMD)
                return ret;
 
-       if (std_id & V4L2_STD_525_60)
+       vou_dev->rect.top = vou_dev->rect.left = 0;
+       vou_dev->rect.width = VOU_MAX_IMAGE_WIDTH;
+       if (std_id & V4L2_STD_525_60) {
                sh_vou_reg_ab_set(vou_dev, VOUCR,
                        sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29);
-       else
+               vou_dev->rect.height = 480;
+       } else {
                sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29);
+               vou_dev->rect.height = 576;
+       }
 
+       vou_dev->pix.width = vou_dev->rect.width;
+       vou_dev->pix.height = vou_dev->rect.height;
+       vou_dev->pix.bytesperline =
+               vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpl;
+       vou_dev->pix.sizeimage = vou_dev->pix.height *
+               ((vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpp) >> 3);
        vou_dev->std = std_id;
+       sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix);
 
        return 0;
 }
@@ -921,49 +880,95 @@ static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)
        return 0;
 }
 
-static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
+static int sh_vou_log_status(struct file *file, void *priv)
 {
        struct sh_vou_device *vou_dev = video_drvdata(file);
 
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+       pr_info("VOUER:   0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUER));
+       pr_info("VOUCR:   0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUCR));
+       pr_info("VOUSTR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSTR));
+       pr_info("VOUVCR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVCR));
+       pr_info("VOUISR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUISR));
+       pr_info("VOUBCR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUBCR));
+       pr_info("VOUDPR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDPR));
+       pr_info("VOUDSR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDSR));
+       pr_info("VOUVPR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVPR));
+       pr_info("VOUIR:   0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUIR));
+       pr_info("VOUSRR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSRR));
+       pr_info("VOUMSR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUMSR));
+       pr_info("VOUHIR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUHIR));
+       pr_info("VOUDFR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDFR));
+       pr_info("VOUAD1R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD1R));
+       pr_info("VOUAD2R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD2R));
+       pr_info("VOUAIR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAIR));
+       pr_info("VOUSWR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSWR));
+       pr_info("VOURCR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURCR));
+       pr_info("VOURPR:  0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURPR));
+       return 0;
+}
 
-       a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-       a->c = vou_dev->rect;
+static int sh_vou_g_selection(struct file *file, void *fh,
+                             struct v4l2_selection *sel)
+{
+       struct sh_vou_device *vou_dev = video_drvdata(file);
 
+       if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+       switch (sel->target) {
+       case V4L2_SEL_TGT_COMPOSE:
+               sel->r = vou_dev->rect;
+               break;
+       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+               sel->r.left = 0;
+               sel->r.top = 0;
+               sel->r.width = VOU_MAX_IMAGE_WIDTH;
+               if (vou_dev->std & V4L2_STD_525_60)
+                       sel->r.height = 480;
+               else
+                       sel->r.height = 576;
+               break;
+       default:
+               return -EINVAL;
+       }
        return 0;
 }
 
 /* Assume a dull encoder, do all the work ourselves. */
-static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
+static int sh_vou_s_selection(struct file *file, void *fh,
+                             struct v4l2_selection *sel)
 {
-       struct v4l2_crop a_writable = *a;
+       struct v4l2_rect *rect = &sel->r;
        struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct v4l2_rect *rect = &a_writable.c;
        struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT};
        struct v4l2_pix_format *pix = &vou_dev->pix;
        struct sh_vou_geometry geo;
-       struct v4l2_mbus_framefmt mbfmt = {
+       struct v4l2_subdev_format format = {
+               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
                /* Revisit: is this the correct code? */
-               .code = MEDIA_BUS_FMT_YUYV8_2X8,
-               .field = V4L2_FIELD_INTERLACED,
-               .colorspace = V4L2_COLORSPACE_SMPTE170M,
+               .format.code = MEDIA_BUS_FMT_YUYV8_2X8,
+               .format.field = V4L2_FIELD_INTERLACED,
+               .format.colorspace = V4L2_COLORSPACE_SMPTE170M,
        };
        unsigned int img_height_max;
        int ret;
 
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__,
-               rect->width, rect->height, rect->left, rect->top);
-
-       if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+       if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+           sel->target != V4L2_SEL_TGT_COMPOSE)
                return -EINVAL;
 
+       if (vb2_is_busy(&vou_dev->queue))
+               return -EBUSY;
+
        if (vou_dev->std & V4L2_STD_525_60)
                img_height_max = 480;
        else
                img_height_max = 576;
 
-       v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
-                             &rect->height, 0, img_height_max, 1, 0);
+       v4l_bound_align_image(&rect->width,
+                             VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 1,
+                             &rect->height,
+                             VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);
 
        if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH)
                rect->left = VOU_MAX_IMAGE_WIDTH - rect->width;
@@ -984,22 +989,22 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
         */
        v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
                                   s_crop, &sd_crop);
-       mbfmt.width = geo.output.width;
-       mbfmt.height = geo.output.height;
-       ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
-                                        s_mbus_fmt, &mbfmt);
+       format.format.width = geo.output.width;
+       format.format.height = geo.output.height;
+       ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad,
+                                        set_fmt, NULL, &format);
        /* Must be implemented, so, don't check for -ENOIOCTLCMD */
        if (ret < 0)
                return ret;
 
        /* Sanity checks */
-       if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
-           (unsigned)mbfmt.height > img_height_max ||
-           mbfmt.code != MEDIA_BUS_FMT_YUYV8_2X8)
+       if ((unsigned)format.format.width > VOU_MAX_IMAGE_WIDTH ||
+           (unsigned)format.format.height > img_height_max ||
+           format.format.code != MEDIA_BUS_FMT_YUYV8_2X8)
                return -EIO;
 
-       geo.output.width = mbfmt.width;
-       geo.output.height = mbfmt.height;
+       geo.output.width = format.format.width;
+       geo.output.height = format.format.height;
 
        /*
         * No down-scaling. According to the API, current call has precedence:
@@ -1018,41 +1023,11 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
        return 0;
 }
 
-/*
- * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle
- * is the initial register values, height takes the interlaced format into
- * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can
- * actually only meaningfully contain values <= 720 and <= 240 respectively, and
- * not <= 864 and <= 312.
- */
-static int sh_vou_cropcap(struct file *file, void *priv,
-                         struct v4l2_cropcap *a)
-{
-       struct sh_vou_device *vou_dev = video_drvdata(file);
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       a->type                         = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-       a->bounds.left                  = 0;
-       a->bounds.top                   = 0;
-       a->bounds.width                 = VOU_MAX_IMAGE_WIDTH;
-       a->bounds.height                = VOU_MAX_IMAGE_HEIGHT;
-       /* Default = max, set VOUDPR = 0, which is not hardware default */
-       a->defrect.left                 = 0;
-       a->defrect.top                  = 0;
-       a->defrect.width                = VOU_MAX_IMAGE_WIDTH;
-       a->defrect.height               = VOU_MAX_IMAGE_HEIGHT;
-       a->pixelaspect.numerator        = 1;
-       a->pixelaspect.denominator      = 1;
-
-       return 0;
-}
-
 static irqreturn_t sh_vou_isr(int irq, void *dev_id)
 {
        struct sh_vou_device *vou_dev = dev_id;
        static unsigned long j;
-       struct videobuf_buffer *vb;
+       struct sh_vou_buffer *vb;
        static int cnt;
        u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;
        u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR);
@@ -1065,7 +1040,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)
        }
 
        spin_lock(&vou_dev->lock);
-       if (!vou_dev->active || list_empty(&vou_dev->queue)) {
+       if (!vou_dev->active || list_empty(&vou_dev->buf_list)) {
                if (printk_timed_ratelimit(&j, 500))
                        dev_warn(vou_dev->v4l2_dev.dev,
                                 "IRQ without active buffer: %x!\n", irq_status);
@@ -1087,33 +1062,30 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)
        sh_vou_reg_a_write(vou_dev, VOUIR, masked);
 
        vb = vou_dev->active;
-       list_del(&vb->queue);
-
-       vb->state = VIDEOBUF_DONE;
-       v4l2_get_timestamp(&vb->ts);
-       vb->field_count++;
-       wake_up(&vb->done);
-
-       if (list_empty(&vou_dev->queue)) {
-               /* Stop VOU */
-               dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n",
-                       __func__, cnt);
-               sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
-               vou_dev->active = NULL;
-               vou_dev->status = SH_VOU_INITIALISING;
-               /* Disable End-of-Frame (VSYNC) interrupts */
-               sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
+       if (list_is_singular(&vb->list)) {
+               /* Keep cycling while no next buffer is available */
+               sh_vou_schedule_next(vou_dev, &vb->vb);
                spin_unlock(&vou_dev->lock);
                return IRQ_HANDLED;
        }
 
-       vou_dev->active = list_entry(vou_dev->queue.next,
-                                    struct videobuf_buffer, queue);
+       list_del(&vb->list);
+
+       v4l2_get_timestamp(&vb->vb.timestamp);
+       vb->vb.sequence = vou_dev->sequence++;
+       vb->vb.field = V4L2_FIELD_INTERLACED;
+       vb2_buffer_done(&vb->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+       vou_dev->active = list_entry(vou_dev->buf_list.next,
+                                    struct sh_vou_buffer, list);
 
-       if (vou_dev->active->queue.next != &vou_dev->queue) {
-               struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next,
-                                               struct videobuf_buffer, queue);
-               sh_vou_schedule_next(vou_dev, new);
+       if (list_is_singular(&vou_dev->buf_list)) {
+               /* Keep cycling while no next buffer is available */
+               sh_vou_schedule_next(vou_dev, &vou_dev->active->vb);
+       } else {
+               struct sh_vou_buffer *new = list_entry(vou_dev->active->list.next,
+                                               struct sh_vou_buffer, list);
+               sh_vou_schedule_next(vou_dev, &new->vb);
        }
 
        spin_unlock(&vou_dev->lock);
@@ -1153,6 +1125,8 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
        /* Default - fixed HSYNC length, can be made configurable is required */
        sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000);
 
+       sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix);
+
        return 0;
 }
 
@@ -1160,96 +1134,47 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
 static int sh_vou_open(struct file *file)
 {
        struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file),
-                                              GFP_KERNEL);
-
-       if (!vou_file)
-               return -ENOMEM;
+       int err;
 
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       if (mutex_lock_interruptible(&vou_dev->fop_lock)) {
-               kfree(vou_file);
+       if (mutex_lock_interruptible(&vou_dev->fop_lock))
                return -ERESTARTSYS;
-       }
-       if (atomic_inc_return(&vou_dev->use_count) == 1) {
-               int ret;
+
+       err = v4l2_fh_open(file);
+       if (err)
+               goto done_open;
+       if (v4l2_fh_is_singular_file(file) &&
+           vou_dev->status == SH_VOU_INITIALISING) {
                /* First open */
-               vou_dev->status = SH_VOU_INITIALISING;
                pm_runtime_get_sync(vou_dev->v4l2_dev.dev);
-               ret = sh_vou_hw_init(vou_dev);
-               if (ret < 0) {
-                       atomic_dec(&vou_dev->use_count);
+               err = sh_vou_hw_init(vou_dev);
+               if (err < 0) {
                        pm_runtime_put(vou_dev->v4l2_dev.dev);
+                       v4l2_fh_release(file);
+               } else {
                        vou_dev->status = SH_VOU_IDLE;
-                       mutex_unlock(&vou_dev->fop_lock);
-                       kfree(vou_file);
-                       return ret;
                }
        }
-
-       videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops,
-                                      vou_dev->v4l2_dev.dev, &vou_dev->lock,
-                                      V4L2_BUF_TYPE_VIDEO_OUTPUT,
-                                      V4L2_FIELD_NONE,
-                                      sizeof(struct videobuf_buffer),
-                                      &vou_dev->vdev, &vou_dev->fop_lock);
+done_open:
        mutex_unlock(&vou_dev->fop_lock);
-
-       file->private_data = vou_file;
-
-       return 0;
+       return err;
 }
 
 static int sh_vou_release(struct file *file)
 {
        struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = file->private_data;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+       bool is_last;
 
-       if (!atomic_dec_return(&vou_dev->use_count)) {
-               mutex_lock(&vou_dev->fop_lock);
+       mutex_lock(&vou_dev->fop_lock);
+       is_last = v4l2_fh_is_singular_file(file);
+       _vb2_fop_release(file, NULL);
+       if (is_last) {
                /* Last close */
-               vou_dev->status = SH_VOU_IDLE;
+               vou_dev->status = SH_VOU_INITIALISING;
                sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101);
                pm_runtime_put(vou_dev->v4l2_dev.dev);
-               mutex_unlock(&vou_dev->fop_lock);
        }
-
-       file->private_data = NULL;
-       kfree(vou_file);
-
-       return 0;
-}
-
-static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = file->private_data;
-       int ret;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       if (mutex_lock_interruptible(&vou_dev->fop_lock))
-               return -ERESTARTSYS;
-       ret = videobuf_mmap_mapper(&vou_file->vbq, vma);
-       mutex_unlock(&vou_dev->fop_lock);
-       return ret;
-}
-
-static unsigned int sh_vou_poll(struct file *file, poll_table *wait)
-{
-       struct sh_vou_device *vou_dev = video_drvdata(file);
-       struct sh_vou_file *vou_file = file->private_data;
-       unsigned int res;
-
-       dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
-       mutex_lock(&vou_dev->fop_lock);
-       res = videobuf_poll_stream(file, &vou_file->vbq, wait);
        mutex_unlock(&vou_dev->fop_lock);
-       return res;
+       return 0;
 }
 
 /* sh_vou display ioctl operations */
@@ -1259,17 +1184,23 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
        .vidioc_g_fmt_vid_out           = sh_vou_g_fmt_vid_out,
        .vidioc_s_fmt_vid_out           = sh_vou_s_fmt_vid_out,
        .vidioc_try_fmt_vid_out         = sh_vou_try_fmt_vid_out,
-       .vidioc_reqbufs                 = sh_vou_reqbufs,
-       .vidioc_querybuf                = sh_vou_querybuf,
-       .vidioc_qbuf                    = sh_vou_qbuf,
-       .vidioc_dqbuf                   = sh_vou_dqbuf,
-       .vidioc_streamon                = sh_vou_streamon,
-       .vidioc_streamoff               = sh_vou_streamoff,
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs             = vb2_ioctl_create_bufs,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
+       .vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
+       .vidioc_expbuf                  = vb2_ioctl_expbuf,
+       .vidioc_g_output                = sh_vou_g_output,
+       .vidioc_s_output                = sh_vou_s_output,
+       .vidioc_enum_output             = sh_vou_enum_output,
        .vidioc_s_std                   = sh_vou_s_std,
        .vidioc_g_std                   = sh_vou_g_std,
-       .vidioc_cropcap                 = sh_vou_cropcap,
-       .vidioc_g_crop                  = sh_vou_g_crop,
-       .vidioc_s_crop                  = sh_vou_s_crop,
+       .vidioc_g_selection             = sh_vou_g_selection,
+       .vidioc_s_selection             = sh_vou_s_selection,
+       .vidioc_log_status              = sh_vou_log_status,
 };
 
 static const struct v4l2_file_operations sh_vou_fops = {
@@ -1277,8 +1208,9 @@ static const struct v4l2_file_operations sh_vou_fops = {
        .open           = sh_vou_open,
        .release        = sh_vou_release,
        .unlocked_ioctl = video_ioctl2,
-       .mmap           = sh_vou_mmap,
-       .poll           = sh_vou_poll,
+       .mmap           = vb2_fop_mmap,
+       .poll           = vb2_fop_poll,
+       .write          = vb2_fop_write,
 };
 
 static const struct video_device sh_vou_video_template = {
@@ -1297,8 +1229,9 @@ static int sh_vou_probe(struct platform_device *pdev)
        struct i2c_adapter *i2c_adap;
        struct video_device *vdev;
        struct sh_vou_device *vou_dev;
-       struct resource *reg_res, *region;
+       struct resource *reg_res;
        struct v4l2_subdev *subdev;
+       struct vb2_queue *q;
        int irq, ret;
 
        reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1309,16 +1242,16 @@ static int sh_vou_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL);
+       vou_dev = devm_kzalloc(&pdev->dev, sizeof(*vou_dev), GFP_KERNEL);
        if (!vou_dev)
                return -ENOMEM;
 
-       INIT_LIST_HEAD(&vou_dev->queue);
+       INIT_LIST_HEAD(&vou_dev->buf_list);
        spin_lock_init(&vou_dev->lock);
        mutex_init(&vou_dev->fop_lock);
-       atomic_set(&vou_dev->use_count, 0);
        vou_dev->pdata = vou_pdata;
-       vou_dev->status = SH_VOU_IDLE;
+       vou_dev->status = SH_VOU_INITIALISING;
+       vou_dev->pix_idx = 1;
 
        rect = &vou_dev->rect;
        pix = &vou_dev->pix;
@@ -1331,34 +1264,24 @@ static int sh_vou_probe(struct platform_device *pdev)
        rect->height            = 480;
        pix->width              = VOU_MAX_IMAGE_WIDTH;
        pix->height             = 480;
-       pix->pixelformat        = V4L2_PIX_FMT_YVYU;
-       pix->field              = V4L2_FIELD_NONE;
-       pix->bytesperline       = VOU_MAX_IMAGE_WIDTH * 2;
+       pix->pixelformat        = V4L2_PIX_FMT_NV16;
+       pix->field              = V4L2_FIELD_INTERLACED;
+       pix->bytesperline       = VOU_MAX_IMAGE_WIDTH;
        pix->sizeimage          = VOU_MAX_IMAGE_WIDTH * 2 * 480;
        pix->colorspace         = V4L2_COLORSPACE_SMPTE170M;
 
-       region = request_mem_region(reg_res->start, resource_size(reg_res),
-                                   pdev->name);
-       if (!region) {
-               dev_err(&pdev->dev, "VOU region already claimed\n");
-               ret = -EBUSY;
-               goto ereqmemreg;
-       }
+       vou_dev->base = devm_ioremap_resource(&pdev->dev, reg_res);
+       if (IS_ERR(vou_dev->base))
+               return PTR_ERR(vou_dev->base);
 
-       vou_dev->base = ioremap(reg_res->start, resource_size(reg_res));
-       if (!vou_dev->base) {
-               ret = -ENOMEM;
-               goto emap;
-       }
-
-       ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev);
+       ret = devm_request_irq(&pdev->dev, irq, sh_vou_isr, 0, "vou", vou_dev);
        if (ret < 0)
-               goto ereqirq;
+               return ret;
 
        ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "Error registering v4l2 device\n");
-               goto ev4l2devreg;
+               return ret;
        }
 
        vdev = &vou_dev->vdev;
@@ -1371,6 +1294,30 @@ static int sh_vou_probe(struct platform_device *pdev)
 
        video_set_drvdata(vdev, vou_dev);
 
+       /* Initialize the vb2 queue */
+       q = &vou_dev->queue;
+       q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+       q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE;
+       q->drv_priv = vou_dev;
+       q->buf_struct_size = sizeof(struct sh_vou_buffer);
+       q->ops = &sh_vou_qops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->min_buffers_needed = 2;
+       q->lock = &vou_dev->fop_lock;
+       ret = vb2_queue_init(q);
+       if (ret)
+               goto einitctx;
+
+       vou_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+       if (IS_ERR(vou_dev->alloc_ctx)) {
+               dev_err(&pdev->dev, "Can't allocate buffer context");
+               ret = PTR_ERR(vou_dev->alloc_ctx);
+               goto einitctx;
+       }
+       vdev->queue = q;
+       INIT_LIST_HEAD(&vou_dev->buf_list);
+
        pm_runtime_enable(&pdev->dev);
        pm_runtime_resume(&pdev->dev);
 
@@ -1402,41 +1349,27 @@ ei2cnd:
 ereset:
        i2c_put_adapter(i2c_adap);
 ei2cgadap:
+       vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx);
+einitctx:
        pm_runtime_disable(&pdev->dev);
        v4l2_device_unregister(&vou_dev->v4l2_dev);
-ev4l2devreg:
-       free_irq(irq, vou_dev);
-ereqirq:
-       iounmap(vou_dev->base);
-emap:
-       release_mem_region(reg_res->start, resource_size(reg_res));
-ereqmemreg:
-       kfree(vou_dev);
        return ret;
 }
 
 static int sh_vou_remove(struct platform_device *pdev)
 {
-       int irq = platform_get_irq(pdev, 0);
        struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
        struct sh_vou_device *vou_dev = container_of(v4l2_dev,
                                                struct sh_vou_device, v4l2_dev);
        struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,
                                            struct v4l2_subdev, list);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct resource *reg_res;
 
-       if (irq > 0)
-               free_irq(irq, vou_dev);
        pm_runtime_disable(&pdev->dev);
        video_unregister_device(&vou_dev->vdev);
        i2c_put_adapter(client->adapter);
+       vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx);
        v4l2_device_unregister(&vou_dev->v4l2_dev);
-       iounmap(vou_dev->base);
-       reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (reg_res)
-               release_mem_region(reg_res->start, resource_size(reg_res));
-       kfree(vou_dev);
        return 0;
 }