1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cb7a01acSMauro Carvalho Chehab /* cx25840 VBI functions
3cb7a01acSMauro Carvalho Chehab  */
4cb7a01acSMauro Carvalho Chehab 
5cb7a01acSMauro Carvalho Chehab 
6cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
7cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
8cb7a01acSMauro Carvalho Chehab #include <media/v4l2-common.h>
9d647f0b7SMauro Carvalho Chehab #include <media/drv-intf/cx25840.h>
10cb7a01acSMauro Carvalho Chehab 
11cb7a01acSMauro Carvalho Chehab #include "cx25840-core.h"
12cb7a01acSMauro Carvalho Chehab 
odd_parity(u8 c)13cb7a01acSMauro Carvalho Chehab static int odd_parity(u8 c)
14cb7a01acSMauro Carvalho Chehab {
15cb7a01acSMauro Carvalho Chehab 	c ^= (c >> 4);
16cb7a01acSMauro Carvalho Chehab 	c ^= (c >> 2);
17cb7a01acSMauro Carvalho Chehab 	c ^= (c >> 1);
18cb7a01acSMauro Carvalho Chehab 
19cb7a01acSMauro Carvalho Chehab 	return c & 1;
20cb7a01acSMauro Carvalho Chehab }
21cb7a01acSMauro Carvalho Chehab 
decode_vps(u8 * dst,u8 * p)22cb7a01acSMauro Carvalho Chehab static int decode_vps(u8 * dst, u8 * p)
23cb7a01acSMauro Carvalho Chehab {
24cb7a01acSMauro Carvalho Chehab 	static const u8 biphase_tbl[] = {
25cb7a01acSMauro Carvalho Chehab 		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
26cb7a01acSMauro Carvalho Chehab 		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
27cb7a01acSMauro Carvalho Chehab 		0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
28cb7a01acSMauro Carvalho Chehab 		0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
29cb7a01acSMauro Carvalho Chehab 		0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
30cb7a01acSMauro Carvalho Chehab 		0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
31cb7a01acSMauro Carvalho Chehab 		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
32cb7a01acSMauro Carvalho Chehab 		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
33cb7a01acSMauro Carvalho Chehab 		0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
34cb7a01acSMauro Carvalho Chehab 		0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
35cb7a01acSMauro Carvalho Chehab 		0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
36cb7a01acSMauro Carvalho Chehab 		0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
37cb7a01acSMauro Carvalho Chehab 		0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
38cb7a01acSMauro Carvalho Chehab 		0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
39cb7a01acSMauro Carvalho Chehab 		0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
40cb7a01acSMauro Carvalho Chehab 		0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
41cb7a01acSMauro Carvalho Chehab 		0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
42cb7a01acSMauro Carvalho Chehab 		0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
43cb7a01acSMauro Carvalho Chehab 		0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
44cb7a01acSMauro Carvalho Chehab 		0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
45cb7a01acSMauro Carvalho Chehab 		0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
46cb7a01acSMauro Carvalho Chehab 		0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
47cb7a01acSMauro Carvalho Chehab 		0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
48cb7a01acSMauro Carvalho Chehab 		0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
49cb7a01acSMauro Carvalho Chehab 		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
50cb7a01acSMauro Carvalho Chehab 		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
51cb7a01acSMauro Carvalho Chehab 		0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
52cb7a01acSMauro Carvalho Chehab 		0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
53cb7a01acSMauro Carvalho Chehab 		0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
54cb7a01acSMauro Carvalho Chehab 		0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
55cb7a01acSMauro Carvalho Chehab 		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
56cb7a01acSMauro Carvalho Chehab 		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
57cb7a01acSMauro Carvalho Chehab 	};
58cb7a01acSMauro Carvalho Chehab 
59cb7a01acSMauro Carvalho Chehab 	u8 c, err = 0;
60cb7a01acSMauro Carvalho Chehab 	int i;
61cb7a01acSMauro Carvalho Chehab 
62cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < 2 * 13; i += 2) {
63cb7a01acSMauro Carvalho Chehab 		err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
64cb7a01acSMauro Carvalho Chehab 		c = (biphase_tbl[p[i + 1]] & 0xf) |
65cb7a01acSMauro Carvalho Chehab 		    ((biphase_tbl[p[i]] & 0xf) << 4);
66cb7a01acSMauro Carvalho Chehab 		dst[i / 2] = c;
67cb7a01acSMauro Carvalho Chehab 	}
68cb7a01acSMauro Carvalho Chehab 
69cb7a01acSMauro Carvalho Chehab 	return err & 0xf0;
70cb7a01acSMauro Carvalho Chehab }
71cb7a01acSMauro Carvalho Chehab 
cx25840_g_sliced_fmt(struct v4l2_subdev * sd,struct v4l2_sliced_vbi_format * svbi)72cb7a01acSMauro Carvalho Chehab int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
73cb7a01acSMauro Carvalho Chehab {
74cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
75cb7a01acSMauro Carvalho Chehab 	struct cx25840_state *state = to_state(sd);
76cb7a01acSMauro Carvalho Chehab 	static const u16 lcr2vbi[] = {
77cb7a01acSMauro Carvalho Chehab 		0, V4L2_SLICED_TELETEXT_B, 0,	/* 1 */
78cb7a01acSMauro Carvalho Chehab 		0, V4L2_SLICED_WSS_625, 0,	/* 4 */
79cb7a01acSMauro Carvalho Chehab 		V4L2_SLICED_CAPTION_525,	/* 6 */
80cb7a01acSMauro Carvalho Chehab 		0, 0, V4L2_SLICED_VPS, 0, 0,	/* 9 */
81cb7a01acSMauro Carvalho Chehab 		0, 0, 0, 0
82cb7a01acSMauro Carvalho Chehab 	};
83cb7a01acSMauro Carvalho Chehab 	int is_pal = !(state->std & V4L2_STD_525_60);
84cb7a01acSMauro Carvalho Chehab 	int i;
85cb7a01acSMauro Carvalho Chehab 
8630634e8eSHans Verkuil 	memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
8730634e8eSHans Verkuil 	svbi->service_set = 0;
88cb7a01acSMauro Carvalho Chehab 	/* we're done if raw VBI is active */
89cb7a01acSMauro Carvalho Chehab 	/* TODO: this will have to be changed for generic_mode VBI */
90cb7a01acSMauro Carvalho Chehab 	if ((cx25840_read(client, 0x404) & 0x10) == 0)
91cb7a01acSMauro Carvalho Chehab 		return 0;
92cb7a01acSMauro Carvalho Chehab 
93cb7a01acSMauro Carvalho Chehab 	if (is_pal) {
94cdf472d3SHans Verkuil 		for (i = 7; i <= 23; i++) {
95cdf472d3SHans Verkuil 			u8 v = cx25840_read(client,
96cb7a01acSMauro Carvalho Chehab 				 state->vbi_regs_offset + 0x424 + i - 7);
97cb7a01acSMauro Carvalho Chehab 
98cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[0][i] = lcr2vbi[v >> 4];
99cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
100cb7a01acSMauro Carvalho Chehab 			svbi->service_set |= svbi->service_lines[0][i] |
101cb7a01acSMauro Carvalho Chehab 					     svbi->service_lines[1][i];
102cb7a01acSMauro Carvalho Chehab 		}
103cb7a01acSMauro Carvalho Chehab 	} else {
104cdf472d3SHans Verkuil 		for (i = 10; i <= 21; i++) {
105cdf472d3SHans Verkuil 			u8 v = cx25840_read(client,
106cb7a01acSMauro Carvalho Chehab 				state->vbi_regs_offset + 0x424 + i - 10);
107cb7a01acSMauro Carvalho Chehab 
108cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[0][i] = lcr2vbi[v >> 4];
109cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
110cb7a01acSMauro Carvalho Chehab 			svbi->service_set |= svbi->service_lines[0][i] |
111cb7a01acSMauro Carvalho Chehab 					     svbi->service_lines[1][i];
112cb7a01acSMauro Carvalho Chehab 		}
113cb7a01acSMauro Carvalho Chehab 	}
114cb7a01acSMauro Carvalho Chehab 	return 0;
115cb7a01acSMauro Carvalho Chehab }
116cb7a01acSMauro Carvalho Chehab 
cx25840_s_raw_fmt(struct v4l2_subdev * sd,struct v4l2_vbi_format * fmt)117cb7a01acSMauro Carvalho Chehab int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
118cb7a01acSMauro Carvalho Chehab {
119cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
120cb7a01acSMauro Carvalho Chehab 	struct cx25840_state *state = to_state(sd);
121cb7a01acSMauro Carvalho Chehab 	int is_pal = !(state->std & V4L2_STD_525_60);
122cb7a01acSMauro Carvalho Chehab 	int vbi_offset = is_pal ? 1 : 0;
123cb7a01acSMauro Carvalho Chehab 
124cb7a01acSMauro Carvalho Chehab 	/* Setup standard */
125cb7a01acSMauro Carvalho Chehab 	cx25840_std_setup(client);
126cb7a01acSMauro Carvalho Chehab 
127cdf472d3SHans Verkuil 	/* VBI Offset */
128cdf472d3SHans Verkuil 	if (is_cx23888(state))
129cdf472d3SHans Verkuil 		cx25840_write(client, 0x54f, vbi_offset);
130cb7a01acSMauro Carvalho Chehab 	else
131cb7a01acSMauro Carvalho Chehab 		cx25840_write(client, 0x47f, vbi_offset);
132cb7a01acSMauro Carvalho Chehab 	/* TODO: this will have to be changed for generic_mode VBI */
133cb7a01acSMauro Carvalho Chehab 	cx25840_write(client, 0x404, 0x2e);
134cb7a01acSMauro Carvalho Chehab 	return 0;
135cb7a01acSMauro Carvalho Chehab }
136cb7a01acSMauro Carvalho Chehab 
cx25840_s_sliced_fmt(struct v4l2_subdev * sd,struct v4l2_sliced_vbi_format * svbi)137cb7a01acSMauro Carvalho Chehab int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
138cb7a01acSMauro Carvalho Chehab {
139cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
140cb7a01acSMauro Carvalho Chehab 	struct cx25840_state *state = to_state(sd);
141cb7a01acSMauro Carvalho Chehab 	int is_pal = !(state->std & V4L2_STD_525_60);
142cb7a01acSMauro Carvalho Chehab 	int vbi_offset = is_pal ? 1 : 0;
143cb7a01acSMauro Carvalho Chehab 	int i, x;
144cb7a01acSMauro Carvalho Chehab 	u8 lcr[24];
145cb7a01acSMauro Carvalho Chehab 
146cb7a01acSMauro Carvalho Chehab 	for (x = 0; x <= 23; x++)
147cb7a01acSMauro Carvalho Chehab 		lcr[x] = 0x00;
148cb7a01acSMauro Carvalho Chehab 
149cb7a01acSMauro Carvalho Chehab 	/* Setup standard */
150cb7a01acSMauro Carvalho Chehab 	cx25840_std_setup(client);
151cb7a01acSMauro Carvalho Chehab 
152cb7a01acSMauro Carvalho Chehab 	/* Sliced VBI */
153cdf472d3SHans Verkuil 	/* TODO: this will have to be changed for generic_mode VBI */
154cdf472d3SHans Verkuil 	cx25840_write(client, 0x404, 0x32);	/* Ancillary data */
155cdf472d3SHans Verkuil 	cx25840_write(client, 0x406, 0x13);
156cb7a01acSMauro Carvalho Chehab 	if (is_cx23888(state))
157cb7a01acSMauro Carvalho Chehab 		cx25840_write(client, 0x54f, vbi_offset);
158cb7a01acSMauro Carvalho Chehab 	else
159cb7a01acSMauro Carvalho Chehab 		cx25840_write(client, 0x47f, vbi_offset);
160cb7a01acSMauro Carvalho Chehab 
161cb7a01acSMauro Carvalho Chehab 	if (is_pal) {
162cb7a01acSMauro Carvalho Chehab 		for (i = 0; i <= 6; i++)
163cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[0][i] =
164cb7a01acSMauro Carvalho Chehab 				svbi->service_lines[1][i] = 0;
165cb7a01acSMauro Carvalho Chehab 	} else {
166cb7a01acSMauro Carvalho Chehab 		for (i = 0; i <= 9; i++)
167cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[0][i] =
168cb7a01acSMauro Carvalho Chehab 				svbi->service_lines[1][i] = 0;
169cb7a01acSMauro Carvalho Chehab 
170cb7a01acSMauro Carvalho Chehab 		for (i = 22; i <= 23; i++)
171cb7a01acSMauro Carvalho Chehab 			svbi->service_lines[0][i] =
172cb7a01acSMauro Carvalho Chehab 				svbi->service_lines[1][i] = 0;
173cb7a01acSMauro Carvalho Chehab 	}
174cb7a01acSMauro Carvalho Chehab 
175cb7a01acSMauro Carvalho Chehab 	for (i = 7; i <= 23; i++) {
176cb7a01acSMauro Carvalho Chehab 		for (x = 0; x <= 1; x++) {
177cb7a01acSMauro Carvalho Chehab 			switch (svbi->service_lines[1-x][i]) {
178cb7a01acSMauro Carvalho Chehab 			case V4L2_SLICED_TELETEXT_B:
179cb7a01acSMauro Carvalho Chehab 				lcr[i] |= 1 << (4 * x);
180cb7a01acSMauro Carvalho Chehab 				break;
181cb7a01acSMauro Carvalho Chehab 			case V4L2_SLICED_WSS_625:
182cb7a01acSMauro Carvalho Chehab 				lcr[i] |= 4 << (4 * x);
183cb7a01acSMauro Carvalho Chehab 				break;
184cb7a01acSMauro Carvalho Chehab 			case V4L2_SLICED_CAPTION_525:
185cb7a01acSMauro Carvalho Chehab 				lcr[i] |= 6 << (4 * x);
186cb7a01acSMauro Carvalho Chehab 				break;
187cb7a01acSMauro Carvalho Chehab 			case V4L2_SLICED_VPS:
188cb7a01acSMauro Carvalho Chehab 				lcr[i] |= 9 << (4 * x);
189cb7a01acSMauro Carvalho Chehab 				break;
190cb7a01acSMauro Carvalho Chehab 			}
191cb7a01acSMauro Carvalho Chehab 		}
192cdf472d3SHans Verkuil 	}
193cdf472d3SHans Verkuil 
194cb7a01acSMauro Carvalho Chehab 	if (is_pal) {
195cb7a01acSMauro Carvalho Chehab 		for (x = 1, i = state->vbi_regs_offset + 0x424;
196cdf472d3SHans Verkuil 		     i <= state->vbi_regs_offset + 0x434; i++, x++)
197cdf472d3SHans Verkuil 			cx25840_write(client, i, lcr[6 + x]);
198cb7a01acSMauro Carvalho Chehab 	} else {
199cdf472d3SHans Verkuil 		for (x = 1, i = state->vbi_regs_offset + 0x424;
200cdf472d3SHans Verkuil 		     i <= state->vbi_regs_offset + 0x430; i++, x++)
201cb7a01acSMauro Carvalho Chehab 			cx25840_write(client, i, lcr[9 + x]);
202cb7a01acSMauro Carvalho Chehab 		for (i = state->vbi_regs_offset + 0x431;
203cb7a01acSMauro Carvalho Chehab 		     i <= state->vbi_regs_offset + 0x434; i++)
204cdf472d3SHans Verkuil 			cx25840_write(client, i, 0);
205cdf472d3SHans Verkuil 	}
206cdf472d3SHans Verkuil 
207cdf472d3SHans Verkuil 	cx25840_write(client, state->vbi_regs_offset + 0x43c, 0x16);
208cb7a01acSMauro Carvalho Chehab 	/* TODO: this will have to be changed for generic_mode VBI */
209cb7a01acSMauro Carvalho Chehab 	if (is_cx23888(state))
210cb7a01acSMauro Carvalho Chehab 		cx25840_write(client, 0x428, is_pal ? 0x2a : 0x22);
211cb7a01acSMauro Carvalho Chehab 	else
212cb7a01acSMauro Carvalho Chehab 		cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22);
213cb7a01acSMauro Carvalho Chehab 	return 0;
214cb7a01acSMauro Carvalho Chehab }
215cb7a01acSMauro Carvalho Chehab 
cx25840_decode_vbi_line(struct v4l2_subdev * sd,struct v4l2_decode_vbi_line * vbi)216cb7a01acSMauro Carvalho Chehab int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
217cb7a01acSMauro Carvalho Chehab {
218cb7a01acSMauro Carvalho Chehab 	struct cx25840_state *state = to_state(sd);
219cb7a01acSMauro Carvalho Chehab 	u8 *p = vbi->p;
220cb7a01acSMauro Carvalho Chehab 	int id1, id2, l, err = 0;
221cb7a01acSMauro Carvalho Chehab 
222cb7a01acSMauro Carvalho Chehab 	if (p[0] || p[1] != 0xff || p[2] != 0xff ||
223cb7a01acSMauro Carvalho Chehab 			(p[3] != 0x55 && p[3] != 0x91)) {
224cb7a01acSMauro Carvalho Chehab 		vbi->line = vbi->type = 0;
225cb7a01acSMauro Carvalho Chehab 		return 0;
226cb7a01acSMauro Carvalho Chehab 	}
227cb7a01acSMauro Carvalho Chehab 
228cb7a01acSMauro Carvalho Chehab 	p += 4;
229cb7a01acSMauro Carvalho Chehab 	id1 = p[-1];
230cb7a01acSMauro Carvalho Chehab 	id2 = p[0] & 0xf;
231cb7a01acSMauro Carvalho Chehab 	l = p[2] & 0x3f;
232cb7a01acSMauro Carvalho Chehab 	l += state->vbi_line_offset;
233cb7a01acSMauro Carvalho Chehab 	p += 4;
234cb7a01acSMauro Carvalho Chehab 
235cb7a01acSMauro Carvalho Chehab 	switch (id2) {
236cb7a01acSMauro Carvalho Chehab 	case 1:
237cb7a01acSMauro Carvalho Chehab 		id2 = V4L2_SLICED_TELETEXT_B;
238cb7a01acSMauro Carvalho Chehab 		break;
239cb7a01acSMauro Carvalho Chehab 	case 4:
240cb7a01acSMauro Carvalho Chehab 		id2 = V4L2_SLICED_WSS_625;
241cb7a01acSMauro Carvalho Chehab 		break;
242cb7a01acSMauro Carvalho Chehab 	case 6:
243cb7a01acSMauro Carvalho Chehab 		id2 = V4L2_SLICED_CAPTION_525;
244cb7a01acSMauro Carvalho Chehab 		err = !odd_parity(p[0]) || !odd_parity(p[1]);
245cb7a01acSMauro Carvalho Chehab 		break;
246cb7a01acSMauro Carvalho Chehab 	case 9:
247cb7a01acSMauro Carvalho Chehab 		id2 = V4L2_SLICED_VPS;
248cb7a01acSMauro Carvalho Chehab 		if (decode_vps(p, p) != 0)
249cb7a01acSMauro Carvalho Chehab 			err = 1;
250cb7a01acSMauro Carvalho Chehab 		break;
251cb7a01acSMauro Carvalho Chehab 	default:
252cb7a01acSMauro Carvalho Chehab 		id2 = 0;
253cb7a01acSMauro Carvalho Chehab 		err = 1;
254cb7a01acSMauro Carvalho Chehab 		break;
255cb7a01acSMauro Carvalho Chehab 	}
256cb7a01acSMauro Carvalho Chehab 
257cb7a01acSMauro Carvalho Chehab 	vbi->type = err ? 0 : id2;
258cb7a01acSMauro Carvalho Chehab 	vbi->line = err ? 0 : l;
259 	vbi->is_second_field = err ? 0 : (id1 == 0x55);
260 	vbi->p = p;
261 	return 0;
262 }
263