blob: a26ded7d6faef86d7ff23ee4b44b9786e099b784 [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;
Michael Krufky9c26c8b2006-04-27 01:29:17 -0300202 case TUNER_LG_TDVS_H06XF:
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700203 /* 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;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300218 case TUNER_TDA9887:
219 tda9887_tuner_init(c);
220 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 default:
222 default_tuner_init(c);
223 break;
224 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700225
226 if (t->mode_mask == T_UNINITIALIZED)
227 t->mode_mask = new_mode_mask;
228
Hans Verkuil27487d42006-01-15 15:04:52 -0200229 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700230 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100231 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700232 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233}
234
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700235/*
236 * This function apply tuner config to tuner specified
237 * by tun_setup structure. I addr is unset, then admin status
238 * and tun addr status is more precise then current status,
239 * it's applied. Otherwise status and type are applied only to
240 * tuner with exactly the same addr.
241*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700242
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700243static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700244{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700245 struct tuner *t = i2c_get_clientdata(c);
246
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300247 tuner_dbg("set addr for type %i\n", t->type);
248
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800249 if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET &&
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700250 (t->mode_mask & tun_setup->mode_mask)) ||
Ricardo Cerqueira291d1d72005-11-08 21:38:33 -0800251 tun_setup->addr == c->addr)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700252 set_type(c, tun_setup->type, tun_setup->mode_mask);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700253 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700254}
255
256static inline int check_mode(struct tuner *t, char *cmd)
257{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700258 if ((1 << t->mode & t->mode_mask) == 0) {
259 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700260 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700261
262 switch (t->mode) {
263 case V4L2_TUNER_RADIO:
264 tuner_dbg("Cmd %s accepted for radio\n", cmd);
265 break;
266 case V4L2_TUNER_ANALOG_TV:
267 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
268 break;
269 case V4L2_TUNER_DIGITAL_TV:
270 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
271 break;
272 }
273 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700274}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700275
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700276/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277static int tuner_fixup_std(struct tuner *t)
278{
279 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 switch (pal[0]) {
281 case 'b':
282 case 'B':
283 case 'g':
284 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700285 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 t->std = V4L2_STD_PAL_BG;
287 break;
288 case 'i':
289 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700290 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 t->std = V4L2_STD_PAL_I;
292 break;
293 case 'd':
294 case 'D':
295 case 'k':
296 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700297 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 t->std = V4L2_STD_PAL_DK;
299 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700300 case 'M':
301 case 'm':
302 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
303 t->std = V4L2_STD_PAL_M;
304 break;
305 case 'N':
306 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200307 if (pal[1] == 'c' || pal[1] == 'C') {
308 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
309 t->std = V4L2_STD_PAL_Nc;
310 } else {
311 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
312 t->std = V4L2_STD_PAL_N;
313 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700314 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700315 case '-':
316 /* default parameter, do nothing */
317 break;
318 default:
319 tuner_warn ("pal= argument not recognised\n");
320 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 }
322 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700323 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
324 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200325 case 'b':
326 case 'B':
327 case 'g':
328 case 'G':
329 case 'h':
330 case 'H':
331 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
332 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
333 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700334 case 'd':
335 case 'D':
336 case 'k':
337 case 'K':
338 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
339 t->std = V4L2_STD_SECAM_DK;
340 break;
341 case 'l':
342 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800343 if ((secam[1]=='C')||(secam[1]=='c')) {
344 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
345 t->std = V4L2_STD_SECAM_LC;
346 } else {
347 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
348 t->std = V4L2_STD_SECAM_L;
349 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700350 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700351 case '-':
352 /* default parameter, do nothing */
353 break;
354 default:
355 tuner_warn ("secam= argument not recognised\n");
356 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700357 }
358 }
359
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200360 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
361 switch (ntsc[0]) {
362 case 'm':
363 case 'M':
364 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
365 t->std = V4L2_STD_NTSC_M;
366 break;
367 case 'j':
368 case 'J':
369 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
370 t->std = V4L2_STD_NTSC_M_JP;
371 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200372 case 'k':
373 case 'K':
374 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
375 t->std = V4L2_STD_NTSC_M_KR;
376 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200377 case '-':
378 /* default parameter, do nothing */
379 break;
380 default:
381 tuner_info("ntsc= argument not recognised\n");
382 break;
383 }
384 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 return 0;
386}
387
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200388static void tuner_status(struct i2c_client *client)
389{
390 struct tuner *t = i2c_get_clientdata(client);
391 unsigned long freq, freq_fraction;
392 const char *p;
393
394 switch (t->mode) {
395 case V4L2_TUNER_RADIO: p = "radio"; break;
396 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
397 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
398 default: p = "undefined"; break;
399 }
400 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200401 freq = t->radio_freq / 16000;
402 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200403 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200404 freq = t->tv_freq / 16;
405 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200406 }
407 tuner_info("Tuner mode: %s\n", p);
408 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300409 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200410 if (t->mode != V4L2_TUNER_RADIO)
411 return;
412 if (t->has_signal) {
413 tuner_info("Signal strength: %d\n", t->has_signal(client));
414 }
415 if (t->is_stereo) {
416 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200417 }
418}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200419
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420/* ---------------------------------------------------------------------- */
421
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700422/* static var Used only in tuner_attach and tuner_probe */
423static unsigned default_mode_mask;
424
425/* During client attach, set_type is called by adapter's attach_inform callback.
426 set_type must then be completed by tuner_attach.
427 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
429{
430 struct tuner *t;
431
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700432 client_template.adapter = adap;
433 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Panagiotis Issaris74081872006-01-11 19:40:56 -0200435 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700436 if (NULL == t)
437 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700438 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700440 t->type = UNSET;
441 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
442 t->audmode = V4L2_TUNER_MODE_STEREO;
443 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300444 t->tuner_status = tuner_status;
Hans Verkuilf9195de2006-01-11 19:01:01 -0200445 if (tuner_debug_old) {
446 tuner_debug = tuner_debug_old;
Hans Verkuilfac9e892006-01-09 15:32:40 -0200447 printk(KERN_ERR "tuner: tuner_debug is deprecated and will be removed in 2.6.17.\n");
448 printk(KERN_ERR "tuner: use the debug option instead.\n");
449 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700451 if (show_i2c) {
452 unsigned char buffer[16];
453 int i,rc;
454
455 memset(buffer, 0, sizeof(buffer));
456 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800457 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700458 for (i=0;i<rc;i++)
459 printk("%02x ",buffer[i]);
460 printk("\n");
461 }
Markus Rechberger257c6452006-01-23 17:11:11 -0200462 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700463 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800464 switch (addr) {
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800465 case 0x42:
466 case 0x43:
467 case 0x4a:
468 case 0x4b:
469 /* If chip is not tda8290, don't register.
470 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300471 if (tda8290_probe(&t->i2c) == 0) {
472 tuner_dbg("chip at addr %x is a tda8290\n", addr);
473 } else {
474 /* Default is being tda9887 */
475 t->type = TUNER_TDA9887;
476 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
477 t->mode = T_STANDBY;
478 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800479 }
480 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800481 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700482 if (tea5767_autodetection(&t->i2c) != EINVAL) {
483 t->type = TUNER_TEA5767;
484 t->mode_mask = T_RADIO;
485 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200486 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700487 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700488
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800489 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700490 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800491 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700492 }
493 }
494
495 /* Initializes only the first adapter found */
496 if (default_mode_mask != T_UNINITIALIZED) {
497 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
498 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200499 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
500 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700501 default_mode_mask = T_UNINITIALIZED;
502 }
503
504 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800505register_client:
506 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700507 i2c_attach_client (&t->i2c);
508 set_type (&t->i2c,t->type, t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 return 0;
510}
511
512static int tuner_probe(struct i2c_adapter *adap)
513{
514 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700515 normal_i2c[0] = addr;
516 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700519 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 if (adap->class & I2C_CLASS_TV_ANALOG)
522 return i2c_probe(adap, &addr_data, tuner_attach);
523 return 0;
524}
525
526static int tuner_detach(struct i2c_client *client)
527{
528 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700529 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700531 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700532 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700533 tuner_warn
534 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700535 return err;
536 }
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 kfree(t);
539 return 0;
540}
541
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700542/*
543 * Switch tuner to other mode. If tuner support both tv and radio,
544 * set another frequency to some value (This is needed for some pal
545 * tuners to avoid locking). Otherwise, just put second tuner in
546 * standby mode.
547 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700549static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
550{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800551 if (mode == t->mode)
552 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700553
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800554 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700555
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800556 if (check_mode(t, cmd) == EINVAL) {
557 t->mode = T_STANDBY;
558 if (t->standby)
559 t->standby (client);
560 return EINVAL;
561 }
562 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700563}
564
565#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800566 tuner_dbg("switching to v4l2\n"); \
567 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700568
569static inline int check_v4l2(struct tuner *t)
570{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300571 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
572 TV, v4l1 for radio), until that is fixed this code is disabled.
573 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
574 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700575 return 0;
576}
577
578static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
580 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Hans Verkuilf9195de2006-01-11 19:01:01 -0200582 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200583 v4l_i2c_print_ioctl(&(t->i2c),cmd);
584
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700585 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700587 case TUNER_SET_TYPE_ADDR:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700588 tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
589 ((struct tuner_setup *)arg)->type,
590 ((struct tuner_setup *)arg)->addr,
591 ((struct tuner_setup *)arg)->mode_mask);
592
593 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700594 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200596 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
597 == EINVAL)
598 return 0;
599 if (t->radio_freq)
600 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700602 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200603 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
604 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300605 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200606 if (t->standby)
607 t->standby (client);
608 break;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700609 case VIDIOCSAUDIO:
610 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
611 return 0;
612 if (check_v4l2(t) == EINVAL)
613 return 0;
614
615 /* Should be implemented, since bttv calls it */
616 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700617 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300618 case TDA9887_SET_CONFIG:
619 {
620 int *i = arg;
621
622 t->tda9887_config = *i;
623 set_freq(client, t->tv_freq);
624 break;
625 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 /* --- v4l ioctls --- */
627 /* take care: bttv does userspace copying, we'll get a
628 kernel pointer here... */
629 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700630 {
631 static const v4l2_std_id map[] = {
632 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
633 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
634 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
635 [4 /* bttv */ ] = V4L2_STD_PAL_M,
636 [5 /* bttv */ ] = V4L2_STD_PAL_N,
637 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
638 };
639 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700641 if (check_v4l2(t) == EINVAL)
642 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700643
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700644 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
645 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700647 if (vc->norm < ARRAY_SIZE(map))
648 t->std = map[vc->norm];
649 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200650 if (t->tv_freq)
651 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700652 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700653 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700654 case VIDIOCSFREQ:
655 {
656 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700657
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700658 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
659 return 0;
660 if (check_v4l2(t) == EINVAL)
661 return 0;
662
663 set_freq(client, *v);
664 return 0;
665 }
666 case VIDIOCGTUNER:
667 {
668 struct video_tuner *vt = arg;
669
670 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
671 return 0;
672 if (check_v4l2(t) == EINVAL)
673 return 0;
674
675 if (V4L2_TUNER_RADIO == t->mode) {
676 if (t->has_signal)
677 vt->signal = t->has_signal(client);
678 if (t->is_stereo) {
679 if (t->is_stereo(client))
680 vt->flags |=
681 VIDEO_TUNER_STEREO_ON;
682 else
683 vt->flags &=
684 ~VIDEO_TUNER_STEREO_ON;
685 }
686 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
687
688 vt->rangelow = radio_range[0] * 16000;
689 vt->rangehigh = radio_range[1] * 16000;
690
691 } else {
692 vt->rangelow = tv_range[0] * 16;
693 vt->rangehigh = tv_range[1] * 16;
694 }
695
696 return 0;
697 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700699 {
700 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700702 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
703 return 0;
704 if (check_v4l2(t) == EINVAL)
705 return 0;
706
707 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
708 va->mode = t->is_stereo(client)
709 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
710 return 0;
711 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712
713 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700714 {
715 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700717 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
718 == EINVAL)
719 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700720
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700721 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700723 t->std = *id;
724 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200725 if (t->tv_freq)
726 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700727 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700728 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700729 case VIDIOC_S_FREQUENCY:
730 {
731 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700732
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300733 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
734 == EINVAL)
735 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700736 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200737 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700738
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700739 break;
740 }
741 case VIDIOC_G_FREQUENCY:
742 {
743 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700744
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700745 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
746 return 0;
747 switch_v4l2();
748 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200749 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
750 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700751 break;
752 }
753 case VIDIOC_G_TUNER:
754 {
755 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700756
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700757 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
758 return 0;
759 switch_v4l2();
760
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200761 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300762 if (t->get_afc)
763 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300764 if (t->mode == V4L2_TUNER_ANALOG_TV)
765 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200766 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700767 tuner->rangelow = tv_range[0] * 16;
768 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200769 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700770 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200771
772 /* radio mode */
773 if (t->has_signal)
774 tuner->signal = t->has_signal(client);
775
776 tuner->rxsubchans =
777 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
778 if (t->is_stereo) {
779 tuner->rxsubchans = t->is_stereo(client) ?
780 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
781 }
782
783 tuner->capability |=
784 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
785 tuner->audmode = t->audmode;
786 tuner->rangelow = radio_range[0] * 16000;
787 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700788 break;
789 }
790 case VIDIOC_S_TUNER:
791 {
792 struct v4l2_tuner *tuner = arg;
793
794 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
795 return 0;
796
797 switch_v4l2();
798
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200799 /* do nothing unless we're a radio tuner */
800 if (t->mode != V4L2_TUNER_RADIO)
801 break;
802 t->audmode = tuner->audmode;
803 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700804 break;
805 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200806 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300807 if (t->tuner_status)
808 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200809 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 }
811
812 return 0;
813}
814
Russell King9480e302005-10-28 09:52:56 -0700815static int tuner_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700817 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
818 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 ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 /* FIXME: power down ??? */
822 return 0;
823}
824
Russell King9480e302005-10-28 09:52:56 -0700825static int tuner_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700827 struct i2c_client *c = container_of (dev, struct i2c_client, dev);
828 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700830 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200831 if (V4L2_TUNER_RADIO == t->mode) {
832 if (t->radio_freq)
833 set_freq(c, t->radio_freq);
834 } else {
835 if (t->tv_freq)
836 set_freq(c, t->tv_freq);
837 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 return 0;
839}
840
841/* ----------------------------------------------------------------------- */
842
843static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700844 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700845 .attach_adapter = tuner_probe,
846 .detach_client = tuner_detach,
847 .command = tuner_command,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200849 .name = "tuner",
850 .suspend = tuner_suspend,
851 .resume = tuner_resume,
852 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700854static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200855 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700856 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857};
858
859static int __init tuner_init_module(void)
860{
861 return i2c_add_driver(&driver);
862}
863
864static void __exit tuner_cleanup_module(void)
865{
866 i2c_del_driver(&driver);
867}
868
869module_init(tuner_init_module);
870module_exit(tuner_cleanup_module);
871
872/*
873 * Overrides for Emacs so that we follow Linus's tabbing style.
874 * ---------------------------------------------------------------------------
875 * Local variables:
876 * c-basic-offset: 8
877 * End:
878 */