1*c942fddfSThomas 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 */
89e81a9076SMaciej S. Szmigiero /* 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) {
94cb7a01acSMauro Carvalho Chehab for (i = 7; i <= 23; i++) {
95cdf472d3SHans Verkuil u8 v = cx25840_read(client,
96cdf472d3SHans Verkuil 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 {
104cb7a01acSMauro Carvalho Chehab for (i = 10; i <= 21; i++) {
105cdf472d3SHans Verkuil u8 v = cx25840_read(client,
106cdf472d3SHans Verkuil 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
127cb7a01acSMauro Carvalho Chehab /* VBI Offset */
128cdf472d3SHans Verkuil if (is_cx23888(state))
129cdf472d3SHans Verkuil cx25840_write(client, 0x54f, vbi_offset);
130cdf472d3SHans Verkuil else
131cb7a01acSMauro Carvalho Chehab cx25840_write(client, 0x47f, vbi_offset);
132e81a9076SMaciej S. Szmigiero /* 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 */
153e81a9076SMaciej S. Szmigiero /* TODO: this will have to be changed for generic_mode VBI */
154cb7a01acSMauro Carvalho Chehab cx25840_write(client, 0x404, 0x32); /* Ancillary data */
155cb7a01acSMauro Carvalho Chehab cx25840_write(client, 0x406, 0x13);
156cdf472d3SHans Verkuil if (is_cx23888(state))
157cdf472d3SHans Verkuil cx25840_write(client, 0x54f, vbi_offset);
158cdf472d3SHans Verkuil 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 }
192cb7a01acSMauro Carvalho Chehab }
193cb7a01acSMauro Carvalho Chehab
194cb7a01acSMauro Carvalho Chehab if (is_pal) {
195cdf472d3SHans Verkuil for (x = 1, i = state->vbi_regs_offset + 0x424;
196cdf472d3SHans Verkuil i <= state->vbi_regs_offset + 0x434; i++, x++)
197cb7a01acSMauro Carvalho Chehab 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]);
202cdf472d3SHans Verkuil for (i = state->vbi_regs_offset + 0x431;
203cdf472d3SHans Verkuil i <= state->vbi_regs_offset + 0x434; i++)
204cb7a01acSMauro Carvalho Chehab cx25840_write(client, i, 0);
205cb7a01acSMauro Carvalho Chehab }
206cb7a01acSMauro Carvalho Chehab
207cdf472d3SHans Verkuil cx25840_write(client, state->vbi_regs_offset + 0x43c, 0x16);
208e81a9076SMaciej S. Szmigiero /* TODO: this will have to be changed for generic_mode VBI */
209cdf472d3SHans Verkuil if (is_cx23888(state))
210cdf472d3SHans Verkuil cx25840_write(client, 0x428, is_pal ? 0x2a : 0x22);
211cdf472d3SHans Verkuil 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;
259cb7a01acSMauro Carvalho Chehab vbi->is_second_field = err ? 0 : (id1 == 0x55);
260cb7a01acSMauro Carvalho Chehab vbi->p = p;
261cb7a01acSMauro Carvalho Chehab return 0;
262cb7a01acSMauro Carvalho Chehab }
263