blob: 15dbc6bf42a71c97a23a2389eb623338d6798f19 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <linux/string.h>
11#include <linux/timer.h>
12#include <linux/delay.h>
13#include <linux/errno.h>
14#include <linux/slab.h>
15#include <linux/poll.h>
16#include <linux/i2c.h>
17#include <linux/types.h>
18#include <linux/videodev.h>
19#include <linux/init.h>
20
21#include <media/tuner.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020022#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
24#define UNSET (-1U)
25
26/* standard i2c insmod options */
27static unsigned short normal_i2c[] = {
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080028 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070029 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
30 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070031 I2C_CLIENT_END
32};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070033
Linus Torvalds1da177e2005-04-16 15:20:36 -070034I2C_CLIENT_INSMOD;
35
36/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070037static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070038static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070039static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070040
Linus Torvalds1da177e2005-04-16 15:20:36 -070041/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020042int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070044static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070045static unsigned int radio_range[2] = { 65, 108 };
46
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020047static char pal[] = "--";
48static char secam[] = "--";
49static char ntsc[] = "-";
50
Hans Verkuilf9195de2006-01-11 19:01:01 -020051
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020052module_param(addr, int, 0444);
53module_param(no_autodetect, int, 0444);
54module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020055module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020056module_param_string(pal, pal, sizeof(pal), 0644);
57module_param_string(secam, secam, sizeof(secam), 0644);
58module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070059module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070060module_param_array(radio_range, int, NULL, 0644);
61
62MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
63MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
64MODULE_LICENSE("GPL");
65
Linus Torvalds1da177e2005-04-16 15:20:36 -070066static struct i2c_driver driver;
67static struct i2c_client client_template;
68
69/* ---------------------------------------------------------------------- */
70
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070071/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -070072static void set_tv_freq(struct i2c_client *c, unsigned int freq)
73{
74 struct tuner *t = i2c_get_clientdata(c);
75
76 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070077 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 return;
79 }
Hans Verkuil27487d42006-01-15 15:04:52 -020080 if (NULL == t->set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070081 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 return;
83 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070084 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
85 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
86 freq / 16, freq % 16 * 100 / 16, tv_range[0],
87 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -020088 /* V4L2 spec: if the freq is not possible then the closest
89 possible value should be selected */
90 if (freq < tv_range[0] * 16)
91 freq = tv_range[0] * 16;
92 else
93 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 }
Hans Verkuil27487d42006-01-15 15:04:52 -020095 t->set_tv_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97
98static void set_radio_freq(struct i2c_client *c, unsigned int freq)
99{
100 struct tuner *t = i2c_get_clientdata(c);
101
102 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700103 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 return;
105 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200106 if (NULL == t->set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700107 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 return;
109 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200110 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700111 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
112 freq / 16000, freq % 16000 * 100 / 16000,
113 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200114 /* V4L2 spec: if the freq is not possible then the closest
115 possible value should be selected */
116 if (freq < radio_range[0] * 16000)
117 freq = radio_range[0] * 16000;
118 else
119 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700121
Hans Verkuil27487d42006-01-15 15:04:52 -0200122 t->set_radio_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123}
124
125static void set_freq(struct i2c_client *c, unsigned long freq)
126{
127 struct tuner *t = i2c_get_clientdata(c);
128
129 switch (t->mode) {
130 case V4L2_TUNER_RADIO:
131 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700132 freq / 16000, freq % 16000 * 100 / 16000);
133 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200134 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 break;
136 case V4L2_TUNER_ANALOG_TV:
137 case V4L2_TUNER_DIGITAL_TV:
138 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700139 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200141 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 break;
143 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144}
145
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700146static void set_type(struct i2c_client *c, unsigned int type,
147 unsigned int new_mode_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
149 struct tuner *t = i2c_get_clientdata(c);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700150 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700152 if (type == UNSET || type == TUNER_ABSENT) {
153 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 return;
155 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700157 if (type >= tuner_count) {
158 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
159 return;
160 }
161
162 /* This code detects calls by card attach_inform */
163 if (NULL == t->i2c.dev.driver) {
164 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
165
166 t->type=type;
167 return;
168 }
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700169
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 t->type = type;
171 switch (t->type) {
172 case TUNER_MT2032:
173 microtune_init(c);
174 break;
175 case TUNER_PHILIPS_TDA8290:
176 tda8290_init(c);
177 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700178 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700179 if (tea5767_tuner_init(c) == EINVAL) {
180 t->type = TUNER_ABSENT;
181 t->mode_mask = T_UNINITIALIZED;
182 return;
183 }
184 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700185 break;
186 case TUNER_PHILIPS_FMD1216ME_MK3:
187 buffer[0] = 0x0b;
188 buffer[1] = 0xdc;
189 buffer[2] = 0x9c;
190 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700191 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700192 mdelay(1);
193 buffer[2] = 0x86;
194 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700195 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700196 default_tuner_init(c);
197 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800198 case TUNER_PHILIPS_TD1316:
199 buffer[0] = 0x0b;
200 buffer[1] = 0xdc;
201 buffer[2] = 0x86;
202 buffer[3] = 0xa4;
203 i2c_master_send(c,buffer,4);
204 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200205 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300206 case TUNER_TDA9887:
207 tda9887_tuner_init(c);
208 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 default:
210 default_tuner_init(c);
211 break;
212 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700213
214 if (t->mode_mask == T_UNINITIALIZED)
215 t->mode_mask = new_mode_mask;
216
Hans Verkuil27487d42006-01-15 15:04:52 -0200217 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700218 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100219 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700220 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221}
222
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700223/*
224 * This function apply tuner config to tuner specified
225 * by tun_setup structure. I addr is unset, then admin status
226 * and tun addr status is more precise then current status,
227 * it's applied. Otherwise status and type are applied only to
228 * tuner with exactly the same addr.
229*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700230
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700231static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700232{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700233 struct tuner *t = i2c_get_clientdata(c);
234
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300235 tuner_dbg("set addr for type %i\n", t->type);
236
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800237 if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET &&
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700238 (t->mode_mask & tun_setup->mode_mask)) ||
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800239 tun_setup->addr == c->addr)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700240 set_type(c, tun_setup->type, tun_setup->mode_mask);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700241 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700242}
243
244static inline int check_mode(struct tuner *t, char *cmd)
245{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700246 if ((1 << t->mode & t->mode_mask) == 0) {
247 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700248 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700249
250 switch (t->mode) {
251 case V4L2_TUNER_RADIO:
252 tuner_dbg("Cmd %s accepted for radio\n", cmd);
253 break;
254 case V4L2_TUNER_ANALOG_TV:
255 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
256 break;
257 case V4L2_TUNER_DIGITAL_TV:
258 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
259 break;
260 }
261 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700262}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700263
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700264/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265static int tuner_fixup_std(struct tuner *t)
266{
267 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300269 case '6':
270 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
271 t->std = V4L2_STD_PAL_60;
272 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 case 'b':
274 case 'B':
275 case 'g':
276 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700277 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 t->std = V4L2_STD_PAL_BG;
279 break;
280 case 'i':
281 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700282 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 t->std = V4L2_STD_PAL_I;
284 break;
285 case 'd':
286 case 'D':
287 case 'k':
288 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700289 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 t->std = V4L2_STD_PAL_DK;
291 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700292 case 'M':
293 case 'm':
294 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
295 t->std = V4L2_STD_PAL_M;
296 break;
297 case 'N':
298 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200299 if (pal[1] == 'c' || pal[1] == 'C') {
300 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
301 t->std = V4L2_STD_PAL_Nc;
302 } else {
303 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
304 t->std = V4L2_STD_PAL_N;
305 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700306 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700307 case '-':
308 /* default parameter, do nothing */
309 break;
310 default:
311 tuner_warn ("pal= argument not recognised\n");
312 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 }
314 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700315 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
316 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200317 case 'b':
318 case 'B':
319 case 'g':
320 case 'G':
321 case 'h':
322 case 'H':
323 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
324 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
325 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700326 case 'd':
327 case 'D':
328 case 'k':
329 case 'K':
330 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
331 t->std = V4L2_STD_SECAM_DK;
332 break;
333 case 'l':
334 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800335 if ((secam[1]=='C')||(secam[1]=='c')) {
336 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
337 t->std = V4L2_STD_SECAM_LC;
338 } else {
339 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
340 t->std = V4L2_STD_SECAM_L;
341 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700342 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700343 case '-':
344 /* default parameter, do nothing */
345 break;
346 default:
347 tuner_warn ("secam= argument not recognised\n");
348 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700349 }
350 }
351
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200352 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
353 switch (ntsc[0]) {
354 case 'm':
355 case 'M':
356 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
357 t->std = V4L2_STD_NTSC_M;
358 break;
359 case 'j':
360 case 'J':
361 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
362 t->std = V4L2_STD_NTSC_M_JP;
363 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200364 case 'k':
365 case 'K':
366 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
367 t->std = V4L2_STD_NTSC_M_KR;
368 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200369 case '-':
370 /* default parameter, do nothing */
371 break;
372 default:
373 tuner_info("ntsc= argument not recognised\n");
374 break;
375 }
376 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 return 0;
378}
379
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200380static void tuner_status(struct i2c_client *client)
381{
382 struct tuner *t = i2c_get_clientdata(client);
383 unsigned long freq, freq_fraction;
384 const char *p;
385
386 switch (t->mode) {
387 case V4L2_TUNER_RADIO: p = "radio"; break;
388 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
389 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
390 default: p = "undefined"; break;
391 }
392 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200393 freq = t->radio_freq / 16000;
394 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200395 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200396 freq = t->tv_freq / 16;
397 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200398 }
399 tuner_info("Tuner mode: %s\n", p);
400 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300401 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200402 if (t->mode != V4L2_TUNER_RADIO)
403 return;
404 if (t->has_signal) {
405 tuner_info("Signal strength: %d\n", t->has_signal(client));
406 }
407 if (t->is_stereo) {
408 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200409 }
410}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200411
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412/* ---------------------------------------------------------------------- */
413
Hans Verkuilba8fc392006-06-25 15:34:39 -0300414/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700415static unsigned default_mode_mask;
416
417/* During client attach, set_type is called by adapter's attach_inform callback.
418 set_type must then be completed by tuner_attach.
419 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
421{
422 struct tuner *t;
423
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700424 client_template.adapter = adap;
425 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
Panagiotis Issaris74081872006-01-11 19:40:56 -0200427 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700428 if (NULL == t)
429 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700430 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700432 t->type = UNSET;
433 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
434 t->audmode = V4L2_TUNER_MODE_STEREO;
435 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300436 t->tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700438 if (show_i2c) {
439 unsigned char buffer[16];
440 int i,rc;
441
442 memset(buffer, 0, sizeof(buffer));
443 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800444 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700445 for (i=0;i<rc;i++)
446 printk("%02x ",buffer[i]);
447 printk("\n");
448 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300449 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
450 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
451 return -ENODEV;
452
Markus Rechberger257c6452006-01-23 17:11:11 -0200453 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700454 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800455 switch (addr) {
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800456 case 0x42:
457 case 0x43:
458 case 0x4a:
459 case 0x4b:
460 /* If chip is not tda8290, don't register.
461 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300462 if (tda8290_probe(&t->i2c) == 0) {
463 tuner_dbg("chip at addr %x is a tda8290\n", addr);
464 } else {
465 /* Default is being tda9887 */
466 t->type = TUNER_TDA9887;
467 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
468 t->mode = T_STANDBY;
469 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800470 }
471 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800472 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700473 if (tea5767_autodetection(&t->i2c) != EINVAL) {
474 t->type = TUNER_TEA5767;
475 t->mode_mask = T_RADIO;
476 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200477 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700478 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700479
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800480 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700481 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800482 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700483 }
484 }
485
486 /* Initializes only the first adapter found */
487 if (default_mode_mask != T_UNINITIALIZED) {
488 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
489 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200490 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
491 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700492 default_mode_mask = T_UNINITIALIZED;
493 }
494
495 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800496register_client:
497 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700498 i2c_attach_client (&t->i2c);
499 set_type (&t->i2c,t->type, t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 return 0;
501}
502
503static int tuner_probe(struct i2c_adapter *adap)
504{
505 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700506 normal_i2c[0] = addr;
507 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700510 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 if (adap->class & I2C_CLASS_TV_ANALOG)
513 return i2c_probe(adap, &addr_data, tuner_attach);
514 return 0;
515}
516
517static int tuner_detach(struct i2c_client *client)
518{
519 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700520 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700522 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700523 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700524 tuner_warn
525 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700526 return err;
527 }
528
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 kfree(t);
530 return 0;
531}
532
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700533/*
534 * Switch tuner to other mode. If tuner support both tv and radio,
535 * set another frequency to some value (This is needed for some pal
536 * tuners to avoid locking). Otherwise, just put second tuner in
537 * standby mode.
538 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700540static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
541{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800542 if (mode == t->mode)
543 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700544
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800545 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700546
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800547 if (check_mode(t, cmd) == EINVAL) {
548 t->mode = T_STANDBY;
549 if (t->standby)
550 t->standby (client);
551 return EINVAL;
552 }
553 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700554}
555
556#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800557 tuner_dbg("switching to v4l2\n"); \
558 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700559
560static inline int check_v4l2(struct tuner *t)
561{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300562 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
563 TV, v4l1 for radio), until that is fixed this code is disabled.
564 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
565 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700566 return 0;
567}
568
569static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
571 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Hans Verkuilf9195de2006-01-11 19:01:01 -0200573 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200574 v4l_i2c_print_ioctl(&(t->i2c),cmd);
575
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700576 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700578 case TUNER_SET_TYPE_ADDR:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700579 tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
580 ((struct tuner_setup *)arg)->type,
581 ((struct tuner_setup *)arg)->addr,
582 ((struct tuner_setup *)arg)->mode_mask);
583
584 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700585 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200587 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
588 == EINVAL)
589 return 0;
590 if (t->radio_freq)
591 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700593 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200594 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
595 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300596 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200597 if (t->standby)
598 t->standby (client);
599 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300600#ifdef CONFIG_VIDEO_V4L1
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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 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 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300693#endif
694 case TDA9887_SET_CONFIG:
695 if (t->type == TUNER_TDA9887) {
696 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300698 t->tda9887_config = *i;
699 set_freq(client, t->tv_freq);
700 }
701 break;
702 /* --- v4l ioctls --- */
703 /* take care: bttv does userspace copying, we'll get a
704 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 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
Jean Delvare21b48a72007-03-12 19:20:15 -0300807static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700809 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700811 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 /* FIXME: power down ??? */
813 return 0;
814}
815
Jean Delvare21b48a72007-03-12 19:20:15 -0300816static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700818 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700820 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200821 if (V4L2_TUNER_RADIO == t->mode) {
822 if (t->radio_freq)
823 set_freq(c, t->radio_freq);
824 } else {
825 if (t->tv_freq)
826 set_freq(c, t->tv_freq);
827 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 return 0;
829}
830
831/* ----------------------------------------------------------------------- */
832
833static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700834 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700835 .attach_adapter = tuner_probe,
836 .detach_client = tuner_detach,
837 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300838 .suspend = tuner_suspend,
839 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200841 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200842 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700844static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200845 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700846 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847};
848
849static int __init tuner_init_module(void)
850{
851 return i2c_add_driver(&driver);
852}
853
854static void __exit tuner_cleanup_module(void)
855{
856 i2c_del_driver(&driver);
857}
858
859module_init(tuner_init_module);
860module_exit(tuner_cleanup_module);
861
862/*
863 * Overrides for Emacs so that we follow Linus's tabbing style.
864 * ---------------------------------------------------------------------------
865 * Local variables:
866 * c-basic-offset: 8
867 * End:
868 */