Fix common misspellings
[linux-2.6.git] / drivers / media / video / au0828 / au0828-video.c
index a2a0f79..c03eb29 100644 (file)
@@ -122,6 +122,7 @@ static void au0828_irq_callback(struct urb *urb)
 {
        struct au0828_dmaqueue  *dma_q = urb->context;
        struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
+       unsigned long flags = 0;
        int rc, i;
 
        switch (urb->status) {
@@ -139,9 +140,9 @@ static void au0828_irq_callback(struct urb *urb)
        }
 
        /* Copy data from URB */
-       spin_lock(&dev->slock);
+       spin_lock_irqsave(&dev->slock, flags);
        rc = dev->isoc_ctl.isoc_copy(dev, urb);
-       spin_unlock(&dev->slock);
+       spin_unlock_irqrestore(&dev->slock, flags);
 
        /* Reset urb buffers */
        for (i = 0; i < urb->number_of_packets; i++) {
@@ -314,6 +315,23 @@ static inline void buffer_filled(struct au0828_dev *dev,
        wake_up(&buf->vb.done);
 }
 
+static inline void vbi_buffer_filled(struct au0828_dev *dev,
+                                    struct au0828_dmaqueue *dma_q,
+                                    struct au0828_buffer *buf)
+{
+       /* Advice that buffer was filled */
+       au0828_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);
+}
+
 /*
  * Identify the buffer header type and properly handles
  */
@@ -327,6 +345,9 @@ static void au0828_copy_video(struct au0828_dev *dev,
        int  linesdone, currlinedone, offset, lencopy, remain;
        int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */
 
+       if (len == 0)
+               return;
+
        if (dma_q->pos + len > buf->vb.size)
                len = buf->vb.size - dma_q->pos;
 
@@ -414,17 +435,98 @@ static inline void get_next_buf(struct au0828_dmaqueue *dma_q,
        return;
 }
 
+static void au0828_copy_vbi(struct au0828_dev *dev,
+                             struct au0828_dmaqueue  *dma_q,
+                             struct au0828_buffer *buf,
+                             unsigned char *p,
+                             unsigned char *outp, unsigned long len)
+{
+       unsigned char *startwrite, *startread;
+       int bytesperline;
+       int i, j = 0;
+
+       if (dev == NULL) {
+               au0828_isocdbg("dev is null\n");
+               return;
+       }
+
+       if (dma_q == NULL) {
+               au0828_isocdbg("dma_q is null\n");
+               return;
+       }
+       if (buf == NULL)
+               return;
+       if (p == NULL) {
+               au0828_isocdbg("p is null\n");
+               return;
+       }
+       if (outp == NULL) {
+               au0828_isocdbg("outp is null\n");
+               return;
+       }
+
+       bytesperline = dev->vbi_width;
+
+       if (dma_q->pos + len > buf->vb.size)
+               len = buf->vb.size - dma_q->pos;
+
+       startread = p;
+       startwrite = outp + (dma_q->pos / 2);
+
+       /* Make sure the bottom field populates the second half of the frame */
+       if (buf->top_field == 0)
+               startwrite += bytesperline * dev->vbi_height;
+
+       for (i = 0; i < len; i += 2)
+               startwrite[j++] = startread[i+1];
+
+       dma_q->pos += len;
+}
+
+
+/*
+ * video-buf generic routine to get the next available VBI buffer
+ */
+static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q,
+                                   struct au0828_buffer **buf)
+{
+       struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq);
+       char *outp;
+
+       if (list_empty(&dma_q->active)) {
+               au0828_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 au0828_buffer, vb.queue);
+       /* Cleans up buffer - Useful 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;
+}
+
 /*
  * Controls the isoc copy of each urb packet
  */
 static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
 {
        struct au0828_buffer    *buf;
+       struct au0828_buffer    *vbi_buf;
        struct au0828_dmaqueue  *dma_q = urb->context;
+       struct au0828_dmaqueue  *vbi_dma_q = &dev->vbiq;
        unsigned char *outp = NULL;
+       unsigned char *vbioutp = NULL;
        int i, len = 0, rc = 1;
        unsigned char *p;
        unsigned char fbyte;
+       unsigned int vbi_field_size;
+       unsigned int remain, lencopy;
 
        if (!dev)
                return 0;
@@ -443,6 +545,10 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
        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;
 
@@ -471,7 +577,20 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
                        p += 4;
                        au0828_isocdbg("Video frame %s\n",
                                       (fbyte & 0x40) ? "odd" : "even");
-                       if (!(fbyte & 0x40)) {
+                       if (fbyte & 0x40) {
+                               /* VBI */
+                               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);
+
+                               /* Video */
                                if (buf != NULL)
                                        buffer_filled(dev, dma_q, buf);
                                get_next_buf(dma_q, &buf);
@@ -479,6 +598,15 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
                                        outp = NULL;
                                else
                                        outp = videobuf_to_vmalloc(&buf->vb);
+
+                               /* As long as isoc traffic is arriving, keep
+                                  resetting the timer */
+                               if (dev->vid_timeout_running)
+                                       mod_timer(&dev->vid_timeout,
+                                                 jiffies + (HZ / 10));
+                               if (dev->vbi_timeout_running)
+                                       mod_timer(&dev->vbi_timeout,
+                                                 jiffies + (HZ / 10));
                        }
 
                        if (buf != NULL) {
@@ -488,9 +616,36 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
                                        buf->top_field = 0;
                        }
 
+                       if (vbi_buf != NULL) {
+                               if (fbyte & 0x40)
+                                       vbi_buf->top_field = 1;
+                               else
+                                       vbi_buf->top_field = 0;
+                       }
+
+                       dev->vbi_read = 0;
+                       vbi_dma_q->pos = 0;
                        dma_q->pos = 0;
                }
-               if (buf != NULL)
+
+               vbi_field_size = dev->vbi_width * dev->vbi_height * 2;
+               if (dev->vbi_read < vbi_field_size) {
+                       remain  = vbi_field_size - dev->vbi_read;
+                       if (len < remain)
+                               lencopy = len;
+                       else
+                               lencopy = remain;
+
+                       if (vbi_buf != NULL)
+                               au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+                                               vbioutp, len);
+
+                       len -= lencopy;
+                       p += lencopy;
+                       dev->vbi_read += lencopy;
+               }
+
+               if (dev->vbi_read >= vbi_field_size && buf != NULL)
                        au0828_copy_video(dev, dma_q, buf, p, outp, len);
        }
        return rc;
@@ -642,7 +797,7 @@ int au0828_analog_stream_enable(struct au0828_dev *d)
        au0828_writereg(d, 0x114, 0xa0);
        au0828_writereg(d, 0x115, 0x05);
        /* set y position */
-       au0828_writereg(d, 0x112, 0x02);
+       au0828_writereg(d, 0x112, 0x00);
        au0828_writereg(d, 0x113, 0x00);
        au0828_writereg(d, 0x116, 0xf2);
        au0828_writereg(d, 0x117, 0x00);
@@ -703,47 +858,134 @@ void au0828_analog_unregister(struct au0828_dev *dev)
 
 
 /* Usage lock check functions */
-static int res_get(struct au0828_fh *fh)
+static int res_get(struct au0828_fh *fh, unsigned int bit)
 {
-       struct au0828_dev *dev = fh->dev;
-       int              rc   = 0;
+       struct au0828_dev    *dev = fh->dev;
 
-       /* 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;
+       dprintk(1, "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 au0828_fh *fh, unsigned int bit)
+{
+       return fh->resources & bit;
 }
 
-static int res_check(struct au0828_fh *fh)
+static int res_locked(struct au0828_dev *dev, unsigned int bit)
 {
-       return fh->stream_on;
+       return dev->resources & bit;
 }
 
-static void res_free(struct au0828_fh *fh)
+static void res_free(struct au0828_fh *fh, unsigned int bits)
 {
-       struct au0828_dev *dev = fh->dev;
+       struct au0828_dev    *dev = fh->dev;
+
+       BUG_ON((fh->resources & bits) != bits);
+
+       mutex_lock(&dev->lock);
+       fh->resources  &= ~bits;
+       dev->resources &= ~bits;
+       dprintk(1, "res: put %d\n", bits);
+       mutex_unlock(&dev->lock);
+}
+
+static int get_ressource(struct au0828_fh *fh)
+{
+       switch (fh->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               return AU0828_RESOURCE_VIDEO;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               return AU0828_RESOURCE_VBI;
+       default:
+               BUG();
+               return 0;
+       }
+}
+
+/* This function ensures that video frames continue to be delivered even if
+   the ITU-656 input isn't receiving any data (thereby preventing applications
+   such as tvtime from hanging) */
+void au0828_vid_buffer_timeout(unsigned long data)
+{
+       struct au0828_dev *dev = (struct au0828_dev *) data;
+       struct au0828_dmaqueue *dma_q = &dev->vidq;
+       struct au0828_buffer *buf;
+       unsigned char *vid_data;
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&dev->slock, flags);
+
+       buf = dev->isoc_ctl.buf;
+       if (buf != NULL) {
+               vid_data = videobuf_to_vmalloc(&buf->vb);
+               memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */
+               buffer_filled(dev, dma_q, buf);
+       }
+       get_next_buf(dma_q, &buf);
+
+       if (dev->vid_timeout_running == 1)
+               mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
+
+       spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+void au0828_vbi_buffer_timeout(unsigned long data)
+{
+       struct au0828_dev *dev = (struct au0828_dev *) data;
+       struct au0828_dmaqueue *dma_q = &dev->vbiq;
+       struct au0828_buffer *buf;
+       unsigned char *vbi_data;
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&dev->slock, flags);
+
+       buf = dev->isoc_ctl.vbi_buf;
+       if (buf != NULL) {
+               vbi_data = videobuf_to_vmalloc(&buf->vb);
+               memset(vbi_data, 0x00, buf->vb.size);
+               vbi_buffer_filled(dev, dma_q, buf);
+       }
+       vbi_get_next_buf(dma_q, &buf);
 
-       fh->stream_on = 0;
-       dev->stream_on = 0;
+       if (dev->vbi_timeout_running == 1)
+               mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+       spin_unlock_irqrestore(&dev->slock, flags);
 }
 
+
 static int au0828_v4l2_open(struct file *filp)
 {
        int ret = 0;
+       struct video_device *vdev = video_devdata(filp);
        struct au0828_dev *dev = video_drvdata(filp);
        struct au0828_fh *fh;
-       int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       int type;
 
-#ifdef VBI_IS_WORKING
-       if (video_devdata(filp)->vfl_type == VFL_TYPE_GRABBER)
+       switch (vdev->vfl_type) {
+       case VFL_TYPE_GRABBER:
+               type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               break;
+       case VFL_TYPE_VBI:
                type = V4L2_BUF_TYPE_VBI_CAPTURE;
-#endif
+               break;
+       default:
+               return -EINVAL;
+       }
 
        fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL);
        if (NULL == fh) {
@@ -781,9 +1023,19 @@ static int au0828_v4l2_open(struct file *filp)
        dev->users++;
 
        videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops,
-                                   NULL, &dev->slock, fh->type,
+                                   NULL, &dev->slock,
+                                   V4L2_BUF_TYPE_VIDEO_CAPTURE,
                                    V4L2_FIELD_INTERLACED,
-                                   sizeof(struct au0828_buffer), fh);
+                                   sizeof(struct au0828_buffer), fh, NULL);
+
+       /* VBI Setup */
+       dev->vbi_width = 720;
+       dev->vbi_height = 1;
+       videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops,
+                                   NULL, &dev->slock,
+                                   V4L2_BUF_TYPE_VBI_CAPTURE,
+                                   V4L2_FIELD_SEQ_TB,
+                                   sizeof(struct au0828_buffer), fh, NULL);
 
        return ret;
 }
@@ -794,17 +1046,27 @@ static int au0828_v4l2_close(struct file *filp)
        struct au0828_fh *fh = filp->private_data;
        struct au0828_dev *dev = fh->dev;
 
-       mutex_lock(&dev->lock);
-       if (res_check(fh))
-               res_free(fh);
+       if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
+               /* Cancel timeout thread in case they didn't call streamoff */
+               dev->vid_timeout_running = 0;
+               del_timer_sync(&dev->vid_timeout);
 
-       if (dev->users == 1) {
                videobuf_stop(&fh->vb_vidq);
-               videobuf_mmap_free(&fh->vb_vidq);
+               res_free(fh, AU0828_RESOURCE_VIDEO);
+       }
 
+       if (res_check(fh, AU0828_RESOURCE_VBI)) {
+               /* Cancel timeout thread in case they didn't call streamoff */
+               dev->vbi_timeout_running = 0;
+               del_timer_sync(&dev->vbi_timeout);
+
+               videobuf_stop(&fh->vb_vbiq);
+               res_free(fh, AU0828_RESOURCE_VBI);
+       }
+
+       if (dev->users == 1) {
                if (dev->dev_state & DEV_DISCONNECTED) {
                        au0828_analog_unregister(dev);
-                       mutex_unlock(&dev->lock);
                        kfree(dev);
                        return 0;
                }
@@ -823,10 +1085,11 @@ static int au0828_v4l2_close(struct file *filp)
                        printk(KERN_INFO "Au0828 can't set alternate to 0!\n");
        }
 
+       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;
 }
 
@@ -842,16 +1105,28 @@ static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf,
                return rc;
 
        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, AU0828_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, AU0828_RESOURCE_VBI))
+                       return -EBUSY;
+
+               if (dev->vbi_timeout_running == 0) {
+                       /* Handle case where caller tries to read without
+                          calling streamon first */
+                       dev->vbi_timeout_running = 1;
+                       mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+               }
+
+               return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
+                                           filp->f_flags & O_NONBLOCK);
+       }
+
        return 0;
 }
 
@@ -865,17 +1140,17 @@ static unsigned int au0828_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, AU0828_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, AU0828_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);
+       }
 }
 
 static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
@@ -888,14 +1163,10 @@ static int au0828_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);
 
        return rc;
 }
@@ -906,19 +1177,7 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd,
        int ret;
        int width = format->fmt.pix.width;
        int height = format->fmt.pix.height;
-       unsigned int maxwidth, maxheight;
-
-       maxwidth = 720;
-       maxheight = 480;
 
-#ifdef VBI_IS_WORKING
-       if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
-               dprintk(1, "VBI format set: to be supported!\n");
-               return 0;
-       }
-       if (format->type == V4L2_BUF_TYPE_VBI_CAPTURE)
-               return 0;
-#endif
        if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
 
@@ -999,9 +1258,7 @@ static int vidioc_querycap(struct file *file, void  *priv,
 
        /*set the device capabilities */
        cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
-#ifdef VBI_IS_WORKING
                V4L2_CAP_VBI_CAPTURE |
-#endif
                V4L2_CAP_AUDIO |
                V4L2_CAP_READWRITE |
                V4L2_CAP_STREAMING |
@@ -1056,20 +1313,21 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
        struct au0828_dev *dev = fh->dev;
        int rc;
 
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       mutex_lock(&dev->lock);
+
        if (videobuf_queue_is_busy(&fh->vb_vidq)) {
                printk(KERN_INFO "%s queue busy\n", __func__);
                rc = -EBUSY;
                goto out;
        }
 
-       if (dev->stream_on && !fh->stream_on) {
-               printk(KERN_INFO "%s device in use by another fh\n", __func__);
-               rc = -EBUSY;
-               goto out;
-       }
-
-       return au0828_set_format(dev, VIDIOC_S_FMT, f);
+       rc = au0828_set_format(dev, VIDIOC_S_FMT, f);
 out:
+       mutex_unlock(&dev->lock);
        return rc;
 }
 
@@ -1105,7 +1363,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
 
        tmp = input->index;
 
-       if (tmp > AU0828_MAX_INPUT)
+       if (tmp >= AU0828_MAX_INPUT)
                return -EINVAL;
        if (AUVI_INPUT(tmp).type == 0)
                return -EINVAL;
@@ -1300,6 +1558,29 @@ static int vidioc_s_frequency(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)
+{
+       struct au0828_fh      *fh  = priv;
+       struct au0828_dev     *dev = fh->dev;
+
+       format->fmt.vbi.samples_per_line = dev->vbi_width;
+       format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+       format->fmt.vbi.offset = 0;
+       format->fmt.vbi.flags = 0;
+       format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+
+       format->fmt.vbi.count[0] = dev->vbi_height;
+       format->fmt.vbi.count[1] = dev->vbi_height;
+       format->fmt.vbi.start[0] = 21;
+       format->fmt.vbi.start[1] = 284;
+
+       return 0;
+}
+
 static int vidioc_g_chip_ident(struct file *file, void *priv,
               struct v4l2_dbg_chip_ident *chip)
 {
@@ -1345,25 +1626,37 @@ static int vidioc_cropcap(struct file *file, void *priv,
 static int vidioc_streamon(struct file *file, void *priv,
                           enum v4l2_buf_type type)
 {
-       struct au0828_fh *fh = priv;
-       struct au0828_dev *dev = fh->dev;
-       int rc;
+       struct au0828_fh      *fh  = priv;
+       struct au0828_dev     *dev = fh->dev;
+       int                   rc = -EINVAL;
 
        rc = check_dev(dev);
        if (rc < 0)
                return rc;
 
+       if (unlikely(type != fh->type))
+               return -EINVAL;
+
+       dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
+               fh, type, fh->resources, dev->resources);
+
+       if (unlikely(!res_get(fh, get_ressource(fh))))
+               return -EBUSY;
+
        if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
                au0828_analog_stream_enable(dev);
                v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
        }
 
-       mutex_lock(&dev->lock);
-       rc = res_get(fh);
-
-       if (likely(rc >= 0))
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
                rc = videobuf_streamon(&fh->vb_vidq);
-       mutex_unlock(&dev->lock);
+               dev->vid_timeout_running = 1;
+               mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
+       } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               rc = videobuf_streamon(&fh->vb_vbiq);
+               dev->vbi_timeout_running = 1;
+               mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+       }
 
        return rc;
 }
@@ -1371,38 +1664,48 @@ static int vidioc_streamon(struct file *file, void *priv,
 static int vidioc_streamoff(struct file *file, void *priv,
                            enum v4l2_buf_type type)
 {
-       struct au0828_fh *fh = priv;
-       struct au0828_dev *dev = fh->dev;
-       int i;
-       int ret;
-       int rc;
+       struct au0828_fh      *fh  = priv;
+       struct au0828_dev     *dev = fh->dev;
+       int                   rc;
+       int                   i;
 
        rc = check_dev(dev);
        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;
 
-       if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+       dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
+               fh, type, fh->resources, dev->resources);
+
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               dev->vid_timeout_running = 0;
+               del_timer_sync(&dev->vid_timeout);
+
                v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
-               ret = au0828_stream_interrupt(dev);
-               if (ret != 0)
-                       return ret;
-       }
+               rc = au0828_stream_interrupt(dev);
+               if (rc != 0)
+                       return rc;
 
-       for (i = 0; i < AU0828_MAX_INPUT; i++) {
-               if (AUVI_INPUT(i).audio_setup == NULL)
-                       continue;
-               (AUVI_INPUT(i).audio_setup)(dev, 0);
-       }
+               for (i = 0; i < AU0828_MAX_INPUT; i++) {
+                       if (AUVI_INPUT(i).audio_setup == NULL)
+                               continue;
+                       (AUVI_INPUT(i).audio_setup)(dev, 0);
+               }
 
-       mutex_lock(&dev->lock);
-       videobuf_streamoff(&fh->vb_vidq);
-       res_free(fh);
-       mutex_unlock(&dev->lock);
+               videobuf_streamoff(&fh->vb_vidq);
+               res_free(fh, AU0828_RESOURCE_VIDEO);
+       } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               dev->vbi_timeout_running = 0;
+               del_timer_sync(&dev->vbi_timeout);
+
+               videobuf_streamoff(&fh->vb_vbiq);
+               res_free(fh, AU0828_RESOURCE_VBI);
+       }
 
        return 0;
 }
@@ -1451,7 +1754,12 @@ 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)
+               rc = videobuf_reqbufs(&fh->vb_vidq, rb);
+       else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               rc = videobuf_reqbufs(&fh->vb_vbiq, rb);
+
+       return rc;
 }
 
 static int vidioc_querybuf(struct file *file, void *priv,
@@ -1465,7 +1773,12 @@ 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)
+               rc = videobuf_querybuf(&fh->vb_vidq, b);
+       else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               rc = videobuf_querybuf(&fh->vb_vbiq, b);
+
+       return rc;
 }
 
 static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
@@ -1478,7 +1791,12 @@ 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)
+               rc = videobuf_qbuf(&fh->vb_vidq, b);
+       else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               rc = videobuf_qbuf(&fh->vb_vbiq, b);
+
+       return rc;
 }
 
 static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
@@ -1499,17 +1817,13 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
                dev->greenscreen_detected = 0;
        }
 
-       return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
-}
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
-{
-       struct au0828_fh *fh = priv;
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               rc = videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+       else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               rc = videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & O_NONBLOCK);
 
-       return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
+       return rc;
 }
-#endif
 
 static struct v4l2_file_operations au0828_v4l_fops = {
        .owner      = THIS_MODULE,
@@ -1527,19 +1841,11 @@ 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,
-#ifdef VBI_IS_WORKING
        .vidioc_g_fmt_vbi_cap       = vidioc_g_fmt_vbi_cap,
-       .vidioc_try_fmt_vbi_cap     = vidioc_s_fmt_vbi_cap,
-       .vidioc_s_fmt_vbi_cap       = vidioc_s_fmt_vbi_cap,
-#endif
+       .vidioc_s_fmt_vbi_cap       = vidioc_g_fmt_vbi_cap,
        .vidioc_g_audio             = vidioc_g_audio,
        .vidioc_s_audio             = vidioc_s_audio,
        .vidioc_cropcap             = vidioc_cropcap,
-#ifdef VBI_IS_WORKING
-       .vidioc_g_fmt_sliced_vbi_cap   = vidioc_g_fmt_sliced_vbi_cap,
-       .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
-       .vidioc_s_fmt_sliced_vbi_cap   = vidioc_try_set_sliced_vbi_cap,
-#endif
        .vidioc_reqbufs             = vidioc_reqbufs,
        .vidioc_querybuf            = vidioc_querybuf,
        .vidioc_qbuf                = vidioc_qbuf,
@@ -1562,9 +1868,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_s_register          = vidioc_s_register,
 #endif
        .vidioc_g_chip_ident        = vidioc_g_chip_ident,
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-       .vidiocgmbuf                = vidiocgmbuf,
-#endif
 };
 
 static const struct video_device au0828_video_template = {
@@ -1621,8 +1924,19 @@ int au0828_analog_register(struct au0828_dev *dev,
        spin_lock_init(&dev->slock);
        mutex_init(&dev->lock);
 
+       /* init video dma queues */
        INIT_LIST_HEAD(&dev->vidq.active);
        INIT_LIST_HEAD(&dev->vidq.queued);
+       INIT_LIST_HEAD(&dev->vbiq.active);
+       INIT_LIST_HEAD(&dev->vbiq.queued);
+
+       dev->vid_timeout.function = au0828_vid_buffer_timeout;
+       dev->vid_timeout.data = (unsigned long) dev;
+       init_timer(&dev->vid_timeout);
+
+       dev->vbi_timeout.function = au0828_vbi_buffer_timeout;
+       dev->vbi_timeout.data = (unsigned long) dev;
+       init_timer(&dev->vbi_timeout);
 
        dev->width = NTSC_STD_W;
        dev->height = NTSC_STD_H;
@@ -1638,26 +1952,23 @@ int au0828_analog_register(struct au0828_dev *dev,
                return -ENOMEM;
        }
 
-#ifdef VBI_IS_WORKING
+       /* allocate the VBI struct */
        dev->vbi_dev = video_device_alloc();
        if (NULL == dev->vbi_dev) {
                dprintk(1, "Can't allocate vbi_device.\n");
                kfree(dev->vdev);
                return -ENOMEM;
        }
-#endif
 
        /* Fill the video capture device struct */
        *dev->vdev = au0828_video_template;
        dev->vdev->parent = &dev->usbdev->dev;
        strcpy(dev->vdev->name, "au0828a video");
 
-#ifdef VBI_IS_WORKING
        /* Setup the VBI device */
        *dev->vbi_dev = au0828_video_template;
        dev->vbi_dev->parent = &dev->usbdev->dev;
        strcpy(dev->vbi_dev->name, "au0828a vbi");
-#endif
 
        /* Register the v4l2 device */
        video_set_drvdata(dev->vdev, dev);
@@ -1669,7 +1980,6 @@ int au0828_analog_register(struct au0828_dev *dev,
                return -ENODEV;
        }
 
-#ifdef VBI_IS_WORKING
        /* Register the vbi device */
        video_set_drvdata(dev->vbi_dev, dev);
        retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1);
@@ -1680,7 +1990,6 @@ int au0828_analog_register(struct au0828_dev *dev,
                video_device_release(dev->vdev);
                return -ENODEV;
        }
-#endif
 
        dprintk(1, "%s completed!\n", __func__);