blob: 7f7d5e355bb7ded030a69dd0b2d3fb0685dfdc85 [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;
Michael Krufky9c26c8b2006-04-27 01:29:17 -0300199 case TUNER_LG_TDVS_H06XF:
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700200 /* Set the Auxiliary Byte. */
201 buffer[2] &= ~0x20;
202 buffer[2] |= 0x18;
203 buffer[3] = 0x20;
204 i2c_master_send(c, buffer, 4);
205 default_tuner_init(c);
206 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800207 case TUNER_PHILIPS_TD1316:
208 buffer[0] = 0x0b;
209 buffer[1] = 0xdc;
210 buffer[2] = 0x86;
211 buffer[3] = 0xa4;
212 i2c_master_send(c,buffer,4);
213 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200214 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300215 case TUNER_TDA9887:
216 tda9887_tuner_init(c);
217 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 default:
219 default_tuner_init(c);
220 break;
221 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700222
223 if (t->mode_mask == T_UNINITIALIZED)
224 t->mode_mask = new_mode_mask;
225
Hans Verkuil27487d42006-01-15 15:04:52 -0200226 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700227 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100228 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700229 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230}
231
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700232/*
233 * This function apply tuner config to tuner specified
234 * by tun_setup structure. I addr is unset, then admin status
235 * and tun addr status is more precise then current status,
236 * it's applied. Otherwise status and type are applied only to
237 * tuner with exactly the same addr.
238*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700239
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700240static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700241{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700242 struct tuner *t = i2c_get_clientdata(c);
243
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300244 tuner_dbg("set addr for type %i\n", t->type);
245
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800246 if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET &&
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700247 (t->mode_mask & tun_setup->mode_mask)) ||
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800248 tun_setup->addr == c->addr)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700249 set_type(c, tun_setup->type, tun_setup->mode_mask);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700250 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700251}
252
253static inline int check_mode(struct tuner *t, char *cmd)
254{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700255 if ((1 << t->mode & t->mode_mask) == 0) {
256 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700257 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700258
259 switch (t->mode) {
260 case V4L2_TUNER_RADIO:
261 tuner_dbg("Cmd %s accepted for radio\n", cmd);
262 break;
263 case V4L2_TUNER_ANALOG_TV:
264 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
265 break;
266 case V4L2_TUNER_DIGITAL_TV:
267 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
268 break;
269 }
270 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700271}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700272
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700273/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274static int tuner_fixup_std(struct tuner *t)
275{
276 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 switch (pal[0]) {
278 case 'b':
279 case 'B':
280 case 'g':
281 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700282 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 t->std = V4L2_STD_PAL_BG;
284 break;
285 case 'i':
286 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700287 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 t->std = V4L2_STD_PAL_I;
289 break;
290 case 'd':
291 case 'D':
292 case 'k':
293 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700294 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 t->std = V4L2_STD_PAL_DK;
296 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700297 case 'M':
298 case 'm':
299 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
300 t->std = V4L2_STD_PAL_M;
301 break;
302 case 'N':
303 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200304 if (pal[1] == 'c' || pal[1] == 'C') {
305 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
306 t->std = V4L2_STD_PAL_Nc;
307 } else {
308 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
309 t->std = V4L2_STD_PAL_N;
310 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700311 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700312 case '-':
313 /* default parameter, do nothing */
314 break;
315 default:
316 tuner_warn ("pal= argument not recognised\n");
317 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 }
319 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700320 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
321 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200322 case 'b':
323 case 'B':
324 case 'g':
325 case 'G':
326 case 'h':
327 case 'H':
328 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
329 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
330 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700331 case 'd':
332 case 'D':
333 case 'k':
334 case 'K':
335 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
336 t->std = V4L2_STD_SECAM_DK;
337 break;
338 case 'l':
339 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800340 if ((secam[1]=='C')||(secam[1]=='c')) {
341 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
342 t->std = V4L2_STD_SECAM_LC;
343 } else {
344 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
345 t->std = V4L2_STD_SECAM_L;
346 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700347 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700348 case '-':
349 /* default parameter, do nothing */
350 break;
351 default:
352 tuner_warn ("secam= argument not recognised\n");
353 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700354 }
355 }
356
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200357 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
358 switch (ntsc[0]) {
359 case 'm':
360 case 'M':
361 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
362 t->std = V4L2_STD_NTSC_M;
363 break;
364 case 'j':
365 case 'J':
366 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
367 t->std = V4L2_STD_NTSC_M_JP;
368 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200369 case 'k':
370 case 'K':
371 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
372 t->std = V4L2_STD_NTSC_M_KR;
373 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200374 case '-':
375 /* default parameter, do nothing */
376 break;
377 default:
378 tuner_info("ntsc= argument not recognised\n");
379 break;
380 }
381 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 return 0;
383}
384
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200385static void tuner_status(struct i2c_client *client)
386{
387 struct tuner *t = i2c_get_clientdata(client);
388 unsigned long freq, freq_fraction;
389 const char *p;
390
391 switch (t->mode) {
392 case V4L2_TUNER_RADIO: p = "radio"; break;
393 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
394 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
395 default: p = "undefined"; break;
396 }
397 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200398 freq = t->radio_freq / 16000;
399 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200400 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200401 freq = t->tv_freq / 16;
402 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200403 }
404 tuner_info("Tuner mode: %s\n", p);
405 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300406 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200407 if (t->mode != V4L2_TUNER_RADIO)
408 return;
409 if (t->has_signal) {
410 tuner_info("Signal strength: %d\n", t->has_signal(client));
411 }
412 if (t->is_stereo) {
413 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200414 }
415}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200416
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417/* ---------------------------------------------------------------------- */
418
Hans Verkuilba8fc392006-06-25 15:34:39 -0300419/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700420static unsigned default_mode_mask;
421
422/* During client attach, set_type is called by adapter's attach_inform callback.
423 set_type must then be completed by tuner_attach.
424 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
426{
427 struct tuner *t;
428
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700429 client_template.adapter = adap;
430 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Panagiotis Issaris74081872006-01-11 19:40:56 -0200432 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700433 if (NULL == t)
434 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700435 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700437 t->type = UNSET;
438 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
439 t->audmode = V4L2_TUNER_MODE_STEREO;
440 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300441 t->tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700443 if (show_i2c) {
444 unsigned char buffer[16];
445 int i,rc;
446
447 memset(buffer, 0, sizeof(buffer));
448 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800449 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700450 for (i=0;i<rc;i++)
451 printk("%02x ",buffer[i]);
452 printk("\n");
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 Chehabfd3113e2005-07-31 22:34:43 -0700601 case VIDIOCSAUDIO:
602 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
603 return 0;
604 if (check_v4l2(t) == EINVAL)
605 return 0;
606
607 /* Should be implemented, since bttv calls it */
608 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700609 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300610 case TDA9887_SET_CONFIG:
611 {
612 int *i = arg;
613
614 t->tda9887_config = *i;
615 set_freq(client, t->tv_freq);
616 break;
617 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 /* --- v4l ioctls --- */
619 /* take care: bttv does userspace copying, we'll get a
620 kernel pointer here... */
621 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700622 {
623 static const v4l2_std_id map[] = {
624 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
625 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
626 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
627 [4 /* bttv */ ] = V4L2_STD_PAL_M,
628 [5 /* bttv */ ] = V4L2_STD_PAL_N,
629 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
630 };
631 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700633 if (check_v4l2(t) == EINVAL)
634 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700635
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700636 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
637 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700639 if (vc->norm < ARRAY_SIZE(map))
640 t->std = map[vc->norm];
641 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200642 if (t->tv_freq)
643 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700644 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700645 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700646 case VIDIOCSFREQ:
647 {
648 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700649
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700650 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
651 return 0;
652 if (check_v4l2(t) == EINVAL)
653 return 0;
654
655 set_freq(client, *v);
656 return 0;
657 }
658 case VIDIOCGTUNER:
659 {
660 struct video_tuner *vt = arg;
661
662 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
663 return 0;
664 if (check_v4l2(t) == EINVAL)
665 return 0;
666
667 if (V4L2_TUNER_RADIO == t->mode) {
668 if (t->has_signal)
669 vt->signal = t->has_signal(client);
670 if (t->is_stereo) {
671 if (t->is_stereo(client))
672 vt->flags |=
673 VIDEO_TUNER_STEREO_ON;
674 else
675 vt->flags &=
676 ~VIDEO_TUNER_STEREO_ON;
677 }
678 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
679
680 vt->rangelow = radio_range[0] * 16000;
681 vt->rangehigh = radio_range[1] * 16000;
682
683 } else {
684 vt->rangelow = tv_range[0] * 16;
685 vt->rangehigh = tv_range[1] * 16;
686 }
687
688 return 0;
689 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700691 {
692 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700694 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
695 return 0;
696 if (check_v4l2(t) == EINVAL)
697 return 0;
698
699 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
700 va->mode = t->is_stereo(client)
701 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
702 return 0;
703 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
705 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700706 {
707 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700709 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
710 == EINVAL)
711 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700712
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700713 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700715 t->std = *id;
716 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200717 if (t->tv_freq)
718 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700719 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700720 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700721 case VIDIOC_S_FREQUENCY:
722 {
723 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700724
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300725 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
726 == EINVAL)
727 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700728 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200729 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700730
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700731 break;
732 }
733 case VIDIOC_G_FREQUENCY:
734 {
735 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700736
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700737 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
738 return 0;
739 switch_v4l2();
740 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200741 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
742 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700743 break;
744 }
745 case VIDIOC_G_TUNER:
746 {
747 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700748
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700749 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
750 return 0;
751 switch_v4l2();
752
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200753 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300754 if (t->get_afc)
755 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300756 if (t->mode == V4L2_TUNER_ANALOG_TV)
757 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200758 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700759 tuner->rangelow = tv_range[0] * 16;
760 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200761 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700762 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200763
764 /* radio mode */
765 if (t->has_signal)
766 tuner->signal = t->has_signal(client);
767
768 tuner->rxsubchans =
769 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
770 if (t->is_stereo) {
771 tuner->rxsubchans = t->is_stereo(client) ?
772 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
773 }
774
775 tuner->capability |=
776 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
777 tuner->audmode = t->audmode;
778 tuner->rangelow = radio_range[0] * 16000;
779 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700780 break;
781 }
782 case VIDIOC_S_TUNER:
783 {
784 struct v4l2_tuner *tuner = arg;
785
786 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
787 return 0;
788
789 switch_v4l2();
790
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200791 /* do nothing unless we're a radio tuner */
792 if (t->mode != V4L2_TUNER_RADIO)
793 break;
794 t->audmode = tuner->audmode;
795 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700796 break;
797 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200798 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300799 if (t->tuner_status)
800 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200801 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 }
803
804 return 0;
805}
806
Russell King9480e302005-10-28 09:52:56 -0700807static int tuner_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700809 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
810 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700812 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 /* FIXME: power down ??? */
814 return 0;
815}
816
Russell King9480e302005-10-28 09:52:56 -0700817static int tuner_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700819 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
820 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700822 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200823 if (V4L2_TUNER_RADIO == t->mode) {
824 if (t->radio_freq)
825 set_freq(c, t->radio_freq);
826 } else {
827 if (t->tv_freq)
828 set_freq(c, t->tv_freq);
829 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 return 0;
831}
832
833/* ----------------------------------------------------------------------- */
834
835static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700836 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700837 .attach_adapter = tuner_probe,
838 .detach_client = tuner_detach,
839 .command = tuner_command,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200841 .name = "tuner",
842 .suspend = tuner_suspend,
843 .resume = tuner_resume,
844 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700846static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200847 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700848 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849};
850
851static int __init tuner_init_module(void)
852{
853 return i2c_add_driver(&driver);
854}
855
856static void __exit tuner_cleanup_module(void)
857{
858 i2c_del_driver(&driver);
859}
860
861module_init(tuner_init_module);
862module_exit(tuner_cleanup_module);
863
864/*
865 * Overrides for Emacs so that we follow Linus's tabbing style.
866 * ---------------------------------------------------------------------------
867 * Local variables:
868 * c-basic-offset: 8
869 * End:
870 */