Merge tag 'v2.6.39-rc7'
[pandora-kernel.git] / drivers / media / video / vivi.c
index c49c393..2238a61 100644 (file)
@@ -7,6 +7,9 @@
  *      John Sokol <sokol--a.t--videotechnology.com>
  *      http://v4l.videotechnology.com/
  *
+ *      Conversion to videobuf2 by Pawel Osciak & Marek Szyprowski
+ *      Copyright (c) 2010 Samsung Electronics
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the BSD Licence, GNU General Public License
  * as published by the Free Software Foundation; either version 2 of the
 #include <linux/mutex.h>
 #include <linux/videodev2.h>
 #include <linux/kthread.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
 #include <linux/freezer.h>
-#endif
-#include <media/videobuf-vmalloc.h>
+#include <media/videobuf2-vmalloc.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
 #include <media/v4l2-common.h>
 
 #define VIVI_MODULE_NAME "vivi"
@@ -42,7 +45,7 @@
 #define MAX_HEIGHT 1200
 
 #define VIVI_MAJOR_VERSION 0
-#define VIVI_MINOR_VERSION 7
+#define VIVI_MINOR_VERSION 8
 #define VIVI_RELEASE 0
 #define VIVI_VERSION \
        KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
@@ -133,16 +136,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f)
        return &formats[k];
 }
 
-struct sg_to_addr {
-       int pos;
-       struct scatterlist *sg;
-};
-
 /* buffer for one video frame */
 struct vivi_buffer {
        /* common v4l buffer stuff -- must be first */
-       struct videobuf_buffer vb;
-
+       struct vb2_buffer       vb;
+       struct list_head        list;
        struct vivi_fmt        *fmt;
 };
 
@@ -162,13 +160,20 @@ static LIST_HEAD(vivi_devlist);
 struct vivi_dev {
        struct list_head           vivi_devlist;
        struct v4l2_device         v4l2_dev;
+       struct v4l2_ctrl_handler   ctrl_handler;
 
        /* controls */
-       int                        brightness;
-       int                        contrast;
-       int                        saturation;
-       int                        hue;
-       int                        volume;
+       struct v4l2_ctrl           *brightness;
+       struct v4l2_ctrl           *contrast;
+       struct v4l2_ctrl           *saturation;
+       struct v4l2_ctrl           *hue;
+       struct v4l2_ctrl           *volume;
+       struct v4l2_ctrl           *button;
+       struct v4l2_ctrl           *boolean;
+       struct v4l2_ctrl           *int32;
+       struct v4l2_ctrl           *int64;
+       struct v4l2_ctrl           *menu;
+       struct v4l2_ctrl           *string;
 
        spinlock_t                 slock;
        struct mutex               mutex;
@@ -181,6 +186,7 @@ struct vivi_dev {
        /* Several counters */
        unsigned                   ms;
        unsigned long              jiffies;
+       unsigned                   button_pressed;
 
        int                        mv_count;    /* Controls bars movement */
 
@@ -190,9 +196,10 @@ struct vivi_dev {
        /* video capture */
        struct vivi_fmt            *fmt;
        unsigned int               width, height;
-       struct videobuf_queue      vb_vidq;
+       struct vb2_queue           vb_vidq;
+       enum v4l2_field            field;
+       unsigned int               field_count;
 
-       unsigned long              generating;
        u8                         bars[9][3];
        u8                         line[MAX_WIDTH * 4];
 };
@@ -443,10 +450,10 @@ static void gen_text(struct vivi_dev *dev, char *basep,
 
 static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
 {
-       int hmax = buf->vb.height;
-       int wmax = buf->vb.width;
+       int wmax = dev->width;
+       int hmax = dev->height;
        struct timeval ts;
-       void *vbuf = videobuf_to_vmalloc(&buf->vb);
+       void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
        unsigned ms;
        char str[100];
        int h, line = 1;
@@ -472,22 +479,38 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
                        dev->width, dev->height, dev->input);
        gen_text(dev, vbuf, line++ * 16, 16, str);
 
+       mutex_lock(&dev->ctrl_handler.lock);
        snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
-                       dev->brightness,
-                       dev->contrast,
-                       dev->saturation,
-                       dev->hue);
+                       dev->brightness->cur.val,
+                       dev->contrast->cur.val,
+                       dev->saturation->cur.val,
+                       dev->hue->cur.val);
        gen_text(dev, vbuf, line++ * 16, 16, str);
-       snprintf(str, sizeof(str), " volume %3d ", dev->volume);
+       snprintf(str, sizeof(str), " volume %3d ", dev->volume->cur.val);
        gen_text(dev, vbuf, line++ * 16, 16, str);
+       snprintf(str, sizeof(str), " int32 %d, int64 %lld ",
+                       dev->int32->cur.val,
+                       dev->int64->cur.val64);
+       gen_text(dev, vbuf, line++ * 16, 16, str);
+       snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
+                       dev->boolean->cur.val,
+                       dev->menu->qmenu[dev->menu->cur.val],
+                       dev->string->cur.string);
+       mutex_unlock(&dev->ctrl_handler.lock);
+       gen_text(dev, vbuf, line++ * 16, 16, str);
+       if (dev->button_pressed) {
+               dev->button_pressed--;
+               snprintf(str, sizeof(str), " button pressed!");
+               gen_text(dev, vbuf, line++ * 16, 16, str);
+       }
 
        dev->mv_count += 2;
 
-       /* Advice that buffer was filled */
-       buf->vb.field_count++;
+       buf->vb.v4l2_buf.field = dev->field;
+       dev->field_count++;
+       buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
        do_gettimeofday(&ts);
-       buf->vb.ts = ts;
-       buf->vb.state = VIDEOBUF_DONE;
+       buf->vb.v4l2_buf.timestamp = ts;
 }
 
 static void vivi_thread_tick(struct vivi_dev *dev)
@@ -504,23 +527,17 @@ static void vivi_thread_tick(struct vivi_dev *dev)
                goto unlock;
        }
 
-       buf = list_entry(dma_q->active.next,
-                        struct vivi_buffer, vb.queue);
+       buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
+       list_del(&buf->list);
 
-       /* Nobody is waiting on this buffer, return */
-       if (!waitqueue_active(&buf->vb.done))
-               goto unlock;
-
-       list_del(&buf->vb.queue);
-
-       do_gettimeofday(&buf->vb.ts);
+       do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
 
        /* Fill buffer */
        vivi_fillbuff(dev, buf);
        dprintk(dev, 1, "filled buffer %p\n", buf);
 
-       wake_up(&buf->vb.done);
-       dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i);
+       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+       dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
 unlock:
        spin_unlock_irqrestore(&dev->slock, flags);
 }
@@ -571,17 +588,12 @@ static int vivi_thread(void *data)
        return 0;
 }
 
-static void vivi_start_generating(struct file *file)
+static int vivi_start_generating(struct vivi_dev *dev)
 {
-       struct vivi_dev *dev = video_drvdata(file);
        struct vivi_dmaqueue *dma_q = &dev->vidq;
 
        dprintk(dev, 1, "%s\n", __func__);
 
-       if (test_and_set_bit(0, &dev->generating))
-               return;
-       file->private_data = dev;
-
        /* Resets frame counters */
        dev->ms = 0;
        dev->mv_count = 0;
@@ -593,146 +605,200 @@ static void vivi_start_generating(struct file *file)
 
        if (IS_ERR(dma_q->kthread)) {
                v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
-               clear_bit(0, &dev->generating);
-               return;
+               return PTR_ERR(dma_q->kthread);
        }
        /* Wakes thread */
        wake_up_interruptible(&dma_q->wq);
 
        dprintk(dev, 1, "returning from %s\n", __func__);
+       return 0;
 }
 
-static void vivi_stop_generating(struct file *file)
+static void vivi_stop_generating(struct vivi_dev *dev)
 {
-       struct vivi_dev *dev = video_drvdata(file);
        struct vivi_dmaqueue *dma_q = &dev->vidq;
 
        dprintk(dev, 1, "%s\n", __func__);
 
-       if (!file->private_data)
-               return;
-       if (!test_and_clear_bit(0, &dev->generating))
-               return;
-
        /* shutdown control thread */
        if (dma_q->kthread) {
                kthread_stop(dma_q->kthread);
                dma_q->kthread = NULL;
        }
-       videobuf_stop(&dev->vb_vidq);
-       videobuf_mmap_free(&dev->vb_vidq);
-}
 
-static int vivi_is_generating(struct vivi_dev *dev)
-{
-       return test_bit(0, &dev->generating);
+       /*
+        * Typical driver might need to wait here until dma engine stops.
+        * In this case we can abort imiedetly, so it's just a noop.
+        */
+
+       /* Release all active buffers */
+       while (!list_empty(&dma_q->active)) {
+               struct vivi_buffer *buf;
+               buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
+               list_del(&buf->list);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+               dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+       }
 }
-
 /* ------------------------------------------------------------------
        Videobuf operations
    ------------------------------------------------------------------*/
-static int
-buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+                               unsigned int *nplanes, unsigned long sizes[],
+                               void *alloc_ctxs[])
 {
-       struct vivi_dev *dev = vq->priv_data;
+       struct vivi_dev *dev = vb2_get_drv_priv(vq);
+       unsigned long size;
+
+       size = dev->width * dev->height * 2;
+
+       if (0 == *nbuffers)
+               *nbuffers = 32;
 
-       *size = dev->width * dev->height * 2;
+       while (size * *nbuffers > vid_limit * 1024 * 1024)
+               (*nbuffers)--;
 
-       if (0 == *count)
-               *count = 32;
+       *nplanes = 1;
 
-       while (*size * *count > vid_limit * 1024 * 1024)
-               (*count)--;
+       sizes[0] = size;
 
-       dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__,
-               *count, *size);
+       /*
+        * videobuf2-vmalloc allocator is context-less so no need to set
+        * alloc_ctxs array.
+        */
+
+       dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
+               *nbuffers, size);
 
        return 0;
 }
 
-static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf)
+static int buffer_init(struct vb2_buffer *vb)
 {
-       struct vivi_dev *dev = vq->priv_data;
+       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+       BUG_ON(NULL == dev->fmt);
 
-       dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state);
+       /*
+        * This callback is called once per buffer, after its allocation.
+        *
+        * Vivi does not allow changing format during streaming, but it is
+        * possible to do so when streaming is paused (i.e. in streamoff state).
+        * Buffers however are not freed when going into streamoff and so
+        * buffer size verification has to be done in buffer_prepare, on each
+        * qbuf.
+        * It would be best to move verification code here to buf_init and
+        * s_fmt though.
+        */
 
-       videobuf_vmalloc_free(&buf->vb);
-       dprintk(dev, 1, "free_buffer: freed\n");
-       buf->vb.state = VIDEOBUF_NEEDS_INIT;
+       return 0;
 }
 
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
-                                               enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
 {
-       struct vivi_dev *dev = vq->priv_data;
+       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
        struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
-       int rc;
+       unsigned long size;
 
-       dprintk(dev, 1, "%s, field=%d\n", __func__, field);
+       dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field);
 
        BUG_ON(NULL == dev->fmt);
 
+       /*
+        * Theses properties only change when queue is idle, see s_fmt.
+        * The below checks should not be performed here, on each
+        * buffer_prepare (i.e. on each qbuf). Most of the code in this function
+        * should thus be moved to buffer_init and s_fmt.
+        */
        if (dev->width  < 48 || dev->width  > MAX_WIDTH ||
            dev->height < 32 || dev->height > MAX_HEIGHT)
                return -EINVAL;
 
-       buf->vb.size = dev->width * dev->height * 2;
-       if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+       size = dev->width * dev->height * 2;
+       if (vb2_plane_size(vb, 0) < size) {
+               dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
+                               __func__, vb2_plane_size(vb, 0), size);
                return -EINVAL;
+       }
 
-       /* These properties only change when queue is idle, see s_fmt */
-       buf->fmt       = dev->fmt;
-       buf->vb.width  = dev->width;
-       buf->vb.height = dev->height;
-       buf->vb.field  = field;
+       vb2_set_plane_payload(&buf->vb, 0, size);
+
+       buf->fmt = dev->fmt;
 
        precalculate_bars(dev);
        precalculate_line(dev);
 
-       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-               rc = videobuf_iolock(vq, &buf->vb, NULL);
-               if (rc < 0)
-                       goto fail;
-       }
+       return 0;
+}
 
-       buf->vb.state = VIDEOBUF_PREPARED;
+static int buffer_finish(struct vb2_buffer *vb)
+{
+       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       dprintk(dev, 1, "%s\n", __func__);
        return 0;
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+       dprintk(dev, 1, "%s\n", __func__);
 
-fail:
-       free_buffer(vq, buf);
-       return rc;
 }
 
-static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+static void buffer_queue(struct vb2_buffer *vb)
 {
-       struct vivi_dev *dev = vq->priv_data;
+       struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
        struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
        struct vivi_dmaqueue *vidq = &dev->vidq;
+       unsigned long flags = 0;
 
        dprintk(dev, 1, "%s\n", __func__);
 
-       buf->vb.state = VIDEOBUF_QUEUED;
-       list_add_tail(&buf->vb.queue, &vidq->active);
+       spin_lock_irqsave(&dev->slock, flags);
+       list_add_tail(&buf->list, &vidq->active);
+       spin_unlock_irqrestore(&dev->slock, flags);
 }
 
-static void buffer_release(struct videobuf_queue *vq,
-                          struct videobuf_buffer *vb)
+static int start_streaming(struct vb2_queue *vq)
 {
-       struct vivi_dev *dev = vq->priv_data;
-       struct vivi_buffer *buf  = container_of(vb, struct vivi_buffer, vb);
+       struct vivi_dev *dev = vb2_get_drv_priv(vq);
+       dprintk(dev, 1, "%s\n", __func__);
+       return vivi_start_generating(dev);
+}
 
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
+{
+       struct vivi_dev *dev = vb2_get_drv_priv(vq);
        dprintk(dev, 1, "%s\n", __func__);
+       vivi_stop_generating(dev);
+       return 0;
+}
 
-       free_buffer(vq, buf);
+static void vivi_lock(struct vb2_queue *vq)
+{
+       struct vivi_dev *dev = vb2_get_drv_priv(vq);
+       mutex_lock(&dev->mutex);
 }
 
-static struct videobuf_queue_ops vivi_video_qops = {
-       .buf_setup      = buffer_setup,
-       .buf_prepare    = buffer_prepare,
-       .buf_queue      = buffer_queue,
-       .buf_release    = buffer_release,
+static void vivi_unlock(struct vb2_queue *vq)
+{
+       struct vivi_dev *dev = vb2_get_drv_priv(vq);
+       mutex_unlock(&dev->mutex);
+}
+
+
+static struct vb2_ops vivi_video_qops = {
+       .queue_setup            = queue_setup,
+       .buf_init               = buffer_init,
+       .buf_prepare            = buffer_prepare,
+       .buf_finish             = buffer_finish,
+       .buf_cleanup            = buffer_cleanup,
+       .buf_queue              = buffer_queue,
+       .start_streaming        = start_streaming,
+       .stop_streaming         = stop_streaming,
+       .wait_prepare           = vivi_unlock,
+       .wait_finish            = vivi_lock,
 };
 
 /* ------------------------------------------------------------------
@@ -774,7 +840,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 
        f->fmt.pix.width        = dev->width;
        f->fmt.pix.height       = dev->height;
-       f->fmt.pix.field        = dev->vb_vidq.field;
+       f->fmt.pix.field        = dev->field;
        f->fmt.pix.pixelformat  = dev->fmt->fourcc;
        f->fmt.pix.bytesperline =
                (f->fmt.pix.width * dev->fmt->depth) >> 3;
@@ -820,82 +886,60 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
        struct vivi_dev *dev = video_drvdata(file);
+       struct vb2_queue *q = &dev->vb_vidq;
 
        int ret = vidioc_try_fmt_vid_cap(file, priv, f);
        if (ret < 0)
                return ret;
 
-       if (vivi_is_generating(dev)) {
+       if (vb2_is_streaming(q)) {
                dprintk(dev, 1, "%s device busy\n", __func__);
-               ret = -EBUSY;
-               goto out;
+               return -EBUSY;
        }
 
        dev->fmt = get_format(f);
        dev->width = f->fmt.pix.width;
        dev->height = f->fmt.pix.height;
-       dev->vb_vidq.field = f->fmt.pix.field;
-       ret = 0;
-out:
-       return ret;
+       dev->field = f->fmt.pix.field;
+
+       return 0;
 }
 
 static int vidioc_reqbufs(struct file *file, void *priv,
                          struct v4l2_requestbuffers *p)
 {
        struct vivi_dev *dev = video_drvdata(file);
-
-       return videobuf_reqbufs(&dev->vb_vidq, p);
+       return vb2_reqbufs(&dev->vb_vidq, p);
 }
 
 static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
        struct vivi_dev *dev = video_drvdata(file);
-
-       return videobuf_querybuf(&dev->vb_vidq, p);
+       return vb2_querybuf(&dev->vb_vidq, p);
 }
 
 static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
        struct vivi_dev *dev = video_drvdata(file);
-
-       return videobuf_qbuf(&dev->vb_vidq, p);
+       return vb2_qbuf(&dev->vb_vidq, p);
 }
 
 static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
        struct vivi_dev *dev = video_drvdata(file);
-
-       return videobuf_dqbuf(&dev->vb_vidq, p,
-                               file->f_flags & O_NONBLOCK);
+       return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
 }
 
 static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
 {
        struct vivi_dev *dev = video_drvdata(file);
-       int ret;
-
-       if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-       ret = videobuf_streamon(&dev->vb_vidq);
-       if (ret)
-               return ret;
-
-       vivi_start_generating(file);
-       return 0;
+       return vb2_streamon(&dev->vb_vidq, i);
 }
 
 static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
 {
        struct vivi_dev *dev = video_drvdata(file);
-       int ret;
-
-       if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-       ret = videobuf_streamoff(&dev->vb_vidq);
-       if (!ret)
-               vivi_stop_generating(file);
-       return ret;
+       return vb2_streamoff(&dev->vb_vidq, i);
 }
 
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
@@ -938,80 +982,14 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
 }
 
 /* --- controls ---------------------------------------------- */
-static int vidioc_queryctrl(struct file *file, void *priv,
-                           struct v4l2_queryctrl *qc)
-{
-       switch (qc->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 200);
-       case V4L2_CID_BRIGHTNESS:
-               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127);
-       case V4L2_CID_CONTRAST:
-               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 16);
-       case V4L2_CID_SATURATION:
-               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127);
-       case V4L2_CID_HUE:
-               return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
-       }
-       return -EINVAL;
-}
 
-static int vidioc_g_ctrl(struct file *file, void *priv,
-                        struct v4l2_control *ctrl)
+static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct vivi_dev *dev = video_drvdata(file);
+       struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
 
-       switch (ctrl->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               ctrl->value = dev->volume;
-               return 0;
-       case V4L2_CID_BRIGHTNESS:
-               ctrl->value = dev->brightness;
-               return 0;
-       case V4L2_CID_CONTRAST:
-               ctrl->value = dev->contrast;
-               return 0;
-       case V4L2_CID_SATURATION:
-               ctrl->value = dev->saturation;
-               return 0;
-       case V4L2_CID_HUE:
-               ctrl->value = dev->hue;
-               return 0;
-       }
-       return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-                               struct v4l2_control *ctrl)
-{
-       struct vivi_dev *dev = video_drvdata(file);
-       struct v4l2_queryctrl qc;
-       int err;
-
-       qc.id = ctrl->id;
-       err = vidioc_queryctrl(file, priv, &qc);
-       if (err < 0)
-               return err;
-       if (ctrl->value < qc.minimum || ctrl->value > qc.maximum)
-               return -ERANGE;
-       switch (ctrl->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               dev->volume = ctrl->value;
-               return 0;
-       case V4L2_CID_BRIGHTNESS:
-               dev->brightness = ctrl->value;
-               return 0;
-       case V4L2_CID_CONTRAST:
-               dev->contrast = ctrl->value;
-               return 0;
-       case V4L2_CID_SATURATION:
-               dev->saturation = ctrl->value;
-               return 0;
-       case V4L2_CID_HUE:
-               dev->hue = ctrl->value;
-               return 0;
-       }
-       return -EINVAL;
+       if (ctrl == dev->button)
+               dev->button_pressed = 30;
+       return 0;
 }
 
 /* ------------------------------------------------------------------
@@ -1023,21 +1001,19 @@ vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 {
        struct vivi_dev *dev = video_drvdata(file);
 
-       vivi_start_generating(file);
-       return videobuf_read_stream(&dev->vb_vidq, data, count, ppos, 0,
-                                       file->f_flags & O_NONBLOCK);
+       dprintk(dev, 1, "read called\n");
+       return vb2_read(&dev->vb_vidq, data, count, ppos,
+                      file->f_flags & O_NONBLOCK);
 }
 
 static unsigned int
 vivi_poll(struct file *file, struct poll_table_struct *wait)
 {
        struct vivi_dev *dev = video_drvdata(file);
-       struct videobuf_queue *q = &dev->vb_vidq;
+       struct vb2_queue *q = &dev->vb_vidq;
 
        dprintk(dev, 1, "%s\n", __func__);
-
-       vivi_start_generating(file);
-       return videobuf_poll_stream(file, q, wait);
+       return vb2_poll(q, file, wait);
 }
 
 static int vivi_close(struct file *file)
@@ -1045,11 +1021,12 @@ static int vivi_close(struct file *file)
        struct video_device  *vdev = video_devdata(file);
        struct vivi_dev *dev = video_drvdata(file);
 
-       vivi_stop_generating(file);
+       dprintk(dev, 1, "close called (dev=%s), file %p\n",
+               video_device_node_name(vdev), file);
 
-       dprintk(dev, 1, "close called (dev=%s)\n",
-               video_device_node_name(vdev));
-       return 0;
+       if (v4l2_fh_is_singular_file(file))
+               vb2_queue_release(&dev->vb_vidq);
+       return v4l2_fh_release(file);
 }
 
 static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
@@ -1059,8 +1036,7 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
 
        dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
 
-       ret = videobuf_mmap_mapper(&dev->vb_vidq, vma);
-
+       ret = vb2_mmap(&dev->vb_vidq, vma);
        dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
                (unsigned long)vma->vm_start,
                (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
@@ -1068,8 +1044,82 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
        return ret;
 }
 
+static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
+       .s_ctrl = vivi_s_ctrl,
+};
+
+#define VIVI_CID_CUSTOM_BASE   (V4L2_CID_USER_BASE | 0xf000)
+
+static const struct v4l2_ctrl_config vivi_ctrl_button = {
+       .ops = &vivi_ctrl_ops,
+       .id = VIVI_CID_CUSTOM_BASE + 0,
+       .name = "Button",
+       .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_boolean = {
+       .ops = &vivi_ctrl_ops,
+       .id = VIVI_CID_CUSTOM_BASE + 1,
+       .name = "Boolean",
+       .type = V4L2_CTRL_TYPE_BOOLEAN,
+       .min = 0,
+       .max = 1,
+       .step = 1,
+       .def = 1,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_int32 = {
+       .ops = &vivi_ctrl_ops,
+       .id = VIVI_CID_CUSTOM_BASE + 2,
+       .name = "Integer 32 Bits",
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .min = 0x80000000,
+       .max = 0x7fffffff,
+       .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_int64 = {
+       .ops = &vivi_ctrl_ops,
+       .id = VIVI_CID_CUSTOM_BASE + 3,
+       .name = "Integer 64 Bits",
+       .type = V4L2_CTRL_TYPE_INTEGER64,
+};
+
+static const char * const vivi_ctrl_menu_strings[] = {
+       "Menu Item 0 (Skipped)",
+       "Menu Item 1",
+       "Menu Item 2 (Skipped)",
+       "Menu Item 3",
+       "Menu Item 4",
+       "Menu Item 5 (Skipped)",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_menu = {
+       .ops = &vivi_ctrl_ops,
+       .id = VIVI_CID_CUSTOM_BASE + 4,
+       .name = "Menu",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .min = 1,
+       .max = 4,
+       .def = 3,
+       .menu_skip_mask = 0x04,
+       .qmenu = vivi_ctrl_menu_strings,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_string = {
+       .ops = &vivi_ctrl_ops,
+       .id = VIVI_CID_CUSTOM_BASE + 5,
+       .name = "String",
+       .type = V4L2_CTRL_TYPE_STRING,
+       .min = 2,
+       .max = 4,
+       .step = 1,
+};
+
 static const struct v4l2_file_operations vivi_fops = {
        .owner          = THIS_MODULE,
+       .open           = v4l2_fh_open,
        .release        = vivi_close,
        .read           = vivi_read,
        .poll           = vivi_poll,
@@ -1093,9 +1143,6 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
        .vidioc_s_input       = vidioc_s_input,
        .vidioc_streamon      = vidioc_streamon,
        .vidioc_streamoff     = vidioc_streamoff,
-       .vidioc_queryctrl     = vidioc_queryctrl,
-       .vidioc_g_ctrl        = vidioc_g_ctrl,
-       .vidioc_s_ctrl        = vidioc_s_ctrl,
 };
 
 static struct video_device vivi_template = {
@@ -1126,6 +1173,7 @@ static int vivi_release(void)
                        video_device_node_name(dev->vfd));
                video_unregister_device(dev->vfd);
                v4l2_device_unregister(&dev->v4l2_dev);
+               v4l2_ctrl_handler_free(&dev->ctrl_handler);
                kfree(dev);
        }
 
@@ -1136,6 +1184,8 @@ static int __init vivi_create_instance(int inst)
 {
        struct vivi_dev *dev;
        struct video_device *vfd;
+       struct v4l2_ctrl_handler *hdl;
+       struct vb2_queue *q;
        int ret;
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -1151,20 +1201,46 @@ static int __init vivi_create_instance(int inst)
        dev->fmt = &formats[0];
        dev->width = 640;
        dev->height = 480;
-       dev->volume = 200;
-       dev->brightness = 127;
-       dev->contrast = 16;
-       dev->saturation = 127;
-       dev->hue = 0;
+       hdl = &dev->ctrl_handler;
+       v4l2_ctrl_handler_init(hdl, 11);
+       dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+                       V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
+       dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+       dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 16);
+       dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 127);
+       dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+                       V4L2_CID_HUE, -128, 127, 1, 0);
+       dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
+       dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
+       dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
+       dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
+       dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
+       dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
+       if (hdl->error) {
+               ret = hdl->error;
+               goto unreg_dev;
+       }
+       dev->v4l2_dev.ctrl_handler = hdl;
 
        /* initialize locks */
        spin_lock_init(&dev->slock);
-       mutex_init(&dev->mutex);
 
-       videobuf_queue_vmalloc_init(&dev->vb_vidq, &vivi_video_qops,
-                       NULL, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                       V4L2_FIELD_INTERLACED,
-                       sizeof(struct vivi_buffer), dev, &dev->mutex);
+       /* initialize queue */
+       q = &dev->vb_vidq;
+       memset(q, 0, sizeof(dev->vb_vidq));
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+       q->drv_priv = dev;
+       q->buf_struct_size = sizeof(struct vivi_buffer);
+       q->ops = &vivi_video_qops;
+       q->mem_ops = &vb2_vmalloc_memops;
+
+       vb2_queue_init(q);
+
+       mutex_init(&dev->mutex);
 
        /* init video dma queues */
        INIT_LIST_HEAD(&dev->vidq.active);
@@ -1178,6 +1254,12 @@ static int __init vivi_create_instance(int inst)
        *vfd = vivi_template;
        vfd->debug = debug;
        vfd->v4l2_dev = &dev->v4l2_dev;
+       set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+
+       /*
+        * Provide a mutex to v4l2 core. It will be used to protect
+        * all fops and v4l2 ioctls.
+        */
        vfd->lock = &dev->mutex;
 
        ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
@@ -1200,6 +1282,7 @@ static int __init vivi_create_instance(int inst)
 rel_vdev:
        video_device_release(vfd);
 unreg_dev:
+       v4l2_ctrl_handler_free(hdl);
        v4l2_device_unregister(&dev->v4l2_dev);
 free_dev:
        kfree(dev);