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 <st...@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.farnswo...@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 --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)
 
-- 
1.7.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to