blob: 40590bae5ff7f55652ae57039d9347ccbb6f8566 [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]) {
270 case 'b':
271 case 'B':
272 case 'g':
273 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700274 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 t->std = V4L2_STD_PAL_BG;
276 break;
277 case 'i':
278 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700279 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 t->std = V4L2_STD_PAL_I;
281 break;
282 case 'd':
283 case 'D':
284 case 'k':
285 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700286 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 t->std = V4L2_STD_PAL_DK;
288 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700289 case 'M':
290 case 'm':
291 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
292 t->std = V4L2_STD_PAL_M;
293 break;
294 case 'N':
295 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200296 if (pal[1] == 'c' || pal[1] == 'C') {
297 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
298 t->std = V4L2_STD_PAL_Nc;
299 } else {
300 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
301 t->std = V4L2_STD_PAL_N;
302 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700303 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700304 case '-':
305 /* default parameter, do nothing */
306 break;
307 default:
308 tuner_warn ("pal= argument not recognised\n");
309 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 }
311 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700312 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
313 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200314 case 'b':
315 case 'B':
316 case 'g':
317 case 'G':
318 case 'h':
319 case 'H':
320 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
321 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
322 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700323 case 'd':
324 case 'D':
325 case 'k':
326 case 'K':
327 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
328 t->std = V4L2_STD_SECAM_DK;
329 break;
330 case 'l':
331 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800332 if ((secam[1]=='C')||(secam[1]=='c')) {
333 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
334 t->std = V4L2_STD_SECAM_LC;
335 } else {
336 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
337 t->std = V4L2_STD_SECAM_L;
338 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700339 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700340 case '-':
341 /* default parameter, do nothing */
342 break;
343 default:
344 tuner_warn ("secam= argument not recognised\n");
345 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700346 }
347 }
348
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200349 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
350 switch (ntsc[0]) {
351 case 'm':
352 case 'M':
353 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
354 t->std = V4L2_STD_NTSC_M;
355 break;
356 case 'j':
357 case 'J':
358 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
359 t->std = V4L2_STD_NTSC_M_JP;
360 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200361 case 'k':
362 case 'K':
363 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
364 t->std = V4L2_STD_NTSC_M_KR;
365 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200366 case '-':
367 /* default parameter, do nothing */
368 break;
369 default:
370 tuner_info("ntsc= argument not recognised\n");
371 break;
372 }
373 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 return 0;
375}
376
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200377static void tuner_status(struct i2c_client *client)
378{
379 struct tuner *t = i2c_get_clientdata(client);
380 unsigned long freq, freq_fraction;
381 const char *p;
382
383 switch (t->mode) {
384 case V4L2_TUNER_RADIO: p = "radio"; break;
385 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
386 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
387 default: p = "undefined"; break;
388 }
389 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200390 freq = t->radio_freq / 16000;
391 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200392 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200393 freq = t->tv_freq / 16;
394 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200395 }
396 tuner_info("Tuner mode: %s\n", p);
397 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300398 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200399 if (t->mode != V4L2_TUNER_RADIO)
400 return;
401 if (t->has_signal) {
402 tuner_info("Signal strength: %d\n", t->has_signal(client));
403 }
404 if (t->is_stereo) {
405 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200406 }
407}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200408
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409/* ---------------------------------------------------------------------- */
410
Hans Verkuilba8fc392006-06-25 15:34:39 -0300411/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700412static unsigned default_mode_mask;
413
414/* During client attach, set_type is called by adapter's attach_inform callback.
415 set_type must then be completed by tuner_attach.
416 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
418{
419 struct tuner *t;
420
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700421 client_template.adapter = adap;
422 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
Panagiotis Issaris74081872006-01-11 19:40:56 -0200424 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700425 if (NULL == t)
426 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700427 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700429 t->type = UNSET;
430 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
431 t->audmode = V4L2_TUNER_MODE_STEREO;
432 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300433 t->tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700435 if (show_i2c) {
436 unsigned char buffer[16];
437 int i,rc;
438
439 memset(buffer, 0, sizeof(buffer));
440 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800441 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700442 for (i=0;i<rc;i++)
443 printk("%02x ",buffer[i]);
444 printk("\n");
445 }
Markus Rechberger257c6452006-01-23 17:11:11 -0200446 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700447 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800448 switch (addr) {
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800449 case 0x42:
450 case 0x43:
451 case 0x4a:
452 case 0x4b:
453 /* If chip is not tda8290, don't register.
454 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300455 if (tda8290_probe(&t->i2c) == 0) {
456 tuner_dbg("chip at addr %x is a tda8290\n", addr);
457 } else {
458 /* Default is being tda9887 */
459 t->type = TUNER_TDA9887;
460 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
461 t->mode = T_STANDBY;
462 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800463 }
464 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800465 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700466 if (tea5767_autodetection(&t->i2c) != EINVAL) {
467 t->type = TUNER_TEA5767;
468 t->mode_mask = T_RADIO;
469 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200470 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700471 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700472
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800473 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700474 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800475 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700476 }
477 }
478
479 /* Initializes only the first adapter found */
480 if (default_mode_mask != T_UNINITIALIZED) {
481 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
482 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200483 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
484 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700485 default_mode_mask = T_UNINITIALIZED;
486 }
487
488 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800489register_client:
490 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700491 i2c_attach_client (&t->i2c);
492 set_type (&t->i2c,t->type, t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 return 0;
494}
495
496static int tuner_probe(struct i2c_adapter *adap)
497{
498 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700499 normal_i2c[0] = addr;
500 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700503 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 if (adap->class & I2C_CLASS_TV_ANALOG)
506 return i2c_probe(adap, &addr_data, tuner_attach);
507 return 0;
508}
509
510static int tuner_detach(struct i2c_client *client)
511{
512 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700513 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700515 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700516 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700517 tuner_warn
518 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700519 return err;
520 }
521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 kfree(t);
523 return 0;
524}
525
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700526/*
527 * Switch tuner to other mode. If tuner support both tv and radio,
528 * set another frequency to some value (This is needed for some pal
529 * tuners to avoid locking). Otherwise, just put second tuner in
530 * standby mode.
531 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700533static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
534{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800535 if (mode == t->mode)
536 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700537
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800538 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700539
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800540 if (check_mode(t, cmd) == EINVAL) {
541 t->mode = T_STANDBY;
542 if (t->standby)
543 t->standby (client);
544 return EINVAL;
545 }
546 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700547}
548
549#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800550 tuner_dbg("switching to v4l2\n"); \
551 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700552
553static inline int check_v4l2(struct tuner *t)
554{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300555 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
556 TV, v4l1 for radio), until that is fixed this code is disabled.
557 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
558 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700559 return 0;
560}
561
562static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563{
564 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
Hans Verkuilf9195de2006-01-11 19:01:01 -0200566 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200567 v4l_i2c_print_ioctl(&(t->i2c),cmd);
568
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700569 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700571 case TUNER_SET_TYPE_ADDR:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700572 tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
573 ((struct tuner_setup *)arg)->type,
574 ((struct tuner_setup *)arg)->addr,
575 ((struct tuner_setup *)arg)->mode_mask);
576
577 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700578 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200580 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
581 == EINVAL)
582 return 0;
583 if (t->radio_freq)
584 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700586 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200587 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
588 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300589 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200590 if (t->standby)
591 t->standby (client);
592 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300593#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700594 case VIDIOCSAUDIO:
595 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
596 return 0;
597 if (check_v4l2(t) == EINVAL)
598 return 0;
599
600 /* Should be implemented, since bttv calls it */
601 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700602 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700604 {
605 static const v4l2_std_id map[] = {
606 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
607 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
608 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
609 [4 /* bttv */ ] = V4L2_STD_PAL_M,
610 [5 /* bttv */ ] = V4L2_STD_PAL_N,
611 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
612 };
613 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700615 if (check_v4l2(t) == EINVAL)
616 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700617
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700618 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
619 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700621 if (vc->norm < ARRAY_SIZE(map))
622 t->std = map[vc->norm];
623 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200624 if (t->tv_freq)
625 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700626 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700627 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700628 case VIDIOCSFREQ:
629 {
630 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700631
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700632 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
633 return 0;
634 if (check_v4l2(t) == EINVAL)
635 return 0;
636
637 set_freq(client, *v);
638 return 0;
639 }
640 case VIDIOCGTUNER:
641 {
642 struct video_tuner *vt = arg;
643
644 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
645 return 0;
646 if (check_v4l2(t) == EINVAL)
647 return 0;
648
649 if (V4L2_TUNER_RADIO == t->mode) {
650 if (t->has_signal)
651 vt->signal = t->has_signal(client);
652 if (t->is_stereo) {
653 if (t->is_stereo(client))
654 vt->flags |=
655 VIDEO_TUNER_STEREO_ON;
656 else
657 vt->flags &=
658 ~VIDEO_TUNER_STEREO_ON;
659 }
660 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
661
662 vt->rangelow = radio_range[0] * 16000;
663 vt->rangehigh = radio_range[1] * 16000;
664
665 } else {
666 vt->rangelow = tv_range[0] * 16;
667 vt->rangehigh = tv_range[1] * 16;
668 }
669
670 return 0;
671 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700673 {
674 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700676 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
677 return 0;
678 if (check_v4l2(t) == EINVAL)
679 return 0;
680
681 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
682 va->mode = t->is_stereo(client)
683 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
684 return 0;
685 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300686#endif
687 case TDA9887_SET_CONFIG:
688 if (t->type == TUNER_TDA9887) {
689 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300691 t->tda9887_config = *i;
692 set_freq(client, t->tv_freq);
693 }
694 break;
695 /* --- v4l ioctls --- */
696 /* take care: bttv does userspace copying, we'll get a
697 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700699 {
700 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700702 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
703 == EINVAL)
704 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700705
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700706 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700708 t->std = *id;
709 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200710 if (t->tv_freq)
711 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700712 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700713 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700714 case VIDIOC_S_FREQUENCY:
715 {
716 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700717
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300718 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
719 == EINVAL)
720 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700721 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200722 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700723
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700724 break;
725 }
726 case VIDIOC_G_FREQUENCY:
727 {
728 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700729
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700730 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
731 return 0;
732 switch_v4l2();
733 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200734 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
735 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700736 break;
737 }
738 case VIDIOC_G_TUNER:
739 {
740 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700741
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700742 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
743 return 0;
744 switch_v4l2();
745
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200746 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300747 if (t->get_afc)
748 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300749 if (t->mode == V4L2_TUNER_ANALOG_TV)
750 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200751 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700752 tuner->rangelow = tv_range[0] * 16;
753 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200754 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700755 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200756
757 /* radio mode */
758 if (t->has_signal)
759 tuner->signal = t->has_signal(client);
760
761 tuner->rxsubchans =
762 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
763 if (t->is_stereo) {
764 tuner->rxsubchans = t->is_stereo(client) ?
765 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
766 }
767
768 tuner->capability |=
769 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
770 tuner->audmode = t->audmode;
771 tuner->rangelow = radio_range[0] * 16000;
772 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700773 break;
774 }
775 case VIDIOC_S_TUNER:
776 {
777 struct v4l2_tuner *tuner = arg;
778
779 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
780 return 0;
781
782 switch_v4l2();
783
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200784 /* do nothing unless we're a radio tuner */
785 if (t->mode != V4L2_TUNER_RADIO)
786 break;
787 t->audmode = tuner->audmode;
788 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700789 break;
790 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200791 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300792 if (t->tuner_status)
793 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200794 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 }
796
797 return 0;
798}
799
Russell King9480e302005-10-28 09:52:56 -0700800static int tuner_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700802 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
803 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700805 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 /* FIXME: power down ??? */
807 return 0;
808}
809
Russell King9480e302005-10-28 09:52:56 -0700810static int tuner_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700812 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
813 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700815 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200816 if (V4L2_TUNER_RADIO == t->mode) {
817 if (t->radio_freq)
818 set_freq(c, t->radio_freq);
819 } else {
820 if (t->tv_freq)
821 set_freq(c, t->tv_freq);
822 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 return 0;
824}
825
826/* ----------------------------------------------------------------------- */
827
828static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700829 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700830 .attach_adapter = tuner_probe,
831 .detach_client = tuner_detach,
832 .command = tuner_command,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200834 .name = "tuner",
835 .suspend = tuner_suspend,
836 .resume = tuner_resume,
837 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700839static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200840 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700841 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842};
843
844static int __init tuner_init_module(void)
845{
846 return i2c_add_driver(&driver);
847}
848
849static void __exit tuner_cleanup_module(void)
850{
851 i2c_del_driver(&driver);
852}
853
854module_init(tuner_init_module);
855module_exit(tuner_cleanup_module);
856
857/*
858 * Overrides for Emacs so that we follow Linus's tabbing style.
859 * ---------------------------------------------------------------------------
860 * Local variables:
861 * c-basic-offset: 8
862 * End:
863 */