blob: 406b85cd6064219c029f6f0d3f99dc28a9265790 [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
Michael Krufkyb2083192007-05-29 22:54:06 -0300180 /* discard private data, in case set_type() was previously called */
181 kfree(t->priv);
182 t->priv = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 switch (t->type) {
184 case TUNER_MT2032:
185 microtune_init(c);
186 break;
187 case TUNER_PHILIPS_TDA8290:
188 tda8290_init(c);
189 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700190 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700191 if (tea5767_tuner_init(c) == EINVAL) {
192 t->type = TUNER_ABSENT;
193 t->mode_mask = T_UNINITIALIZED;
194 return;
195 }
196 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700197 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300198#ifdef CONFIG_TUNER_5761
199 case TUNER_TEA5761:
200 if (tea5761_tuner_init(c) == EINVAL) {
201 t->type = TUNER_ABSENT;
202 t->mode_mask = T_UNINITIALIZED;
203 return -ENODEV;
204 }
205 t->mode_mask = T_RADIO;
206 break;
207#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700208 case TUNER_PHILIPS_FMD1216ME_MK3:
209 buffer[0] = 0x0b;
210 buffer[1] = 0xdc;
211 buffer[2] = 0x9c;
212 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700213 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700214 mdelay(1);
215 buffer[2] = 0x86;
216 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700217 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700218 default_tuner_init(c);
219 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800220 case TUNER_PHILIPS_TD1316:
221 buffer[0] = 0x0b;
222 buffer[1] = 0xdc;
223 buffer[2] = 0x86;
224 buffer[3] = 0xa4;
225 i2c_master_send(c,buffer,4);
226 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200227 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300228 case TUNER_TDA9887:
229 tda9887_tuner_init(c);
230 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 default:
232 default_tuner_init(c);
233 break;
234 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700235
236 if (t->mode_mask == T_UNINITIALIZED)
237 t->mode_mask = new_mode_mask;
238
Hans Verkuil27487d42006-01-15 15:04:52 -0200239 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700240 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100241 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700242 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243}
244
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700245/*
246 * This function apply tuner config to tuner specified
247 * by tun_setup structure. I addr is unset, then admin status
248 * and tun addr status is more precise then current status,
249 * it's applied. Otherwise status and type are applied only to
250 * tuner with exactly the same addr.
251*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700252
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700253static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700254{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700255 struct tuner *t = i2c_get_clientdata(c);
256
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300257 tuner_dbg("set addr for type %i\n", t->type);
258
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300259 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
260 (t->mode_mask & tun_setup->mode_mask))) ||
261 (tun_setup->addr == c->addr)) {
262 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300263 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700264 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700265}
266
267static inline int check_mode(struct tuner *t, char *cmd)
268{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700269 if ((1 << t->mode & t->mode_mask) == 0) {
270 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700271 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700272
273 switch (t->mode) {
274 case V4L2_TUNER_RADIO:
275 tuner_dbg("Cmd %s accepted for radio\n", cmd);
276 break;
277 case V4L2_TUNER_ANALOG_TV:
278 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
279 break;
280 case V4L2_TUNER_DIGITAL_TV:
281 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
282 break;
283 }
284 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700285}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700286
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700287/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288static int tuner_fixup_std(struct tuner *t)
289{
290 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300292 case '6':
293 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
294 t->std = V4L2_STD_PAL_60;
295 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 case 'b':
297 case 'B':
298 case 'g':
299 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700300 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 t->std = V4L2_STD_PAL_BG;
302 break;
303 case 'i':
304 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700305 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 t->std = V4L2_STD_PAL_I;
307 break;
308 case 'd':
309 case 'D':
310 case 'k':
311 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700312 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 t->std = V4L2_STD_PAL_DK;
314 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700315 case 'M':
316 case 'm':
317 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
318 t->std = V4L2_STD_PAL_M;
319 break;
320 case 'N':
321 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200322 if (pal[1] == 'c' || pal[1] == 'C') {
323 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
324 t->std = V4L2_STD_PAL_Nc;
325 } else {
326 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
327 t->std = V4L2_STD_PAL_N;
328 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700329 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700330 case '-':
331 /* default parameter, do nothing */
332 break;
333 default:
334 tuner_warn ("pal= argument not recognised\n");
335 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 }
337 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700338 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
339 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200340 case 'b':
341 case 'B':
342 case 'g':
343 case 'G':
344 case 'h':
345 case 'H':
346 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
347 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
348 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700349 case 'd':
350 case 'D':
351 case 'k':
352 case 'K':
353 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
354 t->std = V4L2_STD_SECAM_DK;
355 break;
356 case 'l':
357 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800358 if ((secam[1]=='C')||(secam[1]=='c')) {
359 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
360 t->std = V4L2_STD_SECAM_LC;
361 } else {
362 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
363 t->std = V4L2_STD_SECAM_L;
364 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700365 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700366 case '-':
367 /* default parameter, do nothing */
368 break;
369 default:
370 tuner_warn ("secam= argument not recognised\n");
371 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700372 }
373 }
374
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200375 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
376 switch (ntsc[0]) {
377 case 'm':
378 case 'M':
379 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
380 t->std = V4L2_STD_NTSC_M;
381 break;
382 case 'j':
383 case 'J':
384 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
385 t->std = V4L2_STD_NTSC_M_JP;
386 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200387 case 'k':
388 case 'K':
389 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
390 t->std = V4L2_STD_NTSC_M_KR;
391 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200392 case '-':
393 /* default parameter, do nothing */
394 break;
395 default:
396 tuner_info("ntsc= argument not recognised\n");
397 break;
398 }
399 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 return 0;
401}
402
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200403static void tuner_status(struct i2c_client *client)
404{
405 struct tuner *t = i2c_get_clientdata(client);
406 unsigned long freq, freq_fraction;
407 const char *p;
408
409 switch (t->mode) {
410 case V4L2_TUNER_RADIO: p = "radio"; break;
411 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
412 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
413 default: p = "undefined"; break;
414 }
415 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200416 freq = t->radio_freq / 16000;
417 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200418 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200419 freq = t->tv_freq / 16;
420 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200421 }
422 tuner_info("Tuner mode: %s\n", p);
423 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300424 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200425 if (t->mode != V4L2_TUNER_RADIO)
426 return;
427 if (t->has_signal) {
428 tuner_info("Signal strength: %d\n", t->has_signal(client));
429 }
430 if (t->is_stereo) {
431 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200432 }
433}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200434
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435/* ---------------------------------------------------------------------- */
436
Hans Verkuilba8fc392006-06-25 15:34:39 -0300437/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700438static unsigned default_mode_mask;
439
440/* During client attach, set_type is called by adapter's attach_inform callback.
441 set_type must then be completed by tuner_attach.
442 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
444{
445 struct tuner *t;
446
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700447 client_template.adapter = adap;
448 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
Panagiotis Issaris74081872006-01-11 19:40:56 -0200450 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700451 if (NULL == t)
452 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700453 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700455 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700456 t->audmode = V4L2_TUNER_MODE_STEREO;
457 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300458 t->tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700460 if (show_i2c) {
461 unsigned char buffer[16];
462 int i,rc;
463
464 memset(buffer, 0, sizeof(buffer));
465 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800466 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700467 for (i=0;i<rc;i++)
468 printk("%02x ",buffer[i]);
469 printk("\n");
470 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300471 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
472 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
473 return -ENODEV;
474
Markus Rechberger257c6452006-01-23 17:11:11 -0200475 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700476 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800477 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300478#ifdef CONFIG_TUNER_5761
479 case 0x10:
480 if (tea5761_autodetection(&t->i2c) != EINVAL) {
481 t->type = TUNER_TEA5761;
482 t->mode_mask = T_RADIO;
483 t->mode = T_STANDBY;
484 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
485 default_mode_mask &= ~T_RADIO;
486
487 goto register_client;
488 }
489 break;
490#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800491 case 0x42:
492 case 0x43:
493 case 0x4a:
494 case 0x4b:
495 /* If chip is not tda8290, don't register.
496 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300497 if (tda8290_probe(&t->i2c) == 0) {
498 tuner_dbg("chip at addr %x is a tda8290\n", addr);
499 } else {
500 /* Default is being tda9887 */
501 t->type = TUNER_TDA9887;
502 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
503 t->mode = T_STANDBY;
504 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800505 }
506 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800507 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700508 if (tea5767_autodetection(&t->i2c) != EINVAL) {
509 t->type = TUNER_TEA5767;
510 t->mode_mask = T_RADIO;
511 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200512 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700513 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700514
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800515 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700516 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800517 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700518 }
519 }
520
521 /* Initializes only the first adapter found */
522 if (default_mode_mask != T_UNINITIALIZED) {
523 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
524 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200525 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
526 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700527 default_mode_mask = T_UNINITIALIZED;
528 }
529
530 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800531register_client:
532 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700533 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300534 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 return 0;
536}
537
538static int tuner_probe(struct i2c_adapter *adap)
539{
540 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700541 normal_i2c[0] = addr;
542 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700545 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 if (adap->class & I2C_CLASS_TV_ANALOG)
548 return i2c_probe(adap, &addr_data, tuner_attach);
549 return 0;
550}
551
552static int tuner_detach(struct i2c_client *client)
553{
554 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700555 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700557 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700558 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700559 tuner_warn
560 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700561 return err;
562 }
563
Michael Krufkyb2083192007-05-29 22:54:06 -0300564 kfree(t->priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 kfree(t);
566 return 0;
567}
568
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700569/*
570 * Switch tuner to other mode. If tuner support both tv and radio,
571 * set another frequency to some value (This is needed for some pal
572 * tuners to avoid locking). Otherwise, just put second tuner in
573 * standby mode.
574 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700576static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
577{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800578 if (mode == t->mode)
579 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700580
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800581 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700582
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800583 if (check_mode(t, cmd) == EINVAL) {
584 t->mode = T_STANDBY;
585 if (t->standby)
586 t->standby (client);
587 return EINVAL;
588 }
589 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700590}
591
592#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800593 tuner_dbg("switching to v4l2\n"); \
594 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700595
596static inline int check_v4l2(struct tuner *t)
597{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300598 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
599 TV, v4l1 for radio), until that is fixed this code is disabled.
600 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
601 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700602 return 0;
603}
604
605static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606{
607 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
Hans Verkuilf9195de2006-01-11 19:01:01 -0200609 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200610 v4l_i2c_print_ioctl(&(t->i2c),cmd);
611
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700612 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700614 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300615 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 -0700616 ((struct tuner_setup *)arg)->type,
617 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300618 ((struct tuner_setup *)arg)->mode_mask,
619 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700620
621 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700622 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200624 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
625 == EINVAL)
626 return 0;
627 if (t->radio_freq)
628 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700630 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200631 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
632 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300633 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200634 if (t->standby)
635 t->standby (client);
636 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300637#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700638 case VIDIOCSAUDIO:
639 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
640 return 0;
641 if (check_v4l2(t) == EINVAL)
642 return 0;
643
644 /* Should be implemented, since bttv calls it */
645 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700646 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700648 {
649 static const v4l2_std_id map[] = {
650 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
651 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
652 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
653 [4 /* bttv */ ] = V4L2_STD_PAL_M,
654 [5 /* bttv */ ] = V4L2_STD_PAL_N,
655 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
656 };
657 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700659 if (check_v4l2(t) == EINVAL)
660 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700661
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700662 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
663 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700665 if (vc->norm < ARRAY_SIZE(map))
666 t->std = map[vc->norm];
667 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200668 if (t->tv_freq)
669 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700670 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700671 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700672 case VIDIOCSFREQ:
673 {
674 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700675
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700676 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
677 return 0;
678 if (check_v4l2(t) == EINVAL)
679 return 0;
680
681 set_freq(client, *v);
682 return 0;
683 }
684 case VIDIOCGTUNER:
685 {
686 struct video_tuner *vt = arg;
687
688 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
689 return 0;
690 if (check_v4l2(t) == EINVAL)
691 return 0;
692
693 if (V4L2_TUNER_RADIO == t->mode) {
694 if (t->has_signal)
695 vt->signal = t->has_signal(client);
696 if (t->is_stereo) {
697 if (t->is_stereo(client))
698 vt->flags |=
699 VIDEO_TUNER_STEREO_ON;
700 else
701 vt->flags &=
702 ~VIDEO_TUNER_STEREO_ON;
703 }
704 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
705
706 vt->rangelow = radio_range[0] * 16000;
707 vt->rangehigh = radio_range[1] * 16000;
708
709 } else {
710 vt->rangelow = tv_range[0] * 16;
711 vt->rangehigh = tv_range[1] * 16;
712 }
713
714 return 0;
715 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700717 {
718 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700720 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
721 return 0;
722 if (check_v4l2(t) == EINVAL)
723 return 0;
724
725 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
726 va->mode = t->is_stereo(client)
727 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
728 return 0;
729 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300730#endif
731 case TDA9887_SET_CONFIG:
732 if (t->type == TUNER_TDA9887) {
733 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300735 t->tda9887_config = *i;
736 set_freq(client, t->tv_freq);
737 }
738 break;
739 /* --- v4l ioctls --- */
740 /* take care: bttv does userspace copying, we'll get a
741 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700743 {
744 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700746 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
747 == EINVAL)
748 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700749
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700750 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700752 t->std = *id;
753 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200754 if (t->tv_freq)
755 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700756 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700757 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758 case VIDIOC_S_FREQUENCY:
759 {
760 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700761
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300762 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
763 == EINVAL)
764 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700765 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200766 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700767
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700768 break;
769 }
770 case VIDIOC_G_FREQUENCY:
771 {
772 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700773
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700774 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
775 return 0;
776 switch_v4l2();
777 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200778 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
779 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700780 break;
781 }
782 case VIDIOC_G_TUNER:
783 {
784 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700785
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700786 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
787 return 0;
788 switch_v4l2();
789
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200790 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300791 if (t->get_afc)
792 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300793 if (t->mode == V4L2_TUNER_ANALOG_TV)
794 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200795 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700796 tuner->rangelow = tv_range[0] * 16;
797 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200798 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700799 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200800
801 /* radio mode */
802 if (t->has_signal)
803 tuner->signal = t->has_signal(client);
804
805 tuner->rxsubchans =
806 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
807 if (t->is_stereo) {
808 tuner->rxsubchans = t->is_stereo(client) ?
809 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
810 }
811
812 tuner->capability |=
813 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
814 tuner->audmode = t->audmode;
815 tuner->rangelow = radio_range[0] * 16000;
816 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700817 break;
818 }
819 case VIDIOC_S_TUNER:
820 {
821 struct v4l2_tuner *tuner = arg;
822
823 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
824 return 0;
825
826 switch_v4l2();
827
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200828 /* do nothing unless we're a radio tuner */
829 if (t->mode != V4L2_TUNER_RADIO)
830 break;
831 t->audmode = tuner->audmode;
832 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700833 break;
834 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200835 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300836 if (t->tuner_status)
837 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200838 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 }
840
841 return 0;
842}
843
Jean Delvare21b48a72007-03-12 19:20:15 -0300844static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700846 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700848 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 /* FIXME: power down ??? */
850 return 0;
851}
852
Jean Delvare21b48a72007-03-12 19:20:15 -0300853static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700855 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700857 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200858 if (V4L2_TUNER_RADIO == t->mode) {
859 if (t->radio_freq)
860 set_freq(c, t->radio_freq);
861 } else {
862 if (t->tv_freq)
863 set_freq(c, t->tv_freq);
864 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 return 0;
866}
867
868/* ----------------------------------------------------------------------- */
869
870static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700871 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700872 .attach_adapter = tuner_probe,
873 .detach_client = tuner_detach,
874 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300875 .suspend = tuner_suspend,
876 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200878 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200879 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700881static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200882 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700883 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884};
885
886static int __init tuner_init_module(void)
887{
888 return i2c_add_driver(&driver);
889}
890
891static void __exit tuner_cleanup_module(void)
892{
893 i2c_del_driver(&driver);
894}
895
896module_init(tuner_init_module);
897module_exit(tuner_cleanup_module);
898
899/*
900 * Overrides for Emacs so that we follow Linus's tabbing style.
901 * ---------------------------------------------------------------------------
902 * Local variables:
903 * c-basic-offset: 8
904 * End:
905 */