diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index 8137a8c94bfc365a1d63ffd677cf8cd1ed236d60..d0f093d1d0df23c887b7b5865d5fee5f4e74c794 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 cc807232b820136f962b62d5319ea1f6b8500f28..2479c6f8641195a1ffed7dc664cb47389c95080d 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 d4107f6933f892a24dcf308977d505e6d7790258..3128b7fce07d60ad699340a004321ee5ced8367f 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 0000000000000000000000000000000000000000..b5802d4cb62302be808312470848cbc9dda0228e
--- /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 04c9ecc3c22a4f34892d33aa3be150648e84aab8..7022f7ba61bbb6fa2dbd4e5ffe85dd26c4c2237c 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 1656d2cf34a9d28557cffcce5032ccd21d357e75..8dac50b9c00b1521d4d70f6d09455f695da27125 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 {\