diff mbox

cx18: mmap() support for raw YUV video capture

Message ID 1302089576-17122-1-git-send-email-simon.farnsworth@onelan.co.uk (mailing list archive)
State Accepted
Headers show

Commit Message

Simon Farnsworth April 6, 2011, 11:32 a.m. UTC
Add support for mmap method streaming of raw YUV video on cx18-based
hardware, in addition to the existing support for read() streaming of
raw YUV and MPEG-2 encoded video.

Signed-off-by: Steven Toth <stoth@kernellabs.com>

I forward-ported this from Steven's original work, done under contract
to ONELAN. The original code is at
http://www.kernellabs.com/hg/~stoth/cx18-videobuf

Signed-off-by: Simon Farnsworth <simon.farnsworth@onelan.co.uk>
---
I'm currently running an older version of this patch on a 2.6.37
kernel - I figured I should try and get this into mainline sooner
rather than later, given the current rate of change in V4L2.

I've compile tested this patch against
git://linuxtv.org/media_tree.git staging/for_v2.6.40

I can rework or port to a different tree as required, but for some of
the more complex changes, I may need a bit of guidance.

 drivers/media/video/cx18/Kconfig        |    2 +
 drivers/media/video/cx18/cx18-driver.h  |   25 ++++
 drivers/media/video/cx18/cx18-fileops.c |  214 +++++++++++++++++++++++++++++++
 drivers/media/video/cx18/cx18-fileops.h |    2 +
 drivers/media/video/cx18/cx18-ioctl.c   |  136 ++++++++++++++++++--
 drivers/media/video/cx18/cx18-mailbox.c |   70 ++++++++++
 drivers/media/video/cx18/cx18-streams.c |   23 ++++
 drivers/media/video/cx18/cx23418.h      |    6 +
 8 files changed, 466 insertions(+), 12 deletions(-)
diff mbox

Patch

diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
index d9d2f6a..104ad55e 100644
--- a/drivers/media/video/cx18/Kconfig
+++ b/drivers/media/video/cx18/Kconfig
@@ -2,6 +2,8 @@  config VIDEO_CX18
 	tristate "Conexant cx23418 MPEG encoder support"
 	depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL
 	select I2C_ALGOBIT
+	select VIDEOBUF_DVB
+	select VIDEOBUF_VMALLOC
 	depends on RC_CORE
 	select VIDEO_TUNER
 	select VIDEO_TVEEPROM
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index b86a740..70e1e04 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -65,6 +65,10 @@ 
 #include "dvb_net.h"
 #include "dvbdev.h"
 
+/* Videobuf / YUV support */
+#include <media/videobuf-core.h>
+#include <media/videobuf-vmalloc.h>
+
 #ifndef CONFIG_PCI
 #  error "This driver requires kernel PCI support."
 #endif
@@ -403,6 +407,23 @@  struct cx18_stream {
 	struct cx18_queue q_idle;	/* idle - not in rotation */
 
 	struct work_struct out_work_order;
+
+	/* Videobuf for YUV video */
+	u32 pixelformat;
+	struct list_head vb_capture;    /* video capture queue */
+	spinlock_t vb_lock;
+	struct v4l2_framebuffer fbuf;
+	v4l2_std_id tvnorm; /* selected tv norm */
+	struct timer_list vb_timeout;
+	int vbwidth;
+	int vbheight;
+};
+
+struct cx18_videobuf_buffer {
+	/* Common video buffer sub-system struct */
+	struct videobuf_buffer vb;
+	v4l2_std_id tvnorm; /* selected tv norm */
+	u32 bytes_used;
 };
 
 struct cx18_open_id {
@@ -410,6 +431,10 @@  struct cx18_open_id {
 	u32 open_id;
 	int type;
 	struct cx18 *cx;
+
+	struct videobuf_queue vbuf_q;
+	spinlock_t s_lock; /* Protect vbuf_q */
+	enum v4l2_buf_type vb_type;
 };
 
 static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh)
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index e9802d9..c74eafd 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -597,6 +597,13 @@  ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
 	mutex_unlock(&cx->serialize_lock);
 	if (rc)
 		return rc;
+
+	if ((id->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+		return videobuf_read_stream(&id->vbuf_q, buf, count, pos, 0,
+			filp->f_flags & O_NONBLOCK);
+	}
+
 	return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
 }
 
@@ -622,6 +629,11 @@  unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
 		CX18_DEBUG_FILE("Encoder poll started capture\n");
 	}
 
+	if ((id->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+		return videobuf_poll_stream(filp, &id->vbuf_q, wait);
+	}
+
 	/* add stream's waitq to the poll list */
 	CX18_DEBUG_HI_FILE("Encoder poll\n");
 	poll_wait(filp, &s->waitq, wait);
@@ -633,6 +645,58 @@  unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
 	return 0;
 }
 
+int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+	if ((id->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+
+		/* Start a capture if there is none */
+		if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+			int rc;
+
+			mutex_lock(&cx->serialize_lock);
+			rc = cx18_start_capture(id);
+			mutex_unlock(&cx->serialize_lock);
+			if (rc) {
+				CX18_DEBUG_INFO(
+					"Could not start capture for %s (%d)\n",
+					s->name, rc);
+				return -EINVAL;
+			}
+			CX18_DEBUG_FILE("Encoder poll started capture\n");
+		}
+
+		return videobuf_mmap_mapper(&id->vbuf_q, vma);
+	}
+
+	return -EINVAL;
+}
+
+void cx18_vb_timeout(unsigned long data)
+{
+	struct cx18_stream *s = (struct cx18_stream *)data;
+	struct cx18_videobuf_buffer *buf;
+	unsigned long flags;
+
+	/* Return all of the buffers in error state, so the vbi/vid inode
+	 * can return from blocking.
+	 */
+	spin_lock_irqsave(&s->vb_lock, flags);
+	while (!list_empty(&s->vb_capture)) {
+		buf = list_entry(s->vb_capture.next,
+			struct cx18_videobuf_buffer, vb.queue);
+		list_del(&buf->vb.queue);
+		buf->vb.state = VIDEOBUF_ERROR;
+		wake_up(&buf->vb.done);
+	}
+	spin_unlock_irqrestore(&s->vb_lock, flags);
+}
+
 void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
 {
 	struct cx18 *cx = id->cx;
@@ -716,12 +780,150 @@  int cx18_v4l2_close(struct file *filp)
 		cx18_release_stream(s);
 	} else {
 		cx18_stop_capture(id, 0);
+		if (id->type == CX18_ENC_STREAM_TYPE_YUV)
+			videobuf_mmap_free(&id->vbuf_q);
 	}
 	kfree(id);
 	mutex_unlock(&cx->serialize_lock);
 	return 0;
 }
 
+void cx18_dma_free(struct videobuf_queue *q,
+	struct cx18_stream *s, struct cx18_videobuf_buffer *buf)
+{
+	videobuf_waiton(q, &buf->vb, 0, 0);
+	videobuf_vmalloc_free(&buf->vb);
+	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int cx18_prepare_buffer(struct videobuf_queue *q,
+	struct cx18_stream *s,
+	struct cx18_videobuf_buffer *buf,
+	u32 pixelformat,
+	unsigned int width, unsigned int height,
+	enum v4l2_field field)
+{
+	int rc = 0;
+
+	/* check settings */
+	buf->bytes_used = 0;
+
+	if ((width  < 48) || (height < 32))
+		return -EINVAL;
+
+	buf->vb.size = (width * height * 16 /*fmt->depth*/) >> 3;
+	if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
+		return -EINVAL;
+
+	/* alloc + fill struct (if changed) */
+	if (buf->vb.width != width || buf->vb.height != height ||
+	    buf->vb.field != field || s->pixelformat != pixelformat ||
+	    buf->tvnorm != s->tvnorm) {
+
+		buf->vb.width  = width;
+		buf->vb.height = height;
+		buf->vb.field  = field;
+		buf->tvnorm    = s->tvnorm;
+		s->pixelformat = pixelformat;
+
+		cx18_dma_free(q, s, buf);
+	}
+
+	if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
+		return -EINVAL;
+
+	if (buf->vb.field == 0)
+		buf->vb.field = V4L2_FIELD_INTERLACED;
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		buf->vb.width  = width;
+		buf->vb.height = height;
+		buf->vb.field  = field;
+		buf->tvnorm    = s->tvnorm;
+		s->pixelformat = pixelformat;
+
+		rc = videobuf_iolock(q, &buf->vb, &s->fbuf);
+		if (rc != 0)
+			goto fail;
+	}
+	buf->vb.state = VIDEOBUF_PREPARED;
+	return 0;
+
+fail:
+	cx18_dma_free(q, s, buf);
+	return rc;
+
+}
+
+#define VB_MIN_BUFFERS 32
+#define VB_MIN_BUFSIZE 0x208000
+
+static int buffer_setup(struct videobuf_queue *q,
+	unsigned int *count, unsigned int *size)
+{
+	struct cx18_open_id *id = q->priv_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	*size = 2 * s->vbwidth * s->vbheight;
+	if (*count == 0)
+		*count = VB_MIN_BUFFERS;
+
+	while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE)
+		(*count)--;
+
+	q->field = V4L2_FIELD_INTERLACED;
+	q->last = V4L2_FIELD_INTERLACED;
+
+	return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+	struct videobuf_buffer *vb,
+	enum v4l2_field field)
+{
+	struct cx18_videobuf_buffer *buf =
+		container_of(vb, struct cx18_videobuf_buffer, vb);
+	struct cx18_open_id *id = q->priv_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	return cx18_prepare_buffer(q, s, buf, s->pixelformat,
+		s->vbwidth, s->vbheight, field);
+}
+
+static void buffer_release(struct videobuf_queue *q,
+	struct videobuf_buffer *vb)
+{
+	struct cx18_videobuf_buffer *buf =
+		container_of(vb, struct cx18_videobuf_buffer, vb);
+	struct cx18_open_id *id = q->priv_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	cx18_dma_free(q, s, buf);
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+	struct cx18_videobuf_buffer *buf =
+		container_of(vb, struct cx18_videobuf_buffer, vb);
+	struct cx18_open_id *id = q->priv_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	buf->vb.state = VIDEOBUF_QUEUED;
+
+	list_add_tail(&buf->vb.queue, &s->vb_capture);
+}
+
+static struct videobuf_queue_ops cx18_videobuf_qops = {
+	.buf_setup    = buffer_setup,
+	.buf_prepare  = buffer_prepare,
+	.buf_queue    = buffer_queue,
+	.buf_release  = buffer_release,
+};
+
 static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
 {
 	struct cx18 *cx = s->cx;
@@ -740,6 +942,9 @@  static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
 	item->cx = cx;
 	item->type = s->type;
 
+	spin_lock_init(&item->s_lock);
+	item->vb_type = 0;
+
 	item->open_id = cx->open_id++;
 	filp->private_data = &item->fh;
 
@@ -774,6 +979,15 @@  static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
 		/* Done! Unmute and continue. */
 		cx18_unmute(cx);
 	}
+	if (item->type == CX18_ENC_STREAM_TYPE_YUV) {
+		item->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		videobuf_queue_vmalloc_init(&item->vbuf_q, &cx18_videobuf_qops,
+			&cx->pci_dev->dev, &item->s_lock,
+			V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			V4L2_FIELD_INTERLACED,
+			sizeof(struct cx18_videobuf_buffer),
+			item, &cx->serialize_lock);
+	}
 	v4l2_fh_add(&item->fh);
 	return 0;
 }
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h
index 5c8fcb8..b9e5110 100644
--- a/drivers/media/video/cx18/cx18-fileops.h
+++ b/drivers/media/video/cx18/cx18-fileops.h
@@ -33,6 +33,8 @@  int cx18_start_capture(struct cx18_open_id *id);
 void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
 void cx18_mute(struct cx18 *cx);
 void cx18_unmute(struct cx18 *cx);
+int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma);
+void cx18_vb_timeout(unsigned long data);
 
 /* Shared with cx18-alsa module */
 int cx18_claim_stream(struct cx18_open_id *id, int type);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 86c30b9..28ff1ba 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -41,6 +41,18 @@ 
 #include <media/tveeprom.h>
 #include <media/v4l2-chip-ident.h>
 
+static struct v4l2_fmtdesc formats[] = {
+	{ 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+	  "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 }
+	},
+	{ 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
+	  "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 }
+	},
+	{ 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+	  "YUYV 4:2:2", V4L2_PIX_FMT_YUYV, { 0, 0, 0, 0 }
+	},
+};
+
 u16 cx18_service2vbi(int type)
 {
 	switch (type) {
@@ -150,6 +162,7 @@  static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
 {
 	struct cx18_open_id *id = fh2id(fh);
 	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
 	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
 
 	pixfmt->width = cx->cxhdl.width;
@@ -158,7 +171,7 @@  static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
 	pixfmt->field = V4L2_FIELD_INTERLACED;
 	pixfmt->priv = 0;
 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
-		pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+		pixfmt->pixelformat = s->pixelformat;
 		/* YUV size is (Y=(h*720) + UV=(h*(720/2))) */
 		pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
 		pixfmt->bytesperline = 720;
@@ -237,7 +250,6 @@  static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
 	h = min(h, cx->is_50hz ? 576 : 480);
 	h = max(h, min_h);
 
-	cx18_g_fmt_vid_cap(file, fh, fmt);
 	fmt->fmt.pix.width = w;
 	fmt->fmt.pix.height = h;
 	return 0;
@@ -274,6 +286,7 @@  static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
 	struct cx18_open_id *id = fh2id(fh);
 	struct cx18 *cx = id->cx;
 	struct v4l2_mbus_framefmt mbus_fmt;
+	struct cx18_stream *s = &cx->streams[id->type];
 	int ret;
 	int w, h;
 
@@ -283,6 +296,10 @@  static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
 	w = fmt->fmt.pix.width;
 	h = fmt->fmt.pix.height;
 
+	s->pixelformat = fmt->fmt.pix.pixelformat;
+	s->vbheight = h;
+	s->vbwidth = w;
+
 	if (cx->cxhdl.width == w && cx->cxhdl.height == h)
 		return 0;
 
@@ -540,16 +557,7 @@  static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
 static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
 					struct v4l2_fmtdesc *fmt)
 {
-	static struct v4l2_fmtdesc formats[] = {
-		{ 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
-		  "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 }
-		},
-		{ 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
-		  "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 }
-		}
-	};
-
-	if (fmt->index > 1)
+	if (fmt->index > ARRAY_SIZE(formats) - 1)
 		return -EINVAL;
 	*fmt = formats[fmt->index];
 	return 0;
@@ -863,6 +871,104 @@  static int cx18_g_enc_index(struct file *file, void *fh,
 	return 0;
 }
 
+static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id)
+{
+	struct videobuf_queue *q = NULL;
+
+	switch (id->vb_type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		q = &id->vbuf_q;
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		break;
+	default:
+		break;
+	}
+	return q;
+}
+
+static int cx18_streamon(struct file *file, void *priv,
+	enum v4l2_buf_type type)
+{
+	struct cx18_open_id *id = file->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	/* Start the hardware only if we're the video device */
+	if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	if (id->type != CX18_ENC_STREAM_TYPE_YUV)
+		return -EINVAL;
+
+	/* Establish a buffer timeout */
+	mod_timer(&s->vb_timeout, jiffies + (HZ * 2));
+
+	return videobuf_streamon(cx18_vb_queue(id));
+}
+
+static int cx18_streamoff(struct file *file, void *priv,
+	enum v4l2_buf_type type)
+{
+	struct cx18_open_id *id = file->private_data;
+
+	/* Start the hardware only if we're the video device */
+	if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	if (id->type != CX18_ENC_STREAM_TYPE_YUV)
+		return -EINVAL;
+
+	return videobuf_streamoff(cx18_vb_queue(id));
+}
+
+static int cx18_reqbufs(struct file *file, void *priv,
+	struct v4l2_requestbuffers *rb)
+{
+	struct cx18_open_id *id = file->private_data;
+
+	if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	return videobuf_reqbufs(cx18_vb_queue(id), rb);
+}
+
+static int cx18_querybuf(struct file *file, void *priv,
+	struct v4l2_buffer *b)
+{
+	struct cx18_open_id *id = file->private_data;
+
+	if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	return videobuf_querybuf(cx18_vb_queue(id), b);
+}
+
+static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+	struct cx18_open_id *id = file->private_data;
+
+	if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	return videobuf_qbuf(cx18_vb_queue(id), b);
+}
+
+static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+	struct cx18_open_id *id = file->private_data;
+	if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+		(id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+		return -EINVAL;
+
+	return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK);
+}
+
 static int cx18_encoder_cmd(struct file *file, void *fh,
 				struct v4l2_encoder_cmd *enc)
 {
@@ -1081,6 +1187,12 @@  static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
 	.vidioc_s_register              = cx18_s_register,
 #endif
 	.vidioc_default                 = cx18_default,
+	.vidioc_streamon                = cx18_streamon,
+	.vidioc_streamoff               = cx18_streamoff,
+	.vidioc_reqbufs                 = cx18_reqbufs,
+	.vidioc_querybuf                = cx18_querybuf,
+	.vidioc_qbuf                    = cx18_qbuf,
+	.vidioc_dqbuf                   = cx18_dqbuf,
 };
 
 void cx18_set_funcs(struct video_device *vdev)
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index 9605d54..d4d8873 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -81,6 +81,7 @@  static const struct cx18_api_info api_info[] = {
 	API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM,           0),
 	API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER,      0),
 	API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS,                    0),
+	API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM,                  0),
 	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK,			0),
 	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL,			API_FAST),
 	API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL,			API_SLOW),
@@ -158,6 +159,72 @@  static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)
 	}
 }
 
+static void cx18_mdl_send_to_videobuf(struct cx18_stream *s,
+	struct cx18_mdl *mdl)
+{
+	struct cx18_videobuf_buffer *vb_buf;
+	struct cx18_buffer *buf;
+	u8 *p, u;
+	u32 offset = 0;
+	int dispatch = 0;
+	int i;
+
+	if (mdl->bytesused == 0)
+		return;
+
+	/* Acquire a videobuf buffer, clone to and and release it */
+	spin_lock(&s->vb_lock);
+	if (list_empty(&s->vb_capture))
+		goto out;
+
+	vb_buf = list_entry(s->vb_capture.next, struct cx18_videobuf_buffer,
+		vb.queue);
+
+	p = videobuf_to_vmalloc(&vb_buf->vb);
+	if (!p)
+		goto out;
+
+	offset = vb_buf->bytes_used;
+	list_for_each_entry(buf, &mdl->buf_list, list) {
+		if (buf->bytesused == 0)
+			break;
+
+		if ((offset + buf->bytesused) <= vb_buf->vb.bsize) {
+			memcpy(p + offset, buf->buf, buf->bytesused);
+			offset += buf->bytesused;
+			vb_buf->bytes_used += buf->bytesused;
+		}
+	}
+
+	/* If we've filled the buffer as per the callers res then dispatch it */
+	if (vb_buf->bytes_used >= (vb_buf->vb.width * vb_buf->vb.height * 2)) {
+		dispatch = 1;
+		vb_buf->bytes_used = 0;
+	}
+
+	/* */
+	if (dispatch) {
+
+		if (s->pixelformat == V4L2_PIX_FMT_YUYV) {
+			/* UYVY to YUYV */
+			for (i = 0; i < (720 * 480 * 2); i += 2) {
+				u = *(p + i);
+				*(p + i) = *(p + i + 1);
+				*(p + i + 1) = u;
+			}
+		}
+
+		do_gettimeofday(&vb_buf->vb.ts);
+		list_del(&vb_buf->vb.queue);
+		vb_buf->vb.state = VIDEOBUF_DONE;
+		wake_up(&vb_buf->vb.done);
+	}
+
+	mod_timer(&s->vb_timeout, jiffies + (HZ / 10));
+
+out:
+	spin_unlock(&s->vb_lock);
+}
 
 static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s,
 				  struct cx18_mdl *mdl)
@@ -263,6 +330,9 @@  static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
 			} else {
 				cx18_enqueue(s, mdl, &s->q_full);
 			}
+		} else if (s->type == CX18_ENC_STREAM_TYPE_YUV) {
+			cx18_mdl_send_to_videobuf(s, mdl);
+			cx18_enqueue(s, mdl, &s->q_free);
 		} else {
 			cx18_enqueue(s, mdl, &s->q_full);
 			if (s->type == CX18_ENC_STREAM_TYPE_IDX)
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index c6e2ca3..53f5e4f 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -44,6 +44,7 @@  static struct v4l2_file_operations cx18_v4l2_enc_fops = {
 	.unlocked_ioctl = cx18_v4l2_ioctl,
 	.release = cx18_v4l2_close,
 	.poll = cx18_v4l2_enc_poll,
+	.mmap = cx18_v4l2_mmap,
 };
 
 /* offset from 0 to register ts v4l2 minors on */
@@ -132,6 +133,15 @@  static void cx18_stream_init(struct cx18 *cx, int type)
 	cx18_queue_init(&s->q_idle);
 
 	INIT_WORK(&s->out_work_order, cx18_out_work_handler);
+
+	INIT_LIST_HEAD(&s->vb_capture);
+	s->vb_timeout.function = cx18_vb_timeout;
+	s->vb_timeout.data = (unsigned long)s;
+	init_timer(&s->vb_timeout);
+	spin_lock_init(&s->vb_lock);
+
+	/* Assume the previous pixel default */
+	s->pixelformat = V4L2_PIX_FMT_HM12;
 }
 
 static int cx18_prep_dev(struct cx18 *cx, int type)
@@ -721,6 +731,19 @@  int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
 		    test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
 			cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
 			  (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1);
+
+		/* Enable the Video Format Converter for UYVY 4:2:2 support,
+		 * rather than the default HM12 Macroblovk 4:2:0 support.
+		 */
+		if (captype == CAPTURE_CHANNEL_TYPE_YUV) {
+			if (s->pixelformat == V4L2_PIX_FMT_YUYV)
+				cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
+					s->handle, 1);
+			else
+				/* If in doubt, default to HM12 */
+				cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
+					s->handle, 0);
+		}
 	}
 
 	if (atomic_read(&cx->tot_capturing) == 0) {
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 935f557..767a8d2 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -342,6 +342,12 @@ 
    ReturnCode */
 #define CX18_CPU_GET_ENC_PTS			(CPU_CMD_MASK_CAPTURE | 0x0022)
 
+/* Description: Set VFC parameters
+   IN[0] - task handle
+   IN[1] - VFC enable flag, 1 - enable, 0 - disable
+*/
+#define CX18_CPU_SET_VFC_PARAM                  (CPU_CMD_MASK_CAPTURE | 0x0023)
+
 /* Below is the list of commands related to the data exchange */
 #define CPU_CMD_MASK_DE 			(CPU_CMD_MASK | 0x040000)