blob: f64baa4b00253721caa62774de59b9799c7f3710 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#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
Michael Krufky5e453dc2006-01-09 15:32:31 -020012#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <media/tuner.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014
Mauro Carvalho Chehab674434c2005-12-12 00:37:28 -080015
Linus Torvalds1da177e2005-04-16 15:20:36 -070016/* 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)
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -070026 - Hauppauge PVR-150/500 (possibly more)
Linus Torvalds1da177e2005-04-16 15:20:36 -070027*/
28
29
30/* Addresses to scan */
31static unsigned short normal_i2c[] = {
32 0x84 >>1,
33 0x86 >>1,
34 0x96 >>1,
35 I2C_CLIENT_END,
36};
Linus Torvalds1da177e2005-04-16 15:20:36 -070037I2C_CLIENT_INSMOD;
38
39/* insmod options */
40static unsigned int debug = 0;
41module_param(debug, int, 0644);
42MODULE_LICENSE("GPL");
43
44/* ---------------------------------------------------------------------- */
45
46#define UNSET (-1U)
Hans Verkuil4eb0c142005-11-08 21:37:18 -080047#define tda9887_info(fmt, arg...) do {\
48 printk(KERN_INFO "%s %d-%04x: " fmt, t->client.name, \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080049 i2c_adapter_id(t->client.adapter), t->client.addr , ##arg); } while (0)
Hans Verkuil4eb0c142005-11-08 21:37:18 -080050#define tda9887_dbg(fmt, arg...) do {\
51 if (debug) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080052 printk(KERN_INFO "%s %d-%04x: " fmt, t->client.name, \
53 i2c_adapter_id(t->client.adapter), t->client.addr , ##arg); } while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
55struct tda9887 {
56 struct i2c_client client;
57 v4l2_std_id std;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -070058 enum tuner_mode mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 unsigned int config;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 unsigned int using_v4l2;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070061 unsigned int radio_mode;
Hans Verkuil299392b2005-11-08 21:37:42 -080062 unsigned char data[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -070063};
64
65struct tvnorm {
66 v4l2_std_id std;
67 char *name;
68 unsigned char b;
69 unsigned char c;
70 unsigned char e;
71};
72
73static struct i2c_driver driver;
74static struct i2c_client client_template;
75
76/* ---------------------------------------------------------------------- */
77
78//
79// TDA defines
80//
81
82//// first reg (b)
83#define cVideoTrapBypassOFF 0x00 // bit b0
84#define cVideoTrapBypassON 0x01 // bit b0
85
86#define cAutoMuteFmInactive 0x00 // bit b1
87#define cAutoMuteFmActive 0x02 // bit b1
88
89#define cIntercarrier 0x00 // bit b2
90#define cQSS 0x04 // bit b2
91
92#define cPositiveAmTV 0x00 // bit b3:4
93#define cFmRadio 0x08 // bit b3:4
94#define cNegativeFmTV 0x10 // bit b3:4
95
96
97#define cForcedMuteAudioON 0x20 // bit b5
98#define cForcedMuteAudioOFF 0x00 // bit b5
99
100#define cOutputPort1Active 0x00 // bit b6
101#define cOutputPort1Inactive 0x40 // bit b6
102
103#define cOutputPort2Active 0x00 // bit b7
104#define cOutputPort2Inactive 0x80 // bit b7
105
106
107//// second reg (c)
108#define cDeemphasisOFF 0x00 // bit c5
109#define cDeemphasisON 0x20 // bit c5
110
111#define cDeemphasis75 0x00 // bit c6
112#define cDeemphasis50 0x40 // bit c6
113
114#define cAudioGain0 0x00 // bit c7
115#define cAudioGain6 0x80 // bit c7
116
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200117#define cTopMask 0x1f // bit c0:4
118#define cTopPalSecamDefault 0x14 // bit c0:4
119#define cTopNtscRadioDefault 0x10 // bit c0:4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
121//// third reg (e)
122#define cAudioIF_4_5 0x00 // bit e0:1
123#define cAudioIF_5_5 0x01 // bit e0:1
124#define cAudioIF_6_0 0x02 // bit e0:1
125#define cAudioIF_6_5 0x03 // bit e0:1
126
127
128#define cVideoIF_58_75 0x00 // bit e2:4
129#define cVideoIF_45_75 0x04 // bit e2:4
130#define cVideoIF_38_90 0x08 // bit e2:4
131#define cVideoIF_38_00 0x0C // bit e2:4
132#define cVideoIF_33_90 0x10 // bit e2:4
133#define cVideoIF_33_40 0x14 // bit e2:4
134#define cRadioIF_45_75 0x18 // bit e2:4
135#define cRadioIF_38_90 0x1C // bit e2:4
136
137
138#define cTunerGainNormal 0x00 // bit e5
139#define cTunerGainLow 0x20 // bit e5
140
141#define cGating_18 0x00 // bit e6
142#define cGating_36 0x40 // bit e6
143
144#define cAgcOutON 0x80 // bit e7
145#define cAgcOutOFF 0x00 // bit e7
146
147/* ---------------------------------------------------------------------- */
148
149static struct tvnorm tvnorms[] = {
150 {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200151 .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
152 .name = "PAL-BGHN",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 .b = ( cNegativeFmTV |
154 cQSS ),
155 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200156 cDeemphasis50 |
157 cTopPalSecamDefault),
158 .e = ( cGating_36 |
159 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 cVideoIF_38_90 ),
161 },{
162 .std = V4L2_STD_PAL_I,
163 .name = "PAL-I",
164 .b = ( cNegativeFmTV |
165 cQSS ),
166 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200167 cDeemphasis50 |
168 cTopPalSecamDefault),
169 .e = ( cGating_36 |
170 cAudioIF_6_0 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 cVideoIF_38_90 ),
172 },{
173 .std = V4L2_STD_PAL_DK,
174 .name = "PAL-DK",
175 .b = ( cNegativeFmTV |
176 cQSS ),
177 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200178 cDeemphasis50 |
179 cTopPalSecamDefault),
180 .e = ( cGating_36 |
181 cAudioIF_6_5 |
182 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200184 .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
185 .name = "PAL-M/Nc",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 .b = ( cNegativeFmTV |
187 cQSS ),
188 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200189 cDeemphasis75 |
190 cTopNtscRadioDefault),
191 .e = ( cGating_36 |
192 cAudioIF_4_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 cVideoIF_45_75 ),
194 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200195 .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
196 .name = "SECAM-BGH",
197 .b = ( cPositiveAmTV |
198 cQSS ),
199 .c = ( cTopPalSecamDefault),
200 .e = ( cGating_36 |
201 cAudioIF_5_5 |
202 cVideoIF_38_90 ),
203 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 .std = V4L2_STD_SECAM_L,
205 .name = "SECAM-L",
206 .b = ( cPositiveAmTV |
207 cQSS ),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200208 .c = ( cTopPalSecamDefault),
Nickolay V. Shmyrev3375c392005-11-08 21:37:05 -0800209 .e = ( cGating_36 |
Nickolay V. Shmyrev5f7591c2005-11-08 21:37:04 -0800210 cAudioIF_6_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 cVideoIF_38_90 ),
212 },{
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200213 .std = V4L2_STD_SECAM_LC,
214 .name = "SECAM-L'",
215 .b = ( cOutputPort2Inactive |
216 cPositiveAmTV |
217 cQSS ),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200218 .c = ( cTopPalSecamDefault),
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200219 .e = ( cGating_36 |
220 cAudioIF_6_5 |
221 cVideoIF_33_90 ),
222 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 .std = V4L2_STD_SECAM_DK,
224 .name = "SECAM-DK",
225 .b = ( cNegativeFmTV |
226 cQSS ),
227 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200228 cDeemphasis50 |
229 cTopPalSecamDefault),
230 .e = ( cGating_36 |
231 cAudioIF_6_5 |
232 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 },{
234 .std = V4L2_STD_NTSC_M,
235 .name = "NTSC-M",
236 .b = ( cNegativeFmTV |
237 cQSS ),
238 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200239 cDeemphasis75 |
240 cTopNtscRadioDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 .e = ( cGating_36 |
242 cAudioIF_4_5 |
243 cVideoIF_45_75 ),
244 },{
245 .std = V4L2_STD_NTSC_M_JP,
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200246 .name = "NTSC-M-JP",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 .b = ( cNegativeFmTV |
248 cQSS ),
249 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200250 cDeemphasis50 |
251 cTopNtscRadioDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 .e = ( cGating_36 |
253 cAudioIF_4_5 |
254 cVideoIF_58_75 ),
255 }
256};
257
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700258static struct tvnorm radio_stereo = {
259 .name = "Radio Stereo",
260 .b = ( cFmRadio |
261 cQSS ),
262 .c = ( cDeemphasisOFF |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200263 cAudioGain6 |
264 cTopNtscRadioDefault),
265 .e = ( cTunerGainLow |
266 cAudioIF_5_5 |
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700267 cRadioIF_38_90 ),
268};
269
270static struct tvnorm radio_mono = {
271 .name = "Radio Mono",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 .b = ( cFmRadio |
273 cQSS ),
274 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200275 cDeemphasis75 |
276 cTopNtscRadioDefault),
277 .e = ( cTunerGainLow |
278 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 cRadioIF_38_90 ),
280};
281
282/* ---------------------------------------------------------------------- */
283
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800284static void dump_read_message(struct tda9887 *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
286 static char *afc[16] = {
287 "- 12.5 kHz",
288 "- 37.5 kHz",
289 "- 62.5 kHz",
290 "- 87.5 kHz",
291 "-112.5 kHz",
292 "-137.5 kHz",
293 "-162.5 kHz",
294 "-187.5 kHz [min]",
295 "+187.5 kHz [max]",
296 "+162.5 kHz",
297 "+137.5 kHz",
298 "+112.5 kHz",
299 "+ 87.5 kHz",
300 "+ 62.5 kHz",
301 "+ 37.5 kHz",
302 "+ 12.5 kHz",
303 };
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800304 tda9887_info("read: 0x%2x\n", buf[0]);
305 tda9887_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
306 tda9887_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
307 tda9887_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
308 tda9887_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
309 tda9887_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800312static void dump_write_message(struct tda9887 *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
314 static char *sound[4] = {
315 "AM/TV",
316 "FM/radio",
317 "FM/TV",
318 "FM/radio"
319 };
320 static char *adjust[32] = {
321 "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
322 "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
323 "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7",
324 "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15"
325 };
326 static char *deemph[4] = {
327 "no", "no", "75", "50"
328 };
329 static char *carrier[4] = {
330 "4.5 MHz",
331 "5.5 MHz",
332 "6.0 MHz",
333 "6.5 MHz / AM"
334 };
335 static char *vif[8] = {
336 "58.75 MHz",
337 "45.75 MHz",
338 "38.9 MHz",
339 "38.0 MHz",
340 "33.9 MHz",
341 "33.4 MHz",
342 "45.75 MHz + pin13",
343 "38.9 MHz + pin13",
344 };
345 static char *rif[4] = {
346 "44 MHz",
347 "52 MHz",
348 "52 MHz",
349 "44 MHz",
350 };
351
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800352 tda9887_info("write: byte B 0x%02x\n",buf[1]);
353 tda9887_info(" B0 video mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 (buf[1] & 0x01) ? "video trap" : "sound trap");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800355 tda9887_info(" B1 auto mute fm : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 (buf[1] & 0x02) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800357 tda9887_info(" B2 carrier mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 (buf[1] & 0x04) ? "QSS" : "Intercarrier");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800359 tda9887_info(" B3-4 tv sound/radio : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 sound[(buf[1] & 0x18) >> 3]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800361 tda9887_info(" B5 force mute audio: %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 (buf[1] & 0x20) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800363 tda9887_info(" B6 output port 1 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800365 tda9887_info(" B7 output port 2 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
367
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800368 tda9887_info("write: byte C 0x%02x\n",buf[2]);
369 tda9887_info(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]);
370 tda9887_info(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]);
371 tda9887_info(" C7 audio gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 (buf[2] & 0x80) ? "-6" : "0");
373
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800374 tda9887_info("write: byte E 0x%02x\n",buf[3]);
375 tda9887_info(" E0-1 sound carrier : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 carrier[(buf[3] & 0x03)]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800377 tda9887_info(" E6 l pll gating : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 (buf[3] & 0x40) ? "36" : "13");
379
380 if (buf[1] & 0x08) {
381 /* radio */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800382 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 rif[(buf[3] & 0x0c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800384 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 (buf[3] & 0x80)
386 ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
387 : "fm radio carrier afc");
388 } else {
389 /* video */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800390 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 vif[(buf[3] & 0x1c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800392 tda9887_info(" E5 tuner gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 (buf[3] & 0x80)
394 ? ((buf[3] & 0x20) ? "external" : "normal")
395 : ((buf[3] & 0x20) ? "minimum" : "normal"));
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800396 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 (buf[3] & 0x80)
398 ? ((buf[3] & 0x20)
399 ? "pin3 port, pin22 vif agc out"
400 : "pin22 port, pin3 vif acg ext in")
401 : "pin3+pin22 port");
402 }
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800403 tda9887_info("--\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404}
405
406/* ---------------------------------------------------------------------- */
407
408static int tda9887_set_tvnorm(struct tda9887 *t, char *buf)
409{
410 struct tvnorm *norm = NULL;
411 int i;
412
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700413 if (t->mode == T_RADIO) {
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700414 if (t->radio_mode == V4L2_TUNER_MODE_MONO)
415 norm = &radio_mono;
416 else
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700417 norm = &radio_stereo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 } else {
419 for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
420 if (tvnorms[i].std & t->std) {
421 norm = tvnorms+i;
422 break;
423 }
424 }
425 }
426 if (NULL == norm) {
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800427 tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 return -1;
429 }
430
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800431 tda9887_dbg("configure for: %s\n",norm->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 buf[1] = norm->b;
433 buf[2] = norm->c;
434 buf[3] = norm->e;
435 return 0;
436}
437
438static unsigned int port1 = UNSET;
439static unsigned int port2 = UNSET;
440static unsigned int qss = UNSET;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200441static unsigned int adjust = UNSET;
442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443module_param(port1, int, 0644);
444module_param(port2, int, 0644);
445module_param(qss, int, 0644);
446module_param(adjust, int, 0644);
447
448static int tda9887_set_insmod(struct tda9887 *t, char *buf)
449{
450 if (UNSET != port1) {
451 if (port1)
452 buf[1] |= cOutputPort1Inactive;
453 else
454 buf[1] &= ~cOutputPort1Inactive;
455 }
456 if (UNSET != port2) {
457 if (port2)
458 buf[1] |= cOutputPort2Inactive;
459 else
460 buf[1] &= ~cOutputPort2Inactive;
461 }
462
463 if (UNSET != qss) {
464 if (qss)
465 buf[1] |= cQSS;
466 else
467 buf[1] &= ~cQSS;
468 }
469
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200470 if (adjust >= 0x00 && adjust < 0x20) {
471 buf[2] &= ~cTopMask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 buf[2] |= adjust;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200473 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 return 0;
475}
476
477static int tda9887_set_config(struct tda9887 *t, char *buf)
478{
479 if (t->config & TDA9887_PORT1_ACTIVE)
480 buf[1] &= ~cOutputPort1Inactive;
481 if (t->config & TDA9887_PORT1_INACTIVE)
482 buf[1] |= cOutputPort1Inactive;
483 if (t->config & TDA9887_PORT2_ACTIVE)
484 buf[1] &= ~cOutputPort2Inactive;
485 if (t->config & TDA9887_PORT2_INACTIVE)
486 buf[1] |= cOutputPort2Inactive;
487
488 if (t->config & TDA9887_QSS)
489 buf[1] |= cQSS;
490 if (t->config & TDA9887_INTERCARRIER)
491 buf[1] &= ~cQSS;
492
493 if (t->config & TDA9887_AUTOMUTE)
494 buf[1] |= cAutoMuteFmActive;
495 if (t->config & TDA9887_DEEMPHASIS_MASK) {
496 buf[2] &= ~0x60;
497 switch (t->config & TDA9887_DEEMPHASIS_MASK) {
498 case TDA9887_DEEMPHASIS_NONE:
499 buf[2] |= cDeemphasisOFF;
500 break;
501 case TDA9887_DEEMPHASIS_50:
502 buf[2] |= cDeemphasisON | cDeemphasis50;
503 break;
504 case TDA9887_DEEMPHASIS_75:
505 buf[2] |= cDeemphasisON | cDeemphasis75;
506 break;
507 }
508 }
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200509 if (t->config & TDA9887_TOP_SET) {
510 buf[2] &= ~cTopMask;
511 buf[2] |= (t->config >> 8) & cTopMask;
512 }
Nickolay V. Shmyrev3ae1adc2005-11-08 21:37:39 -0800513 if ((t->config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
514 buf[1] &= ~cQSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 return 0;
516}
517
518/* ---------------------------------------------------------------------- */
519
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200520static char pal[] = "--";
521static char secam[] = "--";
522static char ntsc[] = "-";
523
Bert Wesarg975e0462005-04-16 15:25:43 -0700524module_param_string(pal, pal, sizeof(pal), 0644);
Bert Wesarg975e0462005-04-16 15:25:43 -0700525module_param_string(secam, secam, sizeof(secam), 0644);
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200526module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
528static int tda9887_fixup_std(struct tda9887 *t)
529{
530 /* get more precise norm info from insmod option */
531 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
532 switch (pal[0]) {
533 case 'b':
534 case 'B':
535 case 'g':
536 case 'G':
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200537 case 'h':
538 case 'H':
539 case 'n':
540 case 'N':
541 if (pal[1] == 'c' || pal[1] == 'C') {
542 tda9887_dbg("insmod fixup: PAL => PAL-Nc\n");
543 t->std = V4L2_STD_PAL_Nc;
544 } else {
545 tda9887_dbg("insmod fixup: PAL => PAL-BGHN\n");
546 t->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N;
547 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 break;
549 case 'i':
550 case 'I':
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800551 tda9887_dbg("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 t->std = V4L2_STD_PAL_I;
553 break;
554 case 'd':
555 case 'D':
556 case 'k':
557 case 'K':
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800558 tda9887_dbg("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 t->std = V4L2_STD_PAL_DK;
560 break;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200561 case 'm':
562 case 'M':
563 tda9887_dbg("insmod fixup: PAL => PAL-M\n");
564 t->std = V4L2_STD_PAL_M;
565 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700566 case '-':
567 /* default parameter, do nothing */
568 break;
569 default:
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800570 tda9887_info("pal= argument not recognised\n");
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700571 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 }
573 }
574 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
575 switch (secam[0]) {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200576 case 'b':
577 case 'B':
578 case 'g':
579 case 'G':
580 case 'h':
581 case 'H':
582 tda9887_dbg("insmod fixup: SECAM => SECAM-BGH\n");
583 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
584 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 case 'd':
586 case 'D':
587 case 'k':
588 case 'K':
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800589 tda9887_dbg("insmod fixup: SECAM => SECAM-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 t->std = V4L2_STD_SECAM_DK;
591 break;
592 case 'l':
593 case 'L':
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200594 if (secam[1] == 'c' || secam[1] == 'C') {
595 tda9887_dbg("insmod fixup: SECAM => SECAM-L'\n");
596 t->std = V4L2_STD_SECAM_LC;
597 } else {
598 tda9887_dbg("insmod fixup: SECAM => SECAM-L\n");
599 t->std = V4L2_STD_SECAM_L;
600 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700602 case '-':
603 /* default parameter, do nothing */
604 break;
605 default:
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800606 tda9887_info("secam= argument not recognised\n");
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700607 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 }
609 }
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200610 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
611 switch (ntsc[0]) {
612 case 'm':
613 case 'M':
614 tda9887_dbg("insmod fixup: NTSC => NTSC-M\n");
615 t->std = V4L2_STD_NTSC_M;
616 break;
617 case 'j':
618 case 'J':
619 tda9887_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
620 t->std = V4L2_STD_NTSC_M_JP;
621 break;
622 case '-':
623 /* default parameter, do nothing */
624 break;
625 default:
626 tda9887_info("ntsc= argument not recognised\n");
627 break;
628 }
629 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return 0;
631}
632
633static int tda9887_status(struct tda9887 *t)
634{
635 unsigned char buf[1];
636 int rc;
637
638 memset(buf,0,sizeof(buf));
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800639 if (1 != (rc = i2c_master_recv(&t->client,buf,1)))
640 tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800641 dump_read_message(t, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 return 0;
643}
644
645static int tda9887_configure(struct tda9887 *t)
646{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 int rc;
648
Hans Verkuil299392b2005-11-08 21:37:42 -0800649 memset(t->data,0,sizeof(t->data));
650 tda9887_set_tvnorm(t,t->data);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700651
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200652 /* A note on the port settings:
653 These settings tend to depend on the specifics of the board.
654 By default they are set to inactive (bit value 1) by this driver,
655 overwriting any changes made by the tvnorm. This means that it
656 is the responsibility of the module using the tda9887 to set
657 these values in case of changes in the tvnorm.
658 In many cases port 2 should be made active (0) when selecting
659 SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
660
661 For the other standards the tda9887 application note says that
662 the ports should be set to active (0), but, again, that may
663 differ depending on the precise hardware configuration.
664 */
Hans Verkuil299392b2005-11-08 21:37:42 -0800665 t->data[1] |= cOutputPort1Inactive;
666 t->data[1] |= cOutputPort2Inactive;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700667
Hans Verkuil299392b2005-11-08 21:37:42 -0800668 tda9887_set_config(t,t->data);
669 tda9887_set_insmod(t,t->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700671 if (t->mode == T_STANDBY) {
Hans Verkuil299392b2005-11-08 21:37:42 -0800672 t->data[1] |= cForcedMuteAudioON;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700673 }
674
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800675 tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
Hans Verkuil299392b2005-11-08 21:37:42 -0800676 t->data[1],t->data[2],t->data[3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 if (debug > 1)
Hans Verkuil299392b2005-11-08 21:37:42 -0800678 dump_write_message(t, t->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800680 if (4 != (rc = i2c_master_send(&t->client,t->data,4)))
681 tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
683 if (debug > 2) {
684 msleep_interruptible(1000);
685 tda9887_status(t);
686 }
687 return 0;
688}
689
690/* ---------------------------------------------------------------------- */
691
692static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind)
693{
694 struct tda9887 *t;
695
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800696 client_template.adapter = adap;
697 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800699 if (NULL == (t = kmalloc(sizeof(*t), GFP_KERNEL)))
700 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 memset(t,0,sizeof(*t));
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700702
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 t->client = client_template;
704 t->std = 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700705 t->radio_mode = V4L2_TUNER_MODE_STEREO;
706
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800707 tda9887_info("chip found @ 0x%x (%s)\n", addr<<1, adap->name);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800708
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700709 i2c_set_clientdata(&t->client, t);
710 i2c_attach_client(&t->client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
712 return 0;
713}
714
715static int tda9887_probe(struct i2c_adapter *adap)
716{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 if (adap->class & I2C_CLASS_TV_ANALOG)
718 return i2c_probe(adap, &addr_data, tda9887_attach);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 return 0;
720}
721
722static int tda9887_detach(struct i2c_client *client)
723{
724 struct tda9887 *t = i2c_get_clientdata(client);
725
726 i2c_detach_client(client);
727 kfree(t);
728 return 0;
729}
730
731#define SWITCH_V4L2 if (!t->using_v4l2 && debug) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800732 tda9887_info("switching to v4l2\n"); \
733 t->using_v4l2 = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734#define CHECK_V4L2 if (t->using_v4l2) { if (debug) \
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800735 tda9887_info("ignore v4l1 call\n"); \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800736 return 0; }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
738static int
739tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg)
740{
741 struct tda9887 *t = i2c_get_clientdata(client);
742
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800743 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
745 /* --- configuration --- */
746 case AUDC_SET_RADIO:
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700747 {
748 t->mode = T_RADIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 tda9887_configure(t);
750 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700751 }
752 case TUNER_SET_STANDBY:
753 {
754 t->mode = T_STANDBY;
755 tda9887_configure(t);
756 break;
757 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 case TDA9887_SET_CONFIG:
759 {
760 int *i = arg;
761
762 t->config = *i;
763 tda9887_configure(t);
764 break;
765 }
766 /* --- v4l ioctls --- */
767 /* take care: bttv does userspace copying, we'll get a
768 kernel pointer here... */
769 case VIDIOCSCHAN:
770 {
771 static const v4l2_std_id map[] = {
772 [ VIDEO_MODE_PAL ] = V4L2_STD_PAL,
773 [ VIDEO_MODE_NTSC ] = V4L2_STD_NTSC_M,
774 [ VIDEO_MODE_SECAM ] = V4L2_STD_SECAM,
775 [ 4 /* bttv */ ] = V4L2_STD_PAL_M,
776 [ 5 /* bttv */ ] = V4L2_STD_PAL_N,
777 [ 6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
778 };
779 struct video_channel *vc = arg;
780
781 CHECK_V4L2;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700782 t->mode = T_ANALOG_TV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 if (vc->norm < ARRAY_SIZE(map))
784 t->std = map[vc->norm];
785 tda9887_fixup_std(t);
786 tda9887_configure(t);
787 break;
788 }
789 case VIDIOC_S_STD:
790 {
791 v4l2_std_id *id = arg;
792
793 SWITCH_V4L2;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700794 t->mode = T_ANALOG_TV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 t->std = *id;
796 tda9887_fixup_std(t);
797 tda9887_configure(t);
798 break;
799 }
800 case VIDIOC_S_FREQUENCY:
801 {
802 struct v4l2_frequency *f = arg;
803
804 SWITCH_V4L2;
805 if (V4L2_TUNER_ANALOG_TV == f->type) {
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700806 if (t->mode == T_ANALOG_TV)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 return 0;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700808 t->mode = T_ANALOG_TV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 }
810 if (V4L2_TUNER_RADIO == f->type) {
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700811 if (t->mode == T_RADIO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 return 0;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700813 t->mode = T_RADIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 }
815 tda9887_configure(t);
816 break;
817 }
818 case VIDIOC_G_TUNER:
819 {
820 static int AFC_BITS_2_kHz[] = {
821 -12500, -37500, -62500, -97500,
822 -112500, -137500, -162500, -187500,
823 187500, 162500, 137500, 112500,
824 97500 , 62500, 37500 , 12500
825 };
826 struct v4l2_tuner* tuner = arg;
827
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700828 if (t->mode == T_RADIO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 __u8 reg = 0;
830 tuner->afc=0;
831 if (1 == i2c_master_recv(&t->client,&reg,1))
832 tuner->afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
833 }
834 break;
835 }
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700836 case VIDIOC_S_TUNER:
837 {
838 struct v4l2_tuner* tuner = arg;
839
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700840 if (t->mode == T_RADIO) {
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700841 t->radio_mode = tuner->audmode;
842 tda9887_configure (t);
843 }
844 break;
845 }
Hans Verkuil299392b2005-11-08 21:37:42 -0800846 case VIDIOC_LOG_STATUS:
847 {
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200848 tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", t->data[1], t->data[2], t->data[3]);
Hans Verkuil299392b2005-11-08 21:37:42 -0800849 break;
850 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 default:
852 /* nothing */
853 break;
854 }
855 return 0;
856}
857
Russell King9480e302005-10-28 09:52:56 -0700858static int tda9887_suspend(struct device * dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859{
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800860 struct i2c_client *c = container_of(dev, struct i2c_client, dev);
861 struct tda9887 *t = i2c_get_clientdata(c);
862
863 tda9887_dbg("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 return 0;
865}
866
Russell King9480e302005-10-28 09:52:56 -0700867static int tda9887_resume(struct device * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868{
869 struct i2c_client *c = container_of(dev, struct i2c_client, dev);
870 struct tda9887 *t = i2c_get_clientdata(c);
871
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800872 tda9887_dbg("resume\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 tda9887_configure(t);
874 return 0;
875}
876
877/* ----------------------------------------------------------------------- */
878
879static struct i2c_driver driver = {
Mauro Carvalho Chehabafd1a0c2005-12-12 00:37:27 -0800880 .id = -1, /* FIXME */
Mauro Carvalho Chehabafd1a0c2005-12-12 00:37:27 -0800881 .attach_adapter = tda9887_probe,
882 .detach_client = tda9887_detach,
883 .command = tda9887_command,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 .driver = {
Laurent Riffard604f28e2005-11-26 20:43:39 +0100885 .name = "i2c tda9887 driver",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 .suspend = tda9887_suspend,
887 .resume = tda9887_resume,
888 },
889};
890static struct i2c_client client_template =
891{
Jean Delvarefae91e72005-08-15 19:57:04 +0200892 .name = "tda9887",
Mauro Carvalho Chehabafd1a0c2005-12-12 00:37:27 -0800893 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894};
895
896static int __init tda9887_init_module(void)
897{
898 return i2c_add_driver(&driver);
899}
900
901static void __exit tda9887_cleanup_module(void)
902{
903 i2c_del_driver(&driver);
904}
905
906module_init(tda9887_init_module);
907module_exit(tda9887_cleanup_module);
908
909/*
910 * Overrides for Emacs so that we follow Linus's tabbing style.
911 * ---------------------------------------------------------------------------
912 * Local variables:
913 * c-basic-offset: 8
914 * End:
915 */