V4L/DVB: vivi and mem2mem_testdev need slab.h to build
[safe/jmp/linux-2.6] / drivers / media / video / vivi.c
index 65c8af1..e17b6fe 100644 (file)
  * License, or (at your option) any later version
  */
 #include <linux/module.h>
-#include <linux/delay.h>
 #include <linux/errno.h>
-#include <linux/fs.h>
 #include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/sched.h>
-#include <linux/pci.h>
-#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/font.h>
 #include <linux/version.h>
 #include <linux/mutex.h>
 #include <linux/videodev2.h>
-#include <linux/dma-mapping.h>
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-/* Include V4L1 specific functions. Should be removed soon */
-#include <linux/videodev.h>
+#include <linux/kthread.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+#include <linux/freezer.h>
 #endif
-#include <linux/interrupt.h>
 #include <media/videobuf-vmalloc.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
-#include <linux/kthread.h>
-#include <linux/highmem.h>
-#include <linux/freezer.h>
+#include <media/v4l2-common.h>
 
 #define VIVI_MODULE_NAME "vivi"
 
 #define WAKE_DENOMINATOR 1001
 #define BUFFER_TIMEOUT     msecs_to_jiffies(500)  /* 0.5 seconds */
 
-#include "font.h"
+#define MAX_WIDTH 1920
+#define MAX_HEIGHT 1200
 
 #define VIVI_MAJOR_VERSION 0
-#define VIVI_MINOR_VERSION 5
+#define VIVI_MINOR_VERSION 7
 #define VIVI_RELEASE 0
 #define VIVI_VERSION \
        KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
 
-/* Declare static vars that will be used as parameters */
-static unsigned int vid_limit = 16;    /* Video memory limit, in Mb */
-static int video_nr = -1;              /* /dev/videoN, -1 for autodetect */
-static int n_devs = 1;                 /* Number of virtual devices */
+MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
+MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
+MODULE_LICENSE("Dual BSD/GPL");
+
+static unsigned video_nr = -1;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
 
-/* supported controls */
-static struct v4l2_queryctrl vivi_qctrl[] = {
-       {
-               .id            = V4L2_CID_AUDIO_VOLUME,
-               .name          = "Volume",
-               .minimum       = 0,
-               .maximum       = 65535,
-               .step          = 65535/100,
-               .default_value = 65535,
-               .flags         = 0,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-       }, {
-               .id            = V4L2_CID_BRIGHTNESS,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Brightness",
-               .minimum       = 0,
-               .maximum       = 255,
-               .step          = 1,
-               .default_value = 127,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_CONTRAST,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Contrast",
-               .minimum       = 0,
-               .maximum       = 255,
-               .step          = 0x1,
-               .default_value = 0x10,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_SATURATION,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Saturation",
-               .minimum       = 0,
-               .maximum       = 255,
-               .step          = 0x1,
-               .default_value = 127,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_HUE,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Hue",
-               .minimum       = -128,
-               .maximum       = 127,
-               .step          = 0x1,
-               .default_value = 0,
-               .flags         = 0,
-       }
-};
+static unsigned n_devs = 1;
+module_param(n_devs, uint, 0644);
+MODULE_PARM_DESC(n_devs, "number of video devices to create");
+
+static unsigned debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, uint, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
 
-static int qctl_regs[ARRAY_SIZE(vivi_qctrl)];
+/* Global font descriptor */
+static const u8 *font8x16;
 
-#define dprintk(dev, level, fmt, arg...)                               \
-       do {                                                            \
-               if (dev->vfd->debug >= (level))                         \
-                       printk(KERN_DEBUG "vivi: " fmt , ## arg);       \
-       } while (0)
+#define dprintk(dev, level, fmt, arg...) \
+       v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
 
 /* ------------------------------------------------------------------
        Basic structures
@@ -128,12 +83,56 @@ struct vivi_fmt {
        int   depth;
 };
 
-static struct vivi_fmt format = {
-       .name     = "4:2:2, packed, YUYV",
-       .fourcc   = V4L2_PIX_FMT_YUYV,
-       .depth    = 16,
+static struct vivi_fmt formats[] = {
+       {
+               .name     = "4:2:2, packed, YUYV",
+               .fourcc   = V4L2_PIX_FMT_YUYV,
+               .depth    = 16,
+       },
+       {
+               .name     = "4:2:2, packed, UYVY",
+               .fourcc   = V4L2_PIX_FMT_UYVY,
+               .depth    = 16,
+       },
+       {
+               .name     = "RGB565 (LE)",
+               .fourcc   = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+               .depth    = 16,
+       },
+       {
+               .name     = "RGB565 (BE)",
+               .fourcc   = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+               .depth    = 16,
+       },
+       {
+               .name     = "RGB555 (LE)",
+               .fourcc   = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+               .depth    = 16,
+       },
+       {
+               .name     = "RGB555 (BE)",
+               .fourcc   = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+               .depth    = 16,
+       },
 };
 
+static struct vivi_fmt *get_format(struct v4l2_format *f)
+{
+       struct vivi_fmt *fmt;
+       unsigned int k;
+
+       for (k = 0; k < ARRAY_SIZE(formats); k++) {
+               fmt = &formats[k];
+               if (fmt->fourcc == f->fmt.pix.pixelformat)
+                       break;
+       }
+
+       if (k == ARRAY_SIZE(formats))
+               return NULL;
+
+       return &formats[k];
+}
+
 struct sg_to_addr {
        int pos;
        struct scatterlist *sg;
@@ -162,34 +161,40 @@ static LIST_HEAD(vivi_devlist);
 
 struct vivi_dev {
        struct list_head           vivi_devlist;
+       struct v4l2_device         v4l2_dev;
+
+       /* controls */
+       int                        brightness;
+       int                        contrast;
+       int                        saturation;
+       int                        hue;
+       int                        volume;
 
        spinlock_t                 slock;
        struct mutex               mutex;
 
-       int                        users;
-
        /* various device info */
        struct video_device        *vfd;
 
        struct vivi_dmaqueue       vidq;
 
        /* Several counters */
-       int                        h, m, s, ms;
+       unsigned                   ms;
        unsigned long              jiffies;
-       char                       timestr[13];
 
        int                        mv_count;    /* Controls bars movement */
-};
 
-struct vivi_fh {
-       struct vivi_dev            *dev;
+       /* Input Number */
+       int                        input;
 
        /* video capture */
        struct vivi_fmt            *fmt;
        unsigned int               width, height;
        struct videobuf_queue      vb_vidq;
 
-       enum v4l2_buf_type         type;
+       unsigned long              generating;
+       u8                         bars[9][3];
+       u8                         line[MAX_WIDTH * 4];
 };
 
 /* ------------------------------------------------------------------
@@ -200,27 +205,50 @@ struct vivi_fh {
 
 enum colors {
        WHITE,
-       AMBAR,
+       AMBER,
        CYAN,
        GREEN,
        MAGENTA,
        RED,
        BLUE,
        BLACK,
+       TEXT_BLACK,
+};
+
+/* R   G   B */
+#define COLOR_WHITE    {204, 204, 204}
+#define COLOR_AMBER    {208, 208,   0}
+#define COLOR_CYAN     {  0, 206, 206}
+#define        COLOR_GREEN     {  0, 239,   0}
+#define COLOR_MAGENTA  {239,   0, 239}
+#define COLOR_RED      {205,   0,   0}
+#define COLOR_BLUE     {  0,   0, 255}
+#define COLOR_BLACK    {  0,   0,   0}
+
+struct bar_std {
+       u8 bar[9][3];
 };
 
-static u8 bars[8][3] = {
-       /* R   G   B */
-       {204, 204, 204},  /* white */
-       {208, 208,   0},  /* ambar */
-       {  0, 206, 206},  /* cyan */
-       {  0, 239,   0},  /* green */
-       {239,   0, 239},  /* magenta */
-       {205,   0,   0},  /* red */
-       {  0,   0, 255},  /* blue */
-       {  0,   0,   0},  /* black */
+/* Maximum number of bars are 10 - otherwise, the input print code
+   should be modified */
+static struct bar_std bars[] = {
+       {       /* Standard ITU-R color bar sequence */
+               { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN,
+                 COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK }
+       }, {
+               { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE,
+                 COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK }
+       }, {
+               { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE,
+                 COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK }
+       }, {
+               { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE,
+                 COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK }
+       },
 };
 
+#define NUM_INPUTS ARRAY_SIZE(bars)
+
 #define TO_Y(r, g, b) \
        (((16829 * r + 33039 * g + 6416 * b  + 32768) >> 16) + 16)
 /* RGB to  V(Cr) Color transform */
@@ -230,150 +258,230 @@ static u8 bars[8][3] = {
 #define TO_U(r, g, b) \
        (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
 
-#define TSTAMP_MIN_Y 24
-#define TSTAMP_MAX_Y TSTAMP_MIN_Y+15
-#define TSTAMP_MIN_X 64
-
-static void gen_line(char *basep, int inipos, int wmax,
-               int hmax, int line, int count, char *timestr)
+/* precalculate color bar values to speed up rendering */
+static void precalculate_bars(struct vivi_dev *dev)
 {
-       int  w, i, j, y;
-       int pos = inipos;
-       char *p, *s;
-       u8   chr, r, g, b, color;
+       u8 r, g, b;
+       int k, is_yuv;
+
+       for (k = 0; k < 9; k++) {
+               r = bars[dev->input].bar[k][0];
+               g = bars[dev->input].bar[k][1];
+               b = bars[dev->input].bar[k][2];
+               is_yuv = 0;
+
+               switch (dev->fmt->fourcc) {
+               case V4L2_PIX_FMT_YUYV:
+               case V4L2_PIX_FMT_UYVY:
+                       is_yuv = 1;
+                       break;
+               case V4L2_PIX_FMT_RGB565:
+               case V4L2_PIX_FMT_RGB565X:
+                       r >>= 3;
+                       g >>= 2;
+                       b >>= 3;
+                       break;
+               case V4L2_PIX_FMT_RGB555:
+               case V4L2_PIX_FMT_RGB555X:
+                       r >>= 3;
+                       g >>= 3;
+                       b >>= 3;
+                       break;
+               }
+
+               if (is_yuv) {
+                       dev->bars[k][0] = TO_Y(r, g, b);        /* Luma */
+                       dev->bars[k][1] = TO_U(r, g, b);        /* Cb */
+                       dev->bars[k][2] = TO_V(r, g, b);        /* Cr */
+               } else {
+                       dev->bars[k][0] = r;
+                       dev->bars[k][1] = g;
+                       dev->bars[k][2] = b;
+               }
+       }
+}
 
-       /* We will just duplicate the second pixel at the packet */
-       wmax /= 2;
+#define TSTAMP_MIN_Y   24
+#define TSTAMP_MAX_Y   (TSTAMP_MIN_Y + 15)
+#define TSTAMP_INPUT_X 10
+#define TSTAMP_MIN_X   (54 + TSTAMP_INPUT_X)
 
-       /* Generate a standard color bar pattern */
-       for (w = 0; w < wmax; w++) {
-               int colorpos = ((w + count) * 8/(wmax + 1)) % 8;
-               r = bars[colorpos][0];
-               g = bars[colorpos][1];
-               b = bars[colorpos][2];
+static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos)
+{
+       u8 r_y, g_u, b_v;
+       int color;
+       u8 *p;
 
-               for (color = 0; color < 4; color++) {
-                       p = basep + pos;
+       r_y = dev->bars[colorpos][0]; /* R or precalculated Y */
+       g_u = dev->bars[colorpos][1]; /* G or precalculated U */
+       b_v = dev->bars[colorpos][2]; /* B or precalculated V */
 
+       for (color = 0; color < 4; color++) {
+               p = buf + color;
+
+               switch (dev->fmt->fourcc) {
+               case V4L2_PIX_FMT_YUYV:
                        switch (color) {
                        case 0:
                        case 2:
-                               *p = TO_Y(r, g, b);     /* Luma */
+                               *p = r_y;
                                break;
                        case 1:
-                               *p = TO_U(r, g, b);     /* Cb */
+                               *p = g_u;
                                break;
                        case 3:
-                               *p = TO_V(r, g, b);     /* Cr */
+                               *p = b_v;
                                break;
                        }
-                       pos++;
+                       break;
+               case V4L2_PIX_FMT_UYVY:
+                       switch (color) {
+                       case 1:
+                       case 3:
+                               *p = r_y;
+                               break;
+                       case 0:
+                               *p = g_u;
+                               break;
+                       case 2:
+                               *p = b_v;
+                               break;
+                       }
+                       break;
+               case V4L2_PIX_FMT_RGB565:
+                       switch (color) {
+                       case 0:
+                       case 2:
+                               *p = (g_u << 5) | b_v;
+                               break;
+                       case 1:
+                       case 3:
+                               *p = (r_y << 3) | (g_u >> 3);
+                               break;
+                       }
+                       break;
+               case V4L2_PIX_FMT_RGB565X:
+                       switch (color) {
+                       case 0:
+                       case 2:
+                               *p = (r_y << 3) | (g_u >> 3);
+                               break;
+                       case 1:
+                       case 3:
+                               *p = (g_u << 5) | b_v;
+                               break;
+                       }
+                       break;
+               case V4L2_PIX_FMT_RGB555:
+                       switch (color) {
+                       case 0:
+                       case 2:
+                               *p = (g_u << 5) | b_v;
+                               break;
+                       case 1:
+                       case 3:
+                               *p = (r_y << 2) | (g_u >> 3);
+                               break;
+                       }
+                       break;
+               case V4L2_PIX_FMT_RGB555X:
+                       switch (color) {
+                       case 0:
+                       case 2:
+                               *p = (r_y << 2) | (g_u >> 3);
+                               break;
+                       case 1:
+                       case 3:
+                               *p = (g_u << 5) | b_v;
+                               break;
+                       }
+                       break;
                }
        }
+}
 
-       /* Checks if it is possible to show timestamp */
-       if (TSTAMP_MAX_Y >= hmax)
-               goto end;
-       if (TSTAMP_MIN_X + strlen(timestr) >= wmax)
-               goto end;
+static void precalculate_line(struct vivi_dev *dev)
+{
+       int w;
+
+       for (w = 0; w < dev->width * 2; w += 2) {
+               int colorpos = (w / (dev->width / 8) % 8);
+
+               gen_twopix(dev, dev->line + w * 2, colorpos);
+       }
+}
+
+static void gen_text(struct vivi_dev *dev, char *basep,
+                                       int y, int x, char *text)
+{
+       int line;
+
+       /* Checks if it is possible to show string */
+       if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width)
+               return;
 
        /* Print stream time */
-       if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) {
-               j = TSTAMP_MIN_X;
-               for (s = timestr; *s; s++) {
-                       chr = rom8x16_bits[(*s-0x30)*16+line-TSTAMP_MIN_Y];
-                       for (i = 0; i < 7; i++) {
-                               if (chr & 1 << (7 - i)) {
-                                       /* Font color*/
-                                       r = 0;
-                                       g = 198;
-                                       b = 0;
-                               } else {
-                                       /* Background color */
-                                       r = bars[BLACK][0];
-                                       g = bars[BLACK][1];
-                                       b = bars[BLACK][2];
-                               }
-
-                               pos = inipos + j * 2;
-                               for (color = 0; color < 4; color++) {
-                                       p = basep + pos;
-
-                                       y = TO_Y(r, g, b);
-
-                                       switch (color) {
-                                       case 0:
-                                       case 2:
-                                               *p = TO_Y(r, g, b); /* Luma */
-                                               break;
-                                       case 1:
-                                               *p = TO_U(r, g, b); /* Cb */
-                                               break;
-                                       case 3:
-                                               *p = TO_V(r, g, b); /* Cr */
-                                               break;
-                                       }
-                                       pos++;
-                               }
-                               j++;
+       for (line = y; line < y + 16; line++) {
+               int j = 0;
+               char *pos = basep + line * dev->width * 2 + x * 2;
+               char *s;
+
+               for (s = text; *s; s++) {
+                       u8 chr = font8x16[*s * 16 + line - y];
+                       int i;
+
+                       for (i = 0; i < 7; i++, j++) {
+                               /* Draw white font on black background */
+                               if (chr & (1 << (7 - i)))
+                                       gen_twopix(dev, pos + j * 2, WHITE);
+                               else
+                                       gen_twopix(dev, pos + j * 2, TEXT_BLACK);
                        }
                }
        }
-
-end:
-       return;
 }
 
 static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
 {
-       int h , pos = 0;
-       int hmax  = buf->vb.height;
-       int wmax  = buf->vb.width;
+       int hmax = buf->vb.height;
+       int wmax = buf->vb.width;
        struct timeval ts;
-       char *tmpbuf;
        void *vbuf = videobuf_to_vmalloc(&buf->vb);
+       unsigned ms;
+       char str[100];
+       int h, line = 1;
 
        if (!vbuf)
                return;
 
-       tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC);
-       if (!tmpbuf)
-               return;
-
-       for (h = 0; h < hmax; h++) {
-               gen_line(tmpbuf, 0, wmax, hmax, h, dev->mv_count,
-                        dev->timestr);
-               memcpy(vbuf + pos, tmpbuf, wmax * 2);
-               pos += wmax*2;
-       }
-
-       dev->mv_count++;
-
-       kfree(tmpbuf);
+       for (h = 0; h < hmax; h++)
+               memcpy(vbuf + h * wmax * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2);
 
        /* Updates stream time */
 
-       dev->ms += jiffies_to_msecs(jiffies-dev->jiffies);
+       dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
        dev->jiffies = jiffies;
-       if (dev->ms >= 1000) {
-               dev->ms -= 1000;
-               dev->s++;
-               if (dev->s >= 60) {
-                       dev->s -= 60;
-                       dev->m++;
-                       if (dev->m > 60) {
-                               dev->m -= 60;
-                               dev->h++;
-                               if (dev->h > 24)
-                                       dev->h -= 24;
-                       }
-               }
-       }
-       sprintf(dev->timestr, "%02d:%02d:%02d:%03d",
-                       dev->h, dev->m, dev->s, dev->ms);
-
-       dprintk(dev, 2, "vivifill at %s: Buffer 0x%08lx size= %d\n",
-                       dev->timestr, (unsigned long)tmpbuf, pos);
+       ms = dev->ms;
+       snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ",
+                       (ms / (60 * 60 * 1000)) % 24,
+                       (ms / (60 * 1000)) % 60,
+                       (ms / 1000) % 60,
+                       ms % 1000);
+       gen_text(dev, vbuf, line++ * 16, 16, str);
+       snprintf(str, sizeof(str), " %dx%d, input %d ",
+                       dev->width, dev->height, dev->input);
+       gen_text(dev, vbuf, line++ * 16, 16, str);
+
+       snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
+                       dev->brightness,
+                       dev->contrast,
+                       dev->saturation,
+                       dev->hue);
+       gen_text(dev, vbuf, line++ * 16, 16, str);
+       snprintf(str, sizeof(str), " volume %3d ", dev->volume);
+       gen_text(dev, vbuf, line++ * 16, 16, str);
+
+       dev->mv_count += 2;
 
        /* Advice that buffer was filled */
        buf->vb.field_count++;
@@ -382,12 +490,10 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
        buf->vb.state = VIDEOBUF_DONE;
 }
 
-static void vivi_thread_tick(struct vivi_fh *fh)
+static void vivi_thread_tick(struct vivi_dev *dev)
 {
-       struct vivi_buffer *buf;
-       struct vivi_dev *dev = fh->dev;
        struct vivi_dmaqueue *dma_q = &dev->vidq;
-
+       struct vivi_buffer *buf;
        unsigned long flags = 0;
 
        dprintk(dev, 1, "Thread tick\n");
@@ -417,15 +523,13 @@ static void vivi_thread_tick(struct vivi_fh *fh)
        dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i);
 unlock:
        spin_unlock_irqrestore(&dev->slock, flags);
-       return;
 }
 
 #define frames_to_ms(frames)                                   \
        ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)
 
-static void vivi_sleep(struct vivi_fh *fh)
+static void vivi_sleep(struct vivi_dev *dev)
 {
-       struct vivi_dev *dev = fh->dev;
        struct vivi_dmaqueue *dma_q = &dev->vidq;
        int timeout;
        DECLARE_WAITQUEUE(wait, current);
@@ -440,7 +544,7 @@ static void vivi_sleep(struct vivi_fh *fh)
        /* Calculate time to wake up */
        timeout = msecs_to_jiffies(frames_to_ms(1));
 
-       vivi_thread_tick(fh);
+       vivi_thread_tick(dev);
 
        schedule_timeout_interruptible(timeout);
 
@@ -451,15 +555,14 @@ stop_task:
 
 static int vivi_thread(void *data)
 {
-       struct vivi_fh  *fh = data;
-       struct vivi_dev *dev = fh->dev;
+       struct vivi_dev *dev = data;
 
        dprintk(dev, 1, "thread started\n");
 
        set_freezable();
 
        for (;;) {
-               vivi_sleep(fh);
+               vivi_sleep(dev);
 
                if (kthread_should_stop())
                        break;
@@ -468,39 +571,61 @@ static int vivi_thread(void *data)
        return 0;
 }
 
-static int vivi_start_thread(struct vivi_fh *fh)
+static void vivi_start_generating(struct file *file)
 {
-       struct vivi_dev *dev = fh->dev;
+       struct vivi_dev *dev = video_drvdata(file);
        struct vivi_dmaqueue *dma_q = &dev->vidq;
 
-       dma_q->frame = 0;
-       dma_q->ini_jiffies = jiffies;
-
        dprintk(dev, 1, "%s\n", __func__);
 
-       dma_q->kthread = kthread_run(vivi_thread, fh, "vivi");
+       if (test_and_set_bit(0, &dev->generating))
+               return;
+       file->private_data = dev;
+
+       /* Resets frame counters */
+       dev->ms = 0;
+       dev->mv_count = 0;
+       dev->jiffies = jiffies;
+
+       dma_q->frame = 0;
+       dma_q->ini_jiffies = jiffies;
+       dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);
 
        if (IS_ERR(dma_q->kthread)) {
-               printk(KERN_ERR "vivi: kernel_thread() failed\n");
-               return PTR_ERR(dma_q->kthread);
+               v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+               clear_bit(0, &dev->generating);
+               return;
        }
        /* Wakes thread */
        wake_up_interruptible(&dma_q->wq);
 
        dprintk(dev, 1, "returning from %s\n", __func__);
-       return 0;
 }
 
-static void vivi_stop_thread(struct vivi_dmaqueue  *dma_q)
+static void vivi_stop_generating(struct file *file)
 {
-       struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq);
+       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);
 }
 
 /* ------------------------------------------------------------------
@@ -509,10 +634,9 @@ static void vivi_stop_thread(struct vivi_dmaqueue  *dma_q)
 static int
 buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
 {
-       struct vivi_fh  *fh = vq->priv_data;
-       struct vivi_dev *dev  = fh->dev;
+       struct vivi_dev *dev = vq->priv_data;
 
-       *size = fh->width*fh->height*2;
+       *size = dev->width * dev->height * 2;
 
        if (0 == *count)
                *count = 32;
@@ -528,48 +652,44 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
 
 static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf)
 {
-       struct vivi_fh  *fh = vq->priv_data;
-       struct vivi_dev *dev  = fh->dev;
+       struct vivi_dev *dev = vq->priv_data;
 
        dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state);
 
-       if (in_interrupt())
-               BUG();
-
        videobuf_vmalloc_free(&buf->vb);
        dprintk(dev, 1, "free_buffer: freed\n");
        buf->vb.state = VIDEOBUF_NEEDS_INIT;
 }
 
-#define norm_maxw() 1024
-#define norm_maxh() 768
 static int
 buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
                                                enum v4l2_field field)
 {
-       struct vivi_fh     *fh  = vq->priv_data;
-       struct vivi_dev    *dev = fh->dev;
+       struct vivi_dev *dev = vq->priv_data;
        struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
        int rc;
 
        dprintk(dev, 1, "%s, field=%d\n", __func__, field);
 
-       BUG_ON(NULL == fh->fmt);
+       BUG_ON(NULL == dev->fmt);
 
-       if (fh->width  < 48 || fh->width  > norm_maxw() ||
-           fh->height < 32 || fh->height > norm_maxh())
+       if (dev->width  < 48 || dev->width  > MAX_WIDTH ||
+           dev->height < 32 || dev->height > MAX_HEIGHT)
                return -EINVAL;
 
-       buf->vb.size = fh->width*fh->height*2;
-       if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+       buf->vb.size = dev->width * dev->height * 2;
+       if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
                return -EINVAL;
 
        /* These properties only change when queue is idle, see s_fmt */
-       buf->fmt       = fh->fmt;
-       buf->vb.width  = fh->width;
-       buf->vb.height = fh->height;
+       buf->fmt       = dev->fmt;
+       buf->vb.width  = dev->width;
+       buf->vb.height = dev->height;
        buf->vb.field  = field;
 
+       precalculate_bars(dev);
+       precalculate_line(dev);
+
        if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
                rc = videobuf_iolock(vq, &buf->vb, NULL);
                if (rc < 0)
@@ -577,7 +697,6 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
        }
 
        buf->vb.state = VIDEOBUF_PREPARED;
-
        return 0;
 
 fail:
@@ -588,9 +707,8 @@ fail:
 static void
 buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 {
-       struct vivi_buffer    *buf  = container_of(vb, struct vivi_buffer, vb);
-       struct vivi_fh        *fh   = vq->priv_data;
-       struct vivi_dev       *dev  = fh->dev;
+       struct vivi_dev *dev = vq->priv_data;
+       struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
        struct vivi_dmaqueue *vidq = &dev->vidq;
 
        dprintk(dev, 1, "%s\n", __func__);
@@ -602,9 +720,8 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 static void buffer_release(struct videobuf_queue *vq,
                           struct videobuf_buffer *vb)
 {
-       struct vivi_buffer   *buf  = container_of(vb, struct vivi_buffer, vb);
-       struct vivi_fh       *fh   = vq->priv_data;
-       struct vivi_dev      *dev  = (struct vivi_dev *)fh->dev;
+       struct vivi_dev *dev = vq->priv_data;
+       struct vivi_buffer *buf  = container_of(vb, struct vivi_buffer, vb);
 
        dprintk(dev, 1, "%s\n", __func__);
 
@@ -624,59 +741,61 @@ static struct videobuf_queue_ops vivi_video_qops = {
 static int vidioc_querycap(struct file *file, void  *priv,
                                        struct v4l2_capability *cap)
 {
+       struct vivi_dev *dev = video_drvdata(file);
+
        strcpy(cap->driver, "vivi");
        strcpy(cap->card, "vivi");
+       strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
        cap->version = VIVI_VERSION;
-       cap->capabilities =     V4L2_CAP_VIDEO_CAPTURE |
-                               V4L2_CAP_STREAMING     |
-                               V4L2_CAP_READWRITE;
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
+                           V4L2_CAP_READWRITE;
        return 0;
 }
 
 static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
-       if (f->index > 0)
+       struct vivi_fmt *fmt;
+
+       if (f->index >= ARRAY_SIZE(formats))
                return -EINVAL;
 
-       strlcpy(f->description, format.name, sizeof(f->description));
-       f->pixelformat = format.fourcc;
+       fmt = &formats[f->index];
+
+       strlcpy(f->description, fmt->name, sizeof(f->description));
+       f->pixelformat = fmt->fourcc;
        return 0;
 }
 
 static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
-       struct vivi_fh *fh = priv;
+       struct vivi_dev *dev = video_drvdata(file);
 
-       f->fmt.pix.width        = fh->width;
-       f->fmt.pix.height       = fh->height;
-       f->fmt.pix.field        = fh->vb_vidq.field;
-       f->fmt.pix.pixelformat  = fh->fmt->fourcc;
+       f->fmt.pix.width        = dev->width;
+       f->fmt.pix.height       = dev->height;
+       f->fmt.pix.field        = dev->vb_vidq.field;
+       f->fmt.pix.pixelformat  = dev->fmt->fourcc;
        f->fmt.pix.bytesperline =
-               (f->fmt.pix.width * fh->fmt->depth) >> 3;
+               (f->fmt.pix.width * dev->fmt->depth) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;
-
-       return (0);
+       return 0;
 }
 
 static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                        struct v4l2_format *f)
 {
-       struct vivi_fh  *fh  = priv;
-       struct vivi_dev *dev = fh->dev;
+       struct vivi_dev *dev = video_drvdata(file);
        struct vivi_fmt *fmt;
        enum v4l2_field field;
-       unsigned int maxw, maxh;
 
-       if (format.fourcc != f->fmt.pix.pixelformat) {
-               dprintk(dev, 1, "Fourcc format (0x%08x) invalid. "
-                       "Driver accepts only 0x%08x\n",
-                       f->fmt.pix.pixelformat, format.fourcc);
+       fmt = get_format(f);
+       if (!fmt) {
+               dprintk(dev, 1, "Fourcc format (0x%08x) invalid.\n",
+                       f->fmt.pix.pixelformat);
                return -EINVAL;
        }
-       fmt = &format;
 
        field = f->fmt.pix.field;
 
@@ -687,120 +806,109 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                return -EINVAL;
        }
 
-       maxw  = norm_maxw();
-       maxh  = norm_maxh();
-
        f->fmt.pix.field = field;
-       if (f->fmt.pix.height < 32)
-               f->fmt.pix.height = 32;
-       if (f->fmt.pix.height > maxh)
-               f->fmt.pix.height = maxh;
-       if (f->fmt.pix.width < 48)
-               f->fmt.pix.width = 48;
-       if (f->fmt.pix.width > maxw)
-               f->fmt.pix.width = maxw;
-       f->fmt.pix.width &= ~0x03;
+       v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
+                             &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
        f->fmt.pix.bytesperline =
                (f->fmt.pix.width * fmt->depth) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;
-
        return 0;
 }
 
-/*FIXME: This seems to be generic enough to be at videodev2 */
 static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
-       struct vivi_fh  *fh = priv;
-       struct videobuf_queue *q = &fh->vb_vidq;
+       struct vivi_dev *dev = video_drvdata(file);
+       struct videobuf_queue *q = &dev->vb_vidq;
 
-       int ret = vidioc_try_fmt_vid_cap(file, fh, f);
+       int ret = vidioc_try_fmt_vid_cap(file, priv, f);
        if (ret < 0)
-               return (ret);
+               return ret;
 
        mutex_lock(&q->vb_lock);
 
-       if (videobuf_queue_is_busy(&fh->vb_vidq)) {
-               dprintk(fh->dev, 1, "%s queue busy\n", __func__);
+       if (vivi_is_generating(dev)) {
+               dprintk(dev, 1, "%s device busy\n", __func__);
                ret = -EBUSY;
                goto out;
        }
 
-       fh->fmt           = &format;
-       fh->width         = f->fmt.pix.width;
-       fh->height        = f->fmt.pix.height;
-       fh->vb_vidq.field = f->fmt.pix.field;
-       fh->type          = f->type;
-
+       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:
        mutex_unlock(&q->vb_lock);
-
-       return (ret);
+       return ret;
 }
 
 static int vidioc_reqbufs(struct file *file, void *priv,
                          struct v4l2_requestbuffers *p)
 {
-       struct vivi_fh  *fh = priv;
+       struct vivi_dev *dev = video_drvdata(file);
 
-       return (videobuf_reqbufs(&fh->vb_vidq, p));
+       return videobuf_reqbufs(&dev->vb_vidq, p);
 }
 
 static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
-       struct vivi_fh  *fh = priv;
+       struct vivi_dev *dev = video_drvdata(file);
 
-       return (videobuf_querybuf(&fh->vb_vidq, p));
+       return videobuf_querybuf(&dev->vb_vidq, p);
 }
 
 static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
-       struct vivi_fh *fh = priv;
+       struct vivi_dev *dev = video_drvdata(file);
 
-       return (videobuf_qbuf(&fh->vb_vidq, p));
+       return videobuf_qbuf(&dev->vb_vidq, p);
 }
 
 static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
-       struct vivi_fh  *fh = priv;
+       struct vivi_dev *dev = video_drvdata(file);
 
-       return (videobuf_dqbuf(&fh->vb_vidq, p,
-                               file->f_flags & O_NONBLOCK));
+       return videobuf_dqbuf(&dev->vb_vidq, p,
+                               file->f_flags & O_NONBLOCK);
 }
 
 #ifdef CONFIG_VIDEO_V4L1_COMPAT
 static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
 {
-       struct vivi_fh  *fh = priv;
+       struct vivi_dev *dev = video_drvdata(file);
 
-       return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
+       return videobuf_cgmbuf(&dev->vb_vidq, mbuf, 8);
 }
 #endif
 
 static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
 {
-       struct vivi_fh  *fh = priv;
+       struct vivi_dev *dev = video_drvdata(file);
+       int ret;
 
-       if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-       if (i != fh->type)
+       if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
+       ret = videobuf_streamon(&dev->vb_vidq);
+       if (ret)
+               return ret;
 
-       return videobuf_streamon(&fh->vb_vidq);
+       vivi_start_generating(file);
+       return 0;
 }
 
 static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
 {
-       struct vivi_fh  *fh = priv;
+       struct vivi_dev *dev = video_drvdata(file);
+       int ret;
 
-       if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+       if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
-       if (i != fh->type)
-               return -EINVAL;
-
-       return videobuf_streamoff(&fh->vb_vidq);
+       ret = videobuf_streamoff(&dev->vb_vidq);
+       if (!ret)
+               vivi_stop_generating(file);
+       return ret;
 }
 
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
@@ -812,73 +920,110 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
 static int vidioc_enum_input(struct file *file, void *priv,
                                struct v4l2_input *inp)
 {
-       if (inp->index != 0)
+       if (inp->index >= NUM_INPUTS)
                return -EINVAL;
 
        inp->type = V4L2_INPUT_TYPE_CAMERA;
        inp->std = V4L2_STD_525_60;
-       strcpy(inp->name, "Camera");
-
-       return (0);
+       sprintf(inp->name, "Camera %u", inp->index);
+       return 0;
 }
 
 static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 {
-       *i = 0;
+       struct vivi_dev *dev = video_drvdata(file);
 
-       return (0);
+       *i = dev->input;
+       return 0;
 }
+
 static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
 {
-       if (i > 0)
+       struct vivi_dev *dev = video_drvdata(file);
+
+       if (i >= NUM_INPUTS)
                return -EINVAL;
 
-       return (0);
+       dev->input = i;
+       precalculate_bars(dev);
+       precalculate_line(dev);
+       return 0;
 }
 
-       /* --- controls ---------------------------------------------- */
+/* --- controls ---------------------------------------------- */
 static int vidioc_queryctrl(struct file *file, void *priv,
                            struct v4l2_queryctrl *qc)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
-               if (qc->id && qc->id == vivi_qctrl[i].id) {
-                       memcpy(qc, &(vivi_qctrl[i]),
-                               sizeof(*qc));
-                       return (0);
-               }
-
+       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)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
-               if (ctrl->id == vivi_qctrl[i].id) {
-                       ctrl->value = qctl_regs[i];
-                       return (0);
-               }
-
+       struct vivi_dev *dev = video_drvdata(file);
+
+       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)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
-               if (ctrl->id == vivi_qctrl[i].id) {
-                       if (ctrl->value < vivi_qctrl[i].minimum
-                           || ctrl->value > vivi_qctrl[i].maximum) {
-                                       return (-ERANGE);
-                               }
-                       qctl_regs[i] = ctrl->value;
-                       return (0);
-               }
+       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;
 }
 
@@ -886,189 +1031,63 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
        File operations for the device
    ------------------------------------------------------------------*/
 
-#define line_buf_size(norm) (norm_maxw(norm)*(format.depth+7)/8)
-
-static int vivi_open(struct inode *inode, struct file *file)
-{
-       int minor = iminor(inode);
-       struct vivi_dev *dev;
-       struct vivi_fh *fh = NULL;
-       int i;
-       int retval = 0;
-
-       printk(KERN_DEBUG "vivi: open called (minor=%d)\n", minor);
-
-       lock_kernel();
-       list_for_each_entry(dev, &vivi_devlist, vivi_devlist)
-               if (dev->vfd->minor == minor)
-                       goto found;
-       unlock_kernel();
-       return -ENODEV;
-
-found:
-       mutex_lock(&dev->mutex);
-       dev->users++;
-
-       if (dev->users > 1) {
-               dev->users--;
-               retval = -EBUSY;
-               goto unlock;
-       }
-
-       dprintk(dev, 1, "open minor=%d type=%s users=%d\n", minor,
-               v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);
-
-       /* allocate + initialize per filehandle data */
-       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
-       if (NULL == fh) {
-               dev->users--;
-               retval = -ENOMEM;
-               goto unlock;
-       }
-unlock:
-       mutex_unlock(&dev->mutex);
-       if (retval) {
-               unlock_kernel();
-               return retval;
-       }
-
-       file->private_data = fh;
-       fh->dev      = dev;
-
-       fh->type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       fh->fmt      = &format;
-       fh->width    = 640;
-       fh->height   = 480;
-
-       /* Put all controls at a sane state */
-       for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
-               qctl_regs[i] = vivi_qctrl[i].default_value;
-
-       /* Resets frame counters */
-       dev->h = 0;
-       dev->m = 0;
-       dev->s = 0;
-       dev->ms = 0;
-       dev->mv_count = 0;
-       dev->jiffies = jiffies;
-       sprintf(dev->timestr, "%02d:%02d:%02d:%03d",
-                       dev->h, dev->m, dev->s, dev->ms);
-
-       videobuf_queue_vmalloc_init(&fh->vb_vidq, &vivi_video_qops,
-                       NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED,
-                       sizeof(struct vivi_buffer), fh);
-
-       vivi_start_thread(fh);
-       unlock_kernel();
-
-       return 0;
-}
-
 static ssize_t
 vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 {
-       struct vivi_fh *fh = file->private_data;
+       struct vivi_dev *dev = video_drvdata(file);
 
-       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
+       vivi_start_generating(file);
+       return videobuf_read_stream(&dev->vb_vidq, data, count, ppos, 0,
                                        file->f_flags & O_NONBLOCK);
-       }
-       return 0;
 }
 
 static unsigned int
 vivi_poll(struct file *file, struct poll_table_struct *wait)
 {
-       struct vivi_fh        *fh = file->private_data;
-       struct vivi_dev       *dev = fh->dev;
-       struct videobuf_queue *q = &fh->vb_vidq;
+       struct vivi_dev *dev = video_drvdata(file);
+       struct videobuf_queue *q = &dev->vb_vidq;
 
        dprintk(dev, 1, "%s\n", __func__);
 
-       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
-               return POLLERR;
-
+       vivi_start_generating(file);
        return videobuf_poll_stream(file, q, wait);
 }
 
-static int vivi_close(struct inode *inode, struct file *file)
+static int vivi_close(struct file *file)
 {
-       struct vivi_fh         *fh = file->private_data;
-       struct vivi_dev *dev       = fh->dev;
-       struct vivi_dmaqueue *vidq = &dev->vidq;
-
-       int minor = iminor(inode);
-
-       vivi_stop_thread(vidq);
-       videobuf_stop(&fh->vb_vidq);
-       videobuf_mmap_free(&fh->vb_vidq);
-
-       kfree(fh);
-
-       mutex_lock(&dev->mutex);
-       dev->users--;
-       mutex_unlock(&dev->mutex);
-
-       dprintk(dev, 1, "close called (minor=%d, users=%d)\n",
-               minor, dev->users);
-
-       return 0;
-}
-
-static int vivi_release(void)
-{
-       struct vivi_dev *dev;
-       struct list_head *list;
-
-       while (!list_empty(&vivi_devlist)) {
-               list = vivi_devlist.next;
-               list_del(list);
-               dev = list_entry(list, struct vivi_dev, vivi_devlist);
-
-               if (-1 != dev->vfd->minor) {
-                       printk(KERN_INFO "%s: unregistering /dev/video%d\n",
-                               VIVI_MODULE_NAME, dev->vfd->minor);
-                       video_unregister_device(dev->vfd);
-               } else {
-                       printk(KERN_INFO "%s: releasing /dev/video%d\n",
-                               VIVI_MODULE_NAME, dev->vfd->minor);
-                       video_device_release(dev->vfd);
-               }
+       struct video_device  *vdev = video_devdata(file);
+       struct vivi_dev *dev = video_drvdata(file);
 
-               kfree(dev);
-       }
+       vivi_stop_generating(file);
 
+       dprintk(dev, 1, "close called (dev=%s)\n",
+               video_device_node_name(vdev));
        return 0;
 }
 
 static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       struct vivi_fh  *fh = file->private_data;
-       struct vivi_dev *dev = fh->dev;
+       struct vivi_dev *dev = video_drvdata(file);
        int ret;
 
        dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
 
-       ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+       ret = videobuf_mmap_mapper(&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,
+               (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
                ret);
-
        return ret;
 }
 
-static const struct file_operations vivi_fops = {
+static const struct v4l2_file_operations vivi_fops = {
        .owner          = THIS_MODULE,
-       .open           = vivi_open,
        .release        = vivi_close,
        .read           = vivi_read,
        .poll           = vivi_poll,
        .ioctl          = video_ioctl2, /* V4L2 ioctl handler */
-       .compat_ioctl   = v4l_compat_ioctl32,
        .mmap           = vivi_mmap,
-       .llseek         = no_llseek,
 };
 
 static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
@@ -1085,11 +1104,11 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
        .vidioc_enum_input    = vidioc_enum_input,
        .vidioc_g_input       = vidioc_g_input,
        .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,
-       .vidioc_streamon      = vidioc_streamon,
-       .vidioc_streamoff     = vidioc_streamoff,
 #ifdef CONFIG_VIDEO_V4L1_COMPAT
        .vidiocgmbuf          = vidiocgmbuf,
 #endif
@@ -1099,93 +1118,151 @@ static struct video_device vivi_template = {
        .name           = "vivi",
        .fops           = &vivi_fops,
        .ioctl_ops      = &vivi_ioctl_ops,
-       .minor          = -1,
        .release        = video_device_release,
 
        .tvnorms              = V4L2_STD_525_60,
        .current_norm         = V4L2_STD_NTSC_M,
 };
+
 /* -----------------------------------------------------------------
        Initialization and module stuff
    ------------------------------------------------------------------*/
 
-/* This routine allocates from 1 to n_devs virtual drivers.
+static int vivi_release(void)
+{
+       struct vivi_dev *dev;
+       struct list_head *list;
 
-   The real maximum number of virtual drivers will depend on how many drivers
-   will succeed. This is limited to the maximum number of devices that
-   videodev supports. Since there are 64 minors for video grabbers, this is
-   currently the theoretical maximum limit. However, a further limit does
-   exist at videodev that forbids any driver to register more than 32 video
-   grabbers.
- */
-static int __init vivi_init(void)
+       while (!list_empty(&vivi_devlist)) {
+               list = vivi_devlist.next;
+               list_del(list);
+               dev = list_entry(list, struct vivi_dev, vivi_devlist);
+
+               v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+                       video_device_node_name(dev->vfd));
+               video_unregister_device(dev->vfd);
+               v4l2_device_unregister(&dev->v4l2_dev);
+               kfree(dev);
+       }
+
+       return 0;
+}
+
+static int __init vivi_create_instance(int inst)
 {
-       int ret = -ENOMEM, i;
        struct vivi_dev *dev;
        struct video_device *vfd;
+       int ret;
 
-       if (n_devs <= 0)
-               n_devs = 1;
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
+                       "%s-%03d", VIVI_MODULE_NAME, inst);
+       ret = v4l2_device_register(NULL, &dev->v4l2_dev);
+       if (ret)
+               goto free_dev;
+
+       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;
+
+       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);
+
+       /* init video dma queues */
+       INIT_LIST_HEAD(&dev->vidq.active);
+       init_waitqueue_head(&dev->vidq.wq);
+
+       /* initialize locks */
+       spin_lock_init(&dev->slock);
+       mutex_init(&dev->mutex);
+
+       ret = -ENOMEM;
+       vfd = video_device_alloc();
+       if (!vfd)
+               goto unreg_dev;
+
+       *vfd = vivi_template;
+       vfd->debug = debug;
+       vfd->v4l2_dev = &dev->v4l2_dev;
+
+       ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
+       if (ret < 0)
+               goto rel_vdev;
 
-       for (i = 0; i < n_devs; i++) {
-               dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-               if (!dev)
-                       break;
+       video_set_drvdata(vfd, dev);
 
-               /* init video dma queues */
-               INIT_LIST_HEAD(&dev->vidq.active);
-               init_waitqueue_head(&dev->vidq.wq);
+       /* Now that everything is fine, let's add it to device list */
+       list_add_tail(&dev->vivi_devlist, &vivi_devlist);
 
-               /* initialize locks */
-               spin_lock_init(&dev->slock);
-               mutex_init(&dev->mutex);
+       if (video_nr != -1)
+               video_nr++;
 
-               vfd = video_device_alloc();
-               if (!vfd) {
-                       kfree(dev);
-                       break;
-               }
+       dev->vfd = vfd;
+       v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+                 video_device_node_name(vfd));
+       return 0;
+
+rel_vdev:
+       video_device_release(vfd);
+unreg_dev:
+       v4l2_device_unregister(&dev->v4l2_dev);
+free_dev:
+       kfree(dev);
+       return ret;
+}
+
+/* This routine allocates from 1 to n_devs virtual drivers.
 
-               *vfd = vivi_template;
+   The real maximum number of virtual drivers will depend on how many drivers
+   will succeed. This is limited to the maximum number of devices that
+   videodev supports, which is equal to VIDEO_NUM_DEVICES.
+ */
+static int __init vivi_init(void)
+{
+       const struct font_desc *font = find_font("VGA8x16");
+       int ret = 0, i;
 
-               ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
-               if (ret < 0) {
-                       video_device_release(vfd);
-                       kfree(dev);
+       if (font == NULL) {
+               printk(KERN_ERR "vivi: could not find font\n");
+               return -ENODEV;
+       }
+       font8x16 = font->data;
 
-                       /* If some registers succeeded, keep driver */
+       if (n_devs <= 0)
+               n_devs = 1;
+
+       for (i = 0; i < n_devs; i++) {
+               ret = vivi_create_instance(i);
+               if (ret) {
+                       /* If some instantiations succeeded, keep driver */
                        if (i)
                                ret = 0;
-
                        break;
                }
-
-               /* Now that everything is fine, let's add it to device list */
-               list_add_tail(&dev->vivi_devlist, &vivi_devlist);
-
-               snprintf(vfd->name, sizeof(vfd->name), "%s (%i)",
-                        vivi_template.name, vfd->minor);
-
-               if (video_nr >= 0)
-                       video_nr++;
-
-               dev->vfd = vfd;
-               printk(KERN_INFO "%s: V4L2 device registered as /dev/video%d\n",
-                       VIVI_MODULE_NAME, vfd->minor);
        }
 
        if (ret < 0) {
-               vivi_release();
-               printk(KERN_INFO "Error %d while loading vivi driver\n", ret);
-       } else {
-               printk(KERN_INFO "Video Technology Magazine Virtual Video "
+               printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
+               return ret;
+       }
+
+       printk(KERN_INFO "Video Technology Magazine Virtual Video "
                        "Capture Board ver %u.%u.%u successfully loaded.\n",
                        (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
                        VIVI_VERSION & 0xFF);
 
-               /* n_devs will reflect the actual number of allocated devices */
-               n_devs = i;
-       }
+       /* n_devs will reflect the actual number of allocated devices */
+       n_devs = i;
 
        return ret;
 }
@@ -1197,19 +1274,3 @@ static void __exit vivi_exit(void)
 
 module_init(vivi_init);
 module_exit(vivi_exit);
-
-MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
-MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
-MODULE_LICENSE("Dual BSD/GPL");
-
-module_param(video_nr, uint, 0444);
-MODULE_PARM_DESC(video_nr, "video iminor start number");
-
-module_param(n_devs, uint, 0444);
-MODULE_PARM_DESC(n_devs, "number of video devices to create");
-
-module_param_named(debug, vivi_template.debug, int, 0444);
-MODULE_PARM_DESC(debug, "activates debug info");
-
-module_param(vid_limit, int, 0644);
-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");