V4L/DVB (12881): em28xx: fix codingstyle issues in em28xx-video.c
[safe/jmp/linux-2.6] / drivers / media / video / em28xx / em28xx-video.c
index 8e61b2c..3a1dfb7 100644 (file)
@@ -49,7 +49,7 @@
                      "Sascha Sommer <saschasommer@freenet.de>"
 
 #define DRIVER_DESC         "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE  KERNEL_VERSION(0, 1, 1)
+#define EM28XX_VERSION_CODE  KERNEL_VERSION(0, 1, 2)
 
 #define em28xx_videodbg(fmt, arg...) do {\
        if (video_debug) \
@@ -90,16 +90,41 @@ MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
 /* supported video standards */
 static struct em28xx_fmt format[] = {
        {
-               .name     = "16bpp YUY2, 4:2:2, packed",
+               .name     = "16 bpp YUY2, 4:2:2, packed",
                .fourcc   = V4L2_PIX_FMT_YUYV,
                .depth    = 16,
                .reg      = EM28XX_OUTFMT_YUV422_Y0UY1V,
+       }, {
+               .name     = "16 bpp RGB 565, LE",
+               .fourcc   = V4L2_PIX_FMT_RGB565,
+               .depth    = 16,
+               .reg      = EM28XX_OUTFMT_RGB_16_656,
+       }, {
+               .name     = "8 bpp Bayer BGBG..GRGR",
+               .fourcc   = V4L2_PIX_FMT_SBGGR8,
+               .depth    = 8,
+               .reg      = EM28XX_OUTFMT_RGB_8_BGBG,
+       }, {
+               .name     = "8 bpp Bayer GRGR..BGBG",
+               .fourcc   = V4L2_PIX_FMT_SGRBG8,
+               .depth    = 8,
+               .reg      = EM28XX_OUTFMT_RGB_8_GRGR,
+       }, {
+               .name     = "8 bpp Bayer GBGB..RGRG",
+               .fourcc   = V4L2_PIX_FMT_SGBRG8,
+               .depth    = 8,
+               .reg      = EM28XX_OUTFMT_RGB_8_GBGB,
+       }, {
+               .name     = "12 bpp YUV411",
+               .fourcc   = V4L2_PIX_FMT_YUV411P,
+               .depth    = 12,
+               .reg      = EM28XX_OUTFMT_YUV411,
        },
 };
 
 /* supported controls */
 /* Common to all boards */
-static struct v4l2_queryctrl em28xx_qctrl[] = {
+static struct v4l2_queryctrl ac97_qctrl[] = {
        {
                .id = V4L2_CID_AUDIO_VOLUME,
                .type = V4L2_CTRL_TYPE_INTEGER,
@@ -108,7 +133,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
                .maximum = 0x1f,
                .step = 0x1,
                .default_value = 0x1f,
-               .flags = 0,
+               .flags = V4L2_CTRL_FLAG_SLIDER,
        }, {
                .id = V4L2_CID_AUDIO_MUTE,
                .type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -138,7 +163,24 @@ static inline void buffer_filled(struct em28xx *dev,
        buf->vb.field_count++;
        do_gettimeofday(&buf->vb.ts);
 
-       dev->isoc_ctl.buf = NULL;
+       dev->isoc_ctl.vid_buf = NULL;
+
+       list_del(&buf->vb.queue);
+       wake_up(&buf->vb.done);
+}
+
+static inline void vbi_buffer_filled(struct em28xx *dev,
+                                    struct em28xx_dmaqueue *dma_q,
+                                    struct em28xx_buffer *buf)
+{
+       /* Advice that buffer was filled */
+       em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+
+       buf->vb.state = VIDEOBUF_DONE;
+       buf->vb.field_count++;
+       do_gettimeofday(&buf->vb.ts);
+
+       dev->isoc_ctl.vbi_buf = NULL;
 
        list_del(&buf->vb.queue);
        wake_up(&buf->vb.done);
@@ -169,15 +211,24 @@ static void em28xx_copy_video(struct em28xx *dev,
        startread = p;
        remain = len;
 
-       /* Interlaces frame */
-       if (buf->top_field)
+       if (dev->progressive)
                fieldstart = outp;
-       else
-               fieldstart = outp + bytesperline;
+       else {
+               /* Interlaces two half frames */
+               if (buf->top_field)
+                       fieldstart = outp;
+               else
+                       fieldstart = outp + bytesperline;
+       }
 
        linesdone = dma_q->pos / bytesperline;
        currlinedone = dma_q->pos % bytesperline;
-       offset = linesdone * bytesperline * 2 + currlinedone;
+
+       if (dev->progressive)
+               offset = linesdone * bytesperline + currlinedone;
+       else
+               offset = linesdone * bytesperline * 2 + currlinedone;
+
        startwrite = fieldstart + offset;
        lencopy = bytesperline - currlinedone;
        lencopy = lencopy > remain ? remain : lencopy;
@@ -186,7 +237,8 @@ static void em28xx_copy_video(struct em28xx *dev,
                em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
                               ((char *)startwrite + lencopy) -
                               ((char *)outp + buf->vb.size));
-               lencopy = remain = (char *)outp + buf->vb.size - (char *)startwrite;
+               remain = (char *)outp + buf->vb.size - (char *)startwrite;
+               lencopy = remain;
        }
        if (lencopy <= 0)
                return;
@@ -202,8 +254,10 @@ static void em28xx_copy_video(struct em28xx *dev,
                else
                        lencopy = bytesperline;
 
-               if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
-                       em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n",
+               if ((char *)startwrite + lencopy > (char *)outp +
+                   buf->vb.size) {
+                       em28xx_isocdbg("Overflow of %zi bytes past buffer end"
+                                      "(2)\n",
                                       ((char *)startwrite + lencopy) -
                                       ((char *)outp + buf->vb.size));
                        lencopy = remain = (char *)outp + buf->vb.size -
@@ -220,6 +274,63 @@ static void em28xx_copy_video(struct em28xx *dev,
        dma_q->pos += len;
 }
 
+static void em28xx_copy_vbi(struct em28xx *dev,
+                             struct em28xx_dmaqueue  *dma_q,
+                             struct em28xx_buffer *buf,
+                             unsigned char *p,
+                             unsigned char *outp, unsigned long len)
+{
+       void *startwrite, *startread;
+       int  offset;
+       int bytesperline = 720;
+
+       if (dev == NULL) {
+               em28xx_isocdbg("dev is null\n");
+               return;
+       }
+
+       if (dma_q == NULL) {
+               em28xx_isocdbg("dma_q is null\n");
+               return;
+       }
+       if (buf == NULL) {
+               return;
+       }
+       if (p == NULL) {
+               em28xx_isocdbg("p is null\n");
+               return;
+       }
+       if (outp == NULL) {
+               em28xx_isocdbg("outp is null\n");
+               return;
+       }
+
+       if (dma_q->pos + len > buf->vb.size)
+               len = buf->vb.size - dma_q->pos;
+
+       if ((p[0] == 0x33 && p[1] == 0x95) ||
+           (p[0] == 0x88 && p[1] == 0x88)) {
+               /* Header field, advance past it */
+               p += 4;
+       } else {
+               len += 4;
+       }
+
+       startread = p;
+
+       startwrite = outp + dma_q->pos;
+       offset = dma_q->pos;
+
+       /* Make sure the bottom field populates the second half of the frame */
+       if (buf->top_field == 0) {
+               startwrite += bytesperline * 0x0c;
+               offset += bytesperline * 0x0c;
+       }
+
+       memcpy(startwrite, startread, len);
+       dma_q->pos += len;
+}
+
 static inline void print_err_status(struct em28xx *dev,
                                     int packet, int status)
 {
@@ -270,7 +381,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
 
        if (list_empty(&dma_q->active)) {
                em28xx_isocdbg("No active queue to serve\n");
-               dev->isoc_ctl.buf = NULL;
+               dev->isoc_ctl.vid_buf = NULL;
                *buf = NULL;
                return;
        }
@@ -282,7 +393,34 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
        outp = videobuf_to_vmalloc(&(*buf)->vb);
        memset(outp, 0, (*buf)->vb.size);
 
-       dev->isoc_ctl.buf = *buf;
+       dev->isoc_ctl.vid_buf = *buf;
+
+       return;
+}
+
+/*
+ * video-buf generic routine to get the next available VBI buffer
+ */
+static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q,
+                                   struct em28xx_buffer **buf)
+{
+       struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq);
+       char *outp;
+
+       if (list_empty(&dma_q->active)) {
+               em28xx_isocdbg("No active queue to serve\n");
+               dev->isoc_ctl.vbi_buf = NULL;
+               *buf = NULL;
+               return;
+       }
+
+       /* Get the next buffer */
+       *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
+       /* Cleans up buffer - Usefull for testing for frame/URB loss */
+       outp = videobuf_to_vmalloc(&(*buf)->vb);
+       memset(outp, 0x00, (*buf)->vb.size);
+
+       dev->isoc_ctl.vbi_buf = *buf;
 
        return;
 }
@@ -293,7 +431,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
 static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
 {
        struct em28xx_buffer    *buf;
-       struct em28xx_dmaqueue  *dma_q = urb->context;
+       struct em28xx_dmaqueue  *dma_q = &dev->vidq;
        unsigned char *outp = NULL;
        int i, len = 0, rc = 1;
        unsigned char *p;
@@ -310,7 +448,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
                        return 0;
        }
 
-       buf = dev->isoc_ctl.buf;
+       buf = dev->isoc_ctl.vid_buf;
        if (buf != NULL)
                outp = videobuf_to_vmalloc(&buf->vb);
 
@@ -347,9 +485,9 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
                }
                if (p[0] == 0x22 && p[1] == 0x5a) {
                        em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
-                                      len, (p[2] & 1)? "odd" : "even");
+                                      len, (p[2] & 1) ? "odd" : "even");
 
-                       if (!(p[2] & 1)) {
+                       if (dev->progressive || !(p[2] & 1)) {
                                if (buf != NULL)
                                        buffer_filled(dev, dma_q, buf);
                                get_next_buf(dma_q, &buf);
@@ -374,6 +512,153 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
        return rc;
 }
 
+/* Version of isoc handler that takes into account a mixture of video and
+   VBI data */
+static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
+{
+       struct em28xx_buffer    *buf, *vbi_buf;
+       struct em28xx_dmaqueue  *dma_q = &dev->vidq;
+       struct em28xx_dmaqueue  *vbi_dma_q = &dev->vbiq;
+       unsigned char *outp = NULL;
+       unsigned char *vbioutp = NULL;
+       int i, len = 0, rc = 1;
+       unsigned char *p;
+       int vbi_size;
+
+       if (!dev)
+               return 0;
+
+       if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+               return 0;
+
+       if (urb->status < 0) {
+               print_err_status(dev, -1, urb->status);
+               if (urb->status == -ENOENT)
+                       return 0;
+       }
+
+       buf = dev->isoc_ctl.vid_buf;
+       if (buf != NULL)
+               outp = videobuf_to_vmalloc(&buf->vb);
+
+       vbi_buf = dev->isoc_ctl.vbi_buf;
+       if (vbi_buf != NULL)
+               vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
+
+       for (i = 0; i < urb->number_of_packets; i++) {
+               int status = urb->iso_frame_desc[i].status;
+
+               if (status < 0) {
+                       print_err_status(dev, i, status);
+                       if (urb->iso_frame_desc[i].status != -EPROTO)
+                               continue;
+               }
+
+               len = urb->iso_frame_desc[i].actual_length - 4;
+
+               if (urb->iso_frame_desc[i].actual_length <= 0) {
+                       /* em28xx_isocdbg("packet %d is empty",i); - spammy */
+                       continue;
+               }
+               if (urb->iso_frame_desc[i].actual_length >
+                                               dev->max_pkt_size) {
+                       em28xx_isocdbg("packet bigger than packet size");
+                       continue;
+               }
+
+               p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+               /* capture type 0 = vbi start
+                  capture type 1 = video start
+                  capture type 2 = video in progress */
+               if (p[0] == 0x33 && p[1] == 0x95) {
+                       dev->capture_type = 0;
+                       dev->vbi_read = 0;
+                       em28xx_isocdbg("VBI START HEADER!!!\n");
+                       dev->cur_field = p[2];
+               }
+
+               /* FIXME: get rid of hard-coded value */
+               vbi_size = 720 * 0x0c;
+
+               if (dev->capture_type == 0) {
+                       if (dev->vbi_read >= vbi_size) {
+                               /* We've already read all the VBI data, so
+                                  treat the rest as video */
+                               em28xx_isocdbg("dev->vbi_read > vbi_size\n");
+                       } else if ((dev->vbi_read + len) < vbi_size) {
+                               /* This entire frame is VBI data */
+                               if (dev->vbi_read == 0 &&
+                                   (!(dev->cur_field & 1))) {
+                                       /* Brand new frame */
+                                       if (vbi_buf != NULL)
+                                               vbi_buffer_filled(dev,
+                                                                 vbi_dma_q,
+                                                                 vbi_buf);
+                                       vbi_get_next_buf(vbi_dma_q, &vbi_buf);
+                                       if (vbi_buf == NULL)
+                                               vbioutp = NULL;
+                                       else
+                                               vbioutp = videobuf_to_vmalloc(
+                                                       &vbi_buf->vb);
+                               }
+
+                               if (dev->vbi_read == 0) {
+                                       vbi_dma_q->pos = 0;
+                                       if (vbi_buf != NULL) {
+                                               if (dev->cur_field & 1)
+                                                       vbi_buf->top_field = 0;
+                                               else
+                                                       vbi_buf->top_field = 1;
+                                       }
+                               }
+
+                               dev->vbi_read += len;
+                               em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+                                               vbioutp, len);
+                       } else {
+                               /* Some of this frame is VBI data and some is
+                                  video data */
+                               int vbi_data_len = vbi_size - dev->vbi_read;
+                               dev->vbi_read += vbi_data_len;
+                               em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+                                               vbioutp, vbi_data_len);
+                               dev->capture_type = 1;
+                               p += vbi_data_len;
+                               len -= vbi_data_len;
+                       }
+               }
+
+               if (dev->capture_type == 1) {
+                       dev->capture_type = 2;
+                       em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
+                                      len, (p[2] & 1) ? "odd" : "even");
+
+                       if (dev->progressive || !(dev->cur_field & 1)) {
+                               if (buf != NULL)
+                                       buffer_filled(dev, dma_q, buf);
+                               get_next_buf(dma_q, &buf);
+                               if (buf == NULL)
+                                       outp = NULL;
+                               else
+                                       outp = videobuf_to_vmalloc(&buf->vb);
+                       }
+                       if (buf != NULL) {
+                               if (dev->cur_field & 1)
+                                       buf->top_field = 0;
+                               else
+                                       buf->top_field = 1;
+                       }
+
+                       dma_q->pos = 0;
+               }
+               if (buf != NULL && dev->capture_type == 2)
+                       em28xx_copy_video(dev, dma_q, buf, p, outp, len);
+       }
+       return rc;
+}
+
+
 /* ------------------------------------------------------------------
        Videobuf operations
    ------------------------------------------------------------------*/
@@ -385,7 +670,8 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
        struct em28xx        *dev = fh->dev;
        struct v4l2_frequency f;
 
-       *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
+       *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)
+               >> 3;
 
        if (0 == *count)
                *count = EM28XX_DEF_BUF;
@@ -398,7 +684,7 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
        f.frequency = dev->ctl_freq;
        f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 
-       em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
 
        return 0;
 }
@@ -422,8 +708,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
           VIDEOBUF_ACTIVE, it won't be, though.
        */
        spin_lock_irqsave(&dev->slock, flags);
-       if (dev->isoc_ctl.buf == buf)
-               dev->isoc_ctl.buf = NULL;
+       if (dev->isoc_ctl.vid_buf == buf)
+               dev->isoc_ctl.vid_buf = NULL;
        spin_unlock_irqrestore(&dev->slock, flags);
 
        videobuf_vmalloc_free(&buf->vb);
@@ -439,7 +725,8 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
        struct em28xx        *dev = fh->dev;
        int                  rc = 0, urb_init = 0;
 
-       buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
+       buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
+                       + 7) >> 3;
 
        if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
                return -EINVAL;
@@ -458,9 +745,16 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
                urb_init = 1;
 
        if (urb_init) {
-               rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
-                                     EM28XX_NUM_BUFS, dev->max_pkt_size,
-                                     em28xx_isoc_copy);
+               if (em28xx_vbi_supported(dev) == 1)
+                       rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+                                             EM28XX_NUM_BUFS,
+                                             dev->max_pkt_size,
+                                             em28xx_isoc_copy_vbi);
+               else
+                       rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+                                             EM28XX_NUM_BUFS,
+                                             dev->max_pkt_size,
+                                             em28xx_isoc_copy);
                if (rc < 0)
                        goto fail;
        }
@@ -476,7 +770,9 @@ fail:
 static void
 buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 {
-       struct em28xx_buffer    *buf     = container_of(vb, struct em28xx_buffer, vb);
+       struct em28xx_buffer    *buf     = container_of(vb,
+                                                       struct em28xx_buffer,
+                                                       vb);
        struct em28xx_fh        *fh      = vq->priv_data;
        struct em28xx           *dev     = fh->dev;
        struct em28xx_dmaqueue  *vidq    = &dev->vidq;
@@ -489,7 +785,9 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 static void buffer_release(struct videobuf_queue *vq,
                                struct videobuf_buffer *vb)
 {
-       struct em28xx_buffer   *buf  = container_of(vb, struct em28xx_buffer, vb);
+       struct em28xx_buffer   *buf  = container_of(vb,
+                                                   struct em28xx_buffer,
+                                                   vb);
        struct em28xx_fh       *fh   = vq->priv_data;
        struct em28xx          *dev  = (struct em28xx *)fh->dev;
 
@@ -509,10 +807,6 @@ static struct videobuf_queue_ops em28xx_video_qops = {
 
 static void video_mux(struct em28xx *dev, int index)
 {
-       struct v4l2_routing route;
-
-       route.input = INPUT(index)->vmux;
-       route.output = 0;
        dev->ctl_input = index;
        dev->ctl_ainput = INPUT(index)->amux;
        dev->ctl_aoutput = INPUT(index)->aout;
@@ -520,59 +814,111 @@ static void video_mux(struct em28xx *dev, int index)
        if (!dev->ctl_aoutput)
                dev->ctl_aoutput = EM28XX_AOUT_MASTER;
 
-       em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+                       INPUT(index)->vmux, 0, 0);
 
        if (dev->board.has_msp34xx) {
                if (dev->i2s_speed) {
-                       em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ,
-                               &dev->i2s_speed);
+                       v4l2_device_call_all(&dev->v4l2_dev, 0, audio,
+                               s_i2s_clock_freq, dev->i2s_speed);
                }
-               route.input = dev->ctl_ainput;
-               route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
                /* Note: this is msp3400 specific */
-               em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING,
-                       &route);
+               v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+                        dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
+       }
+
+       if (dev->board.adecoder != EM28XX_NOADECODER) {
+               v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+                       dev->ctl_ainput, dev->ctl_aoutput, 0);
        }
 
        em28xx_audio_analog_set(dev);
 }
 
 /* Usage lock check functions */
-static int res_get(struct em28xx_fh *fh)
+static int res_get(struct em28xx_fh *fh, unsigned int bit)
 {
        struct em28xx    *dev = fh->dev;
-       int              rc   = 0;
 
-       /* This instance already has stream_on */
-       if (fh->stream_on)
-               return rc;
+       if (fh->resources & bit)
+               /* have it already allocated */
+               return 1;
 
-       if (dev->stream_on)
-               return -EBUSY;
+       /* is it free? */
+       mutex_lock(&dev->lock);
+       if (dev->resources & bit) {
+               /* no, someone else uses it */
+               mutex_unlock(&dev->lock);
+               return 0;
+       }
+       /* it's free, grab it */
+       fh->resources  |= bit;
+       dev->resources |= bit;
+       em28xx_videodbg("res: get %d\n", bit);
+       mutex_unlock(&dev->lock);
+       return 1;
+}
 
-       dev->stream_on = 1;
-       fh->stream_on  = 1;
-       return rc;
+static int res_check(struct em28xx_fh *fh, unsigned int bit)
+{
+       return fh->resources & bit;
 }
 
-static int res_check(struct em28xx_fh *fh)
+static int res_locked(struct em28xx *dev, unsigned int bit)
 {
-       return (fh->stream_on);
+       return dev->resources & bit;
 }
 
-static void res_free(struct em28xx_fh *fh)
+static void res_free(struct em28xx_fh *fh, unsigned int bits)
 {
        struct em28xx    *dev = fh->dev;
 
-       fh->stream_on = 0;
-       dev->stream_on = 0;
+       BUG_ON((fh->resources & bits) != bits);
+
+       mutex_lock(&dev->lock);
+       fh->resources  &= ~bits;
+       dev->resources &= ~bits;
+       em28xx_videodbg("res: put %d\n", bits);
+       mutex_unlock(&dev->lock);
+}
+
+static int get_ressource(struct em28xx_fh *fh)
+{
+       switch (fh->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               return EM28XX_RESOURCE_VIDEO;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               return EM28XX_RESOURCE_VBI;
+       default:
+               BUG();
+               return 0;
+       }
 }
 
 /*
- * em28xx_get_ctrl()
- * return the current saturation, brightness or contrast, mute state
+ * ac97_queryctrl()
+ * return the ac97 supported controls
  */
-static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+static int ac97_queryctrl(struct v4l2_queryctrl *qc)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+               if (qc->id && qc->id == ac97_qctrl[i].id) {
+                       memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
+                       return 0;
+               }
+       }
+
+       /* Control is not ac97 related */
+       return 1;
+}
+
+/*
+ * ac97_get_ctrl()
+ * return the current values for ac97 mute and volume
+ */
+static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
 {
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
@@ -582,29 +928,41 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
                ctrl->value = dev->volume;
                return 0;
        default:
-               return -EINVAL;
+               /* Control is not ac97 related */
+               return 1;
        }
 }
 
 /*
- * em28xx_set_ctrl()
- * mute or set new saturation, brightness or contrast
+ * ac97_set_ctrl()
+ * set values for ac97 mute and volume
  */
-static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
 {
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++)
+               if (ctrl->id == ac97_qctrl[i].id)
+                       goto handle;
+
+       /* Announce that hasn't handle it */
+       return 1;
+
+handle:
+       if (ctrl->value < ac97_qctrl[i].minimum ||
+           ctrl->value > ac97_qctrl[i].maximum)
+               return -ERANGE;
+
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
-               if (ctrl->value != dev->mute) {
-                       dev->mute = ctrl->value;
-                       return em28xx_audio_analog_set(dev);
-               }
-               return 0;
+               dev->mute = ctrl->value;
+               break;
        case V4L2_CID_AUDIO_VOLUME:
                dev->volume = ctrl->value;
-               return em28xx_audio_analog_set(dev);
-       default:
-               return -EINVAL;
+               break;
        }
+
+       return em28xx_audio_analog_set(dev);
 }
 
 static int check_dev(struct em28xx *dev)
@@ -626,8 +984,8 @@ static void get_scale(struct em28xx *dev,
                        unsigned int width, unsigned int height,
                        unsigned int *hscale, unsigned int *vscale)
 {
-       unsigned int          maxw   = norm_maxw(dev);
-       unsigned int          maxh   = norm_maxh(dev);
+       unsigned int          maxw = norm_maxw(dev);
+       unsigned int          maxh = norm_maxh(dev);
 
        *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
        if (*hscale >= 0x4000)
@@ -658,7 +1016,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
        f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
 
        /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
-       f->fmt.pix.field = dev->interlaced ?
+       if (dev->progressive)
+               f->fmt.pix.field = V4L2_FIELD_NONE;
+       else
+               f->fmt.pix.field = dev->interlaced ?
                           V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
 
        mutex_unlock(&dev->lock);
@@ -681,8 +1042,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 {
        struct em28xx_fh      *fh    = priv;
        struct em28xx         *dev   = fh->dev;
-       int                   width  = f->fmt.pix.width;
-       int                   height = f->fmt.pix.height;
+       unsigned int          width  = f->fmt.pix.width;
+       unsigned int          height = f->fmt.pix.height;
        unsigned int          maxw   = norm_maxw(dev);
        unsigned int          maxh   = norm_maxh(dev);
        unsigned int          hscale, vscale;
@@ -695,34 +1056,21 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                return -EINVAL;
        }
 
-       /* width must even because of the YUYV format
-          height must be even because of interlacing */
-       height &= 0xfffe;
-       width  &= 0xfffe;
-
-       if (unlikely(height < 32))
-               height = 32;
-       if (unlikely(height > maxh))
-               height = maxh;
-       if (unlikely(width < 48))
-               width = 48;
-       if (unlikely(width > maxw))
-               width = maxw;
-
        if (dev->board.is_em2800) {
                /* the em2800 can only scale down to 50% */
-               if (height % (maxh / 2))
-                       height = maxh;
-               if (width % (maxw / 2))
-                       width = maxw;
-               /* according to empiatech support */
-               /* the MaxPacketSize is to small to support */
-               /* framesizes larger than 640x480 @ 30 fps */
-               /* or 640x576 @ 25 fps. As this would cut */
-               /* of a part of the image we prefer */
-               /* 360x576 or 360x480 for now */
+               height = height > (3 * maxh / 4) ? maxh : maxh / 2;
+               width = width > (3 * maxw / 4) ? maxw : maxw / 2;
+               /* According to empiatech support the MaxPacketSize is too small
+                * to support framesizes larger than 640x480 @ 30 fps or 640x576
+                * @ 25 fps.  As this would cut of a part of the image we prefer
+                * 360x576 or 360x480 for now */
                if (width == maxw && height == maxh)
                        width /= 2;
+       } else {
+               /* width must even because of the YUYV format
+                  height must be even because of interlacing */
+               v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
+                                     1, 0);
        }
 
        get_scale(dev, width, height, &hscale, &vscale);
@@ -736,7 +1084,33 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
        f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
        f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
        f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-       f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+       if (dev->progressive)
+               f->fmt.pix.field = V4L2_FIELD_NONE;
+       else
+               f->fmt.pix.field = dev->interlaced ?
+                          V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
+
+       return 0;
+}
+
+static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
+                                  unsigned width, unsigned height)
+{
+       struct em28xx_fmt     *fmt;
+
+       fmt = format_by_fourcc(fourcc);
+       if (!fmt)
+               return -EINVAL;
+
+       dev->format = fmt;
+       dev->width  = width;
+       dev->height = height;
+
+       /* set new image size */
+       get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+       em28xx_set_alternate(dev);
+       em28xx_resolution_set(dev);
 
        return 0;
 }
@@ -747,7 +1121,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
        int                   rc;
-       struct em28xx_fmt     *fmt;
 
        rc = check_dev(dev);
        if (rc < 0)
@@ -757,41 +1130,36 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 
        vidioc_try_fmt_vid_cap(file, priv, f);
 
-       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-       if (!fmt) {
-               rc = -EINVAL;
-               goto out;
-       }
-
        if (videobuf_queue_is_busy(&fh->vb_vidq)) {
                em28xx_errdev("%s queue busy\n", __func__);
                rc = -EBUSY;
                goto out;
        }
 
-       if (dev->stream_on && !fh->stream_on) {
-               em28xx_errdev("%s device in use by another fh\n", __func__);
-               rc = -EBUSY;
-               goto out;
-       }
-
-       /* set new image size */
-       dev->width = f->fmt.pix.width;
-       dev->height = f->fmt.pix.height;
-       dev->format = fmt;
-       get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
-
-       em28xx_set_alternate(dev);
-       em28xx_resolution_set(dev);
-
-       rc = 0;
+       rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
+                               f->fmt.pix.width, f->fmt.pix.height);
 
 out:
        mutex_unlock(&dev->lock);
        return rc;
 }
 
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+       struct em28xx_fh   *fh  = priv;
+       struct em28xx      *dev = fh->dev;
+       int                rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       *norm = dev->norm;
+
+       return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
 {
        struct em28xx_fh   *fh  = priv;
        struct em28xx      *dev = fh->dev;
@@ -816,12 +1184,47 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
        get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
 
        em28xx_resolution_set(dev);
-       em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->norm);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
 
        mutex_unlock(&dev->lock);
        return 0;
 }
 
+static int vidioc_g_parm(struct file *file, void *priv,
+                        struct v4l2_streamparm *p)
+{
+       struct em28xx_fh   *fh  = priv;
+       struct em28xx      *dev = fh->dev;
+       int rc = 0;
+
+       if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       if (dev->board.is_webcam)
+               rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0,
+                                               video, g_parm, p);
+       else
+               v4l2_video_std_frame_period(dev->norm,
+                                                &p->parm.capture.timeperframe);
+
+       return rc;
+}
+
+static int vidioc_s_parm(struct file *file, void *priv,
+                        struct v4l2_streamparm *p)
+{
+       struct em28xx_fh   *fh  = priv;
+       struct em28xx      *dev = fh->dev;
+
+       if (!dev->board.is_webcam)
+               return -EINVAL;
+
+       if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p);
+}
+
 static const char *iname[] = {
        [EM28XX_VMUX_COMPOSITE1] = "Composite1",
        [EM28XX_VMUX_COMPOSITE2] = "Composite2",
@@ -899,6 +1302,9 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
        struct em28xx_fh   *fh    = priv;
        struct em28xx      *dev   = fh->dev;
 
+       if (!dev->audio_mode.has_audio)
+               return -EINVAL;
+
        switch (a->index) {
        case EM28XX_AMUX_VIDEO:
                strcpy(a->name, "Television");
@@ -940,6 +1346,9 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
        struct em28xx      *dev = fh->dev;
 
 
+       if (!dev->audio_mode.has_audio)
+               return -EINVAL;
+
        if (a->index >= MAX_EM28XX_INPUT)
                return -EINVAL;
        if (0 == INPUT(a->index)->type)
@@ -963,7 +1372,6 @@ static int vidioc_queryctrl(struct file *file, void *priv,
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
        int                   id  = qc->id;
-       int                   i;
        int                   rc;
 
        rc = check_dev(dev);
@@ -974,16 +1382,16 @@ static int vidioc_queryctrl(struct file *file, void *priv,
 
        qc->id = id;
 
-       if (!dev->board.has_msp34xx) {
-               for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
-                       if (qc->id && qc->id == em28xx_qctrl[i].id) {
-                               memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
-                               return 0;
-                       }
-               }
+       /* enumberate AC97 controls */
+       if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+               rc = ac97_queryctrl(qc);
+               if (!rc)
+                       return 0;
        }
+
+       /* enumberate V4L2 device controls */
        mutex_lock(&dev->lock);
-       em28xx_i2c_call_clients(dev, VIDIOC_QUERYCTRL, qc);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
        mutex_unlock(&dev->lock);
 
        if (qc->type)
@@ -1006,10 +1414,17 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
 
        mutex_lock(&dev->lock);
 
-       if (dev->board.has_msp34xx)
-               em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
+       /* Set an AC97 control */
+       if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+               rc = ac97_get_ctrl(dev, ctrl);
        else
-               rc = em28xx_get_ctrl(dev, ctrl);
+               rc = 1;
+
+       /* It were not an AC97 control. Sends it to the v4l2 dev interface */
+       if (rc == 1) {
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
+               rc = 0;
+       }
 
        mutex_unlock(&dev->lock);
        return rc;
@@ -1020,7 +1435,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 {
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
-       u8                    i;
        int                   rc;
 
        rc = check_dev(dev);
@@ -1029,28 +1443,31 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 
        mutex_lock(&dev->lock);
 
-       if (dev->board.has_msp34xx)
-               em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
-       else {
+       /* Set an AC97 control */
+       if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+               rc = ac97_set_ctrl(dev, ctrl);
+       else
                rc = 1;
-               for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
-                       if (ctrl->id == em28xx_qctrl[i].id) {
-                               if (ctrl->value < em28xx_qctrl[i].minimum ||
-                                   ctrl->value > em28xx_qctrl[i].maximum) {
-                                       rc = -ERANGE;
-                                       break;
-                               }
 
-                               rc = em28xx_set_ctrl(dev, ctrl);
-                               break;
-                       }
-               }
-       }
-
-       /* Control not found - try to send it to the attached devices */
+       /* It isn't an AC97 control. Sends it to the v4l2 dev interface */
        if (rc == 1) {
-               em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
-               rc = 0;
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
+
+               /*
+                * In the case of non-AC97 volume controls, we still need
+                * to do some setups at em28xx, in order to mute/unmute
+                * and to adjust audio volume. However, the value ranges
+                * should be checked by the corresponding V4L subdriver.
+                */
+               switch (ctrl->id) {
+               case V4L2_CID_AUDIO_MUTE:
+                       dev->mute = ctrl->value;
+                       rc = em28xx_audio_analog_set(dev);
+                       break;
+               case V4L2_CID_AUDIO_VOLUME:
+                       dev->volume = ctrl->value;
+                       rc = em28xx_audio_analog_set(dev);
+               }
        }
 
        mutex_unlock(&dev->lock);
@@ -1074,10 +1491,9 @@ static int vidioc_g_tuner(struct file *file, void *priv,
        strcpy(t->name, "Tuner");
 
        mutex_lock(&dev->lock);
-
-       em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
-
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
        mutex_unlock(&dev->lock);
+
        return 0;
 }
 
@@ -1096,10 +1512,9 @@ static int vidioc_s_tuner(struct file *file, void *priv,
                return -EINVAL;
 
        mutex_lock(&dev->lock);
-
-       em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
-
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
        mutex_unlock(&dev->lock);
+
        return 0;
 }
 
@@ -1139,7 +1554,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
        mutex_lock(&dev->lock);
 
        dev->ctl_freq = f->frequency;
-       em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
 
        mutex_unlock(&dev->lock);
 
@@ -1168,7 +1583,7 @@ static int vidioc_g_chip_ident(struct file *file, void *priv,
        chip->ident = V4L2_IDENT_NONE;
        chip->revision = 0;
 
-       em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_CHIP_IDENT, chip);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
 
        return 0;
 }
@@ -1193,11 +1608,12 @@ static int vidioc_g_register(struct file *file, void *priv,
                reg->size = 1;
                return 0;
        case V4L2_CHIP_MATCH_I2C_DRIVER:
-               em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg);
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
                return 0;
        case V4L2_CHIP_MATCH_I2C_ADDR:
-               /* Not supported yet */
-               return -EINVAL;
+               /* TODO: is this correct? */
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+               return 0;
        default:
                if (!v4l2_chip_match_host(&reg->match))
                        return -EINVAL;
@@ -1245,11 +1661,12 @@ static int vidioc_s_register(struct file *file, void *priv,
 
                return rc;
        case V4L2_CHIP_MATCH_I2C_DRIVER:
-               em28xx_i2c_call_clients(dev, VIDIOC_DBG_S_REGISTER, reg);
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
                return 0;
        case V4L2_CHIP_MATCH_I2C_ADDR:
-               /* Not supported yet */
-               return -EINVAL;
+               /* TODO: is this correct? */
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+               return 0;
        default:
                if (!v4l2_chip_match_host(&reg->match))
                        return -EINVAL;
@@ -1293,20 +1710,25 @@ static int vidioc_streamon(struct file *file, void *priv,
 {
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
-       int                   rc;
+       int                   rc = -EINVAL;
 
        rc = check_dev(dev);
        if (rc < 0)
                return rc;
 
+       if (unlikely(type != fh->type))
+               return -EINVAL;
 
-       mutex_lock(&dev->lock);
-       rc = res_get(fh);
+       em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
+                       fh, type, fh->resources, dev->resources);
 
-       if (likely(rc >= 0))
-               rc = videobuf_streamon(&fh->vb_vidq);
+       if (unlikely(!res_get(fh, get_ressource(fh))))
+               return -EBUSY;
 
-       mutex_unlock(&dev->lock);
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               rc = videobuf_streamon(&fh->vb_vidq);
+       else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               rc = videobuf_streamon(&fh->vb_vbiq);
 
        return rc;
 }
@@ -1322,17 +1744,22 @@ static int vidioc_streamoff(struct file *file, void *priv,
        if (rc < 0)
                return rc;
 
-       if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+       if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
                return -EINVAL;
        if (type != fh->type)
                return -EINVAL;
 
-       mutex_lock(&dev->lock);
-
-       videobuf_streamoff(&fh->vb_vidq);
-       res_free(fh);
+       em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
+                       fh, type, fh->resources, dev->resources);
 
-       mutex_unlock(&dev->lock);
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               videobuf_streamoff(&fh->vb_vidq);
+               res_free(fh, EM28XX_RESOURCE_VIDEO);
+       } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               videobuf_streamoff(&fh->vb_vbiq);
+               res_free(fh, EM28XX_RESOURCE_VBI);
+       }
 
        return 0;
 }
@@ -1345,16 +1772,21 @@ static int vidioc_querycap(struct file *file, void  *priv,
 
        strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
        strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
-       strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
+       usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
 
        cap->version = EM28XX_VERSION_CODE;
 
        cap->capabilities =
                        V4L2_CAP_SLICED_VBI_CAPTURE |
                        V4L2_CAP_VIDEO_CAPTURE |
-                       V4L2_CAP_AUDIO |
                        V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
 
+       if (dev->vbi_dev)
+               cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
+
+       if (dev->audio_mode.has_audio)
+               cap->capabilities |= V4L2_CAP_AUDIO;
+
        if (dev->tuner_type != TUNER_ABSENT)
                cap->capabilities |= V4L2_CAP_TUNER;
 
@@ -1388,13 +1820,13 @@ static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
        mutex_lock(&dev->lock);
 
        f->fmt.sliced.service_set = 0;
-
-       em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, g_fmt, f);
 
        if (f->fmt.sliced.service_set == 0)
                rc = -EINVAL;
 
        mutex_unlock(&dev->lock);
+
        return rc;
 }
 
@@ -1410,7 +1842,7 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
                return rc;
 
        mutex_lock(&dev->lock);
-       em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, g_fmt, f);
        mutex_unlock(&dev->lock);
 
        if (f->fmt.sliced.service_set == 0)
@@ -1419,6 +1851,45 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
        return 0;
 }
 
+/* RAW VBI ioctls */
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+                               struct v4l2_format *format)
+{
+       format->fmt.vbi.samples_per_line = 720;
+       format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+       format->fmt.vbi.offset = 0;
+       format->fmt.vbi.flags = 0;
+
+       /* Varies by video standard (NTSC, PAL, etc.) */
+       /* FIXME: hard-coded for NTSC support */
+       format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */
+       format->fmt.vbi.count[0] = 12;
+       format->fmt.vbi.count[1] = 12;
+       format->fmt.vbi.start[0] = 10;
+       format->fmt.vbi.start[1] = 273;
+
+       return 0;
+}
+
+static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
+                               struct v4l2_format *format)
+{
+       format->fmt.vbi.samples_per_line = 720;
+       format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+       format->fmt.vbi.offset = 0;
+       format->fmt.vbi.flags = 0;
+
+       /* Varies by video standard (NTSC, PAL, etc.) */
+       /* FIXME: hard-coded for NTSC support */
+       format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */
+       format->fmt.vbi.count[0] = 12;
+       format->fmt.vbi.count[1] = 12;
+       format->fmt.vbi.start[0] = 10;
+       format->fmt.vbi.start[1] = 273;
+
+       return 0;
+}
 
 static int vidioc_reqbufs(struct file *file, void *priv,
                          struct v4l2_requestbuffers *rb)
@@ -1431,7 +1902,10 @@ static int vidioc_reqbufs(struct file *file, void *priv,
        if (rc < 0)
                return rc;
 
-       return (videobuf_reqbufs(&fh->vb_vidq, rb));
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return videobuf_reqbufs(&fh->vb_vidq, rb);
+       else
+               return videobuf_reqbufs(&fh->vb_vbiq, rb);
 }
 
 static int vidioc_querybuf(struct file *file, void *priv,
@@ -1445,7 +1919,18 @@ static int vidioc_querybuf(struct file *file, void *priv,
        if (rc < 0)
                return rc;
 
-       return (videobuf_querybuf(&fh->vb_vidq, b));
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return videobuf_querybuf(&fh->vb_vidq, b);
+       else {
+               /* FIXME: I'm not sure yet whether this is a bug in zvbi or
+                  the videobuf framework, but we probably shouldn't be
+                  returning a buffer larger than that which was asked for.
+                  At a minimum, it causes a crash in zvbi since it does
+                  a memcpy based on the source buffer length */
+               int result = videobuf_querybuf(&fh->vb_vbiq, b);
+               b->length = 17280;
+               return result;
+       }
 }
 
 static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
@@ -1458,7 +1943,10 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
        if (rc < 0)
                return rc;
 
-       return (videobuf_qbuf(&fh->vb_vidq, b));
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return videobuf_qbuf(&fh->vb_vidq, b);
+       else
+               return videobuf_qbuf(&fh->vb_vbiq, b);
 }
 
 static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
@@ -1471,8 +1959,12 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
        if (rc < 0)
                return rc;
 
-       return (videobuf_dqbuf(&fh->vb_vidq, b,
-                               file->f_flags & O_NONBLOCK));
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags &
+                                     O_NONBLOCK);
+       else
+               return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags &
+                                     O_NONBLOCK);
 }
 
 #ifdef CONFIG_VIDEO_V4L1_COMPAT
@@ -1480,7 +1972,10 @@ static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
 {
        struct em28xx_fh  *fh = priv;
 
-       return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
+       else
+               return videobuf_cgmbuf(&fh->vb_vbiq, mbuf, 8);
 }
 #endif
 
@@ -1496,7 +1991,7 @@ static int radio_querycap(struct file *file, void  *priv,
 
        strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
        strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
-       strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
+       usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
 
        cap->version = EM28XX_VERSION_CODE;
        cap->capabilities = V4L2_CAP_TUNER;
@@ -1515,7 +2010,7 @@ static int radio_g_tuner(struct file *file, void *priv,
        t->type = V4L2_TUNER_RADIO;
 
        mutex_lock(&dev->lock);
-       em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
        mutex_unlock(&dev->lock);
 
        return 0;
@@ -1550,7 +2045,7 @@ static int radio_s_tuner(struct file *file, void *priv,
                return -EINVAL;
 
        mutex_lock(&dev->lock);
-       em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
        mutex_unlock(&dev->lock);
 
        return 0;
@@ -1576,9 +2071,9 @@ static int radio_queryctrl(struct file *file, void *priv,
                qc->id >= V4L2_CID_LASTP1)
                return -EINVAL;
 
-       for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
-               if (qc->id && qc->id == em28xx_qctrl[i].id) {
-                       memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+       for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+               if (qc->id && qc->id == ac97_qctrl[i].id) {
+                       memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
                        return 0;
                }
        }
@@ -1597,6 +2092,7 @@ static int em28xx_v4l2_open(struct file *filp)
        struct em28xx *dev;
        enum v4l2_buf_type fh_type;
        struct em28xx_fh *fh;
+       enum v4l2_field field;
 
        dev = em28xx_get_device(minor, &fh_type, &radio);
 
@@ -1621,11 +2117,6 @@ static int em28xx_v4l2_open(struct file *filp)
        filp->private_data = fh;
 
        if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
-               dev->width = norm_maxw(dev);
-               dev->height = norm_maxh(dev);
-               dev->hscale = 0;
-               dev->vscale = 0;
-
                em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
                em28xx_set_alternate(dev);
                em28xx_resolution_set(dev);
@@ -1638,14 +2129,26 @@ static int em28xx_v4l2_open(struct file *filp)
        }
        if (fh->radio) {
                em28xx_videodbg("video_open: setting radio device\n");
-               em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL);
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
        }
 
        dev->users++;
 
+       if (dev->progressive)
+               field = V4L2_FIELD_NONE;
+       else
+               field = V4L2_FIELD_INTERLACED;
+
        videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
-                       NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED,
-                       sizeof(struct em28xx_buffer), fh);
+                                   NULL, &dev->slock,
+                                   V4L2_BUF_TYPE_VIDEO_CAPTURE, field,
+                                   sizeof(struct em28xx_buffer), fh);
+
+       videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops,
+                                   NULL, &dev->slock,
+                                   V4L2_BUF_TYPE_VBI_CAPTURE,
+                                   V4L2_FIELD_SEQ_TB,
+                                   sizeof(struct em28xx_buffer), fh);
 
        mutex_unlock(&dev->lock);
 
@@ -1702,26 +2205,27 @@ static int em28xx_v4l2_close(struct file *filp)
 
        em28xx_videodbg("users=%d\n", dev->users);
 
+       if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
+               videobuf_stop(&fh->vb_vidq);
+               res_free(fh, EM28XX_RESOURCE_VIDEO);
+       }
 
-       mutex_lock(&dev->lock);
-       if (res_check(fh))
-               res_free(fh);
+       if (res_check(fh, EM28XX_RESOURCE_VBI)) {
+               videobuf_stop(&fh->vb_vbiq);
+               res_free(fh, EM28XX_RESOURCE_VBI);
+       }
 
        if (dev->users == 1) {
-               videobuf_stop(&fh->vb_vidq);
-               videobuf_mmap_free(&fh->vb_vidq);
-
                /* the device is already disconnect,
                   free the remaining resources */
                if (dev->state & DEV_DISCONNECTED) {
                        em28xx_release_resources(dev);
-                       mutex_unlock(&dev->lock);
                        kfree(dev);
                        return 0;
                }
 
                /* Save some power by putting tuner to sleep */
-               em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
 
                /* do this before setting alternate! */
                em28xx_uninit_isoc(dev);
@@ -1736,10 +2240,12 @@ static int em28xx_v4l2_close(struct file *filp)
                                        "0 (error=%i)\n", errCode);
                }
        }
+
+       videobuf_mmap_free(&fh->vb_vidq);
+       videobuf_mmap_free(&fh->vb_vbiq);
        kfree(fh);
        dev->users--;
        wake_up_interruptible_nr(&dev->open, 1);
-       mutex_unlock(&dev->lock);
        return 0;
 }
 
@@ -1764,16 +2270,22 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
         */
 
        if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               mutex_lock(&dev->lock);
-               rc = res_get(fh);
-               mutex_unlock(&dev->lock);
-
-               if (unlikely(rc < 0))
-                       return rc;
+               if (res_locked(dev, EM28XX_RESOURCE_VIDEO))
+                       return -EBUSY;
 
                return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
                                        filp->f_flags & O_NONBLOCK);
        }
+
+
+       if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               if (!res_get(fh, EM28XX_RESOURCE_VBI))
+                       return -EBUSY;
+
+               return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
+                                       filp->f_flags & O_NONBLOCK);
+       }
+
        return 0;
 }
 
@@ -1781,7 +2293,7 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
  * em28xx_v4l2_poll()
  * will allocate buffers when called for the first time
  */
-static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
+static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait)
 {
        struct em28xx_fh *fh = filp->private_data;
        struct em28xx *dev = fh->dev;
@@ -1791,17 +2303,17 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
        if (rc < 0)
                return rc;
 
-       mutex_lock(&dev->lock);
-       rc = res_get(fh);
-       mutex_unlock(&dev->lock);
-
-       if (unlikely(rc < 0))
-               return POLLERR;
-
-       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               if (!res_get(fh, EM28XX_RESOURCE_VIDEO))
+                       return POLLERR;
+               return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+       } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               if (!res_get(fh, EM28XX_RESOURCE_VBI))
+                       return POLLERR;
+               return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
+       } else {
                return POLLERR;
-
-       return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+       }
 }
 
 /*
@@ -1817,14 +2329,10 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
        if (rc < 0)
                return rc;
 
-       mutex_lock(&dev->lock);
-       rc = res_get(fh);
-       mutex_unlock(&dev->lock);
-
-       if (unlikely(rc < 0))
-               return rc;
-
-       rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+       else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
 
        em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
                (unsigned long)vma->vm_start,
@@ -1850,6 +2358,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_g_fmt_vid_cap       = vidioc_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap     = vidioc_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap       = vidioc_s_fmt_vid_cap,
+       .vidioc_g_fmt_vbi_cap       = vidioc_g_fmt_vbi_cap,
+       .vidioc_s_fmt_vbi_cap       = vidioc_s_fmt_vbi_cap,
        .vidioc_g_audio             = vidioc_g_audio,
        .vidioc_s_audio             = vidioc_s_audio,
        .vidioc_cropcap             = vidioc_cropcap,
@@ -1862,7 +2372,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_querybuf            = vidioc_querybuf,
        .vidioc_qbuf                = vidioc_qbuf,
        .vidioc_dqbuf               = vidioc_dqbuf,
+       .vidioc_g_std               = vidioc_g_std,
        .vidioc_s_std               = vidioc_s_std,
+       .vidioc_g_parm              = vidioc_g_parm,
+       .vidioc_s_parm              = vidioc_s_parm,
        .vidioc_enum_input          = vidioc_enum_input,
        .vidioc_g_input             = vidioc_g_input,
        .vidioc_s_input             = vidioc_s_input,
@@ -1934,19 +2447,20 @@ static struct video_device em28xx_radio_template = {
 
 
 static struct video_device *em28xx_vdev_init(struct em28xx *dev,
-                                            const struct video_device *template,
-                                            const char *type_name)
+                                       const struct video_device *template,
+                                       const char *type_name)
 {
        struct video_device *vfd;
 
        vfd = video_device_alloc();
        if (NULL == vfd)
                return NULL;
-       *vfd = *template;
-       vfd->minor   = -1;
-       vfd->parent = &dev->udev->dev;
-       vfd->release = video_device_release;
-       vfd->debug = video_debug;
+
+       *vfd            = *template;
+       vfd->minor      = -1;
+       vfd->v4l2_dev   = &dev->v4l2_dev;
+       vfd->release    = video_device_release;
+       vfd->debug      = video_debug;
 
        snprintf(vfd->name, sizeof(vfd->name), "%s %s",
                 dev->name, type_name);
@@ -1966,27 +2480,24 @@ int em28xx_register_analog_devices(struct em28xx *dev)
 
        /* set default norm */
        dev->norm = em28xx_video_template.current_norm;
-       dev->width = norm_maxw(dev);
-       dev->height = norm_maxh(dev);
        dev->interlaced = EM28XX_INTERLACED_DEFAULT;
-       dev->hscale = 0;
-       dev->vscale = 0;
        dev->ctl_input = 0;
 
        /* Analog specific initialization */
        dev->format = &format[0];
+       em28xx_set_video_format(dev, format[0].fourcc,
+                               norm_maxw(dev), norm_maxh(dev));
+
        video_mux(dev, dev->ctl_input);
 
        /* Audio defaults */
        dev->mute = 1;
        dev->volume = 0x1f;
 
-       /* enable vbi capturing */
-
 /*     em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
-       val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
-       em28xx_write_reg(dev, EM28XX_R0F_XCLK, (EM28XX_XCLK_AUDIO_UNMUTE | val));
-       em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
+       val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
+       em28xx_write_reg(dev, EM28XX_R0F_XCLK,
+                        (EM28XX_XCLK_AUDIO_UNMUTE | val));
 
        em28xx_set_outfmt(dev);
        em28xx_colorlevels_set_default(dev);
@@ -2009,18 +2520,22 @@ int em28xx_register_analog_devices(struct em28xx *dev)
        }
 
        /* Allocate and fill vbi video_device struct */
-       dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi");
+       if (em28xx_vbi_supported(dev) == 1) {
+               dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
+                                               "vbi");
 
-       /* register v4l2 vbi video_device */
-       ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
-                                       vbi_nr[dev->devno]);
-       if (ret < 0) {
-               em28xx_errdev("unable to register vbi device\n");
-               return ret;
+               /* register v4l2 vbi video_device */
+               ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+                                           vbi_nr[dev->devno]);
+               if (ret < 0) {
+                       em28xx_errdev("unable to register vbi device\n");
+                       return ret;
+               }
        }
 
        if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
-               dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, "radio");
+               dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
+                                                 "radio");
                if (!dev->radio_dev) {
                        em28xx_errdev("cannot allocate video_device.\n");
                        return -ENODEV;
@@ -2035,8 +2550,12 @@ int em28xx_register_analog_devices(struct em28xx *dev)
                            dev->radio_dev->num);
        }
 
-       em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
-                               dev->vdev->num, dev->vbi_dev->num);
+       em28xx_info("V4L2 video device registered as /dev/video%d\n",
+                               dev->vdev->num);
+
+       if (dev->vbi_dev)
+               em28xx_info("V4L2 VBI device registered as /dev/vbi%d\n",
+                           dev->vbi_dev->num);
 
        return 0;
 }