1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21c1e45d1SHans Verkuil /*
31c1e45d1SHans Verkuil * cx18 ADEC VBI functions
41c1e45d1SHans Verkuil *
51c1e45d1SHans Verkuil * Derived from cx25840-vbi.c
61c1e45d1SHans Verkuil *
71c1e45d1SHans Verkuil * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
81c1e45d1SHans Verkuil */
91c1e45d1SHans Verkuil
101c1e45d1SHans Verkuil
11*df698f3dSKuan-Wei Chiu #include <linux/bitops.h>
121c1e45d1SHans Verkuil #include "cx18-driver.h"
131c1e45d1SHans Verkuil
14302df970SAndy Walls /*
15c1994084SAndy Walls * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode,
16c1994084SAndy Walls * NN counts 1 byte Dwords, an IDID with the VBI line # in it.
17302df970SAndy Walls * Thus, according to the VIP-2 Spec, our VBI ancillary data lines
18302df970SAndy Walls * (should!) look like:
19302df970SAndy Walls * 4 byte EAV code: 0xff 0x00 0x00 0xRP
20302df970SAndy Walls * unknown number of possible idle bytes
21302df970SAndy Walls * 3 byte Anc data preamble: 0x00 0xff 0xff
22302df970SAndy Walls * 1 byte data identifier: ne010iii (parity bits, 010, DID bits)
23302df970SAndy Walls * 1 byte secondary data id: nessssss (parity bits, SDID bits)
24302df970SAndy Walls * 1 byte data word count: necccccc (parity bits, NN Dword count)
25c1994084SAndy Walls * 2 byte Internal DID: VBI-line-# 0x80
26c1994084SAndy Walls * NN data bytes
27302df970SAndy Walls * 1 byte checksum
28302df970SAndy Walls * Fill bytes needed to fil out to 4*NN bytes of payload
29302df970SAndy Walls *
30302df970SAndy Walls * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, &
31302df970SAndy Walls * in the vertical blanking interval are:
32302df970SAndy Walls * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0)
33302df970SAndy Walls * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0)
34302df970SAndy Walls *
35302df970SAndy Walls * Since the V bit is only allowed to toggle in the EAV RP code, just
36302df970SAndy Walls * before the first active region line and for active lines, they are:
37302df970SAndy Walls * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0)
38302df970SAndy Walls * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0)
39302df970SAndy Walls *
40302df970SAndy Walls * The user application DID bytes we care about are:
41302df970SAndy Walls * 0x91 (1 0 010 0 !ActiveLine AncDataPresent)
42302df970SAndy Walls * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent)
43302df970SAndy Walls *
44302df970SAndy Walls */
45302df970SAndy Walls static const u8 sliced_vbi_did[2] = { 0x91, 0x55 };
46302df970SAndy Walls
47302df970SAndy Walls struct vbi_anc_data {
48302df970SAndy Walls /* u8 eav[4]; */
49302df970SAndy Walls /* u8 idle[]; Variable number of idle bytes */
50302df970SAndy Walls u8 preamble[3];
51302df970SAndy Walls u8 did;
52302df970SAndy Walls u8 sdid;
53302df970SAndy Walls u8 data_count;
54302df970SAndy Walls u8 idid[2];
55fe1b585cSGustavo A. R. Silva u8 payload[]; /* data_count of payload */
56302df970SAndy Walls /* u8 checksum; */
57302df970SAndy Walls /* u8 fill[]; Variable number of fill bytes */
58302df970SAndy Walls };
59302df970SAndy Walls
decode_vps(u8 * dst,u8 * p)601c1e45d1SHans Verkuil static int decode_vps(u8 *dst, u8 *p)
611c1e45d1SHans Verkuil {
621c1e45d1SHans Verkuil static const u8 biphase_tbl[] = {
631c1e45d1SHans Verkuil 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
641c1e45d1SHans Verkuil 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
651c1e45d1SHans Verkuil 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
661c1e45d1SHans Verkuil 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
671c1e45d1SHans Verkuil 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
681c1e45d1SHans Verkuil 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
691c1e45d1SHans Verkuil 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
701c1e45d1SHans Verkuil 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
711c1e45d1SHans Verkuil 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
721c1e45d1SHans Verkuil 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
731c1e45d1SHans Verkuil 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
741c1e45d1SHans Verkuil 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
751c1e45d1SHans Verkuil 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
761c1e45d1SHans Verkuil 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
771c1e45d1SHans Verkuil 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
781c1e45d1SHans Verkuil 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
791c1e45d1SHans Verkuil 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
801c1e45d1SHans Verkuil 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
811c1e45d1SHans Verkuil 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
821c1e45d1SHans Verkuil 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
831c1e45d1SHans Verkuil 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
841c1e45d1SHans Verkuil 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
851c1e45d1SHans Verkuil 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
861c1e45d1SHans Verkuil 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
871c1e45d1SHans Verkuil 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
881c1e45d1SHans Verkuil 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
891c1e45d1SHans Verkuil 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
901c1e45d1SHans Verkuil 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
911c1e45d1SHans Verkuil 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
921c1e45d1SHans Verkuil 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
931c1e45d1SHans Verkuil 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
941c1e45d1SHans Verkuil 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
951c1e45d1SHans Verkuil };
961c1e45d1SHans Verkuil
971c1e45d1SHans Verkuil u8 c, err = 0;
981c1e45d1SHans Verkuil int i;
991c1e45d1SHans Verkuil
1001c1e45d1SHans Verkuil for (i = 0; i < 2 * 13; i += 2) {
1011c1e45d1SHans Verkuil err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
1021c1e45d1SHans Verkuil c = (biphase_tbl[p[i + 1]] & 0xf) |
1031c1e45d1SHans Verkuil ((biphase_tbl[p[i]] & 0xf) << 4);
1041c1e45d1SHans Verkuil dst[i / 2] = c;
1051c1e45d1SHans Verkuil }
1061c1e45d1SHans Verkuil
1071c1e45d1SHans Verkuil return err & 0xf0;
1081c1e45d1SHans Verkuil }
1091c1e45d1SHans Verkuil
cx18_av_g_sliced_fmt(struct v4l2_subdev * sd,struct v4l2_sliced_vbi_format * svbi)1101585927dSHans Verkuil int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
1111c1e45d1SHans Verkuil {
1121585927dSHans Verkuil struct cx18 *cx = v4l2_get_subdevdata(sd);
1131c1e45d1SHans Verkuil struct cx18_av_state *state = &cx->av_state;
11441c129a8SHans Verkuil static const u16 lcr2vbi[] = {
1151c1e45d1SHans Verkuil 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
1161c1e45d1SHans Verkuil 0, V4L2_SLICED_WSS_625, 0, /* 4 */
1171c1e45d1SHans Verkuil V4L2_SLICED_CAPTION_525, /* 6 */
118cb95a40eSAndy Walls 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
1191c1e45d1SHans Verkuil 0, 0, 0, 0
1201c1e45d1SHans Verkuil };
1211c1e45d1SHans Verkuil int is_pal = !(state->std & V4L2_STD_525_60);
1221c1e45d1SHans Verkuil int i;
1231c1e45d1SHans Verkuil
12430634e8eSHans Verkuil memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
12530634e8eSHans Verkuil svbi->service_set = 0;
12630634e8eSHans Verkuil
1271c1e45d1SHans Verkuil /* we're done if raw VBI is active */
1281c1e45d1SHans Verkuil if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
12941c129a8SHans Verkuil return 0;
1301c1e45d1SHans Verkuil
1311c1e45d1SHans Verkuil if (is_pal) {
1321c1e45d1SHans Verkuil for (i = 7; i <= 23; i++) {
1331c1e45d1SHans Verkuil u8 v = cx18_av_read(cx, 0x424 + i - 7);
1341c1e45d1SHans Verkuil
1351c1e45d1SHans Verkuil svbi->service_lines[0][i] = lcr2vbi[v >> 4];
1361c1e45d1SHans Verkuil svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
1371c1e45d1SHans Verkuil svbi->service_set |= svbi->service_lines[0][i] |
1381c1e45d1SHans Verkuil svbi->service_lines[1][i];
1391c1e45d1SHans Verkuil }
1401c1e45d1SHans Verkuil } else {
1411c1e45d1SHans Verkuil for (i = 10; i <= 21; i++) {
1421c1e45d1SHans Verkuil u8 v = cx18_av_read(cx, 0x424 + i - 10);
1431c1e45d1SHans Verkuil
1441c1e45d1SHans Verkuil svbi->service_lines[0][i] = lcr2vbi[v >> 4];
1451c1e45d1SHans Verkuil svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
1461c1e45d1SHans Verkuil svbi->service_set |= svbi->service_lines[0][i] |
1471c1e45d1SHans Verkuil svbi->service_lines[1][i];
1481c1e45d1SHans Verkuil }
1491c1e45d1SHans Verkuil }
15041c129a8SHans Verkuil return 0;
1511c1e45d1SHans Verkuil }
1521c1e45d1SHans Verkuil
cx18_av_s_raw_fmt(struct v4l2_subdev * sd,struct v4l2_vbi_format * fmt)1531585927dSHans Verkuil int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
1541c1e45d1SHans Verkuil {
1551585927dSHans Verkuil struct cx18 *cx = v4l2_get_subdevdata(sd);
15641c129a8SHans Verkuil struct cx18_av_state *state = &cx->av_state;
1571c1e45d1SHans Verkuil
15803b52c36SHans Verkuil /* Setup standard */
15903b52c36SHans Verkuil cx18_av_std_setup(cx);
1601c1e45d1SHans Verkuil
1611c1e45d1SHans Verkuil /* VBI Offset */
162812b1f9dSAndy Walls cx18_av_write(cx, 0x47f, state->slicer_line_delay);
1631c1e45d1SHans Verkuil cx18_av_write(cx, 0x404, 0x2e);
16441c129a8SHans Verkuil return 0;
1651c1e45d1SHans Verkuil }
1661c1e45d1SHans Verkuil
cx18_av_s_sliced_fmt(struct v4l2_subdev * sd,struct v4l2_sliced_vbi_format * svbi)1671585927dSHans Verkuil int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
1681585927dSHans Verkuil {
1691585927dSHans Verkuil struct cx18 *cx = v4l2_get_subdevdata(sd);
1701585927dSHans Verkuil struct cx18_av_state *state = &cx->av_state;
1711585927dSHans Verkuil int is_pal = !(state->std & V4L2_STD_525_60);
1721585927dSHans Verkuil int i, x;
1731585927dSHans Verkuil u8 lcr[24];
1741585927dSHans Verkuil
1751c1e45d1SHans Verkuil for (x = 0; x <= 23; x++)
1761c1e45d1SHans Verkuil lcr[x] = 0x00;
1771c1e45d1SHans Verkuil
17803b52c36SHans Verkuil /* Setup standard */
17903b52c36SHans Verkuil cx18_av_std_setup(cx);
1801c1e45d1SHans Verkuil
1811c1e45d1SHans Verkuil /* Sliced VBI */
1821c1e45d1SHans Verkuil cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
1831c1e45d1SHans Verkuil cx18_av_write(cx, 0x406, 0x13);
184812b1f9dSAndy Walls cx18_av_write(cx, 0x47f, state->slicer_line_delay);
1851c1e45d1SHans Verkuil
186c1994084SAndy Walls /* Force impossible lines to 0 */
1871c1e45d1SHans Verkuil if (is_pal) {
1881c1e45d1SHans Verkuil for (i = 0; i <= 6; i++)
1891c1e45d1SHans Verkuil svbi->service_lines[0][i] =
1901c1e45d1SHans Verkuil svbi->service_lines[1][i] = 0;
1911c1e45d1SHans Verkuil } else {
1921c1e45d1SHans Verkuil for (i = 0; i <= 9; i++)
1931c1e45d1SHans Verkuil svbi->service_lines[0][i] =
1941c1e45d1SHans Verkuil svbi->service_lines[1][i] = 0;
1951c1e45d1SHans Verkuil
1961c1e45d1SHans Verkuil for (i = 22; i <= 23; i++)
1971c1e45d1SHans Verkuil svbi->service_lines[0][i] =
1981c1e45d1SHans Verkuil svbi->service_lines[1][i] = 0;
1991c1e45d1SHans Verkuil }
2001c1e45d1SHans Verkuil
201c1994084SAndy Walls /* Build register values for requested service lines */
2021c1e45d1SHans Verkuil for (i = 7; i <= 23; i++) {
2031c1e45d1SHans Verkuil for (x = 0; x <= 1; x++) {
2041c1e45d1SHans Verkuil switch (svbi->service_lines[1-x][i]) {
2051c1e45d1SHans Verkuil case V4L2_SLICED_TELETEXT_B:
2061c1e45d1SHans Verkuil lcr[i] |= 1 << (4 * x);
2071c1e45d1SHans Verkuil break;
2081c1e45d1SHans Verkuil case V4L2_SLICED_WSS_625:
2091c1e45d1SHans Verkuil lcr[i] |= 4 << (4 * x);
2101c1e45d1SHans Verkuil break;
2111c1e45d1SHans Verkuil case V4L2_SLICED_CAPTION_525:
2121c1e45d1SHans Verkuil lcr[i] |= 6 << (4 * x);
2131c1e45d1SHans Verkuil break;
2141c1e45d1SHans Verkuil case V4L2_SLICED_VPS:
215cb95a40eSAndy Walls lcr[i] |= 9 << (4 * x);
2161c1e45d1SHans Verkuil break;
2171c1e45d1SHans Verkuil }
2181c1e45d1SHans Verkuil }
2191c1e45d1SHans Verkuil }
2201c1e45d1SHans Verkuil
2211c1e45d1SHans Verkuil if (is_pal) {
2221c1e45d1SHans Verkuil for (x = 1, i = 0x424; i <= 0x434; i++, x++)
2231c1e45d1SHans Verkuil cx18_av_write(cx, i, lcr[6 + x]);
2241c1e45d1SHans Verkuil } else {
2251c1e45d1SHans Verkuil for (x = 1, i = 0x424; i <= 0x430; i++, x++)
2261c1e45d1SHans Verkuil cx18_av_write(cx, i, lcr[9 + x]);
2271c1e45d1SHans Verkuil for (i = 0x431; i <= 0x434; i++)
2281c1e45d1SHans Verkuil cx18_av_write(cx, i, 0);
2291c1e45d1SHans Verkuil }
2301c1e45d1SHans Verkuil
2311c1e45d1SHans Verkuil cx18_av_write(cx, 0x43c, 0x16);
2325ab74052SAndy Walls /* Should match vblank set in cx18_av_std_setup() */
233929a3ad1SAndy Walls cx18_av_write(cx, 0x474, is_pal ? 38 : 26);
23441c129a8SHans Verkuil return 0;
2351c1e45d1SHans Verkuil }
2361c1e45d1SHans Verkuil
cx18_av_decode_vbi_line(struct v4l2_subdev * sd,struct v4l2_decode_vbi_line * vbi)23741c129a8SHans Verkuil int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
23841c129a8SHans Verkuil struct v4l2_decode_vbi_line *vbi)
2391c1e45d1SHans Verkuil {
24041c129a8SHans Verkuil struct cx18 *cx = v4l2_get_subdevdata(sd);
24141c129a8SHans Verkuil struct cx18_av_state *state = &cx->av_state;
242302df970SAndy Walls struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p;
24341c129a8SHans Verkuil u8 *p;
244302df970SAndy Walls int did, sdid, l, err = 0;
2451c1e45d1SHans Verkuil
246302df970SAndy Walls /*
247302df970SAndy Walls * Check for the ancillary data header for sliced VBI
248302df970SAndy Walls */
249302df970SAndy Walls if (anc->preamble[0] ||
250302df970SAndy Walls anc->preamble[1] != 0xff || anc->preamble[2] != 0xff ||
251302df970SAndy Walls (anc->did != sliced_vbi_did[0] &&
252302df970SAndy Walls anc->did != sliced_vbi_did[1])) {
2531c1e45d1SHans Verkuil vbi->line = vbi->type = 0;
25441c129a8SHans Verkuil return 0;
2551c1e45d1SHans Verkuil }
2561c1e45d1SHans Verkuil
257302df970SAndy Walls did = anc->did;
258302df970SAndy Walls sdid = anc->sdid & 0xf;
259302df970SAndy Walls l = anc->idid[0] & 0x3f;
260812b1f9dSAndy Walls l += state->slicer_line_offset;
261302df970SAndy Walls p = anc->payload;
2621c1e45d1SHans Verkuil
263302df970SAndy Walls /* Decode the SDID set by the slicer */
264302df970SAndy Walls switch (sdid) {
2651c1e45d1SHans Verkuil case 1:
266302df970SAndy Walls sdid = V4L2_SLICED_TELETEXT_B;
2671c1e45d1SHans Verkuil break;
2681c1e45d1SHans Verkuil case 4:
269302df970SAndy Walls sdid = V4L2_SLICED_WSS_625;
2701c1e45d1SHans Verkuil break;
2711c1e45d1SHans Verkuil case 6:
272302df970SAndy Walls sdid = V4L2_SLICED_CAPTION_525;
273*df698f3dSKuan-Wei Chiu err = !parity8(p[0]) || !parity8(p[1]);
2741c1e45d1SHans Verkuil break;
275cb95a40eSAndy Walls case 9:
276302df970SAndy Walls sdid = V4L2_SLICED_VPS;
2771c1e45d1SHans Verkuil if (decode_vps(p, p) != 0)
2781c1e45d1SHans Verkuil err = 1;
2791c1e45d1SHans Verkuil break;
2801c1e45d1SHans Verkuil default:
281302df970SAndy Walls sdid = 0;
2821c1e45d1SHans Verkuil err = 1;
2831c1e45d1SHans Verkuil break;
2841c1e45d1SHans Verkuil }
2851c1e45d1SHans Verkuil
286302df970SAndy Walls vbi->type = err ? 0 : sdid;
2871c1e45d1SHans Verkuil vbi->line = err ? 0 : l;
288302df970SAndy Walls vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]);
2891c1e45d1SHans Verkuil vbi->p = p;
2901c1e45d1SHans Verkuil return 0;
2911c1e45d1SHans Verkuil }
292