V4L/DVB (12742): em28xx: add raw VBI support for NTSC
authorDevin Heitmueller <dheitmueller@kernellabs.com>
Tue, 1 Sep 2009 04:54:54 +0000 (01:54 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 19 Sep 2009 02:47:41 +0000 (23:47 -0300)
Add support for raw VBI capture for the em28xx bridge, currently only for
NTSC.  Support for PAL capture to follow shortly (including the removal of
numerous hard-coded NTSC-specific sizes for capture buffers, etc).

Note that the code currently changes the default current norm from PAL to
NTSC (so that zvbi-ntsc-cc works properly).  The default norm really should
be moved into a board-level parameter.

This work was sponsored by EyeMagnet Limited.

Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/em28xx/Makefile
drivers/media/video/em28xx/em28xx-cards.c
drivers/media/video/em28xx/em28xx-core.c
drivers/media/video/em28xx/em28xx-vbi.c [new file with mode: 0644]
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h

index 8137a8c..d0f093d 100644 (file)
@@ -1,5 +1,5 @@
 em28xx-objs     := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
-                  em28xx-input.o
+                  em28xx-input.o em28xx-vbi.o
 
 em28xx-alsa-objs := em28xx-audio.o
 
index cc80723..2479c6f 100644 (file)
@@ -2590,6 +2590,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
        /* 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);
 
 
        if (dev->board.has_msp34xx) {
index d4107f6..3128b7f 100644 (file)
@@ -964,6 +964,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
                     int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
 {
        struct em28xx_dmaqueue *dma_q = &dev->vidq;
+       struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
        int i;
        int sb_size, pipe;
        struct urb *urb;
@@ -993,7 +994,8 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
        }
 
        dev->isoc_ctl.max_pkt_size = max_pkt_size;
-       dev->isoc_ctl.buf = NULL;
+       dev->isoc_ctl.vid_buf = NULL;
+       dev->isoc_ctl.vbi_buf = NULL;
 
        sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
 
@@ -1043,6 +1045,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
        }
 
        init_waitqueue_head(&dma_q->wq);
+       init_waitqueue_head(&vbi_dma_q->wq);
 
        em28xx_capture_start(dev, 1);
 
diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c
new file mode 100644 (file)
index 0000000..b5802d4
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+   em28xx-vbi.c - VBI driver for em28xx
+
+   Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+
+   This work was sponsored by EyeMagnet Limited.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include "em28xx.h"
+
+static unsigned int vbibufs = 5;
+module_param(vbibufs,int,0644);
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug;
+module_param(vbi_debug,int,0644);
+MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
+
+#define dprintk(level,fmt, arg...)     if (vbi_debug >= level) \
+       printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static void
+free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
+{
+       struct em28xx_fh     *fh  = vq->priv_data;
+       struct em28xx        *dev = fh->dev;
+       unsigned long flags = 0;
+       if (in_interrupt())
+               BUG();
+
+       /* We used to wait for the buffer to finish here, but this didn't work
+          because, as we were keeping the state as VIDEOBUF_QUEUED,
+          videobuf_queue_cancel marked it as finished for us.
+          (Also, it could wedge forever if the hardware was misconfigured.)
+
+          This should be safe; by the time we get here, the buffer isn't
+          queued anymore. If we ever start marking the buffers as
+          VIDEOBUF_ACTIVE, it won't be, though.
+       */
+       spin_lock_irqsave(&dev->slock, flags);
+       if (dev->isoc_ctl.vbi_buf == buf)
+               dev->isoc_ctl.vbi_buf = NULL;
+       spin_unlock_irqrestore(&dev->slock, flags);
+
+       videobuf_vmalloc_free(&buf->vb);
+       buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+       *size = 720 * 12 * 2;
+       if (0 == *count)
+               *count = vbibufs;
+       if (*count < 2)
+               *count = 2;
+       if (*count > 32)
+               *count = 32;
+       return 0;
+}
+
+static int
+vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+           enum v4l2_field field)
+{
+       struct em28xx_fh     *fh  = q->priv_data;
+       struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+       int                  rc = 0;
+       unsigned int size;
+
+       size = 720 * 12 * 2;
+
+       buf->vb.size = size;
+
+       if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+               return -EINVAL;
+
+       buf->vb.width  = 720;
+       buf->vb.height = 12;
+       buf->vb.field  = field;
+
+       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+               rc = videobuf_iolock(q, &buf->vb, NULL);
+               if (rc < 0)
+                       goto fail;
+       }
+
+       buf->vb.state = VIDEOBUF_PREPARED;
+       return 0;
+
+fail:
+       free_buffer(q, buf);
+       return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_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  *vbiq    = &dev->vbiq;
+
+       buf->vb.state = VIDEOBUF_QUEUED;
+       list_add_tail(&buf->vb.queue, &vbiq->active);
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+       free_buffer(q, buf);
+}
+
+struct videobuf_queue_ops em28xx_vbi_qops = {
+       .buf_setup    = vbi_setup,
+       .buf_prepare  = vbi_prepare,
+       .buf_queue    = vbi_queue,
+       .buf_release  = vbi_release,
+};
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
index 04c9ecc..7022f7b 100644 (file)
@@ -163,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);
@@ -256,6 +273,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) {
+               printk("dev is null\n");
+               return;
+       }
+
+       if (dma_q == NULL) {
+               printk("dma_q is null\n");
+               return;
+       }
+       if (buf == NULL) {
+               return;
+       }
+       if (p == NULL) {
+               printk("p is null\n");
+               return;
+       }
+       if (outp == NULL) {
+               printk("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)
 {
@@ -306,7 +380,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;
        }
@@ -318,7 +392,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;
 }
@@ -346,7 +447,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);
 
@@ -416,6 +517,7 @@ 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;
@@ -434,9 +536,14 @@ static inline int em28xx_isoc_copy_vbi(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);
+
+       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;
 
@@ -480,12 +587,41 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
                                printk("djh c should never happen\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;
@@ -570,8 +706,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);
@@ -1542,8 +1678,12 @@ static int vidioc_streamon(struct file *file, void *priv,
        mutex_lock(&dev->lock);
        rc = res_get(fh);
 
-       if (likely(rc >= 0))
-               rc = videobuf_streamon(&fh->vb_vidq);
+       if (likely(rc >= 0)) {
+               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);
+       }
 
        mutex_unlock(&dev->lock);
 
@@ -1561,14 +1701,19 @@ 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);
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               videobuf_streamoff(&fh->vb_vidq);
+       else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               videobuf_streamoff(&fh->vb_vbiq);
+
        res_free(fh);
 
        mutex_unlock(&dev->lock);
@@ -1589,6 +1734,7 @@ static int vidioc_querycap(struct file *file, void  *priv,
        cap->version = EM28XX_VERSION_CODE;
 
        cap->capabilities =
+                       V4L2_CAP_VBI_CAPTURE |
                        V4L2_CAP_SLICED_VBI_CAPTURE |
                        V4L2_CAP_VIDEO_CAPTURE |
                        V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
@@ -1660,6 +1806,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)
@@ -1672,7 +1857,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,
@@ -1686,7 +1874,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)
@@ -1699,7 +1898,11 @@ 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)
@@ -1712,7 +1915,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
@@ -1720,7 +1928,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
 
@@ -1884,9 +2095,17 @@ static int em28xx_v4l2_open(struct file *filp)
        else
                field = V4L2_FIELD_INTERLACED;
 
-       videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
-                       NULL, &dev->slock, fh->type, field,
-                       sizeof(struct em28xx_buffer), fh);
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
+                                           NULL, &dev->slock, fh->type, field,
+                                           sizeof(struct em28xx_buffer), fh);
+
+       if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               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);
 
@@ -1948,7 +2167,7 @@ static int em28xx_v4l2_close(struct file *filp)
        if (res_check(fh))
                res_free(fh);
 
-       if (dev->users == 1) {
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 1) {
                videobuf_stop(&fh->vb_vidq);
                videobuf_mmap_free(&fh->vb_vidq);
 
@@ -1977,6 +2196,12 @@ static int em28xx_v4l2_close(struct file *filp)
                                        "0 (error=%i)\n", errCode);
                }
        }
+
+       if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               videobuf_stop(&fh->vb_vbiq);
+               videobuf_mmap_free(&fh->vb_vbiq);
+       }
+
        kfree(fh);
        dev->users--;
        wake_up_interruptible_nr(&dev->open, 1);
@@ -2015,6 +2240,17 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
                return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
                                        filp->f_flags & O_NONBLOCK);
        }
+
+
+       if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               mutex_lock(&dev->lock);
+               rc = res_get(fh);
+               mutex_unlock(&dev->lock);
+
+               return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
+                                       filp->f_flags & O_NONBLOCK);
+       }
+
        return 0;
 }
 
@@ -2039,10 +2275,12 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait)
        if (unlikely(rc < 0))
                return POLLERR;
 
-       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+       else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+               return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
+       else
                return POLLERR;
-
-       return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
 }
 
 /*
@@ -2091,6 +2329,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,
@@ -2136,7 +2376,9 @@ static const struct video_device em28xx_video_template = {
        .minor                      = -1,
 
        .tvnorms                    = V4L2_STD_ALL,
-       .current_norm               = V4L2_STD_PAL,
+       /* FIXME: we need this to be NTSC for VBI to work - it should
+          be moved to a per-board definition */
+       .current_norm               = V4L2_STD_NTSC,
 };
 
 static const struct v4l2_file_operations radio_fops = {
index 1656d2c..8dac50b 100644 (file)
@@ -214,7 +214,8 @@ struct em28xx_usb_isoc_ctl {
        int                             tmp_buf_len;
 
                /* Stores already requested buffers */
-       struct em28xx_buffer            *buf;
+       struct em28xx_buffer            *vid_buf;
+       struct em28xx_buffer            *vbi_buf;
 
                /* Stores the number of received fields */
        int                             nfields;
@@ -467,6 +468,7 @@ struct em28xx_fh {
        int           radio;
 
        struct videobuf_queue        vb_vidq;
+       struct videobuf_queue        vb_vbiq;
 
        enum v4l2_buf_type           type;
 };
@@ -565,6 +567,7 @@ struct em28xx {
 
        /* Isoc control struct */
        struct em28xx_dmaqueue vidq;
+       struct em28xx_dmaqueue vbiq;
        struct em28xx_usb_isoc_ctl isoc_ctl;
        spinlock_t slock;
 
@@ -693,6 +696,9 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev);
 int em28xx_ir_init(struct em28xx *dev);
 int em28xx_ir_fini(struct em28xx *dev);
 
+/* Provided by em28xx-vbi.c */
+extern struct videobuf_queue_ops em28xx_vbi_qops;
+
 /* printk macros */
 
 #define em28xx_err(fmt, arg...) do {\