blob: d68137f2eb8c3eb0b24dba961ac41f8bb8d76be8 [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 -020043static unsigned int tuner_debug_old = 0;
44int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070046static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070047static unsigned int radio_range[2] = { 65, 108 };
48
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020049static char pal[] = "--";
50static char secam[] = "--";
51static char ntsc[] = "-";
52
Hans Verkuilf9195de2006-01-11 19:01:01 -020053
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020054module_param(addr, int, 0444);
55module_param(no_autodetect, int, 0444);
56module_param(show_i2c, int, 0444);
Hans Verkuilfac9e892006-01-09 15:32:40 -020057/* Note: tuner_debug is deprecated and will be removed in 2.6.17 */
Hans Verkuilf9195de2006-01-11 19:01:01 -020058module_param_named(tuner_debug,tuner_debug_old, int, 0444);
59module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020060module_param_string(pal, pal, sizeof(pal), 0644);
61module_param_string(secam, secam, sizeof(secam), 0644);
62module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070063module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064module_param_array(radio_range, int, NULL, 0644);
65
66MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
67MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
68MODULE_LICENSE("GPL");
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070static struct i2c_driver driver;
71static struct i2c_client client_template;
72
73/* ---------------------------------------------------------------------- */
74
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070075/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -070076static void set_tv_freq(struct i2c_client *c, unsigned int freq)
77{
78 struct tuner *t = i2c_get_clientdata(c);
79
80 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070081 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 return;
83 }
Hans Verkuil27487d42006-01-15 15:04:52 -020084 if (NULL == t->set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070085 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 return;
87 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070088 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
89 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
90 freq / 16, freq % 16 * 100 / 16, tv_range[0],
91 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -020092 /* V4L2 spec: if the freq is not possible then the closest
93 possible value should be selected */
94 if (freq < tv_range[0] * 16)
95 freq = tv_range[0] * 16;
96 else
97 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 }
Hans Verkuil27487d42006-01-15 15:04:52 -020099 t->set_tv_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100}
101
102static void set_radio_freq(struct i2c_client *c, unsigned int freq)
103{
104 struct tuner *t = i2c_get_clientdata(c);
105
106 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700107 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 return;
109 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200110 if (NULL == t->set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700111 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 return;
113 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200114 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700115 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
116 freq / 16000, freq % 16000 * 100 / 16000,
117 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200118 /* V4L2 spec: if the freq is not possible then the closest
119 possible value should be selected */
120 if (freq < radio_range[0] * 16000)
121 freq = radio_range[0] * 16000;
122 else
123 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700125
Hans Verkuil27487d42006-01-15 15:04:52 -0200126 t->set_radio_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127}
128
129static void set_freq(struct i2c_client *c, unsigned long freq)
130{
131 struct tuner *t = i2c_get_clientdata(c);
132
133 switch (t->mode) {
134 case V4L2_TUNER_RADIO:
135 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700136 freq / 16000, freq % 16000 * 100 / 16000);
137 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200138 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 break;
140 case V4L2_TUNER_ANALOG_TV:
141 case V4L2_TUNER_DIGITAL_TV:
142 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700143 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200145 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 break;
147 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700150static void set_type(struct i2c_client *c, unsigned int type,
151 unsigned int new_mode_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152{
153 struct tuner *t = i2c_get_clientdata(c);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700154 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700156 if (type == UNSET || type == TUNER_ABSENT) {
157 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 return;
159 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700161 if (type >= tuner_count) {
162 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
163 return;
164 }
165
166 /* This code detects calls by card attach_inform */
167 if (NULL == t->i2c.dev.driver) {
168 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
169
170 t->type=type;
171 return;
172 }
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 t->type = type;
175 switch (t->type) {
176 case TUNER_MT2032:
177 microtune_init(c);
178 break;
179 case TUNER_PHILIPS_TDA8290:
180 tda8290_init(c);
181 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700182 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700183 if (tea5767_tuner_init(c) == EINVAL) {
184 t->type = TUNER_ABSENT;
185 t->mode_mask = T_UNINITIALIZED;
186 return;
187 }
188 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700189 break;
190 case TUNER_PHILIPS_FMD1216ME_MK3:
191 buffer[0] = 0x0b;
192 buffer[1] = 0xdc;
193 buffer[2] = 0x9c;
194 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700195 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700196 mdelay(1);
197 buffer[2] = 0x86;
198 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700199 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700200 default_tuner_init(c);
201 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700202 case TUNER_LG_TDVS_H062F:
203 /* Set the Auxiliary Byte. */
204 buffer[2] &= ~0x20;
205 buffer[2] |= 0x18;
206 buffer[3] = 0x20;
207 i2c_master_send(c, buffer, 4);
208 default_tuner_init(c);
209 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800210 case TUNER_PHILIPS_TD1316:
211 buffer[0] = 0x0b;
212 buffer[1] = 0xdc;
213 buffer[2] = 0x86;
214 buffer[3] = 0xa4;
215 i2c_master_send(c,buffer,4);
216 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200217 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
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800244 if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET &&
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700245 (t->mode_mask & tun_setup->mode_mask)) ||
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800246 tun_setup->addr == c->addr)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700247 set_type(c, tun_setup->type, tun_setup->mode_mask);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700248 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700249}
250
251static inline int check_mode(struct tuner *t, char *cmd)
252{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700253 if ((1 << t->mode & t->mode_mask) == 0) {
254 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700255 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700256
257 switch (t->mode) {
258 case V4L2_TUNER_RADIO:
259 tuner_dbg("Cmd %s accepted for radio\n", cmd);
260 break;
261 case V4L2_TUNER_ANALOG_TV:
262 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
263 break;
264 case V4L2_TUNER_DIGITAL_TV:
265 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
266 break;
267 }
268 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700269}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700270
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700271/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272static int tuner_fixup_std(struct tuner *t)
273{
274 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 switch (pal[0]) {
276 case 'b':
277 case 'B':
278 case 'g':
279 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700280 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 t->std = V4L2_STD_PAL_BG;
282 break;
283 case 'i':
284 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700285 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 t->std = V4L2_STD_PAL_I;
287 break;
288 case 'd':
289 case 'D':
290 case 'k':
291 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700292 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 t->std = V4L2_STD_PAL_DK;
294 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700295 case 'M':
296 case 'm':
297 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
298 t->std = V4L2_STD_PAL_M;
299 break;
300 case 'N':
301 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200302 if (pal[1] == 'c' || pal[1] == 'C') {
303 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
304 t->std = V4L2_STD_PAL_Nc;
305 } else {
306 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
307 t->std = V4L2_STD_PAL_N;
308 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700309 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700310 case '-':
311 /* default parameter, do nothing */
312 break;
313 default:
314 tuner_warn ("pal= argument not recognised\n");
315 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 }
317 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700318 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
319 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200320 case 'b':
321 case 'B':
322 case 'g':
323 case 'G':
324 case 'h':
325 case 'H':
326 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
327 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
328 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700329 case 'd':
330 case 'D':
331 case 'k':
332 case 'K':
333 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
334 t->std = V4L2_STD_SECAM_DK;
335 break;
336 case 'l':
337 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800338 if ((secam[1]=='C')||(secam[1]=='c')) {
339 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
340 t->std = V4L2_STD_SECAM_LC;
341 } else {
342 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
343 t->std = V4L2_STD_SECAM_L;
344 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700345 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700346 case '-':
347 /* default parameter, do nothing */
348 break;
349 default:
350 tuner_warn ("secam= argument not recognised\n");
351 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700352 }
353 }
354
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200355 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
356 switch (ntsc[0]) {
357 case 'm':
358 case 'M':
359 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
360 t->std = V4L2_STD_NTSC_M;
361 break;
362 case 'j':
363 case 'J':
364 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
365 t->std = V4L2_STD_NTSC_M_JP;
366 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200367 case 'k':
368 case 'K':
369 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
370 t->std = V4L2_STD_NTSC_M_KR;
371 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200372 case '-':
373 /* default parameter, do nothing */
374 break;
375 default:
376 tuner_info("ntsc= argument not recognised\n");
377 break;
378 }
379 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 return 0;
381}
382
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200383static void tuner_status(struct i2c_client *client)
384{
385 struct tuner *t = i2c_get_clientdata(client);
386 unsigned long freq, freq_fraction;
387 const char *p;
388
389 switch (t->mode) {
390 case V4L2_TUNER_RADIO: p = "radio"; break;
391 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
392 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
393 default: p = "undefined"; break;
394 }
395 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200396 freq = t->radio_freq / 16000;
397 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200398 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200399 freq = t->tv_freq / 16;
400 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200401 }
402 tuner_info("Tuner mode: %s\n", p);
403 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300404 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200405 if (t->mode != V4L2_TUNER_RADIO)
406 return;
407 if (t->has_signal) {
408 tuner_info("Signal strength: %d\n", t->has_signal(client));
409 }
410 if (t->is_stereo) {
411 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200412 }
413}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415/* ---------------------------------------------------------------------- */
416
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700417/* static var Used only in tuner_attach and tuner_probe */
418static unsigned default_mode_mask;
419
420/* During client attach, set_type is called by adapter's attach_inform callback.
421 set_type must then be completed by tuner_attach.
422 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
424{
425 struct tuner *t;
426
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700427 client_template.adapter = adap;
428 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
Panagiotis Issaris74081872006-01-11 19:40:56 -0200430 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700431 if (NULL == t)
432 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700433 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700435 t->type = UNSET;
436 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
437 t->audmode = V4L2_TUNER_MODE_STEREO;
438 t->mode_mask = T_UNINITIALIZED;
Hans Verkuilf9195de2006-01-11 19:01:01 -0200439 if (tuner_debug_old) {
440 tuner_debug = tuner_debug_old;
Hans Verkuilfac9e892006-01-09 15:32:40 -0200441 printk(KERN_ERR "tuner: tuner_debug is deprecated and will be removed in 2.6.17.\n");
442 printk(KERN_ERR "tuner: use the debug option instead.\n");
443 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700445 if (show_i2c) {
446 unsigned char buffer[16];
447 int i,rc;
448
449 memset(buffer, 0, sizeof(buffer));
450 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800451 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700452 for (i=0;i<rc;i++)
453 printk("%02x ",buffer[i]);
454 printk("\n");
455 }
Markus Rechberger257c6452006-01-23 17:11:11 -0200456 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700457 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800458 switch (addr) {
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800459 case 0x42:
460 case 0x43:
461 case 0x4a:
462 case 0x4b:
463 /* If chip is not tda8290, don't register.
464 since it can be tda9887*/
465 if (tda8290_probe(&t->i2c) != 0) {
Nickolay V. Shmyrevb228ede2005-11-08 21:38:11 -0800466 tuner_dbg("chip at addr %x is not a tda8290\n", addr);
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800467 kfree(t);
468 return 0;
469 }
470 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800471 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700472 if (tea5767_autodetection(&t->i2c) != EINVAL) {
473 t->type = TUNER_TEA5767;
474 t->mode_mask = T_RADIO;
475 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200476 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700477 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700478
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800479 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700480 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800481 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700482 }
483 }
484
485 /* Initializes only the first adapter found */
486 if (default_mode_mask != T_UNINITIALIZED) {
487 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
488 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200489 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
490 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700491 default_mode_mask = T_UNINITIALIZED;
492 }
493
494 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800495register_client:
496 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700497 i2c_attach_client (&t->i2c);
498 set_type (&t->i2c,t->type, t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 return 0;
500}
501
502static int tuner_probe(struct i2c_adapter *adap)
503{
504 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700505 normal_i2c[0] = addr;
506 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700509 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700510
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 if (adap->class & I2C_CLASS_TV_ANALOG)
512 return i2c_probe(adap, &addr_data, tuner_attach);
513 return 0;
514}
515
516static int tuner_detach(struct i2c_client *client)
517{
518 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700519 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700521 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700522 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700523 tuner_warn
524 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700525 return err;
526 }
527
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 kfree(t);
529 return 0;
530}
531
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700532/*
533 * Switch tuner to other mode. If tuner support both tv and radio,
534 * set another frequency to some value (This is needed for some pal
535 * tuners to avoid locking). Otherwise, just put second tuner in
536 * standby mode.
537 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700539static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
540{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800541 if (mode == t->mode)
542 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700543
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800544 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700545
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800546 if (check_mode(t, cmd) == EINVAL) {
547 t->mode = T_STANDBY;
548 if (t->standby)
549 t->standby (client);
550 return EINVAL;
551 }
552 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700553}
554
555#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800556 tuner_dbg("switching to v4l2\n"); \
557 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700558
559static inline int check_v4l2(struct tuner *t)
560{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300561 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
562 TV, v4l1 for radio), until that is fixed this code is disabled.
563 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
564 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700565 return 0;
566}
567
568static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
570 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Hans Verkuilf9195de2006-01-11 19:01:01 -0200572 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200573 v4l_i2c_print_ioctl(&(t->i2c),cmd);
574
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700575 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700577 case TUNER_SET_TYPE_ADDR:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700578 tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
579 ((struct tuner_setup *)arg)->type,
580 ((struct tuner_setup *)arg)->addr,
581 ((struct tuner_setup *)arg)->mode_mask);
582
583 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700584 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200586 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
587 == EINVAL)
588 return 0;
589 if (t->radio_freq)
590 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700592 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200593 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
594 return 0;
595 if (t->standby)
596 t->standby (client);
597 break;
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 /* --- v4l ioctls --- */
608 /* take care: bttv does userspace copying, we'll get a
609 kernel pointer here... */
610 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700611 {
612 static const v4l2_std_id map[] = {
613 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
614 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
615 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
616 [4 /* bttv */ ] = V4L2_STD_PAL_M,
617 [5 /* bttv */ ] = V4L2_STD_PAL_N,
618 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
619 };
620 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700622 if (check_v4l2(t) == EINVAL)
623 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700624
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700625 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
626 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700628 if (vc->norm < ARRAY_SIZE(map))
629 t->std = map[vc->norm];
630 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200631 if (t->tv_freq)
632 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700633 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700634 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700635 case VIDIOCSFREQ:
636 {
637 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700638
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700639 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
640 return 0;
641 if (check_v4l2(t) == EINVAL)
642 return 0;
643
644 set_freq(client, *v);
645 return 0;
646 }
647 case VIDIOCGTUNER:
648 {
649 struct video_tuner *vt = arg;
650
651 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
652 return 0;
653 if (check_v4l2(t) == EINVAL)
654 return 0;
655
656 if (V4L2_TUNER_RADIO == t->mode) {
657 if (t->has_signal)
658 vt->signal = t->has_signal(client);
659 if (t->is_stereo) {
660 if (t->is_stereo(client))
661 vt->flags |=
662 VIDEO_TUNER_STEREO_ON;
663 else
664 vt->flags &=
665 ~VIDEO_TUNER_STEREO_ON;
666 }
667 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
668
669 vt->rangelow = radio_range[0] * 16000;
670 vt->rangehigh = radio_range[1] * 16000;
671
672 } else {
673 vt->rangelow = tv_range[0] * 16;
674 vt->rangehigh = tv_range[1] * 16;
675 }
676
677 return 0;
678 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680 {
681 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700683 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
684 return 0;
685 if (check_v4l2(t) == EINVAL)
686 return 0;
687
688 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
689 va->mode = t->is_stereo(client)
690 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
691 return 0;
692 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
694 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700695 {
696 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700698 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
699 == EINVAL)
700 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700701
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700702 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700704 t->std = *id;
705 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200706 if (t->tv_freq)
707 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700708 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700709 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700710 case VIDIOC_S_FREQUENCY:
711 {
712 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700713
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700714 switch_v4l2();
Hartmut Hackmanndf8cf702006-03-03 12:09:26 -0300715 if ((V4L2_TUNER_RADIO == f->type && V4L2_TUNER_RADIO != t->mode)
716 || (V4L2_TUNER_DIGITAL_TV == f->type
717 && V4L2_TUNER_DIGITAL_TV != t->mode)) {
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800718 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700719 == EINVAL)
720 return 0;
721 }
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;
747 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700748 tuner->rangelow = tv_range[0] * 16;
749 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200750 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700751 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200752
753 /* radio mode */
754 if (t->has_signal)
755 tuner->signal = t->has_signal(client);
756
757 tuner->rxsubchans =
758 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
759 if (t->is_stereo) {
760 tuner->rxsubchans = t->is_stereo(client) ?
761 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
762 }
763
764 tuner->capability |=
765 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
766 tuner->audmode = t->audmode;
767 tuner->rangelow = radio_range[0] * 16000;
768 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700769 break;
770 }
771 case VIDIOC_S_TUNER:
772 {
773 struct v4l2_tuner *tuner = arg;
774
775 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
776 return 0;
777
778 switch_v4l2();
779
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200780 /* do nothing unless we're a radio tuner */
781 if (t->mode != V4L2_TUNER_RADIO)
782 break;
783 t->audmode = tuner->audmode;
784 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700785 break;
786 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200787 case VIDIOC_LOG_STATUS:
788 tuner_status(client);
789 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 }
791
792 return 0;
793}
794
Russell King9480e302005-10-28 09:52:56 -0700795static int tuner_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700797 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
798 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700800 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 /* FIXME: power down ??? */
802 return 0;
803}
804
Russell King9480e302005-10-28 09:52:56 -0700805static int tuner_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700807 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
808 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700810 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200811 if (V4L2_TUNER_RADIO == t->mode) {
812 if (t->radio_freq)
813 set_freq(c, t->radio_freq);
814 } else {
815 if (t->tv_freq)
816 set_freq(c, t->tv_freq);
817 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 return 0;
819}
820
821/* ----------------------------------------------------------------------- */
822
823static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700824 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700825 .attach_adapter = tuner_probe,
826 .detach_client = tuner_detach,
827 .command = tuner_command,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200829 .name = "tuner",
830 .suspend = tuner_suspend,
831 .resume = tuner_resume,
832 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700834static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200835 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700836 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837};
838
839static int __init tuner_init_module(void)
840{
841 return i2c_add_driver(&driver);
842}
843
844static void __exit tuner_cleanup_module(void)
845{
846 i2c_del_driver(&driver);
847}
848
849module_init(tuner_init_module);
850module_exit(tuner_cleanup_module);
851
852/*
853 * Overrides for Emacs so that we follow Linus's tabbing style.
854 * ---------------------------------------------------------------------------
855 * Local variables:
856 * c-basic-offset: 8
857 * End:
858 */