summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/em28xx/Makefile2
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c2
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c5
-rw-r--r--drivers/media/video/em28xx/em28xx-vbi.c150
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c290
-rw-r--r--drivers/media/video/em28xx/em28xx.h8
6 files changed, 430 insertions, 27 deletions
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index 8137a8c94bf..d0f093d1d0d 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -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
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index cc807232b82..2479c6f8641 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -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) {
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index d4107f6933f..3128b7fce07 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -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
index 00000000000..b5802d4cb62
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-vbi.c
@@ -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:
+ */
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 04c9ecc3c22..7022f7ba61b 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -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 = {
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 1656d2cf34a..8dac50b9c00 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -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 {\