blob: df994311251e7e85901bc506e3550b83cfede4de [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * i2c tv tuner chip device driver
4 * core core, i.e. kernel interfaces, registering and so on
5 */
6
7#include <linux/module.h>
8#include <linux/moduleparam.h>
9#include <linux/kernel.h>
10#include <linux/sched.h>
11#include <linux/string.h>
12#include <linux/timer.h>
13#include <linux/delay.h>
14#include <linux/errno.h>
15#include <linux/slab.h>
16#include <linux/poll.h>
17#include <linux/i2c.h>
18#include <linux/types.h>
19#include <linux/videodev.h>
20#include <linux/init.h>
21
22#include <media/tuner.h>
23#include <media/audiochip.h>
24
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070025#include "msp3400.h"
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#define UNSET (-1U)
28
29/* standard i2c insmod options */
30static unsigned short normal_i2c[] = {
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080031 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070032 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
33 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 I2C_CLIENT_END
35};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070036
Linus Torvalds1da177e2005-04-16 15:20:36 -070037I2C_CLIENT_INSMOD;
38
39/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070040static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070041static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070042static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070043
Linus Torvalds1da177e2005-04-16 15:20:36 -070044/* insmod options used at runtime => read/write */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070045unsigned int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070047static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070048static unsigned int radio_range[2] = { 65, 108 };
49
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020050static char pal[] = "--";
51static char secam[] = "--";
52static char ntsc[] = "-";
53
54module_param(addr, int, 0444);
55module_param(no_autodetect, int, 0444);
56module_param(show_i2c, int, 0444);
57module_param(tuner_debug, int, 0644);
58
59module_param_string(pal, pal, sizeof(pal), 0644);
60module_param_string(secam, secam, sizeof(secam), 0644);
61module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070062module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063module_param_array(radio_range, int, NULL, 0644);
64
65MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
66MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
67MODULE_LICENSE("GPL");
68
Linus Torvalds1da177e2005-04-16 15:20:36 -070069static struct i2c_driver driver;
70static struct i2c_client client_template;
71
72/* ---------------------------------------------------------------------- */
73
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070074/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static void set_tv_freq(struct i2c_client *c, unsigned int freq)
76{
77 struct tuner *t = i2c_get_clientdata(c);
78
79 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070080 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 return;
82 }
83 if (NULL == t->tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070084 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 return;
86 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070087 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
88 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
89 freq / 16, freq % 16 * 100 / 16, tv_range[0],
90 tv_range[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070092 t->tv_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093}
94
95static void set_radio_freq(struct i2c_client *c, unsigned int freq)
96{
97 struct tuner *t = i2c_get_clientdata(c);
98
99 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700100 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 return;
102 }
103 if (NULL == t->radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700104 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 return;
106 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700107 if (freq <= radio_range[0] * 16000 || freq >= radio_range[1] * 16000) {
108 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
109 freq / 16000, freq % 16000 * 100 / 16000,
110 radio_range[0], radio_range[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700112
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700113 t->radio_freq(c, freq);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700114 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115}
116
117static void set_freq(struct i2c_client *c, unsigned long freq)
118{
119 struct tuner *t = i2c_get_clientdata(c);
120
121 switch (t->mode) {
122 case V4L2_TUNER_RADIO:
123 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700124 freq / 16000, freq % 16000 * 100 / 16000);
125 set_radio_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 break;
127 case V4L2_TUNER_ANALOG_TV:
128 case V4L2_TUNER_DIGITAL_TV:
129 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700130 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 set_tv_freq(c, freq);
132 break;
133 }
134 t->freq = freq;
135}
136
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700137static void set_type(struct i2c_client *c, unsigned int type,
138 unsigned int new_mode_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139{
140 struct tuner *t = i2c_get_clientdata(c);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700141 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700143 if (type == UNSET || type == TUNER_ABSENT) {
144 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 return;
146 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700148 if (type >= tuner_count) {
149 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
150 return;
151 }
152
153 /* This code detects calls by card attach_inform */
154 if (NULL == t->i2c.dev.driver) {
155 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
156
157 t->type=type;
158 return;
159 }
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700160
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 t->type = type;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 switch (t->type) {
164 case TUNER_MT2032:
165 microtune_init(c);
166 break;
167 case TUNER_PHILIPS_TDA8290:
168 tda8290_init(c);
169 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700170 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700171 if (tea5767_tuner_init(c) == EINVAL) {
172 t->type = TUNER_ABSENT;
173 t->mode_mask = T_UNINITIALIZED;
174 return;
175 }
176 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700177 break;
178 case TUNER_PHILIPS_FMD1216ME_MK3:
179 buffer[0] = 0x0b;
180 buffer[1] = 0xdc;
181 buffer[2] = 0x9c;
182 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700183 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700184 mdelay(1);
185 buffer[2] = 0x86;
186 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700187 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700188 default_tuner_init(c);
189 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700190 case TUNER_LG_TDVS_H062F:
191 /* Set the Auxiliary Byte. */
192 buffer[2] &= ~0x20;
193 buffer[2] |= 0x18;
194 buffer[3] = 0x20;
195 i2c_master_send(c, buffer, 4);
196 default_tuner_init(c);
197 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800198 case TUNER_PHILIPS_TD1316:
199 buffer[0] = 0x0b;
200 buffer[1] = 0xdc;
201 buffer[2] = 0x86;
202 buffer[3] = 0xa4;
203 i2c_master_send(c,buffer,4);
204 default_tuner_init(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 default:
206 default_tuner_init(c);
207 break;
208 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700209
210 if (t->mode_mask == T_UNINITIALIZED)
211 t->mode_mask = new_mode_mask;
212
213 set_freq(c, t->freq);
214 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100215 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700216 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217}
218
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700219/*
220 * This function apply tuner config to tuner specified
221 * by tun_setup structure. I addr is unset, then admin status
222 * and tun addr status is more precise then current status,
223 * it's applied. Otherwise status and type are applied only to
224 * tuner with exactly the same addr.
225*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700226
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700227static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700228{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700229 struct tuner *t = i2c_get_clientdata(c);
230
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800231 if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET &&
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700232 (t->mode_mask & tun_setup->mode_mask)) ||
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800233 tun_setup->addr == c->addr)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700234 set_type(c, tun_setup->type, tun_setup->mode_mask);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700235 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700236}
237
238static inline int check_mode(struct tuner *t, char *cmd)
239{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700240 if ((1 << t->mode & t->mode_mask) == 0) {
241 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700242 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700243
244 switch (t->mode) {
245 case V4L2_TUNER_RADIO:
246 tuner_dbg("Cmd %s accepted for radio\n", cmd);
247 break;
248 case V4L2_TUNER_ANALOG_TV:
249 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
250 break;
251 case V4L2_TUNER_DIGITAL_TV:
252 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
253 break;
254 }
255 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700256}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700257
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700258/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259static int tuner_fixup_std(struct tuner *t)
260{
261 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 switch (pal[0]) {
263 case 'b':
264 case 'B':
265 case 'g':
266 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700267 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 t->std = V4L2_STD_PAL_BG;
269 break;
270 case 'i':
271 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700272 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 t->std = V4L2_STD_PAL_I;
274 break;
275 case 'd':
276 case 'D':
277 case 'k':
278 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700279 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 t->std = V4L2_STD_PAL_DK;
281 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700282 case 'M':
283 case 'm':
284 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
285 t->std = V4L2_STD_PAL_M;
286 break;
287 case 'N':
288 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200289 if (pal[1] == 'c' || pal[1] == 'C') {
290 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
291 t->std = V4L2_STD_PAL_Nc;
292 } else {
293 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
294 t->std = V4L2_STD_PAL_N;
295 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700296 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700297 case '-':
298 /* default parameter, do nothing */
299 break;
300 default:
301 tuner_warn ("pal= argument not recognised\n");
302 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
304 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700305 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
306 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200307 case 'b':
308 case 'B':
309 case 'g':
310 case 'G':
311 case 'h':
312 case 'H':
313 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
314 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
315 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700316 case 'd':
317 case 'D':
318 case 'k':
319 case 'K':
320 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
321 t->std = V4L2_STD_SECAM_DK;
322 break;
323 case 'l':
324 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800325 if ((secam[1]=='C')||(secam[1]=='c')) {
326 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
327 t->std = V4L2_STD_SECAM_LC;
328 } else {
329 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
330 t->std = V4L2_STD_SECAM_L;
331 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700332 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700333 case '-':
334 /* default parameter, do nothing */
335 break;
336 default:
337 tuner_warn ("secam= argument not recognised\n");
338 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700339 }
340 }
341
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200342 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
343 switch (ntsc[0]) {
344 case 'm':
345 case 'M':
346 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
347 t->std = V4L2_STD_NTSC_M;
348 break;
349 case 'j':
350 case 'J':
351 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
352 t->std = V4L2_STD_NTSC_M_JP;
353 break;
354 case '-':
355 /* default parameter, do nothing */
356 break;
357 default:
358 tuner_info("ntsc= argument not recognised\n");
359 break;
360 }
361 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 return 0;
363}
364
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200365static void tuner_status(struct i2c_client *client)
366{
367 struct tuner *t = i2c_get_clientdata(client);
368 unsigned long freq, freq_fraction;
369 const char *p;
370
371 switch (t->mode) {
372 case V4L2_TUNER_RADIO: p = "radio"; break;
373 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
374 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
375 default: p = "undefined"; break;
376 }
377 if (t->mode == V4L2_TUNER_RADIO) {
378 freq = t->freq / 16000;
379 freq_fraction = (t->freq % 16000) * 100 / 16000;
380 } else {
381 freq = t->freq / 16;
382 freq_fraction = (t->freq % 16) * 100 / 16;
383 }
384 tuner_info("Tuner mode: %s\n", p);
385 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
386 tuner_info("Standard: 0x%08llx\n", t->std);
387 if (t->mode == V4L2_TUNER_RADIO) {
388 if (t->has_signal) {
389 tuner_info("Signal strength: %d\n", t->has_signal(client));
390 }
391 if (t->is_stereo) {
392 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
393 }
394 }
395}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396/* ---------------------------------------------------------------------- */
397
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700398/* static var Used only in tuner_attach and tuner_probe */
399static unsigned default_mode_mask;
400
401/* During client attach, set_type is called by adapter's attach_inform callback.
402 set_type must then be completed by tuner_attach.
403 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
405{
406 struct tuner *t;
407
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700408 client_template.adapter = adap;
409 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700411 t = kmalloc(sizeof(struct tuner), GFP_KERNEL);
412 if (NULL == t)
413 return -ENOMEM;
414 memset(t, 0, sizeof(struct tuner));
415 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700417 t->type = UNSET;
418 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
419 t->audmode = V4L2_TUNER_MODE_STEREO;
420 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700422 if (show_i2c) {
423 unsigned char buffer[16];
424 int i,rc;
425
426 memset(buffer, 0, sizeof(buffer));
427 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800428 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700429 for (i=0;i<rc;i++)
430 printk("%02x ",buffer[i]);
431 printk("\n");
432 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700433 /* TEA5767 autodetection code - only for addr = 0xc0 */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700434 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800435 switch (addr) {
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800436 case 0x42:
437 case 0x43:
438 case 0x4a:
439 case 0x4b:
440 /* If chip is not tda8290, don't register.
441 since it can be tda9887*/
442 if (tda8290_probe(&t->i2c) != 0) {
Nickolay V. Shmyrevb228ede2005-11-08 21:38:11 -0800443 tuner_dbg("chip at addr %x is not a tda8290\n", addr);
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800444 kfree(t);
445 return 0;
446 }
447 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800448 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700449 if (tea5767_autodetection(&t->i2c) != EINVAL) {
450 t->type = TUNER_TEA5767;
451 t->mode_mask = T_RADIO;
452 t->mode = T_STANDBY;
453 t->freq = 87.5 * 16; /* Sets freq to FM range */
454 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700455
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800456 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700457 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800458 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700459 }
460 }
461
462 /* Initializes only the first adapter found */
463 if (default_mode_mask != T_UNINITIALIZED) {
464 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
465 t->mode_mask = default_mode_mask;
466 t->freq = 400 * 16; /* Sets freq to VHF High */
467 default_mode_mask = T_UNINITIALIZED;
468 }
469
470 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800471register_client:
472 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700473 i2c_attach_client (&t->i2c);
474 set_type (&t->i2c,t->type, t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 return 0;
476}
477
478static int tuner_probe(struct i2c_adapter *adap)
479{
480 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700481 normal_i2c[0] = addr;
482 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700485 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700486
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 if (adap->class & I2C_CLASS_TV_ANALOG)
488 return i2c_probe(adap, &addr_data, tuner_attach);
489 return 0;
490}
491
492static int tuner_detach(struct i2c_client *client)
493{
494 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700495 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700497 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700498 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700499 tuner_warn
500 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700501 return err;
502 }
503
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 kfree(t);
505 return 0;
506}
507
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700508/*
509 * Switch tuner to other mode. If tuner support both tv and radio,
510 * set another frequency to some value (This is needed for some pal
511 * tuners to avoid locking). Otherwise, just put second tuner in
512 * standby mode.
513 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700515static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
516{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800517 if (mode == t->mode)
518 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700519
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800520 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700521
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800522 if (check_mode(t, cmd) == EINVAL) {
523 t->mode = T_STANDBY;
524 if (t->standby)
525 t->standby (client);
526 return EINVAL;
527 }
528 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700529}
530
531#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800532 tuner_dbg("switching to v4l2\n"); \
533 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700534
535static inline int check_v4l2(struct tuner *t)
536{
537 if (t->using_v4l2) {
538 tuner_dbg ("ignore v4l1 call\n");
539 return EINVAL;
540 }
541 return 0;
542}
543
544static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545{
546 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700548 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700550 case TUNER_SET_TYPE_ADDR:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700551 tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
552 ((struct tuner_setup *)arg)->type,
553 ((struct tuner_setup *)arg)->addr,
554 ((struct tuner_setup *)arg)->mode_mask);
555
556 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700557 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 case AUDC_SET_RADIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700559 set_mode(client,t,V4L2_TUNER_RADIO, "AUDC_SET_RADIO");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700561 case TUNER_SET_STANDBY:
562 {
563 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
564 return 0;
565 if (t->standby)
566 t->standby (client);
567 break;
568 }
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700569 case VIDIOCSAUDIO:
570 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
571 return 0;
572 if (check_v4l2(t) == EINVAL)
573 return 0;
574
575 /* Should be implemented, since bttv calls it */
576 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
577
578 break;
579 case MSP_SET_MATRIX:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700580 case TDA9887_SET_CONFIG:
581 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 /* --- v4l ioctls --- */
583 /* take care: bttv does userspace copying, we'll get a
584 kernel pointer here... */
585 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700586 {
587 static const v4l2_std_id map[] = {
588 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
589 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
590 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
591 [4 /* bttv */ ] = V4L2_STD_PAL_M,
592 [5 /* bttv */ ] = V4L2_STD_PAL_N,
593 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
594 };
595 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700597 if (check_v4l2(t) == EINVAL)
598 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700599
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700600 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
601 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700603 if (vc->norm < ARRAY_SIZE(map))
604 t->std = map[vc->norm];
605 tuner_fixup_std(t);
606 if (t->freq)
607 set_tv_freq(client, t->freq);
608 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700609 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700610 case VIDIOCSFREQ:
611 {
612 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700613
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700614 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
615 return 0;
616 if (check_v4l2(t) == EINVAL)
617 return 0;
618
619 set_freq(client, *v);
620 return 0;
621 }
622 case VIDIOCGTUNER:
623 {
624 struct video_tuner *vt = arg;
625
626 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
627 return 0;
628 if (check_v4l2(t) == EINVAL)
629 return 0;
630
631 if (V4L2_TUNER_RADIO == t->mode) {
632 if (t->has_signal)
633 vt->signal = t->has_signal(client);
634 if (t->is_stereo) {
635 if (t->is_stereo(client))
636 vt->flags |=
637 VIDEO_TUNER_STEREO_ON;
638 else
639 vt->flags &=
640 ~VIDEO_TUNER_STEREO_ON;
641 }
642 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
643
644 vt->rangelow = radio_range[0] * 16000;
645 vt->rangehigh = radio_range[1] * 16000;
646
647 } else {
648 vt->rangelow = tv_range[0] * 16;
649 vt->rangehigh = tv_range[1] * 16;
650 }
651
652 return 0;
653 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700655 {
656 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700658 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
659 return 0;
660 if (check_v4l2(t) == EINVAL)
661 return 0;
662
663 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
664 va->mode = t->is_stereo(client)
665 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
666 return 0;
667 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
669 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700670 {
671 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700673 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
674 == EINVAL)
675 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700676
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700677 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700679 t->std = *id;
680 tuner_fixup_std(t);
681 if (t->freq)
682 set_freq(client, t->freq);
683 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700684 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700685 case VIDIOC_S_FREQUENCY:
686 {
687 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700688
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700689 t->freq = f->frequency;
690 switch_v4l2();
691 if (V4L2_TUNER_RADIO == f->type &&
692 V4L2_TUNER_RADIO != t->mode) {
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800693 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700694 == EINVAL)
695 return 0;
696 }
697 set_freq(client,t->freq);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700698
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700699 break;
700 }
701 case VIDIOC_G_FREQUENCY:
702 {
703 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700704
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700705 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
706 return 0;
707 switch_v4l2();
708 f->type = t->mode;
709 f->frequency = t->freq;
710 break;
711 }
712 case VIDIOC_G_TUNER:
713 {
714 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700715
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700716 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
717 return 0;
718 switch_v4l2();
719
720 if (V4L2_TUNER_RADIO == t->mode) {
721
722 if (t->has_signal)
723 tuner->signal = t->has_signal(client);
724
725 if (t->is_stereo) {
726 if (t->is_stereo(client)) {
727 tuner->rxsubchans =
728 V4L2_TUNER_SUB_STEREO |
729 V4L2_TUNER_SUB_MONO;
730 } else {
731 tuner->rxsubchans =
732 V4L2_TUNER_SUB_MONO;
733 }
734 }
735
736 tuner->capability |=
737 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
738
739 tuner->audmode = t->audmode;
740
741 tuner->rangelow = radio_range[0] * 16000;
742 tuner->rangehigh = radio_range[1] * 16000;
743 } else {
744 tuner->rangelow = tv_range[0] * 16;
745 tuner->rangehigh = tv_range[1] * 16;
746 }
747 break;
748 }
749 case VIDIOC_S_TUNER:
750 {
751 struct v4l2_tuner *tuner = arg;
752
753 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
754 return 0;
755
756 switch_v4l2();
757
758 if (V4L2_TUNER_RADIO == t->mode) {
759 t->audmode = tuner->audmode;
760 set_radio_freq(client, t->freq);
761 }
762 break;
763 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200764 case VIDIOC_LOG_STATUS:
765 tuner_status(client);
766 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 default:
Mauro Carvalho Chehabfd1c3882005-11-08 21:36:34 -0800768 tuner_dbg("Unimplemented IOCTL 0x%08x(dir=%d,tp='%c',nr=%d,sz=%d)\n",
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700769 cmd, _IOC_DIR(cmd), _IOC_TYPE(cmd),
770 _IOC_NR(cmd), _IOC_SIZE(cmd));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 break;
772 }
773
774 return 0;
775}
776
Russell King9480e302005-10-28 09:52:56 -0700777static int tuner_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700779 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
780 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700782 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 /* FIXME: power down ??? */
784 return 0;
785}
786
Russell King9480e302005-10-28 09:52:56 -0700787static int tuner_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700789 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
790 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700792 tuner_dbg ("resume\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 if (t->freq)
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700794 set_freq(c, t->freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 return 0;
796}
797
798/* ----------------------------------------------------------------------- */
799
800static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700801 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700802 .attach_adapter = tuner_probe,
803 .detach_client = tuner_detach,
804 .command = tuner_command,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 .driver = {
Laurent Riffard604f28e2005-11-26 20:43:39 +0100806 .name = "tuner",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700807 .suspend = tuner_suspend,
808 .resume = tuner_resume,
809 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700811static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200812 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700813 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814};
815
816static int __init tuner_init_module(void)
817{
818 return i2c_add_driver(&driver);
819}
820
821static void __exit tuner_cleanup_module(void)
822{
823 i2c_del_driver(&driver);
824}
825
826module_init(tuner_init_module);
827module_exit(tuner_cleanup_module);
828
829/*
830 * Overrides for Emacs so that we follow Linus's tabbing style.
831 * ---------------------------------------------------------------------------
832 * Local variables:
833 * c-basic-offset: 8
834 * End:
835 */