xref: /linux/drivers/media/pci/cx18/cx18-av-vbi.c (revision 0cdee263bc5e7b20f657ea09f9272f50c568f35b)
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