From c1994084d6ad7d1a411219727fc135a18c40f9c8 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 5 Feb 2009 22:37:49 -0300 Subject: [PATCH] V4L/DVB (10441): cx18: Fix VBI ioctl() handling and Raw/Sliced VBI state management 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 Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-av-vbi.c | 14 +++-- drivers/media/video/cx18/cx18-ioctl.c | 109 ++++++++++++++++++++++++++------- 2 files changed, 94 insertions(+), 29 deletions(-) diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index 72325d7..b576337 100644 --- a/drivers/media/video/cx18/cx18-av-vbi.c +++ b/drivers/media/video/cx18/cx18-av-vbi.c @@ -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; } } diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 1adb972..a454ede 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -42,6 +42,13 @@ #include #include +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, -- 2.7.4