blob: ee4a493032d6406815ebf0d1fd1304ebae3ad158 [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>
Michael Krufky5e453dc2006-01-09 15:32:31 -020023#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
25#define UNSET (-1U)
26
27/* standard i2c insmod options */
28static unsigned short normal_i2c[] = {
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080029 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070030 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
31 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 I2C_CLIENT_END
33};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070034
Linus Torvalds1da177e2005-04-16 15:20:36 -070035I2C_CLIENT_INSMOD;
36
37/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070038static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070039static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070040static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070041
Linus Torvalds1da177e2005-04-16 15:20:36 -070042/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020043int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070045static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070046static unsigned int radio_range[2] = { 65, 108 };
47
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020048static char pal[] = "--";
49static char secam[] = "--";
50static char ntsc[] = "-";
51
Hans Verkuilf9195de2006-01-11 19:01:01 -020052
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020053module_param(addr, int, 0444);
54module_param(no_autodetect, int, 0444);
55module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020056module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020057module_param_string(pal, pal, sizeof(pal), 0644);
58module_param_string(secam, secam, sizeof(secam), 0644);
59module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070060module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061module_param_array(radio_range, int, NULL, 0644);
62
63MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
64MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
65MODULE_LICENSE("GPL");
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067static struct i2c_driver driver;
68static struct i2c_client client_template;
69
70/* ---------------------------------------------------------------------- */
71
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070072/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -070073static void set_tv_freq(struct i2c_client *c, unsigned int freq)
74{
75 struct tuner *t = i2c_get_clientdata(c);
76
77 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070078 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 return;
80 }
Hans Verkuil27487d42006-01-15 15:04:52 -020081 if (NULL == t->set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070082 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 return;
84 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070085 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
86 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
87 freq / 16, freq % 16 * 100 / 16, tv_range[0],
88 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -020089 /* V4L2 spec: if the freq is not possible then the closest
90 possible value should be selected */
91 if (freq < tv_range[0] * 16)
92 freq = tv_range[0] * 16;
93 else
94 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 }
Hans Verkuil27487d42006-01-15 15:04:52 -020096 t->set_tv_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097}
98
99static void set_radio_freq(struct i2c_client *c, unsigned int freq)
100{
101 struct tuner *t = i2c_get_clientdata(c);
102
103 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700104 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 return;
106 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200107 if (NULL == t->set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700108 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 return;
110 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200111 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700112 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
113 freq / 16000, freq % 16000 * 100 / 16000,
114 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200115 /* V4L2 spec: if the freq is not possible then the closest
116 possible value should be selected */
117 if (freq < radio_range[0] * 16000)
118 freq = radio_range[0] * 16000;
119 else
120 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700122
Hans Verkuil27487d42006-01-15 15:04:52 -0200123 t->set_radio_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124}
125
126static void set_freq(struct i2c_client *c, unsigned long freq)
127{
128 struct tuner *t = i2c_get_clientdata(c);
129
130 switch (t->mode) {
131 case V4L2_TUNER_RADIO:
132 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700133 freq / 16000, freq % 16000 * 100 / 16000);
134 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200135 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 break;
137 case V4L2_TUNER_ANALOG_TV:
138 case V4L2_TUNER_DIGITAL_TV:
139 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700140 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200142 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 break;
144 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145}
146
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700147static void set_type(struct i2c_client *c, unsigned int type,
148 unsigned int new_mode_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
150 struct tuner *t = i2c_get_clientdata(c);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700151 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700153 if (type == UNSET || type == TUNER_ABSENT) {
154 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 return;
156 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700158 if (type >= tuner_count) {
159 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
160 return;
161 }
162
163 /* This code detects calls by card attach_inform */
164 if (NULL == t->i2c.dev.driver) {
165 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
166
167 t->type=type;
168 return;
169 }
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 t->type = type;
172 switch (t->type) {
173 case TUNER_MT2032:
174 microtune_init(c);
175 break;
176 case TUNER_PHILIPS_TDA8290:
177 tda8290_init(c);
178 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700179 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700180 if (tea5767_tuner_init(c) == EINVAL) {
181 t->type = TUNER_ABSENT;
182 t->mode_mask = T_UNINITIALIZED;
183 return;
184 }
185 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700186 break;
187 case TUNER_PHILIPS_FMD1216ME_MK3:
188 buffer[0] = 0x0b;
189 buffer[1] = 0xdc;
190 buffer[2] = 0x9c;
191 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700192 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700193 mdelay(1);
194 buffer[2] = 0x86;
195 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700196 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700197 default_tuner_init(c);
198 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800199 case TUNER_PHILIPS_TD1316:
200 buffer[0] = 0x0b;
201 buffer[1] = 0xdc;
202 buffer[2] = 0x86;
203 buffer[3] = 0xa4;
204 i2c_master_send(c,buffer,4);
205 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200206 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300207 case TUNER_TDA9887:
208 tda9887_tuner_init(c);
209 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 default:
211 default_tuner_init(c);
212 break;
213 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700214
215 if (t->mode_mask == T_UNINITIALIZED)
216 t->mode_mask = new_mode_mask;
217
Hans Verkuil27487d42006-01-15 15:04:52 -0200218 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700219 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100220 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700221 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222}
223
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700224/*
225 * This function apply tuner config to tuner specified
226 * by tun_setup structure. I addr is unset, then admin status
227 * and tun addr status is more precise then current status,
228 * it's applied. Otherwise status and type are applied only to
229 * tuner with exactly the same addr.
230*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700231
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700232static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700233{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700234 struct tuner *t = i2c_get_clientdata(c);
235
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300236 tuner_dbg("set addr for type %i\n", t->type);
237
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800238 if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET &&
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700239 (t->mode_mask & tun_setup->mode_mask)) ||
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800240 tun_setup->addr == c->addr)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700241 set_type(c, tun_setup->type, tun_setup->mode_mask);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700242 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700243}
244
245static inline int check_mode(struct tuner *t, char *cmd)
246{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700247 if ((1 << t->mode & t->mode_mask) == 0) {
248 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700249 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700250
251 switch (t->mode) {
252 case V4L2_TUNER_RADIO:
253 tuner_dbg("Cmd %s accepted for radio\n", cmd);
254 break;
255 case V4L2_TUNER_ANALOG_TV:
256 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
257 break;
258 case V4L2_TUNER_DIGITAL_TV:
259 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
260 break;
261 }
262 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700263}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700264
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700265/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266static int tuner_fixup_std(struct tuner *t)
267{
268 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300270 case '6':
271 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
272 t->std = V4L2_STD_PAL_60;
273 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 case 'b':
275 case 'B':
276 case 'g':
277 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700278 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 t->std = V4L2_STD_PAL_BG;
280 break;
281 case 'i':
282 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700283 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 t->std = V4L2_STD_PAL_I;
285 break;
286 case 'd':
287 case 'D':
288 case 'k':
289 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700290 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 t->std = V4L2_STD_PAL_DK;
292 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700293 case 'M':
294 case 'm':
295 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
296 t->std = V4L2_STD_PAL_M;
297 break;
298 case 'N':
299 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200300 if (pal[1] == 'c' || pal[1] == 'C') {
301 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
302 t->std = V4L2_STD_PAL_Nc;
303 } else {
304 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
305 t->std = V4L2_STD_PAL_N;
306 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700307 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700308 case '-':
309 /* default parameter, do nothing */
310 break;
311 default:
312 tuner_warn ("pal= argument not recognised\n");
313 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 }
315 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700316 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
317 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200318 case 'b':
319 case 'B':
320 case 'g':
321 case 'G':
322 case 'h':
323 case 'H':
324 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
325 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
326 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700327 case 'd':
328 case 'D':
329 case 'k':
330 case 'K':
331 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
332 t->std = V4L2_STD_SECAM_DK;
333 break;
334 case 'l':
335 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800336 if ((secam[1]=='C')||(secam[1]=='c')) {
337 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
338 t->std = V4L2_STD_SECAM_LC;
339 } else {
340 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
341 t->std = V4L2_STD_SECAM_L;
342 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700343 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700344 case '-':
345 /* default parameter, do nothing */
346 break;
347 default:
348 tuner_warn ("secam= argument not recognised\n");
349 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700350 }
351 }
352
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200353 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
354 switch (ntsc[0]) {
355 case 'm':
356 case 'M':
357 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
358 t->std = V4L2_STD_NTSC_M;
359 break;
360 case 'j':
361 case 'J':
362 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
363 t->std = V4L2_STD_NTSC_M_JP;
364 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200365 case 'k':
366 case 'K':
367 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
368 t->std = V4L2_STD_NTSC_M_KR;
369 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200370 case '-':
371 /* default parameter, do nothing */
372 break;
373 default:
374 tuner_info("ntsc= argument not recognised\n");
375 break;
376 }
377 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 return 0;
379}
380
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200381static void tuner_status(struct i2c_client *client)
382{
383 struct tuner *t = i2c_get_clientdata(client);
384 unsigned long freq, freq_fraction;
385 const char *p;
386
387 switch (t->mode) {
388 case V4L2_TUNER_RADIO: p = "radio"; break;
389 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
390 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
391 default: p = "undefined"; break;
392 }
393 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200394 freq = t->radio_freq / 16000;
395 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200396 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200397 freq = t->tv_freq / 16;
398 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200399 }
400 tuner_info("Tuner mode: %s\n", p);
401 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300402 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200403 if (t->mode != V4L2_TUNER_RADIO)
404 return;
405 if (t->has_signal) {
406 tuner_info("Signal strength: %d\n", t->has_signal(client));
407 }
408 if (t->is_stereo) {
409 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200410 }
411}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200412
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413/* ---------------------------------------------------------------------- */
414
Hans Verkuilba8fc392006-06-25 15:34:39 -0300415/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700416static unsigned default_mode_mask;
417
418/* During client attach, set_type is called by adapter's attach_inform callback.
419 set_type must then be completed by tuner_attach.
420 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
422{
423 struct tuner *t;
424
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700425 client_template.adapter = adap;
426 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
Panagiotis Issaris74081872006-01-11 19:40:56 -0200428 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700429 if (NULL == t)
430 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700431 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700433 t->type = UNSET;
434 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
435 t->audmode = V4L2_TUNER_MODE_STEREO;
436 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300437 t->tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700439 if (show_i2c) {
440 unsigned char buffer[16];
441 int i,rc;
442
443 memset(buffer, 0, sizeof(buffer));
444 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800445 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700446 for (i=0;i<rc;i++)
447 printk("%02x ",buffer[i]);
448 printk("\n");
449 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300450 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
451 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
452 return -ENODEV;
453
Markus Rechberger257c6452006-01-23 17:11:11 -0200454 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700455 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800456 switch (addr) {
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800457 case 0x42:
458 case 0x43:
459 case 0x4a:
460 case 0x4b:
461 /* If chip is not tda8290, don't register.
462 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300463 if (tda8290_probe(&t->i2c) == 0) {
464 tuner_dbg("chip at addr %x is a tda8290\n", addr);
465 } else {
466 /* Default is being tda9887 */
467 t->type = TUNER_TDA9887;
468 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
469 t->mode = T_STANDBY;
470 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800471 }
472 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800473 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700474 if (tea5767_autodetection(&t->i2c) != EINVAL) {
475 t->type = TUNER_TEA5767;
476 t->mode_mask = T_RADIO;
477 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200478 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700479 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700480
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800481 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700482 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800483 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700484 }
485 }
486
487 /* Initializes only the first adapter found */
488 if (default_mode_mask != T_UNINITIALIZED) {
489 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
490 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200491 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
492 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700493 default_mode_mask = T_UNINITIALIZED;
494 }
495
496 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800497register_client:
498 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700499 i2c_attach_client (&t->i2c);
500 set_type (&t->i2c,t->type, t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 return 0;
502}
503
504static int tuner_probe(struct i2c_adapter *adap)
505{
506 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700507 normal_i2c[0] = addr;
508 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700511 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 if (adap->class & I2C_CLASS_TV_ANALOG)
514 return i2c_probe(adap, &addr_data, tuner_attach);
515 return 0;
516}
517
518static int tuner_detach(struct i2c_client *client)
519{
520 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700521 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700523 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700524 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700525 tuner_warn
526 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700527 return err;
528 }
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 kfree(t);
531 return 0;
532}
533
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700534/*
535 * Switch tuner to other mode. If tuner support both tv and radio,
536 * set another frequency to some value (This is needed for some pal
537 * tuners to avoid locking). Otherwise, just put second tuner in
538 * standby mode.
539 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700541static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
542{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800543 if (mode == t->mode)
544 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700545
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800546 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700547
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800548 if (check_mode(t, cmd) == EINVAL) {
549 t->mode = T_STANDBY;
550 if (t->standby)
551 t->standby (client);
552 return EINVAL;
553 }
554 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700555}
556
557#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800558 tuner_dbg("switching to v4l2\n"); \
559 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700560
561static inline int check_v4l2(struct tuner *t)
562{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300563 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
564 TV, v4l1 for radio), until that is fixed this code is disabled.
565 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
566 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700567 return 0;
568}
569
570static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571{
572 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
Hans Verkuilf9195de2006-01-11 19:01:01 -0200574 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200575 v4l_i2c_print_ioctl(&(t->i2c),cmd);
576
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700577 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700579 case TUNER_SET_TYPE_ADDR:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700580 tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
581 ((struct tuner_setup *)arg)->type,
582 ((struct tuner_setup *)arg)->addr,
583 ((struct tuner_setup *)arg)->mode_mask);
584
585 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700586 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200588 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
589 == EINVAL)
590 return 0;
591 if (t->radio_freq)
592 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700594 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200595 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
596 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300597 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200598 if (t->standby)
599 t->standby (client);
600 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300601#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700602 case VIDIOCSAUDIO:
603 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
604 return 0;
605 if (check_v4l2(t) == EINVAL)
606 return 0;
607
608 /* Should be implemented, since bttv calls it */
609 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700610 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700612 {
613 static const v4l2_std_id map[] = {
614 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
615 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
616 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
617 [4 /* bttv */ ] = V4L2_STD_PAL_M,
618 [5 /* bttv */ ] = V4L2_STD_PAL_N,
619 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
620 };
621 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700623 if (check_v4l2(t) == EINVAL)
624 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700625
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700626 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
627 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700629 if (vc->norm < ARRAY_SIZE(map))
630 t->std = map[vc->norm];
631 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200632 if (t->tv_freq)
633 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700634 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700635 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700636 case VIDIOCSFREQ:
637 {
638 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700639
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700640 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
641 return 0;
642 if (check_v4l2(t) == EINVAL)
643 return 0;
644
645 set_freq(client, *v);
646 return 0;
647 }
648 case VIDIOCGTUNER:
649 {
650 struct video_tuner *vt = arg;
651
652 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
653 return 0;
654 if (check_v4l2(t) == EINVAL)
655 return 0;
656
657 if (V4L2_TUNER_RADIO == t->mode) {
658 if (t->has_signal)
659 vt->signal = t->has_signal(client);
660 if (t->is_stereo) {
661 if (t->is_stereo(client))
662 vt->flags |=
663 VIDEO_TUNER_STEREO_ON;
664 else
665 vt->flags &=
666 ~VIDEO_TUNER_STEREO_ON;
667 }
668 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
669
670 vt->rangelow = radio_range[0] * 16000;
671 vt->rangehigh = radio_range[1] * 16000;
672
673 } else {
674 vt->rangelow = tv_range[0] * 16;
675 vt->rangehigh = tv_range[1] * 16;
676 }
677
678 return 0;
679 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700681 {
682 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700684 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
685 return 0;
686 if (check_v4l2(t) == EINVAL)
687 return 0;
688
689 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
690 va->mode = t->is_stereo(client)
691 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
692 return 0;
693 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300694#endif
695 case TDA9887_SET_CONFIG:
696 if (t->type == TUNER_TDA9887) {
697 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300699 t->tda9887_config = *i;
700 set_freq(client, t->tv_freq);
701 }
702 break;
703 /* --- v4l ioctls --- */
704 /* take care: bttv does userspace copying, we'll get a
705 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700707 {
708 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700710 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
711 == EINVAL)
712 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700713
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700714 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700716 t->std = *id;
717 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200718 if (t->tv_freq)
719 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700720 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700721 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700722 case VIDIOC_S_FREQUENCY:
723 {
724 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700725
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300726 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
727 == EINVAL)
728 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700729 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200730 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700731
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700732 break;
733 }
734 case VIDIOC_G_FREQUENCY:
735 {
736 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700737
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700738 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
739 return 0;
740 switch_v4l2();
741 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200742 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
743 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700744 break;
745 }
746 case VIDIOC_G_TUNER:
747 {
748 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700749
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700750 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
751 return 0;
752 switch_v4l2();
753
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200754 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300755 if (t->get_afc)
756 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300757 if (t->mode == V4L2_TUNER_ANALOG_TV)
758 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200759 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760 tuner->rangelow = tv_range[0] * 16;
761 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200762 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700763 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200764
765 /* radio mode */
766 if (t->has_signal)
767 tuner->signal = t->has_signal(client);
768
769 tuner->rxsubchans =
770 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
771 if (t->is_stereo) {
772 tuner->rxsubchans = t->is_stereo(client) ?
773 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
774 }
775
776 tuner->capability |=
777 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
778 tuner->audmode = t->audmode;
779 tuner->rangelow = radio_range[0] * 16000;
780 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700781 break;
782 }
783 case VIDIOC_S_TUNER:
784 {
785 struct v4l2_tuner *tuner = arg;
786
787 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
788 return 0;
789
790 switch_v4l2();
791
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200792 /* do nothing unless we're a radio tuner */
793 if (t->mode != V4L2_TUNER_RADIO)
794 break;
795 t->audmode = tuner->audmode;
796 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700797 break;
798 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200799 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300800 if (t->tuner_status)
801 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200802 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 }
804
805 return 0;
806}
807
Russell King9480e302005-10-28 09:52:56 -0700808static int tuner_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700810 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
811 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700813 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 /* FIXME: power down ??? */
815 return 0;
816}
817
Russell King9480e302005-10-28 09:52:56 -0700818static int tuner_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700820 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
821 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700823 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200824 if (V4L2_TUNER_RADIO == t->mode) {
825 if (t->radio_freq)
826 set_freq(c, t->radio_freq);
827 } else {
828 if (t->tv_freq)
829 set_freq(c, t->tv_freq);
830 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 return 0;
832}
833
834/* ----------------------------------------------------------------------- */
835
836static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700837 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700838 .attach_adapter = tuner_probe,
839 .detach_client = tuner_detach,
840 .command = tuner_command,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200842 .name = "tuner",
843 .suspend = tuner_suspend,
844 .resume = tuner_resume,
845 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700847static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200848 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700849 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850};
851
852static int __init tuner_init_module(void)
853{
854 return i2c_add_driver(&driver);
855}
856
857static void __exit tuner_cleanup_module(void)
858{
859 i2c_del_driver(&driver);
860}
861
862module_init(tuner_init_module);
863module_exit(tuner_cleanup_module);
864
865/*
866 * Overrides for Emacs so that we follow Linus's tabbing style.
867 * ---------------------------------------------------------------------------
868 * Local variables:
869 * c-basic-offset: 8
870 * End:
871 */