diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-av-vbi.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-av-vbi.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c new file mode 100644 index 000000000000..d09f1daf4ebf --- /dev/null +++ b/drivers/media/video/cx18/cx18-av-vbi.c | |||
@@ -0,0 +1,413 @@ | |||
1 | /* | ||
2 | * cx18 ADEC VBI functions | ||
3 | * | ||
4 | * Derived from cx25840-vbi.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version 2 | ||
11 | * of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | */ | ||
23 | |||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | |||
27 | static int odd_parity(u8 c) | ||
28 | { | ||
29 | c ^= (c >> 4); | ||
30 | c ^= (c >> 2); | ||
31 | c ^= (c >> 1); | ||
32 | |||
33 | return c & 1; | ||
34 | } | ||
35 | |||
36 | static int decode_vps(u8 *dst, u8 *p) | ||
37 | { | ||
38 | static const u8 biphase_tbl[] = { | ||
39 | 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, | ||
40 | 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, | ||
41 | 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, | ||
42 | 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, | ||
43 | 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, | ||
44 | 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, | ||
45 | 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, | ||
46 | 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, | ||
47 | 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, | ||
48 | 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, | ||
49 | 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, | ||
50 | 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, | ||
51 | 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, | ||
52 | 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, | ||
53 | 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, | ||
54 | 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, | ||
55 | 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, | ||
56 | 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, | ||
57 | 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, | ||
58 | 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, | ||
59 | 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, | ||
60 | 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, | ||
61 | 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, | ||
62 | 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, | ||
63 | 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, | ||
64 | 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, | ||
65 | 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, | ||
66 | 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, | ||
67 | 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, | ||
68 | 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, | ||
69 | 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, | ||
70 | 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, | ||
71 | }; | ||
72 | |||
73 | u8 c, err = 0; | ||
74 | int i; | ||
75 | |||
76 | for (i = 0; i < 2 * 13; i += 2) { | ||
77 | err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; | ||
78 | c = (biphase_tbl[p[i + 1]] & 0xf) | | ||
79 | ((biphase_tbl[p[i]] & 0xf) << 4); | ||
80 | dst[i / 2] = c; | ||
81 | } | ||
82 | |||
83 | return err & 0xf0; | ||
84 | } | ||
85 | |||
86 | void cx18_av_vbi_setup(struct cx18 *cx) | ||
87 | { | ||
88 | struct cx18_av_state *state = &cx->av_state; | ||
89 | v4l2_std_id std = state->std; | ||
90 | int hblank, hactive, burst, vblank, vactive, sc; | ||
91 | int vblank656, src_decimation; | ||
92 | int luma_lpf, uv_lpf, comb; | ||
93 | u32 pll_int, pll_frac, pll_post; | ||
94 | |||
95 | /* datasheet startup, step 8d */ | ||
96 | if (std & ~V4L2_STD_NTSC) | ||
97 | cx18_av_write(cx, 0x49f, 0x11); | ||
98 | else | ||
99 | cx18_av_write(cx, 0x49f, 0x14); | ||
100 | |||
101 | if (std & V4L2_STD_625_50) { | ||
102 | hblank = 0x084; | ||
103 | hactive = 0x2d0; | ||
104 | burst = 0x5d; | ||
105 | vblank = 0x024; | ||
106 | vactive = 0x244; | ||
107 | vblank656 = 0x28; | ||
108 | src_decimation = 0x21f; | ||
109 | |||
110 | luma_lpf = 2; | ||
111 | if (std & V4L2_STD_SECAM) { | ||
112 | uv_lpf = 0; | ||
113 | comb = 0; | ||
114 | sc = 0x0a425f; | ||
115 | } else if (std == V4L2_STD_PAL_Nc) { | ||
116 | uv_lpf = 1; | ||
117 | comb = 0x20; | ||
118 | sc = 556453; | ||
119 | } else { | ||
120 | uv_lpf = 1; | ||
121 | comb = 0x20; | ||
122 | sc = 0x0a8263; | ||
123 | } | ||
124 | } else { | ||
125 | hactive = 720; | ||
126 | hblank = 122; | ||
127 | vactive = 487; | ||
128 | luma_lpf = 1; | ||
129 | uv_lpf = 1; | ||
130 | |||
131 | src_decimation = 0x21f; | ||
132 | if (std == V4L2_STD_PAL_60) { | ||
133 | vblank = 26; | ||
134 | vblank656 = 26; | ||
135 | burst = 0x5b; | ||
136 | luma_lpf = 2; | ||
137 | comb = 0x20; | ||
138 | sc = 0x0a8263; | ||
139 | } else if (std == V4L2_STD_PAL_M) { | ||
140 | vblank = 20; | ||
141 | vblank656 = 24; | ||
142 | burst = 0x61; | ||
143 | comb = 0x20; | ||
144 | |||
145 | sc = 555452; | ||
146 | } else { | ||
147 | vblank = 26; | ||
148 | vblank656 = 26; | ||
149 | burst = 0x5b; | ||
150 | comb = 0x66; | ||
151 | sc = 556063; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | /* DEBUG: Displays configured PLL frequency */ | ||
156 | pll_int = cx18_av_read(cx, 0x108); | ||
157 | pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; | ||
158 | pll_post = cx18_av_read(cx, 0x109); | ||
159 | CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n", | ||
160 | pll_int, pll_frac, pll_post); | ||
161 | |||
162 | if (pll_post) { | ||
163 | int fin, fsc; | ||
164 | int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac); | ||
165 | |||
166 | pll >>= 25; | ||
167 | pll /= pll_post; | ||
168 | CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", | ||
169 | pll / 1000000, pll % 1000000); | ||
170 | CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n", | ||
171 | pll / 8000000, (pll / 8) % 1000000); | ||
172 | |||
173 | fin = ((u64)src_decimation * pll) >> 12; | ||
174 | CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n", | ||
175 | fin / 1000000, fin % 1000000); | ||
176 | |||
177 | fsc = (((u64)sc) * pll) >> 24L; | ||
178 | CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n", | ||
179 | fsc / 1000000, fsc % 1000000); | ||
180 | |||
181 | CX18_DEBUG_INFO("hblank %i, hactive %i, " | ||
182 | "vblank %i , vactive %i, vblank656 %i, src_dec %i," | ||
183 | "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x," | ||
184 | " sc 0x%06x\n", | ||
185 | hblank, hactive, vblank, vactive, vblank656, | ||
186 | src_decimation, burst, luma_lpf, uv_lpf, comb, sc); | ||
187 | } | ||
188 | |||
189 | /* Sets horizontal blanking delay and active lines */ | ||
190 | cx18_av_write(cx, 0x470, hblank); | ||
191 | cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) | | ||
192 | (hactive << 4))); | ||
193 | cx18_av_write(cx, 0x472, hactive >> 4); | ||
194 | |||
195 | /* Sets burst gate delay */ | ||
196 | cx18_av_write(cx, 0x473, burst); | ||
197 | |||
198 | /* Sets vertical blanking delay and active duration */ | ||
199 | cx18_av_write(cx, 0x474, vblank); | ||
200 | cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) | | ||
201 | (vactive << 4))); | ||
202 | cx18_av_write(cx, 0x476, vactive >> 4); | ||
203 | cx18_av_write(cx, 0x477, vblank656); | ||
204 | |||
205 | /* Sets src decimation rate */ | ||
206 | cx18_av_write(cx, 0x478, 0xff & src_decimation); | ||
207 | cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8)); | ||
208 | |||
209 | /* Sets Luma and UV Low pass filters */ | ||
210 | cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); | ||
211 | |||
212 | /* Enables comb filters */ | ||
213 | cx18_av_write(cx, 0x47b, comb); | ||
214 | |||
215 | /* Sets SC Step*/ | ||
216 | cx18_av_write(cx, 0x47c, sc); | ||
217 | cx18_av_write(cx, 0x47d, 0xff & sc >> 8); | ||
218 | cx18_av_write(cx, 0x47e, 0xff & sc >> 16); | ||
219 | |||
220 | /* Sets VBI parameters */ | ||
221 | if (std & V4L2_STD_625_50) { | ||
222 | cx18_av_write(cx, 0x47f, 0x01); | ||
223 | state->vbi_line_offset = 5; | ||
224 | } else { | ||
225 | cx18_av_write(cx, 0x47f, 0x00); | ||
226 | state->vbi_line_offset = 8; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) | ||
231 | { | ||
232 | struct cx18_av_state *state = &cx->av_state; | ||
233 | struct v4l2_format *fmt; | ||
234 | struct v4l2_sliced_vbi_format *svbi; | ||
235 | |||
236 | switch (cmd) { | ||
237 | case VIDIOC_G_FMT: | ||
238 | { | ||
239 | static u16 lcr2vbi[] = { | ||
240 | 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ | ||
241 | 0, V4L2_SLICED_WSS_625, 0, /* 4 */ | ||
242 | V4L2_SLICED_CAPTION_525, /* 6 */ | ||
243 | 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ | ||
244 | 0, 0, 0, 0 | ||
245 | }; | ||
246 | int is_pal = !(state->std & V4L2_STD_525_60); | ||
247 | int i; | ||
248 | |||
249 | fmt = arg; | ||
250 | if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) | ||
251 | return -EINVAL; | ||
252 | svbi = &fmt->fmt.sliced; | ||
253 | memset(svbi, 0, sizeof(*svbi)); | ||
254 | /* we're done if raw VBI is active */ | ||
255 | if ((cx18_av_read(cx, 0x404) & 0x10) == 0) | ||
256 | break; | ||
257 | |||
258 | if (is_pal) { | ||
259 | for (i = 7; i <= 23; i++) { | ||
260 | u8 v = cx18_av_read(cx, 0x424 + i - 7); | ||
261 | |||
262 | svbi->service_lines[0][i] = lcr2vbi[v >> 4]; | ||
263 | svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; | ||
264 | svbi->service_set |= svbi->service_lines[0][i] | | ||
265 | svbi->service_lines[1][i]; | ||
266 | } | ||
267 | } else { | ||
268 | for (i = 10; i <= 21; i++) { | ||
269 | u8 v = cx18_av_read(cx, 0x424 + i - 10); | ||
270 | |||
271 | svbi->service_lines[0][i] = lcr2vbi[v >> 4]; | ||
272 | svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; | ||
273 | svbi->service_set |= svbi->service_lines[0][i] | | ||
274 | svbi->service_lines[1][i]; | ||
275 | } | ||
276 | } | ||
277 | break; | ||
278 | } | ||
279 | |||
280 | case VIDIOC_S_FMT: | ||
281 | { | ||
282 | int is_pal = !(state->std & V4L2_STD_525_60); | ||
283 | int vbi_offset = is_pal ? 1 : 0; | ||
284 | int i, x; | ||
285 | u8 lcr[24]; | ||
286 | |||
287 | fmt = arg; | ||
288 | if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) | ||
289 | return -EINVAL; | ||
290 | svbi = &fmt->fmt.sliced; | ||
291 | if (svbi->service_set == 0) { | ||
292 | /* raw VBI */ | ||
293 | memset(svbi, 0, sizeof(*svbi)); | ||
294 | |||
295 | /* Setup VBI */ | ||
296 | cx18_av_vbi_setup(cx); | ||
297 | |||
298 | /* VBI Offset */ | ||
299 | cx18_av_write(cx, 0x47f, vbi_offset); | ||
300 | cx18_av_write(cx, 0x404, 0x2e); | ||
301 | break; | ||
302 | } | ||
303 | |||
304 | for (x = 0; x <= 23; x++) | ||
305 | lcr[x] = 0x00; | ||
306 | |||
307 | /* Setup VBI */ | ||
308 | cx18_av_vbi_setup(cx); | ||
309 | |||
310 | /* Sliced VBI */ | ||
311 | cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ | ||
312 | cx18_av_write(cx, 0x406, 0x13); | ||
313 | cx18_av_write(cx, 0x47f, vbi_offset); | ||
314 | |||
315 | if (is_pal) { | ||
316 | for (i = 0; i <= 6; i++) | ||
317 | svbi->service_lines[0][i] = | ||
318 | svbi->service_lines[1][i] = 0; | ||
319 | } else { | ||
320 | for (i = 0; i <= 9; i++) | ||
321 | svbi->service_lines[0][i] = | ||
322 | svbi->service_lines[1][i] = 0; | ||
323 | |||
324 | for (i = 22; i <= 23; i++) | ||
325 | svbi->service_lines[0][i] = | ||
326 | svbi->service_lines[1][i] = 0; | ||
327 | } | ||
328 | |||
329 | for (i = 7; i <= 23; i++) { | ||
330 | for (x = 0; x <= 1; x++) { | ||
331 | switch (svbi->service_lines[1-x][i]) { | ||
332 | case V4L2_SLICED_TELETEXT_B: | ||
333 | lcr[i] |= 1 << (4 * x); | ||
334 | break; | ||
335 | case V4L2_SLICED_WSS_625: | ||
336 | lcr[i] |= 4 << (4 * x); | ||
337 | break; | ||
338 | case V4L2_SLICED_CAPTION_525: | ||
339 | lcr[i] |= 6 << (4 * x); | ||
340 | break; | ||
341 | case V4L2_SLICED_VPS: | ||
342 | lcr[i] |= 9 << (4 * x); | ||
343 | break; | ||
344 | } | ||
345 | } | ||
346 | } | ||
347 | |||
348 | if (is_pal) { | ||
349 | for (x = 1, i = 0x424; i <= 0x434; i++, x++) | ||
350 | cx18_av_write(cx, i, lcr[6 + x]); | ||
351 | } else { | ||
352 | for (x = 1, i = 0x424; i <= 0x430; i++, x++) | ||
353 | cx18_av_write(cx, i, lcr[9 + x]); | ||
354 | for (i = 0x431; i <= 0x434; i++) | ||
355 | cx18_av_write(cx, i, 0); | ||
356 | } | ||
357 | |||
358 | cx18_av_write(cx, 0x43c, 0x16); | ||
359 | cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22); | ||
360 | break; | ||
361 | } | ||
362 | |||
363 | case VIDIOC_INT_DECODE_VBI_LINE: | ||
364 | { | ||
365 | struct v4l2_decode_vbi_line *vbi = arg; | ||
366 | u8 *p = vbi->p; | ||
367 | int id1, id2, l, err = 0; | ||
368 | |||
369 | if (p[0] || p[1] != 0xff || p[2] != 0xff || | ||
370 | (p[3] != 0x55 && p[3] != 0x91)) { | ||
371 | vbi->line = vbi->type = 0; | ||
372 | break; | ||
373 | } | ||
374 | |||
375 | p += 4; | ||
376 | id1 = p[-1]; | ||
377 | id2 = p[0] & 0xf; | ||
378 | l = p[2] & 0x3f; | ||
379 | l += state->vbi_line_offset; | ||
380 | p += 4; | ||
381 | |||
382 | switch (id2) { | ||
383 | case 1: | ||
384 | id2 = V4L2_SLICED_TELETEXT_B; | ||
385 | break; | ||
386 | case 4: | ||
387 | id2 = V4L2_SLICED_WSS_625; | ||
388 | break; | ||
389 | case 6: | ||
390 | id2 = V4L2_SLICED_CAPTION_525; | ||
391 | err = !odd_parity(p[0]) || !odd_parity(p[1]); | ||
392 | break; | ||
393 | case 9: | ||
394 | id2 = V4L2_SLICED_VPS; | ||
395 | if (decode_vps(p, p) != 0) | ||
396 | err = 1; | ||
397 | break; | ||
398 | default: | ||
399 | id2 = 0; | ||
400 | err = 1; | ||
401 | break; | ||
402 | } | ||
403 | |||
404 | vbi->type = err ? 0 : id2; | ||
405 | vbi->line = err ? 0 : l; | ||
406 | vbi->is_second_field = err ? 0 : (id1 == 0x55); | ||
407 | vbi->p = p; | ||
408 | break; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | return 0; | ||
413 | } | ||