[media] cx18: mmap() support for raw YUV video capture
authorSteven Toth <stoth@kernellabs.com>
Wed, 6 Apr 2011 11:32:56 +0000 (08:32 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Fri, 20 May 2011 12:28:43 +0000 (09:28 -0300)
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.

[simon.farnsworth@onelan.co.uk: 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: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Simon Farnsworth <simon.farnsworth@onelan.co.uk>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/cx18/Kconfig
drivers/media/video/cx18/cx18-driver.h
drivers/media/video/cx18/cx18-fileops.c
drivers/media/video/cx18/cx18-fileops.h
drivers/media/video/cx18/cx18-ioctl.c
drivers/media/video/cx18/cx18-mailbox.c
drivers/media/video/cx18/cx18-streams.c
drivers/media/video/cx18/cx23418.h

index d788ad6f5c489cce3643a67d196ab4e9a92a03a2..9c232022403ac7984add53c0752d02803d031676 100644 (file)
@@ -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
index b86a740c68df82b3dc833e5b4a577bed6f4fedf8..70e1e0401645b49b1d5e3e529f0f516f61ae1bda 100644 (file)
 #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)
index e9802d99439b15462b371a46563bb6e1093b94fb..c74eafd67f989c7e3f33db713375f26243964ed1 100644 (file)
@@ -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;
 }
index 5c8fcb884f0ad793b9da01c6016c556edc7662c6..b9e5110ad04368765fbe7be12fe4dd7ea869d6d0 100644 (file)
@@ -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);
index 4f041c033c54420dc69caa106c0dcbf778232719..777d7265c8a861483c4dc6d0979c765e7ce21894 100644 (file)
 #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)
index 9605d54bd08390b897ea104a1e98a2a9c837a0f0..d4d88738d8939a23409022b31139c5b302e7f918 100644 (file)
@@ -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)
index 6fbc356113c18077352876da72ef0a93dbc3abdc..eeb455a8b7260ee3c707c99a2dc74f1f64629ed1 100644 (file)
@@ -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)
@@ -729,6 +739,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) {
index 935f557acbd043e31fadf680847b26ddc83717c6..767a8d23e3f27a301de5e0d11c8fde58548cbb70 100644 (file)
    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)