V4L/DVB (10441): cx18: Fix VBI ioctl() handling and Raw/Sliced VBI state management
authorAndy Walls <awalls@radix.net>
Fri, 6 Feb 2009 01:37:49 +0000 (22:37 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 30 Mar 2009 15:42:38 +0000 (12:42 -0300)
More sliced VBI fixes to bring the cx18 driver closer to full V4L2 spec
compliance for VBI and to get sliced VBI working better.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/cx18/cx18-av-vbi.c
drivers/media/video/cx18/cx18-ioctl.c

index 72325d7..b576337 100644 (file)
@@ -25,8 +25,8 @@
 #include "cx18-driver.h"
 
 /*
- * For sliced VBI output, we set up to use VIP-1.1, 10-bit mode,
- * NN counts 4 bytes Dwords, an IDID of 0x00 0x80 or one with the VBI line #.
+ * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode,
+ * NN counts 1 byte Dwords, an IDID with the VBI line # in it.
  * Thus, according to the VIP-2 Spec, our VBI ancillary data lines
  * (should!) look like:
  *     4 byte EAV code:          0xff 0x00 0x00 0xRP
@@ -35,8 +35,8 @@
  *     1 byte data identifier:   ne010iii (parity bits, 010, DID bits)
  *     1 byte secondary data id: nessssss (parity bits, SDID bits)
  *     1 byte data word count:   necccccc (parity bits, NN Dword count)
- *     2 byte Internal DID:      0x00 0x80 (programmed value)
- *     4*NN data bytes
+ *     2 byte Internal DID:      VBI-line-# 0x80
+ *     NN data bytes
  *     1 byte checksum
  *     Fill bytes needed to fil out to 4*NN bytes of payload
  *
@@ -65,7 +65,7 @@ struct vbi_anc_data {
        u8 sdid;
        u8 data_count;
        u8 idid[2];
-       u8 payload[1]; /* 4*data_count of payload */
+       u8 payload[1]; /* data_count of payload */
        /* u8 checksum; */
        /* u8 fill[]; Variable number of fill bytes */
 };
@@ -215,6 +215,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
                cx18_av_write(cx, 0x406, 0x13);
                cx18_av_write(cx, 0x47f, vbi_offset);
 
+               /* Force impossible lines to 0 */
                if (is_pal) {
                        for (i = 0; i <= 6; i++)
                                svbi->service_lines[0][i] =
@@ -229,6 +230,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
                                        svbi->service_lines[1][i] = 0;
                }
 
+               /* Build register values for requested service lines */
                for (i = 7; i <= 23; i++) {
                        for (x = 0; x <= 1; x++) {
                                switch (svbi->service_lines[1-x][i]) {
@@ -242,7 +244,7 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
                                        lcr[i] |= 6 << (4 * x);
                                        break;
                                case V4L2_SLICED_VPS:
-                                       lcr[i] |= 9 << (4 * x);
+                                       lcr[i] |= 7 << (4 * x); /*'840 differs*/
                                        break;
                                }
                        }
index 1adb972..a454ede 100644 (file)
 #include <media/v4l2-chip-ident.h>
 #include <linux/i2c-id.h>
 
+static int cx18_vbi_streaming(struct cx18 *cx)
+{
+       struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+       return (s_vbi->handle != CX18_INVALID_TASK_HANDLE) &&
+               test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+}
+
 u16 cx18_service2vbi(int type)
 {
        switch (type) {
@@ -58,12 +65,21 @@ u16 cx18_service2vbi(int type)
        }
 }
 
+/* Check if VBI services are allowed on the (field, line) for the video std */
 static int valid_service_line(int field, int line, int is_pal)
 {
-       return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+       return (is_pal && line >= 6 &&
+               ((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
               (!is_pal && line >= 10 && line < 22);
 }
 
+/*
+ * For a (field, line, std) and inbound potential set of services for that line,
+ * return the first valid service of those passed in the incoming set for that
+ * line in priority order:
+ * CC, VPS, or WSS over TELETEXT for well known lines
+ * TELETEXT, before VPS, before CC, before WSS, for other lines
+ */
 static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
 {
        u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
@@ -90,6 +106,10 @@ static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
        return 0;
 }
 
+/*
+ * Expand the service_set of *fmt into valid service_lines for the std,
+ * and clear the passed in fmt->service_set
+ */
 void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
 {
        u16 set = fmt->service_set;
@@ -102,6 +122,10 @@ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
        }
 }
 
+/*
+ * Sanitize the service_lines in *fmt per the video std, and return 1
+ * if any service_line is left as valid after santization
+ */
 static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
 {
        int f, l;
@@ -116,6 +140,7 @@ static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
        return set != 0;
 }
 
+/* Compute the service_set from the assumed valid service_lines of *fmt */
 u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
 {
        int f, l;
@@ -162,7 +187,7 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
        struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
 
        vbifmt->sampling_rate = 27000000;
-       vbifmt->offset = 248;
+       vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
        vbifmt->samples_per_line = vbi_active_samples - 4;
        vbifmt->sample_format = V4L2_PIX_FMT_GREY;
        vbifmt->start[0] = cx->vbi.start[0];
@@ -180,12 +205,25 @@ static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
        struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
        struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
 
+       /* sane, V4L2 spec compliant, defaults */
        vbifmt->reserved[0] = 0;
        vbifmt->reserved[1] = 0;
        vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
        memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+       vbifmt->service_set = 0;
+
+       /*
+        * Fetch the configured service_lines and total service_set from the
+        * digitizer/slicer.  Note, cx18_av_vbi() wipes the passed in
+        * fmt->fmt.sliced under valid calling conditions
+        */
+       if (cx18_av_cmd(cx, VIDIOC_G_FMT, fmt))
+               return -EINVAL;
 
-       cx18_av_cmd(cx, VIDIOC_G_FMT, fmt);
+       /* Ensure V4L2 spec compliant output */
+       vbifmt->reserved[0] = 0;
+       vbifmt->reserved[1] = 0;
+       vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
        vbifmt->service_set = cx18_get_service_set(vbifmt);
        return 0;
 }
@@ -224,10 +262,12 @@ static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
        vbifmt->reserved[0] = 0;
        vbifmt->reserved[1] = 0;
 
+       /* If given a service set, expand it validly & clear passed in set */
        if (vbifmt->service_set)
                cx18_expand_service_set(vbifmt, cx->is_50hz);
-       check_service_set(vbifmt, cx->is_50hz);
-       vbifmt->service_set = cx18_get_service_set(vbifmt);
+       /* Sanitize the service_lines, and compute the new set if any valid */
+       if (check_service_set(vbifmt, cx->is_50hz))
+               vbifmt->service_set = cx18_get_service_set(vbifmt);
        return 0;
 }
 
@@ -272,12 +312,22 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
        if (ret)
                return ret;
 
-       if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+       if (!cx18_raw_vbi(cx) && cx18_vbi_streaming(cx))
                return -EBUSY;
 
+       /*
+        * Set the digitizer registers for raw active VBI.
+        * Note cx18_av_vbi_wipes out alot of the passed in fmt under valid
+        * calling conditions
+        */
+       ret = cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+       if (ret)
+               return ret;
+
+       /* Store our new v4l2 (non-)sliced VBI state */
        cx->vbi.sliced_in->service_set = 0;
        cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
-       cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+
        return cx18_g_fmt_vbi_cap(file, fh, fmt);
 }
 
@@ -293,17 +343,20 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
        if (ret)
                return ret;
 
-       ret = cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
-       if (ret)
-               return ret;
-
-       if (check_service_set(vbifmt, cx->is_50hz) == 0)
-               return -EINVAL;
+       cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
 
-       if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+       if (cx18_raw_vbi(cx) && cx18_vbi_streaming(cx))
                return -EBUSY;
+       /*
+        * Set the service_lines requested in the digitizer/slicer registers.
+        * Note, cx18_av_vbi() wipes some "impossible" service lines in the
+        * passed in fmt->fmt.sliced under valid calling conditions
+        */
+       ret = cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+       if (ret)
+               return ret;
+       /* Store our current v4l2 sliced VBI settings */
        cx->vbi.in.type =  V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
-       cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
        memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
        return 0;
 }
@@ -657,16 +710,26 @@ static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
        int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
        int f, l;
 
-       if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
-               for (f = 0; f < 2; f++) {
-                       for (l = 0; l < 24; l++) {
-                               if (valid_service_line(f, l, cx->is_50hz))
-                                       cap->service_lines[f][l] = set;
-                       }
+       if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+               return -EINVAL;
+
+       cap->service_set = 0;
+       for (f = 0; f < 2; f++) {
+               for (l = 0; l < 24; l++) {
+                       if (valid_service_line(f, l, cx->is_50hz)) {
+                               /*
+                                * We can find all v4l2 supported vbi services
+                                * for the standard, on a valid line for the std
+                                */
+                               cap->service_lines[f][l] = set;
+                               cap->service_set |= set;
+                       } else
+                               cap->service_lines[f][l] = 0;
                }
-               return 0;
        }
-       return -EINVAL;
+       for (f = 0; f < 3; f++)
+               cap->reserved[f] = 0;
+       return 0;
 }
 
 static int cx18_g_enc_index(struct file *file, void *fh,