|
@@ -42,6 +42,13 @@
|
|
|
#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,
|