diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/video/tda9887.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/video/tda9887.c')
-rw-r--r-- | drivers/media/video/tda9887.c | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c new file mode 100644 index 000000000000..7fb063a27961 --- /dev/null +++ b/drivers/media/video/tda9887.c | |||
@@ -0,0 +1,801 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/moduleparam.h> | ||
3 | #include <linux/kernel.h> | ||
4 | #include <linux/i2c.h> | ||
5 | #include <linux/types.h> | ||
6 | #include <linux/videodev.h> | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/errno.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include <linux/delay.h> | ||
11 | |||
12 | #include <media/audiochip.h> | ||
13 | #include <media/tuner.h> | ||
14 | #include <media/id.h> | ||
15 | |||
16 | /* Chips: | ||
17 | TDA9885 (PAL, NTSC) | ||
18 | TDA9886 (PAL, SECAM, NTSC) | ||
19 | TDA9887 (PAL, SECAM, NTSC, FM Radio) | ||
20 | |||
21 | found on: | ||
22 | - Pinnacle PCTV (Jul.2002 Version with MT2032, bttv) | ||
23 | TDA9887 (world), TDA9885 (USA) | ||
24 | Note: OP2 of tda988x must be set to 1, else MT2032 is disabled! | ||
25 | - KNC One TV-Station RDS (saa7134) | ||
26 | */ | ||
27 | |||
28 | |||
29 | /* Addresses to scan */ | ||
30 | static unsigned short normal_i2c[] = { | ||
31 | 0x84 >>1, | ||
32 | 0x86 >>1, | ||
33 | 0x96 >>1, | ||
34 | I2C_CLIENT_END, | ||
35 | }; | ||
36 | static unsigned short normal_i2c_range[] = {I2C_CLIENT_END,I2C_CLIENT_END}; | ||
37 | I2C_CLIENT_INSMOD; | ||
38 | |||
39 | /* insmod options */ | ||
40 | static unsigned int debug = 0; | ||
41 | module_param(debug, int, 0644); | ||
42 | MODULE_LICENSE("GPL"); | ||
43 | |||
44 | /* ---------------------------------------------------------------------- */ | ||
45 | |||
46 | #define UNSET (-1U) | ||
47 | #define PREFIX "tda9885/6/7: " | ||
48 | #define dprintk if (debug) printk | ||
49 | |||
50 | struct tda9887 { | ||
51 | struct i2c_client client; | ||
52 | v4l2_std_id std; | ||
53 | unsigned int radio; | ||
54 | unsigned int config; | ||
55 | unsigned int pinnacle_id; | ||
56 | unsigned int using_v4l2; | ||
57 | }; | ||
58 | |||
59 | struct tvnorm { | ||
60 | v4l2_std_id std; | ||
61 | char *name; | ||
62 | unsigned char b; | ||
63 | unsigned char c; | ||
64 | unsigned char e; | ||
65 | }; | ||
66 | |||
67 | static struct i2c_driver driver; | ||
68 | static struct i2c_client client_template; | ||
69 | |||
70 | /* ---------------------------------------------------------------------- */ | ||
71 | |||
72 | // | ||
73 | // TDA defines | ||
74 | // | ||
75 | |||
76 | //// first reg (b) | ||
77 | #define cVideoTrapBypassOFF 0x00 // bit b0 | ||
78 | #define cVideoTrapBypassON 0x01 // bit b0 | ||
79 | |||
80 | #define cAutoMuteFmInactive 0x00 // bit b1 | ||
81 | #define cAutoMuteFmActive 0x02 // bit b1 | ||
82 | |||
83 | #define cIntercarrier 0x00 // bit b2 | ||
84 | #define cQSS 0x04 // bit b2 | ||
85 | |||
86 | #define cPositiveAmTV 0x00 // bit b3:4 | ||
87 | #define cFmRadio 0x08 // bit b3:4 | ||
88 | #define cNegativeFmTV 0x10 // bit b3:4 | ||
89 | |||
90 | |||
91 | #define cForcedMuteAudioON 0x20 // bit b5 | ||
92 | #define cForcedMuteAudioOFF 0x00 // bit b5 | ||
93 | |||
94 | #define cOutputPort1Active 0x00 // bit b6 | ||
95 | #define cOutputPort1Inactive 0x40 // bit b6 | ||
96 | |||
97 | #define cOutputPort2Active 0x00 // bit b7 | ||
98 | #define cOutputPort2Inactive 0x80 // bit b7 | ||
99 | |||
100 | |||
101 | //// second reg (c) | ||
102 | #define cDeemphasisOFF 0x00 // bit c5 | ||
103 | #define cDeemphasisON 0x20 // bit c5 | ||
104 | |||
105 | #define cDeemphasis75 0x00 // bit c6 | ||
106 | #define cDeemphasis50 0x40 // bit c6 | ||
107 | |||
108 | #define cAudioGain0 0x00 // bit c7 | ||
109 | #define cAudioGain6 0x80 // bit c7 | ||
110 | |||
111 | |||
112 | //// third reg (e) | ||
113 | #define cAudioIF_4_5 0x00 // bit e0:1 | ||
114 | #define cAudioIF_5_5 0x01 // bit e0:1 | ||
115 | #define cAudioIF_6_0 0x02 // bit e0:1 | ||
116 | #define cAudioIF_6_5 0x03 // bit e0:1 | ||
117 | |||
118 | |||
119 | #define cVideoIF_58_75 0x00 // bit e2:4 | ||
120 | #define cVideoIF_45_75 0x04 // bit e2:4 | ||
121 | #define cVideoIF_38_90 0x08 // bit e2:4 | ||
122 | #define cVideoIF_38_00 0x0C // bit e2:4 | ||
123 | #define cVideoIF_33_90 0x10 // bit e2:4 | ||
124 | #define cVideoIF_33_40 0x14 // bit e2:4 | ||
125 | #define cRadioIF_45_75 0x18 // bit e2:4 | ||
126 | #define cRadioIF_38_90 0x1C // bit e2:4 | ||
127 | |||
128 | |||
129 | #define cTunerGainNormal 0x00 // bit e5 | ||
130 | #define cTunerGainLow 0x20 // bit e5 | ||
131 | |||
132 | #define cGating_18 0x00 // bit e6 | ||
133 | #define cGating_36 0x40 // bit e6 | ||
134 | |||
135 | #define cAgcOutON 0x80 // bit e7 | ||
136 | #define cAgcOutOFF 0x00 // bit e7 | ||
137 | |||
138 | /* ---------------------------------------------------------------------- */ | ||
139 | |||
140 | static struct tvnorm tvnorms[] = { | ||
141 | { | ||
142 | .std = V4L2_STD_PAL_BG, | ||
143 | .name = "PAL-BG", | ||
144 | .b = ( cNegativeFmTV | | ||
145 | cQSS ), | ||
146 | .c = ( cDeemphasisON | | ||
147 | cDeemphasis50 ), | ||
148 | .e = ( cAudioIF_5_5 | | ||
149 | cVideoIF_38_90 ), | ||
150 | },{ | ||
151 | .std = V4L2_STD_PAL_I, | ||
152 | .name = "PAL-I", | ||
153 | .b = ( cNegativeFmTV | | ||
154 | cQSS ), | ||
155 | .c = ( cDeemphasisON | | ||
156 | cDeemphasis50 ), | ||
157 | .e = ( cAudioIF_6_0 | | ||
158 | cVideoIF_38_90 ), | ||
159 | },{ | ||
160 | .std = V4L2_STD_PAL_DK, | ||
161 | .name = "PAL-DK", | ||
162 | .b = ( cNegativeFmTV | | ||
163 | cQSS ), | ||
164 | .c = ( cDeemphasisON | | ||
165 | cDeemphasis50 ), | ||
166 | .e = ( cAudioIF_6_5 | | ||
167 | cVideoIF_38_00 ), | ||
168 | },{ | ||
169 | .std = V4L2_STD_PAL_M | V4L2_STD_PAL_N, | ||
170 | .name = "PAL-M/N", | ||
171 | .b = ( cNegativeFmTV | | ||
172 | cQSS ), | ||
173 | .c = ( cDeemphasisON | | ||
174 | cDeemphasis75 ), | ||
175 | .e = ( cAudioIF_4_5 | | ||
176 | cVideoIF_45_75 ), | ||
177 | },{ | ||
178 | .std = V4L2_STD_SECAM_L, | ||
179 | .name = "SECAM-L", | ||
180 | .b = ( cPositiveAmTV | | ||
181 | cQSS ), | ||
182 | .e = ( cAudioIF_6_5 | | ||
183 | cVideoIF_38_90 ), | ||
184 | },{ | ||
185 | .std = V4L2_STD_SECAM_DK, | ||
186 | .name = "SECAM-DK", | ||
187 | .b = ( cNegativeFmTV | | ||
188 | cQSS ), | ||
189 | .c = ( cDeemphasisON | | ||
190 | cDeemphasis50 ), | ||
191 | .e = ( cAudioIF_6_5 | | ||
192 | cVideoIF_38_00 ), | ||
193 | },{ | ||
194 | .std = V4L2_STD_NTSC_M, | ||
195 | .name = "NTSC-M", | ||
196 | .b = ( cNegativeFmTV | | ||
197 | cQSS ), | ||
198 | .c = ( cDeemphasisON | | ||
199 | cDeemphasis50 ), | ||
200 | .e = ( cGating_36 | | ||
201 | cAudioIF_4_5 | | ||
202 | cVideoIF_45_75 ), | ||
203 | },{ | ||
204 | .std = V4L2_STD_NTSC_M_JP, | ||
205 | .name = "NTSC-JP", | ||
206 | .b = ( cNegativeFmTV | | ||
207 | cQSS ), | ||
208 | .c = ( cDeemphasisON | | ||
209 | cDeemphasis50 ), | ||
210 | .e = ( cGating_36 | | ||
211 | cAudioIF_4_5 | | ||
212 | cVideoIF_58_75 ), | ||
213 | } | ||
214 | }; | ||
215 | |||
216 | static struct tvnorm radio = { | ||
217 | .name = "radio", | ||
218 | .b = ( cFmRadio | | ||
219 | cQSS ), | ||
220 | .c = ( cDeemphasisON | | ||
221 | cDeemphasis50 ), | ||
222 | .e = ( cAudioIF_5_5 | | ||
223 | cRadioIF_38_90 ), | ||
224 | }; | ||
225 | |||
226 | /* ---------------------------------------------------------------------- */ | ||
227 | |||
228 | static void dump_read_message(unsigned char *buf) | ||
229 | { | ||
230 | static char *afc[16] = { | ||
231 | "- 12.5 kHz", | ||
232 | "- 37.5 kHz", | ||
233 | "- 62.5 kHz", | ||
234 | "- 87.5 kHz", | ||
235 | "-112.5 kHz", | ||
236 | "-137.5 kHz", | ||
237 | "-162.5 kHz", | ||
238 | "-187.5 kHz [min]", | ||
239 | "+187.5 kHz [max]", | ||
240 | "+162.5 kHz", | ||
241 | "+137.5 kHz", | ||
242 | "+112.5 kHz", | ||
243 | "+ 87.5 kHz", | ||
244 | "+ 62.5 kHz", | ||
245 | "+ 37.5 kHz", | ||
246 | "+ 12.5 kHz", | ||
247 | }; | ||
248 | printk(PREFIX "read: 0x%2x\n", buf[0]); | ||
249 | printk(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no"); | ||
250 | printk(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]); | ||
251 | printk(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low"); | ||
252 | printk(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out"); | ||
253 | printk(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low"); | ||
254 | } | ||
255 | |||
256 | static void dump_write_message(unsigned char *buf) | ||
257 | { | ||
258 | static char *sound[4] = { | ||
259 | "AM/TV", | ||
260 | "FM/radio", | ||
261 | "FM/TV", | ||
262 | "FM/radio" | ||
263 | }; | ||
264 | static char *adjust[32] = { | ||
265 | "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", | ||
266 | "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", | ||
267 | "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", | ||
268 | "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15" | ||
269 | }; | ||
270 | static char *deemph[4] = { | ||
271 | "no", "no", "75", "50" | ||
272 | }; | ||
273 | static char *carrier[4] = { | ||
274 | "4.5 MHz", | ||
275 | "5.5 MHz", | ||
276 | "6.0 MHz", | ||
277 | "6.5 MHz / AM" | ||
278 | }; | ||
279 | static char *vif[8] = { | ||
280 | "58.75 MHz", | ||
281 | "45.75 MHz", | ||
282 | "38.9 MHz", | ||
283 | "38.0 MHz", | ||
284 | "33.9 MHz", | ||
285 | "33.4 MHz", | ||
286 | "45.75 MHz + pin13", | ||
287 | "38.9 MHz + pin13", | ||
288 | }; | ||
289 | static char *rif[4] = { | ||
290 | "44 MHz", | ||
291 | "52 MHz", | ||
292 | "52 MHz", | ||
293 | "44 MHz", | ||
294 | }; | ||
295 | |||
296 | printk(PREFIX "write: byte B 0x%02x\n",buf[1]); | ||
297 | printk(" B0 video mode : %s\n", | ||
298 | (buf[1] & 0x01) ? "video trap" : "sound trap"); | ||
299 | printk(" B1 auto mute fm : %s\n", | ||
300 | (buf[1] & 0x02) ? "yes" : "no"); | ||
301 | printk(" B2 carrier mode : %s\n", | ||
302 | (buf[1] & 0x04) ? "QSS" : "Intercarrier"); | ||
303 | printk(" B3-4 tv sound/radio : %s\n", | ||
304 | sound[(buf[1] & 0x18) >> 3]); | ||
305 | printk(" B5 force mute audio: %s\n", | ||
306 | (buf[1] & 0x20) ? "yes" : "no"); | ||
307 | printk(" B6 output port 1 : %s\n", | ||
308 | (buf[1] & 0x40) ? "high (inactive)" : "low (active)"); | ||
309 | printk(" B7 output port 2 : %s\n", | ||
310 | (buf[1] & 0x80) ? "high (inactive)" : "low (active)"); | ||
311 | |||
312 | printk(PREFIX "write: byte C 0x%02x\n",buf[2]); | ||
313 | printk(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]); | ||
314 | printk(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]); | ||
315 | printk(" C7 audio gain : %s\n", | ||
316 | (buf[2] & 0x80) ? "-6" : "0"); | ||
317 | |||
318 | printk(PREFIX "write: byte E 0x%02x\n",buf[3]); | ||
319 | printk(" E0-1 sound carrier : %s\n", | ||
320 | carrier[(buf[3] & 0x03)]); | ||
321 | printk(" E6 l pll ganting : %s\n", | ||
322 | (buf[3] & 0x40) ? "36" : "13"); | ||
323 | |||
324 | if (buf[1] & 0x08) { | ||
325 | /* radio */ | ||
326 | printk(" E2-4 video if : %s\n", | ||
327 | rif[(buf[3] & 0x0c) >> 2]); | ||
328 | printk(" E7 vif agc output : %s\n", | ||
329 | (buf[3] & 0x80) | ||
330 | ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio") | ||
331 | : "fm radio carrier afc"); | ||
332 | } else { | ||
333 | /* video */ | ||
334 | printk(" E2-4 video if : %s\n", | ||
335 | vif[(buf[3] & 0x1c) >> 2]); | ||
336 | printk(" E5 tuner gain : %s\n", | ||
337 | (buf[3] & 0x80) | ||
338 | ? ((buf[3] & 0x20) ? "external" : "normal") | ||
339 | : ((buf[3] & 0x20) ? "minimum" : "normal")); | ||
340 | printk(" E7 vif agc output : %s\n", | ||
341 | (buf[3] & 0x80) | ||
342 | ? ((buf[3] & 0x20) | ||
343 | ? "pin3 port, pin22 vif agc out" | ||
344 | : "pin22 port, pin3 vif acg ext in") | ||
345 | : "pin3+pin22 port"); | ||
346 | } | ||
347 | printk("--\n"); | ||
348 | } | ||
349 | |||
350 | /* ---------------------------------------------------------------------- */ | ||
351 | |||
352 | static int tda9887_set_tvnorm(struct tda9887 *t, char *buf) | ||
353 | { | ||
354 | struct tvnorm *norm = NULL; | ||
355 | int i; | ||
356 | |||
357 | if (t->radio) { | ||
358 | norm = &radio; | ||
359 | } else { | ||
360 | for (i = 0; i < ARRAY_SIZE(tvnorms); i++) { | ||
361 | if (tvnorms[i].std & t->std) { | ||
362 | norm = tvnorms+i; | ||
363 | break; | ||
364 | } | ||
365 | } | ||
366 | } | ||
367 | if (NULL == norm) { | ||
368 | dprintk(PREFIX "Oops: no tvnorm entry found\n"); | ||
369 | return -1; | ||
370 | } | ||
371 | |||
372 | dprintk(PREFIX "configure for: %s\n",norm->name); | ||
373 | buf[1] = norm->b; | ||
374 | buf[2] = norm->c; | ||
375 | buf[3] = norm->e; | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static unsigned int port1 = UNSET; | ||
380 | static unsigned int port2 = UNSET; | ||
381 | static unsigned int qss = UNSET; | ||
382 | static unsigned int adjust = 0x10; | ||
383 | module_param(port1, int, 0644); | ||
384 | module_param(port2, int, 0644); | ||
385 | module_param(qss, int, 0644); | ||
386 | module_param(adjust, int, 0644); | ||
387 | |||
388 | static int tda9887_set_insmod(struct tda9887 *t, char *buf) | ||
389 | { | ||
390 | if (UNSET != port1) { | ||
391 | if (port1) | ||
392 | buf[1] |= cOutputPort1Inactive; | ||
393 | else | ||
394 | buf[1] &= ~cOutputPort1Inactive; | ||
395 | } | ||
396 | if (UNSET != port2) { | ||
397 | if (port2) | ||
398 | buf[1] |= cOutputPort2Inactive; | ||
399 | else | ||
400 | buf[1] &= ~cOutputPort2Inactive; | ||
401 | } | ||
402 | |||
403 | if (UNSET != qss) { | ||
404 | if (qss) | ||
405 | buf[1] |= cQSS; | ||
406 | else | ||
407 | buf[1] &= ~cQSS; | ||
408 | } | ||
409 | |||
410 | if (adjust >= 0x00 && adjust < 0x20) | ||
411 | buf[2] |= adjust; | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static int tda9887_set_config(struct tda9887 *t, char *buf) | ||
416 | { | ||
417 | if (t->config & TDA9887_PORT1_ACTIVE) | ||
418 | buf[1] &= ~cOutputPort1Inactive; | ||
419 | if (t->config & TDA9887_PORT1_INACTIVE) | ||
420 | buf[1] |= cOutputPort1Inactive; | ||
421 | if (t->config & TDA9887_PORT2_ACTIVE) | ||
422 | buf[1] &= ~cOutputPort2Inactive; | ||
423 | if (t->config & TDA9887_PORT2_INACTIVE) | ||
424 | buf[1] |= cOutputPort2Inactive; | ||
425 | |||
426 | if (t->config & TDA9887_QSS) | ||
427 | buf[1] |= cQSS; | ||
428 | if (t->config & TDA9887_INTERCARRIER) | ||
429 | buf[1] &= ~cQSS; | ||
430 | |||
431 | if (t->config & TDA9887_AUTOMUTE) | ||
432 | buf[1] |= cAutoMuteFmActive; | ||
433 | if (t->config & TDA9887_DEEMPHASIS_MASK) { | ||
434 | buf[2] &= ~0x60; | ||
435 | switch (t->config & TDA9887_DEEMPHASIS_MASK) { | ||
436 | case TDA9887_DEEMPHASIS_NONE: | ||
437 | buf[2] |= cDeemphasisOFF; | ||
438 | break; | ||
439 | case TDA9887_DEEMPHASIS_50: | ||
440 | buf[2] |= cDeemphasisON | cDeemphasis50; | ||
441 | break; | ||
442 | case TDA9887_DEEMPHASIS_75: | ||
443 | buf[2] |= cDeemphasisON | cDeemphasis75; | ||
444 | break; | ||
445 | } | ||
446 | } | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | /* ---------------------------------------------------------------------- */ | ||
451 | |||
452 | static int tda9887_set_pinnacle(struct tda9887 *t, char *buf) | ||
453 | { | ||
454 | unsigned int bCarrierMode = UNSET; | ||
455 | |||
456 | if (t->std & V4L2_STD_625_50) { | ||
457 | if ((1 == t->pinnacle_id) || (7 == t->pinnacle_id)) { | ||
458 | bCarrierMode = cIntercarrier; | ||
459 | } else { | ||
460 | bCarrierMode = cQSS; | ||
461 | } | ||
462 | } | ||
463 | if (t->std & V4L2_STD_525_60) { | ||
464 | if ((5 == t->pinnacle_id) || (6 == t->pinnacle_id)) { | ||
465 | bCarrierMode = cIntercarrier; | ||
466 | } else { | ||
467 | bCarrierMode = cQSS; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | if (bCarrierMode != UNSET) { | ||
472 | buf[1] &= ~0x04; | ||
473 | buf[1] |= bCarrierMode; | ||
474 | } | ||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | /* ---------------------------------------------------------------------- */ | ||
479 | |||
480 | static char pal[] = "-"; | ||
481 | module_param_string(pal, pal, 0644, sizeof(pal)); | ||
482 | static char secam[] = "-"; | ||
483 | module_param_string(secam, secam, 0644, sizeof(secam)); | ||
484 | |||
485 | static int tda9887_fixup_std(struct tda9887 *t) | ||
486 | { | ||
487 | /* get more precise norm info from insmod option */ | ||
488 | if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) { | ||
489 | switch (pal[0]) { | ||
490 | case 'b': | ||
491 | case 'B': | ||
492 | case 'g': | ||
493 | case 'G': | ||
494 | dprintk(PREFIX "insmod fixup: PAL => PAL-BG\n"); | ||
495 | t->std = V4L2_STD_PAL_BG; | ||
496 | break; | ||
497 | case 'i': | ||
498 | case 'I': | ||
499 | dprintk(PREFIX "insmod fixup: PAL => PAL-I\n"); | ||
500 | t->std = V4L2_STD_PAL_I; | ||
501 | break; | ||
502 | case 'd': | ||
503 | case 'D': | ||
504 | case 'k': | ||
505 | case 'K': | ||
506 | dprintk(PREFIX "insmod fixup: PAL => PAL-DK\n"); | ||
507 | t->std = V4L2_STD_PAL_DK; | ||
508 | break; | ||
509 | } | ||
510 | } | ||
511 | if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) { | ||
512 | switch (secam[0]) { | ||
513 | case 'd': | ||
514 | case 'D': | ||
515 | case 'k': | ||
516 | case 'K': | ||
517 | dprintk(PREFIX "insmod fixup: SECAM => SECAM-DK\n"); | ||
518 | t->std = V4L2_STD_SECAM_DK; | ||
519 | break; | ||
520 | case 'l': | ||
521 | case 'L': | ||
522 | dprintk(PREFIX "insmod fixup: SECAM => SECAM-L\n"); | ||
523 | t->std = V4L2_STD_SECAM_L; | ||
524 | break; | ||
525 | } | ||
526 | } | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | static int tda9887_status(struct tda9887 *t) | ||
531 | { | ||
532 | unsigned char buf[1]; | ||
533 | int rc; | ||
534 | |||
535 | memset(buf,0,sizeof(buf)); | ||
536 | if (1 != (rc = i2c_master_recv(&t->client,buf,1))) | ||
537 | printk(PREFIX "i2c i/o error: rc == %d (should be 1)\n",rc); | ||
538 | dump_read_message(buf); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static int tda9887_configure(struct tda9887 *t) | ||
543 | { | ||
544 | unsigned char buf[4]; | ||
545 | int rc; | ||
546 | |||
547 | memset(buf,0,sizeof(buf)); | ||
548 | tda9887_set_tvnorm(t,buf); | ||
549 | buf[1] |= cOutputPort1Inactive; | ||
550 | buf[1] |= cOutputPort2Inactive; | ||
551 | if (UNSET != t->pinnacle_id) { | ||
552 | tda9887_set_pinnacle(t,buf); | ||
553 | } | ||
554 | tda9887_set_config(t,buf); | ||
555 | tda9887_set_insmod(t,buf); | ||
556 | |||
557 | #if 0 | ||
558 | /* This as-is breaks some cards, must be fixed in a | ||
559 | * card-specific way, probably using TDA9887_SET_CONFIG to | ||
560 | * turn on/off port2 */ | ||
561 | if (t->std & V4L2_STD_SECAM_L) { | ||
562 | /* secam fixup (FIXME: move this to tvnorms array?) */ | ||
563 | buf[1] &= ~cOutputPort2Inactive; | ||
564 | } | ||
565 | #endif | ||
566 | |||
567 | dprintk(PREFIX "writing: b=0x%02x c=0x%02x e=0x%02x\n", | ||
568 | buf[1],buf[2],buf[3]); | ||
569 | if (debug > 1) | ||
570 | dump_write_message(buf); | ||
571 | |||
572 | if (4 != (rc = i2c_master_send(&t->client,buf,4))) | ||
573 | printk(PREFIX "i2c i/o error: rc == %d (should be 4)\n",rc); | ||
574 | |||
575 | if (debug > 2) { | ||
576 | msleep_interruptible(1000); | ||
577 | tda9887_status(t); | ||
578 | } | ||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | /* ---------------------------------------------------------------------- */ | ||
583 | |||
584 | static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind) | ||
585 | { | ||
586 | struct tda9887 *t; | ||
587 | |||
588 | client_template.adapter = adap; | ||
589 | client_template.addr = addr; | ||
590 | |||
591 | printk(PREFIX "chip found @ 0x%x\n", addr<<1); | ||
592 | |||
593 | if (NULL == (t = kmalloc(sizeof(*t), GFP_KERNEL))) | ||
594 | return -ENOMEM; | ||
595 | memset(t,0,sizeof(*t)); | ||
596 | t->client = client_template; | ||
597 | t->std = 0; | ||
598 | t->pinnacle_id = UNSET; | ||
599 | i2c_set_clientdata(&t->client, t); | ||
600 | i2c_attach_client(&t->client); | ||
601 | |||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | static int tda9887_probe(struct i2c_adapter *adap) | ||
606 | { | ||
607 | #ifdef I2C_CLASS_TV_ANALOG | ||
608 | if (adap->class & I2C_CLASS_TV_ANALOG) | ||
609 | return i2c_probe(adap, &addr_data, tda9887_attach); | ||
610 | #else | ||
611 | switch (adap->id) { | ||
612 | case I2C_ALGO_BIT | I2C_HW_B_BT848: | ||
613 | case I2C_ALGO_BIT | I2C_HW_B_RIVA: | ||
614 | case I2C_ALGO_SAA7134: | ||
615 | return i2c_probe(adap, &addr_data, tda9887_attach); | ||
616 | break; | ||
617 | } | ||
618 | #endif | ||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | static int tda9887_detach(struct i2c_client *client) | ||
623 | { | ||
624 | struct tda9887 *t = i2c_get_clientdata(client); | ||
625 | |||
626 | i2c_detach_client(client); | ||
627 | kfree(t); | ||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | #define SWITCH_V4L2 if (!t->using_v4l2 && debug) \ | ||
632 | printk(PREFIX "switching to v4l2\n"); \ | ||
633 | t->using_v4l2 = 1; | ||
634 | #define CHECK_V4L2 if (t->using_v4l2) { if (debug) \ | ||
635 | printk(PREFIX "ignore v4l1 call\n"); \ | ||
636 | return 0; } | ||
637 | |||
638 | static int | ||
639 | tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) | ||
640 | { | ||
641 | struct tda9887 *t = i2c_get_clientdata(client); | ||
642 | |||
643 | switch (cmd) { | ||
644 | |||
645 | /* --- configuration --- */ | ||
646 | case AUDC_SET_RADIO: | ||
647 | t->radio = 1; | ||
648 | tda9887_configure(t); | ||
649 | break; | ||
650 | |||
651 | case AUDC_CONFIG_PINNACLE: | ||
652 | { | ||
653 | int *i = arg; | ||
654 | |||
655 | t->pinnacle_id = *i; | ||
656 | tda9887_configure(t); | ||
657 | break; | ||
658 | } | ||
659 | case TDA9887_SET_CONFIG: | ||
660 | { | ||
661 | int *i = arg; | ||
662 | |||
663 | t->config = *i; | ||
664 | tda9887_configure(t); | ||
665 | break; | ||
666 | } | ||
667 | /* --- v4l ioctls --- */ | ||
668 | /* take care: bttv does userspace copying, we'll get a | ||
669 | kernel pointer here... */ | ||
670 | case VIDIOCSCHAN: | ||
671 | { | ||
672 | static const v4l2_std_id map[] = { | ||
673 | [ VIDEO_MODE_PAL ] = V4L2_STD_PAL, | ||
674 | [ VIDEO_MODE_NTSC ] = V4L2_STD_NTSC_M, | ||
675 | [ VIDEO_MODE_SECAM ] = V4L2_STD_SECAM, | ||
676 | [ 4 /* bttv */ ] = V4L2_STD_PAL_M, | ||
677 | [ 5 /* bttv */ ] = V4L2_STD_PAL_N, | ||
678 | [ 6 /* bttv */ ] = V4L2_STD_NTSC_M_JP, | ||
679 | }; | ||
680 | struct video_channel *vc = arg; | ||
681 | |||
682 | CHECK_V4L2; | ||
683 | t->radio = 0; | ||
684 | if (vc->norm < ARRAY_SIZE(map)) | ||
685 | t->std = map[vc->norm]; | ||
686 | tda9887_fixup_std(t); | ||
687 | tda9887_configure(t); | ||
688 | break; | ||
689 | } | ||
690 | case VIDIOC_S_STD: | ||
691 | { | ||
692 | v4l2_std_id *id = arg; | ||
693 | |||
694 | SWITCH_V4L2; | ||
695 | t->radio = 0; | ||
696 | t->std = *id; | ||
697 | tda9887_fixup_std(t); | ||
698 | tda9887_configure(t); | ||
699 | break; | ||
700 | } | ||
701 | case VIDIOC_S_FREQUENCY: | ||
702 | { | ||
703 | struct v4l2_frequency *f = arg; | ||
704 | |||
705 | SWITCH_V4L2; | ||
706 | if (V4L2_TUNER_ANALOG_TV == f->type) { | ||
707 | if (t->radio == 0) | ||
708 | return 0; | ||
709 | t->radio = 0; | ||
710 | } | ||
711 | if (V4L2_TUNER_RADIO == f->type) { | ||
712 | if (t->radio == 1) | ||
713 | return 0; | ||
714 | t->radio = 1; | ||
715 | } | ||
716 | tda9887_configure(t); | ||
717 | break; | ||
718 | } | ||
719 | case VIDIOC_G_TUNER: | ||
720 | { | ||
721 | static int AFC_BITS_2_kHz[] = { | ||
722 | -12500, -37500, -62500, -97500, | ||
723 | -112500, -137500, -162500, -187500, | ||
724 | 187500, 162500, 137500, 112500, | ||
725 | 97500 , 62500, 37500 , 12500 | ||
726 | }; | ||
727 | struct v4l2_tuner* tuner = arg; | ||
728 | |||
729 | if (t->radio) { | ||
730 | __u8 reg = 0; | ||
731 | tuner->afc=0; | ||
732 | if (1 == i2c_master_recv(&t->client,®,1)) | ||
733 | tuner->afc = AFC_BITS_2_kHz[(reg>>1)&0x0f]; | ||
734 | } | ||
735 | break; | ||
736 | } | ||
737 | default: | ||
738 | /* nothing */ | ||
739 | break; | ||
740 | } | ||
741 | return 0; | ||
742 | } | ||
743 | |||
744 | static int tda9887_suspend(struct device * dev, u32 state, u32 level) | ||
745 | { | ||
746 | dprintk("tda9887: suspend\n"); | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | static int tda9887_resume(struct device * dev, u32 level) | ||
751 | { | ||
752 | struct i2c_client *c = container_of(dev, struct i2c_client, dev); | ||
753 | struct tda9887 *t = i2c_get_clientdata(c); | ||
754 | |||
755 | dprintk("tda9887: resume\n"); | ||
756 | tda9887_configure(t); | ||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | /* ----------------------------------------------------------------------- */ | ||
761 | |||
762 | static struct i2c_driver driver = { | ||
763 | .owner = THIS_MODULE, | ||
764 | .name = "i2c tda9887 driver", | ||
765 | .id = -1, /* FIXME */ | ||
766 | .flags = I2C_DF_NOTIFY, | ||
767 | .attach_adapter = tda9887_probe, | ||
768 | .detach_client = tda9887_detach, | ||
769 | .command = tda9887_command, | ||
770 | .driver = { | ||
771 | .suspend = tda9887_suspend, | ||
772 | .resume = tda9887_resume, | ||
773 | }, | ||
774 | }; | ||
775 | static struct i2c_client client_template = | ||
776 | { | ||
777 | I2C_DEVNAME("tda9887"), | ||
778 | .flags = I2C_CLIENT_ALLOW_USE, | ||
779 | .driver = &driver, | ||
780 | }; | ||
781 | |||
782 | static int __init tda9887_init_module(void) | ||
783 | { | ||
784 | return i2c_add_driver(&driver); | ||
785 | } | ||
786 | |||
787 | static void __exit tda9887_cleanup_module(void) | ||
788 | { | ||
789 | i2c_del_driver(&driver); | ||
790 | } | ||
791 | |||
792 | module_init(tda9887_init_module); | ||
793 | module_exit(tda9887_cleanup_module); | ||
794 | |||
795 | /* | ||
796 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
797 | * --------------------------------------------------------------------------- | ||
798 | * Local variables: | ||
799 | * c-basic-offset: 8 | ||
800 | * End: | ||
801 | */ | ||