blob: 705daaa2a4ff82a649c19affa1f7c02a2b9c3152 [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 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300446 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
447 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
448 return -ENODEV;
449
Markus Rechberger257c6452006-01-23 17:11:11 -0200450 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700451 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800452 switch (addr) {
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800453 case 0x42:
454 case 0x43:
455 case 0x4a:
456 case 0x4b:
457 /* If chip is not tda8290, don't register.
458 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300459 if (tda8290_probe(&t->i2c) == 0) {
460 tuner_dbg("chip at addr %x is a tda8290\n", addr);
461 } else {
462 /* Default is being tda9887 */
463 t->type = TUNER_TDA9887;
464 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
465 t->mode = T_STANDBY;
466 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800467 }
468 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800469 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700470 if (tea5767_autodetection(&t->i2c) != EINVAL) {
471 t->type = TUNER_TEA5767;
472 t->mode_mask = T_RADIO;
473 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200474 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700475 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700476
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800477 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700478 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800479 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700480 }
481 }
482
483 /* Initializes only the first adapter found */
484 if (default_mode_mask != T_UNINITIALIZED) {
485 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
486 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200487 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
488 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700489 default_mode_mask = T_UNINITIALIZED;
490 }
491
492 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800493register_client:
494 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700495 i2c_attach_client (&t->i2c);
496 set_type (&t->i2c,t->type, t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 return 0;
498}
499
500static int tuner_probe(struct i2c_adapter *adap)
501{
502 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700503 normal_i2c[0] = addr;
504 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700507 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700508
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 if (adap->class & I2C_CLASS_TV_ANALOG)
510 return i2c_probe(adap, &addr_data, tuner_attach);
511 return 0;
512}
513
514static int tuner_detach(struct i2c_client *client)
515{
516 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700517 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700519 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700520 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700521 tuner_warn
522 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700523 return err;
524 }
525
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 kfree(t);
527 return 0;
528}
529
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700530/*
531 * Switch tuner to other mode. If tuner support both tv and radio,
532 * set another frequency to some value (This is needed for some pal
533 * tuners to avoid locking). Otherwise, just put second tuner in
534 * standby mode.
535 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700537static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
538{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800539 if (mode == t->mode)
540 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700541
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800542 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700543
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800544 if (check_mode(t, cmd) == EINVAL) {
545 t->mode = T_STANDBY;
546 if (t->standby)
547 t->standby (client);
548 return EINVAL;
549 }
550 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700551}
552
553#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800554 tuner_dbg("switching to v4l2\n"); \
555 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700556
557static inline int check_v4l2(struct tuner *t)
558{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300559 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
560 TV, v4l1 for radio), until that is fixed this code is disabled.
561 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
562 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700563 return 0;
564}
565
566static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567{
568 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
Hans Verkuilf9195de2006-01-11 19:01:01 -0200570 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200571 v4l_i2c_print_ioctl(&(t->i2c),cmd);
572
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700573 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700575 case TUNER_SET_TYPE_ADDR:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700576 tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
577 ((struct tuner_setup *)arg)->type,
578 ((struct tuner_setup *)arg)->addr,
579 ((struct tuner_setup *)arg)->mode_mask);
580
581 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700582 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200584 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
585 == EINVAL)
586 return 0;
587 if (t->radio_freq)
588 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700590 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200591 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
592 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300593 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200594 if (t->standby)
595 t->standby (client);
596 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300597#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700598 case VIDIOCSAUDIO:
599 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
600 return 0;
601 if (check_v4l2(t) == EINVAL)
602 return 0;
603
604 /* Should be implemented, since bttv calls it */
605 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700606 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700608 {
609 static const v4l2_std_id map[] = {
610 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
611 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
612 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
613 [4 /* bttv */ ] = V4L2_STD_PAL_M,
614 [5 /* bttv */ ] = V4L2_STD_PAL_N,
615 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
616 };
617 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700619 if (check_v4l2(t) == EINVAL)
620 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700621
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700622 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
623 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700625 if (vc->norm < ARRAY_SIZE(map))
626 t->std = map[vc->norm];
627 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200628 if (t->tv_freq)
629 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700630 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700631 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700632 case VIDIOCSFREQ:
633 {
634 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700635
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700636 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
637 return 0;
638 if (check_v4l2(t) == EINVAL)
639 return 0;
640
641 set_freq(client, *v);
642 return 0;
643 }
644 case VIDIOCGTUNER:
645 {
646 struct video_tuner *vt = arg;
647
648 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
649 return 0;
650 if (check_v4l2(t) == EINVAL)
651 return 0;
652
653 if (V4L2_TUNER_RADIO == t->mode) {
654 if (t->has_signal)
655 vt->signal = t->has_signal(client);
656 if (t->is_stereo) {
657 if (t->is_stereo(client))
658 vt->flags |=
659 VIDEO_TUNER_STEREO_ON;
660 else
661 vt->flags &=
662 ~VIDEO_TUNER_STEREO_ON;
663 }
664 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
665
666 vt->rangelow = radio_range[0] * 16000;
667 vt->rangehigh = radio_range[1] * 16000;
668
669 } else {
670 vt->rangelow = tv_range[0] * 16;
671 vt->rangehigh = tv_range[1] * 16;
672 }
673
674 return 0;
675 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700677 {
678 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
681 return 0;
682 if (check_v4l2(t) == EINVAL)
683 return 0;
684
685 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
686 va->mode = t->is_stereo(client)
687 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
688 return 0;
689 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300690#endif
691 case TDA9887_SET_CONFIG:
692 if (t->type == TUNER_TDA9887) {
693 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300695 t->tda9887_config = *i;
696 set_freq(client, t->tv_freq);
697 }
698 break;
699 /* --- v4l ioctls --- */
700 /* take care: bttv does userspace copying, we'll get a
701 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700703 {
704 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700706 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
707 == EINVAL)
708 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700709
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700710 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700712 t->std = *id;
713 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200714 if (t->tv_freq)
715 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700716 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700717 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700718 case VIDIOC_S_FREQUENCY:
719 {
720 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700721
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300722 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
723 == EINVAL)
724 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700725 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200726 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700727
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700728 break;
729 }
730 case VIDIOC_G_FREQUENCY:
731 {
732 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700733
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700734 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
735 return 0;
736 switch_v4l2();
737 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200738 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
739 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700740 break;
741 }
742 case VIDIOC_G_TUNER:
743 {
744 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700745
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700746 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
747 return 0;
748 switch_v4l2();
749
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200750 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300751 if (t->get_afc)
752 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300753 if (t->mode == V4L2_TUNER_ANALOG_TV)
754 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200755 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700756 tuner->rangelow = tv_range[0] * 16;
757 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200758 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700759 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200760
761 /* radio mode */
762 if (t->has_signal)
763 tuner->signal = t->has_signal(client);
764
765 tuner->rxsubchans =
766 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
767 if (t->is_stereo) {
768 tuner->rxsubchans = t->is_stereo(client) ?
769 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
770 }
771
772 tuner->capability |=
773 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
774 tuner->audmode = t->audmode;
775 tuner->rangelow = radio_range[0] * 16000;
776 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700777 break;
778 }
779 case VIDIOC_S_TUNER:
780 {
781 struct v4l2_tuner *tuner = arg;
782
783 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
784 return 0;
785
786 switch_v4l2();
787
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200788 /* do nothing unless we're a radio tuner */
789 if (t->mode != V4L2_TUNER_RADIO)
790 break;
791 t->audmode = tuner->audmode;
792 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700793 break;
794 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200795 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300796 if (t->tuner_status)
797 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200798 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 }
800
801 return 0;
802}
803
Russell King9480e302005-10-28 09:52:56 -0700804static int tuner_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700806 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
807 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700809 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 /* FIXME: power down ??? */
811 return 0;
812}
813
Russell King9480e302005-10-28 09:52:56 -0700814static int tuner_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700816 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
817 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700819 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200820 if (V4L2_TUNER_RADIO == t->mode) {
821 if (t->radio_freq)
822 set_freq(c, t->radio_freq);
823 } else {
824 if (t->tv_freq)
825 set_freq(c, t->tv_freq);
826 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 return 0;
828}
829
830/* ----------------------------------------------------------------------- */
831
832static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700833 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700834 .attach_adapter = tuner_probe,
835 .detach_client = tuner_detach,
836 .command = tuner_command,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200838 .name = "tuner",
839 .suspend = tuner_suspend,
840 .resume = tuner_resume,
841 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700843static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200844 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700845 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846};
847
848static int __init tuner_init_module(void)
849{
850 return i2c_add_driver(&driver);
851}
852
853static void __exit tuner_cleanup_module(void)
854{
855 i2c_del_driver(&driver);
856}
857
858module_init(tuner_init_module);
859module_exit(tuner_cleanup_module);
860
861/*
862 * Overrides for Emacs so that we follow Linus's tabbing style.
863 * ---------------------------------------------------------------------------
864 * Local variables:
865 * c-basic-offset: 8
866 * End:
867 */