blob: 591ca9ce04401fc8a62fd6d1288fe0aa9c619bc1 [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[] = {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030028#ifdef CONFIG_TUNER_5761
29 0x10,
30#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080031 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070032 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
33 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 I2C_CLIENT_END
35};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070036
Linus Torvalds1da177e2005-04-16 15:20:36 -070037I2C_CLIENT_INSMOD;
38
39/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070040static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070041static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070042static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070043
Linus Torvalds1da177e2005-04-16 15:20:36 -070044/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020045int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070047static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070048static unsigned int radio_range[2] = { 65, 108 };
49
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020050static char pal[] = "--";
51static char secam[] = "--";
52static char ntsc[] = "-";
53
Hans Verkuilf9195de2006-01-11 19:01:01 -020054
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020055module_param(addr, int, 0444);
56module_param(no_autodetect, int, 0444);
57module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020058module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020059module_param_string(pal, pal, sizeof(pal), 0644);
60module_param_string(secam, secam, sizeof(secam), 0644);
61module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070062module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063module_param_array(radio_range, int, NULL, 0644);
64
65MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
66MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
67MODULE_LICENSE("GPL");
68
Linus Torvalds1da177e2005-04-16 15:20:36 -070069static struct i2c_driver driver;
70static struct i2c_client client_template;
71
72/* ---------------------------------------------------------------------- */
73
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070074/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static void set_tv_freq(struct i2c_client *c, unsigned int freq)
76{
77 struct tuner *t = i2c_get_clientdata(c);
78
79 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070080 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 return;
82 }
Hans Verkuil27487d42006-01-15 15:04:52 -020083 if (NULL == t->set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070084 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 return;
86 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070087 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
88 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
89 freq / 16, freq % 16 * 100 / 16, tv_range[0],
90 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -020091 /* V4L2 spec: if the freq is not possible then the closest
92 possible value should be selected */
93 if (freq < tv_range[0] * 16)
94 freq = tv_range[0] * 16;
95 else
96 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 }
Hans Verkuil27487d42006-01-15 15:04:52 -020098 t->set_tv_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099}
100
101static void set_radio_freq(struct i2c_client *c, unsigned int freq)
102{
103 struct tuner *t = i2c_get_clientdata(c);
104
105 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700106 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 return;
108 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200109 if (NULL == t->set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700110 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 return;
112 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200113 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700114 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
115 freq / 16000, freq % 16000 * 100 / 16000,
116 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200117 /* V4L2 spec: if the freq is not possible then the closest
118 possible value should be selected */
119 if (freq < radio_range[0] * 16000)
120 freq = radio_range[0] * 16000;
121 else
122 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700124
Hans Verkuil27487d42006-01-15 15:04:52 -0200125 t->set_radio_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126}
127
128static void set_freq(struct i2c_client *c, unsigned long freq)
129{
130 struct tuner *t = i2c_get_clientdata(c);
131
132 switch (t->mode) {
133 case V4L2_TUNER_RADIO:
134 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700135 freq / 16000, freq % 16000 * 100 / 16000);
136 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200137 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 break;
139 case V4L2_TUNER_ANALOG_TV:
140 case V4L2_TUNER_DIGITAL_TV:
141 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700142 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200144 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 break;
146 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147}
148
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700149static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300150 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300151 int (*tuner_callback) (void *dev, int command,int arg))
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300167 t->config = new_config;
168 if (tuner_callback != NULL) {
169 tuner_dbg("defining GPIO callback\n");
170 t->tuner_callback = tuner_callback;
171 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300172
173 /* This code detects calls by card attach_inform */
174 if (NULL == t->i2c.dev.driver) {
175 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
176
177 return;
178 }
179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 switch (t->type) {
181 case TUNER_MT2032:
182 microtune_init(c);
183 break;
184 case TUNER_PHILIPS_TDA8290:
185 tda8290_init(c);
186 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700187 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700188 if (tea5767_tuner_init(c) == EINVAL) {
189 t->type = TUNER_ABSENT;
190 t->mode_mask = T_UNINITIALIZED;
191 return;
192 }
193 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700194 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300195#ifdef CONFIG_TUNER_5761
196 case TUNER_TEA5761:
197 if (tea5761_tuner_init(c) == EINVAL) {
198 t->type = TUNER_ABSENT;
199 t->mode_mask = T_UNINITIALIZED;
200 return -ENODEV;
201 }
202 t->mode_mask = T_RADIO;
203 break;
204#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700205 case TUNER_PHILIPS_FMD1216ME_MK3:
206 buffer[0] = 0x0b;
207 buffer[1] = 0xdc;
208 buffer[2] = 0x9c;
209 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700210 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700211 mdelay(1);
212 buffer[2] = 0x86;
213 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700214 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700215 default_tuner_init(c);
216 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800217 case TUNER_PHILIPS_TD1316:
218 buffer[0] = 0x0b;
219 buffer[1] = 0xdc;
220 buffer[2] = 0x86;
221 buffer[3] = 0xa4;
222 i2c_master_send(c,buffer,4);
223 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200224 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300225 case TUNER_TDA9887:
226 tda9887_tuner_init(c);
227 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 default:
229 default_tuner_init(c);
230 break;
231 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700232
233 if (t->mode_mask == T_UNINITIALIZED)
234 t->mode_mask = new_mode_mask;
235
Hans Verkuil27487d42006-01-15 15:04:52 -0200236 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700237 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100238 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700239 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240}
241
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700242/*
243 * This function apply tuner config to tuner specified
244 * by tun_setup structure. I addr is unset, then admin status
245 * and tun addr status is more precise then current status,
246 * it's applied. Otherwise status and type are applied only to
247 * tuner with exactly the same addr.
248*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700249
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700250static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700251{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700252 struct tuner *t = i2c_get_clientdata(c);
253
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300254 tuner_dbg("set addr for type %i\n", t->type);
255
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300256 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
257 (t->mode_mask & tun_setup->mode_mask))) ||
258 (tun_setup->addr == c->addr)) {
259 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300260 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700261 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700262}
263
264static inline int check_mode(struct tuner *t, char *cmd)
265{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700266 if ((1 << t->mode & t->mode_mask) == 0) {
267 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700268 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700269
270 switch (t->mode) {
271 case V4L2_TUNER_RADIO:
272 tuner_dbg("Cmd %s accepted for radio\n", cmd);
273 break;
274 case V4L2_TUNER_ANALOG_TV:
275 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
276 break;
277 case V4L2_TUNER_DIGITAL_TV:
278 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
279 break;
280 }
281 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700282}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700283
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700284/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285static int tuner_fixup_std(struct tuner *t)
286{
287 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300289 case '6':
290 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
291 t->std = V4L2_STD_PAL_60;
292 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 case 'b':
294 case 'B':
295 case 'g':
296 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700297 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 t->std = V4L2_STD_PAL_BG;
299 break;
300 case 'i':
301 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700302 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 t->std = V4L2_STD_PAL_I;
304 break;
305 case 'd':
306 case 'D':
307 case 'k':
308 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700309 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 t->std = V4L2_STD_PAL_DK;
311 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700312 case 'M':
313 case 'm':
314 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
315 t->std = V4L2_STD_PAL_M;
316 break;
317 case 'N':
318 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200319 if (pal[1] == 'c' || pal[1] == 'C') {
320 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
321 t->std = V4L2_STD_PAL_Nc;
322 } else {
323 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
324 t->std = V4L2_STD_PAL_N;
325 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700326 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700327 case '-':
328 /* default parameter, do nothing */
329 break;
330 default:
331 tuner_warn ("pal= argument not recognised\n");
332 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
334 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700335 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
336 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200337 case 'b':
338 case 'B':
339 case 'g':
340 case 'G':
341 case 'h':
342 case 'H':
343 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
344 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
345 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700346 case 'd':
347 case 'D':
348 case 'k':
349 case 'K':
350 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
351 t->std = V4L2_STD_SECAM_DK;
352 break;
353 case 'l':
354 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800355 if ((secam[1]=='C')||(secam[1]=='c')) {
356 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
357 t->std = V4L2_STD_SECAM_LC;
358 } else {
359 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
360 t->std = V4L2_STD_SECAM_L;
361 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700362 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700363 case '-':
364 /* default parameter, do nothing */
365 break;
366 default:
367 tuner_warn ("secam= argument not recognised\n");
368 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700369 }
370 }
371
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200372 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
373 switch (ntsc[0]) {
374 case 'm':
375 case 'M':
376 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
377 t->std = V4L2_STD_NTSC_M;
378 break;
379 case 'j':
380 case 'J':
381 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
382 t->std = V4L2_STD_NTSC_M_JP;
383 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200384 case 'k':
385 case 'K':
386 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
387 t->std = V4L2_STD_NTSC_M_KR;
388 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200389 case '-':
390 /* default parameter, do nothing */
391 break;
392 default:
393 tuner_info("ntsc= argument not recognised\n");
394 break;
395 }
396 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 return 0;
398}
399
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200400static void tuner_status(struct i2c_client *client)
401{
402 struct tuner *t = i2c_get_clientdata(client);
403 unsigned long freq, freq_fraction;
404 const char *p;
405
406 switch (t->mode) {
407 case V4L2_TUNER_RADIO: p = "radio"; break;
408 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
409 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
410 default: p = "undefined"; break;
411 }
412 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200413 freq = t->radio_freq / 16000;
414 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200415 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200416 freq = t->tv_freq / 16;
417 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200418 }
419 tuner_info("Tuner mode: %s\n", p);
420 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300421 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200422 if (t->mode != V4L2_TUNER_RADIO)
423 return;
424 if (t->has_signal) {
425 tuner_info("Signal strength: %d\n", t->has_signal(client));
426 }
427 if (t->is_stereo) {
428 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200429 }
430}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200431
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432/* ---------------------------------------------------------------------- */
433
Hans Verkuilba8fc392006-06-25 15:34:39 -0300434/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700435static unsigned default_mode_mask;
436
437/* During client attach, set_type is called by adapter's attach_inform callback.
438 set_type must then be completed by tuner_attach.
439 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
441{
442 struct tuner *t;
443
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700444 client_template.adapter = adap;
445 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
Panagiotis Issaris74081872006-01-11 19:40:56 -0200447 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700448 if (NULL == t)
449 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700450 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700452 t->type = UNSET;
453 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
454 t->audmode = V4L2_TUNER_MODE_STEREO;
455 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300456 t->tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700458 if (show_i2c) {
459 unsigned char buffer[16];
460 int i,rc;
461
462 memset(buffer, 0, sizeof(buffer));
463 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800464 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700465 for (i=0;i<rc;i++)
466 printk("%02x ",buffer[i]);
467 printk("\n");
468 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300469 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
470 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
471 return -ENODEV;
472
Markus Rechberger257c6452006-01-23 17:11:11 -0200473 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700474 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800475 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300476#ifdef CONFIG_TUNER_5761
477 case 0x10:
478 if (tea5761_autodetection(&t->i2c) != EINVAL) {
479 t->type = TUNER_TEA5761;
480 t->mode_mask = T_RADIO;
481 t->mode = T_STANDBY;
482 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
483 default_mode_mask &= ~T_RADIO;
484
485 goto register_client;
486 }
487 break;
488#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800489 case 0x42:
490 case 0x43:
491 case 0x4a:
492 case 0x4b:
493 /* If chip is not tda8290, don't register.
494 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300495 if (tda8290_probe(&t->i2c) == 0) {
496 tuner_dbg("chip at addr %x is a tda8290\n", addr);
497 } else {
498 /* Default is being tda9887 */
499 t->type = TUNER_TDA9887;
500 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
501 t->mode = T_STANDBY;
502 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800503 }
504 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800505 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700506 if (tea5767_autodetection(&t->i2c) != EINVAL) {
507 t->type = TUNER_TEA5767;
508 t->mode_mask = T_RADIO;
509 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200510 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700511 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700512
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800513 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700514 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800515 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700516 }
517 }
518
519 /* Initializes only the first adapter found */
520 if (default_mode_mask != T_UNINITIALIZED) {
521 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
522 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200523 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
524 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700525 default_mode_mask = T_UNINITIALIZED;
526 }
527
528 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800529register_client:
530 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700531 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300532 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 return 0;
534}
535
536static int tuner_probe(struct i2c_adapter *adap)
537{
538 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700539 normal_i2c[0] = addr;
540 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700543 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700544
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 if (adap->class & I2C_CLASS_TV_ANALOG)
546 return i2c_probe(adap, &addr_data, tuner_attach);
547 return 0;
548}
549
550static int tuner_detach(struct i2c_client *client)
551{
552 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700553 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700555 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700556 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700557 tuner_warn
558 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700559 return err;
560 }
561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 kfree(t);
563 return 0;
564}
565
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700566/*
567 * Switch tuner to other mode. If tuner support both tv and radio,
568 * set another frequency to some value (This is needed for some pal
569 * tuners to avoid locking). Otherwise, just put second tuner in
570 * standby mode.
571 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700573static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
574{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800575 if (mode == t->mode)
576 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700577
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800578 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700579
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800580 if (check_mode(t, cmd) == EINVAL) {
581 t->mode = T_STANDBY;
582 if (t->standby)
583 t->standby (client);
584 return EINVAL;
585 }
586 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700587}
588
589#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800590 tuner_dbg("switching to v4l2\n"); \
591 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700592
593static inline int check_v4l2(struct tuner *t)
594{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300595 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
596 TV, v4l1 for radio), until that is fixed this code is disabled.
597 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
598 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700599 return 0;
600}
601
602static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603{
604 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
Hans Verkuilf9195de2006-01-11 19:01:01 -0200606 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200607 v4l_i2c_print_ioctl(&(t->i2c),cmd);
608
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700609 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700611 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300612 tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700613 ((struct tuner_setup *)arg)->type,
614 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300615 ((struct tuner_setup *)arg)->mode_mask,
616 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700617
618 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700619 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200621 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
622 == EINVAL)
623 return 0;
624 if (t->radio_freq)
625 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700627 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200628 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
629 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300630 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200631 if (t->standby)
632 t->standby (client);
633 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300634#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700635 case VIDIOCSAUDIO:
636 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
637 return 0;
638 if (check_v4l2(t) == EINVAL)
639 return 0;
640
641 /* Should be implemented, since bttv calls it */
642 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700643 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700645 {
646 static const v4l2_std_id map[] = {
647 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
648 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
649 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
650 [4 /* bttv */ ] = V4L2_STD_PAL_M,
651 [5 /* bttv */ ] = V4L2_STD_PAL_N,
652 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
653 };
654 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700656 if (check_v4l2(t) == EINVAL)
657 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700658
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700659 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
660 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700662 if (vc->norm < ARRAY_SIZE(map))
663 t->std = map[vc->norm];
664 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200665 if (t->tv_freq)
666 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700667 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700668 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700669 case VIDIOCSFREQ:
670 {
671 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700672
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700673 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
674 return 0;
675 if (check_v4l2(t) == EINVAL)
676 return 0;
677
678 set_freq(client, *v);
679 return 0;
680 }
681 case VIDIOCGTUNER:
682 {
683 struct video_tuner *vt = arg;
684
685 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
686 return 0;
687 if (check_v4l2(t) == EINVAL)
688 return 0;
689
690 if (V4L2_TUNER_RADIO == t->mode) {
691 if (t->has_signal)
692 vt->signal = t->has_signal(client);
693 if (t->is_stereo) {
694 if (t->is_stereo(client))
695 vt->flags |=
696 VIDEO_TUNER_STEREO_ON;
697 else
698 vt->flags &=
699 ~VIDEO_TUNER_STEREO_ON;
700 }
701 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
702
703 vt->rangelow = radio_range[0] * 16000;
704 vt->rangehigh = radio_range[1] * 16000;
705
706 } else {
707 vt->rangelow = tv_range[0] * 16;
708 vt->rangehigh = tv_range[1] * 16;
709 }
710
711 return 0;
712 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700714 {
715 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700717 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
718 return 0;
719 if (check_v4l2(t) == EINVAL)
720 return 0;
721
722 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
723 va->mode = t->is_stereo(client)
724 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
725 return 0;
726 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300727#endif
728 case TDA9887_SET_CONFIG:
729 if (t->type == TUNER_TDA9887) {
730 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300732 t->tda9887_config = *i;
733 set_freq(client, t->tv_freq);
734 }
735 break;
736 /* --- v4l ioctls --- */
737 /* take care: bttv does userspace copying, we'll get a
738 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700740 {
741 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700743 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
744 == EINVAL)
745 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700746
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700747 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700749 t->std = *id;
750 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200751 if (t->tv_freq)
752 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700753 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700754 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700755 case VIDIOC_S_FREQUENCY:
756 {
757 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700758
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300759 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
760 == EINVAL)
761 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700762 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200763 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700764
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700765 break;
766 }
767 case VIDIOC_G_FREQUENCY:
768 {
769 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700770
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700771 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
772 return 0;
773 switch_v4l2();
774 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200775 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
776 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700777 break;
778 }
779 case VIDIOC_G_TUNER:
780 {
781 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700782
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700783 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
784 return 0;
785 switch_v4l2();
786
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200787 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300788 if (t->get_afc)
789 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300790 if (t->mode == V4L2_TUNER_ANALOG_TV)
791 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200792 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700793 tuner->rangelow = tv_range[0] * 16;
794 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200795 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700796 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200797
798 /* radio mode */
799 if (t->has_signal)
800 tuner->signal = t->has_signal(client);
801
802 tuner->rxsubchans =
803 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
804 if (t->is_stereo) {
805 tuner->rxsubchans = t->is_stereo(client) ?
806 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
807 }
808
809 tuner->capability |=
810 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
811 tuner->audmode = t->audmode;
812 tuner->rangelow = radio_range[0] * 16000;
813 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700814 break;
815 }
816 case VIDIOC_S_TUNER:
817 {
818 struct v4l2_tuner *tuner = arg;
819
820 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
821 return 0;
822
823 switch_v4l2();
824
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200825 /* do nothing unless we're a radio tuner */
826 if (t->mode != V4L2_TUNER_RADIO)
827 break;
828 t->audmode = tuner->audmode;
829 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700830 break;
831 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200832 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300833 if (t->tuner_status)
834 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200835 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 }
837
838 return 0;
839}
840
Jean Delvare21b48a72007-03-12 19:20:15 -0300841static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700843 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700845 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 /* FIXME: power down ??? */
847 return 0;
848}
849
Jean Delvare21b48a72007-03-12 19:20:15 -0300850static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700852 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700854 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200855 if (V4L2_TUNER_RADIO == t->mode) {
856 if (t->radio_freq)
857 set_freq(c, t->radio_freq);
858 } else {
859 if (t->tv_freq)
860 set_freq(c, t->tv_freq);
861 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 return 0;
863}
864
865/* ----------------------------------------------------------------------- */
866
867static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700868 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700869 .attach_adapter = tuner_probe,
870 .detach_client = tuner_detach,
871 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300872 .suspend = tuner_suspend,
873 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200875 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200876 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700878static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200879 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700880 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881};
882
883static int __init tuner_init_module(void)
884{
885 return i2c_add_driver(&driver);
886}
887
888static void __exit tuner_cleanup_module(void)
889{
890 i2c_del_driver(&driver);
891}
892
893module_init(tuner_init_module);
894module_exit(tuner_cleanup_module);
895
896/*
897 * Overrides for Emacs so that we follow Linus's tabbing style.
898 * ---------------------------------------------------------------------------
899 * Local variables:
900 * c-basic-offset: 8
901 * End:
902 */