blob: 0e71a22f1d4f6cff2f265e796fa1ad454fb002ac [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[] = {
Adrian Bunk9ee476a2007-06-05 05:22:00 -030028#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030029 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 */
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300181 if (t->release)
182 t->release(c);
Michael Krufkyb2083192007-05-29 22:54:06 -0300183 kfree(t->priv);
184 t->priv = NULL;
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300185
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 switch (t->type) {
187 case TUNER_MT2032:
188 microtune_init(c);
189 break;
190 case TUNER_PHILIPS_TDA8290:
191 tda8290_init(c);
192 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700193 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700194 if (tea5767_tuner_init(c) == EINVAL) {
195 t->type = TUNER_ABSENT;
196 t->mode_mask = T_UNINITIALIZED;
197 return;
198 }
199 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700200 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300201#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300202 case TUNER_TEA5761:
203 if (tea5761_tuner_init(c) == EINVAL) {
204 t->type = TUNER_ABSENT;
205 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300206 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300207 }
208 t->mode_mask = T_RADIO;
209 break;
210#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700211 case TUNER_PHILIPS_FMD1216ME_MK3:
212 buffer[0] = 0x0b;
213 buffer[1] = 0xdc;
214 buffer[2] = 0x9c;
215 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700216 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700217 mdelay(1);
218 buffer[2] = 0x86;
219 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700220 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700221 default_tuner_init(c);
222 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800223 case TUNER_PHILIPS_TD1316:
224 buffer[0] = 0x0b;
225 buffer[1] = 0xdc;
226 buffer[2] = 0x86;
227 buffer[3] = 0xa4;
228 i2c_master_send(c,buffer,4);
229 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200230 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300231 case TUNER_TDA9887:
232 tda9887_tuner_init(c);
233 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 default:
235 default_tuner_init(c);
236 break;
237 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700238
239 if (t->mode_mask == T_UNINITIALIZED)
240 t->mode_mask = new_mode_mask;
241
Hans Verkuil27487d42006-01-15 15:04:52 -0200242 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700243 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100244 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700245 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246}
247
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700248/*
249 * This function apply tuner config to tuner specified
250 * by tun_setup structure. I addr is unset, then admin status
251 * and tun addr status is more precise then current status,
252 * it's applied. Otherwise status and type are applied only to
253 * tuner with exactly the same addr.
254*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700255
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700256static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700257{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700258 struct tuner *t = i2c_get_clientdata(c);
259
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300260 tuner_dbg("set addr for type %i\n", t->type);
261
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300262 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
263 (t->mode_mask & tun_setup->mode_mask))) ||
264 (tun_setup->addr == c->addr)) {
265 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300266 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700267 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700268}
269
270static inline int check_mode(struct tuner *t, char *cmd)
271{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700272 if ((1 << t->mode & t->mode_mask) == 0) {
273 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700274 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700275
276 switch (t->mode) {
277 case V4L2_TUNER_RADIO:
278 tuner_dbg("Cmd %s accepted for radio\n", cmd);
279 break;
280 case V4L2_TUNER_ANALOG_TV:
281 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
282 break;
283 case V4L2_TUNER_DIGITAL_TV:
284 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
285 break;
286 }
287 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700288}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700289
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700290/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291static int tuner_fixup_std(struct tuner *t)
292{
293 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300295 case '6':
296 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
297 t->std = V4L2_STD_PAL_60;
298 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 case 'b':
300 case 'B':
301 case 'g':
302 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700303 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 t->std = V4L2_STD_PAL_BG;
305 break;
306 case 'i':
307 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700308 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 t->std = V4L2_STD_PAL_I;
310 break;
311 case 'd':
312 case 'D':
313 case 'k':
314 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700315 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 t->std = V4L2_STD_PAL_DK;
317 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700318 case 'M':
319 case 'm':
320 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
321 t->std = V4L2_STD_PAL_M;
322 break;
323 case 'N':
324 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200325 if (pal[1] == 'c' || pal[1] == 'C') {
326 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
327 t->std = V4L2_STD_PAL_Nc;
328 } else {
329 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
330 t->std = V4L2_STD_PAL_N;
331 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700332 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700333 case '-':
334 /* default parameter, do nothing */
335 break;
336 default:
337 tuner_warn ("pal= argument not recognised\n");
338 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 }
340 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700341 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
342 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200343 case 'b':
344 case 'B':
345 case 'g':
346 case 'G':
347 case 'h':
348 case 'H':
349 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
350 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
351 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700352 case 'd':
353 case 'D':
354 case 'k':
355 case 'K':
356 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
357 t->std = V4L2_STD_SECAM_DK;
358 break;
359 case 'l':
360 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800361 if ((secam[1]=='C')||(secam[1]=='c')) {
362 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
363 t->std = V4L2_STD_SECAM_LC;
364 } else {
365 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
366 t->std = V4L2_STD_SECAM_L;
367 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700368 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700369 case '-':
370 /* default parameter, do nothing */
371 break;
372 default:
373 tuner_warn ("secam= argument not recognised\n");
374 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700375 }
376 }
377
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200378 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
379 switch (ntsc[0]) {
380 case 'm':
381 case 'M':
382 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
383 t->std = V4L2_STD_NTSC_M;
384 break;
385 case 'j':
386 case 'J':
387 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
388 t->std = V4L2_STD_NTSC_M_JP;
389 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200390 case 'k':
391 case 'K':
392 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
393 t->std = V4L2_STD_NTSC_M_KR;
394 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200395 case '-':
396 /* default parameter, do nothing */
397 break;
398 default:
399 tuner_info("ntsc= argument not recognised\n");
400 break;
401 }
402 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 return 0;
404}
405
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200406static void tuner_status(struct i2c_client *client)
407{
408 struct tuner *t = i2c_get_clientdata(client);
409 unsigned long freq, freq_fraction;
410 const char *p;
411
412 switch (t->mode) {
413 case V4L2_TUNER_RADIO: p = "radio"; break;
414 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
415 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
416 default: p = "undefined"; break;
417 }
418 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200419 freq = t->radio_freq / 16000;
420 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200421 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200422 freq = t->tv_freq / 16;
423 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200424 }
425 tuner_info("Tuner mode: %s\n", p);
426 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300427 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200428 if (t->mode != V4L2_TUNER_RADIO)
429 return;
430 if (t->has_signal) {
431 tuner_info("Signal strength: %d\n", t->has_signal(client));
432 }
433 if (t->is_stereo) {
434 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200435 }
436}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200437
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438/* ---------------------------------------------------------------------- */
439
Hans Verkuilba8fc392006-06-25 15:34:39 -0300440/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700441static unsigned default_mode_mask;
442
443/* During client attach, set_type is called by adapter's attach_inform callback.
444 set_type must then be completed by tuner_attach.
445 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
447{
448 struct tuner *t;
449
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700450 client_template.adapter = adap;
451 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
Panagiotis Issaris74081872006-01-11 19:40:56 -0200453 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700454 if (NULL == t)
455 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700456 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700458 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700459 t->audmode = V4L2_TUNER_MODE_STEREO;
460 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300461 t->tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700463 if (show_i2c) {
464 unsigned char buffer[16];
465 int i,rc;
466
467 memset(buffer, 0, sizeof(buffer));
468 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800469 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700470 for (i=0;i<rc;i++)
471 printk("%02x ",buffer[i]);
472 printk("\n");
473 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300474 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
475 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
476 return -ENODEV;
477
Markus Rechberger257c6452006-01-23 17:11:11 -0200478 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700479 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800480 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300481#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300482 case 0x10:
483 if (tea5761_autodetection(&t->i2c) != EINVAL) {
484 t->type = TUNER_TEA5761;
485 t->mode_mask = T_RADIO;
486 t->mode = T_STANDBY;
487 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
488 default_mode_mask &= ~T_RADIO;
489
490 goto register_client;
491 }
492 break;
493#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800494 case 0x42:
495 case 0x43:
496 case 0x4a:
497 case 0x4b:
498 /* If chip is not tda8290, don't register.
499 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300500 if (tda8290_probe(&t->i2c) == 0) {
501 tuner_dbg("chip at addr %x is a tda8290\n", addr);
502 } else {
503 /* Default is being tda9887 */
504 t->type = TUNER_TDA9887;
505 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
506 t->mode = T_STANDBY;
507 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800508 }
509 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800510 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700511 if (tea5767_autodetection(&t->i2c) != EINVAL) {
512 t->type = TUNER_TEA5767;
513 t->mode_mask = T_RADIO;
514 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200515 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700516 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700517
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800518 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700519 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800520 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700521 }
522 }
523
524 /* Initializes only the first adapter found */
525 if (default_mode_mask != T_UNINITIALIZED) {
526 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
527 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200528 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
529 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700530 default_mode_mask = T_UNINITIALIZED;
531 }
532
533 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800534register_client:
535 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700536 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300537 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 return 0;
539}
540
541static int tuner_probe(struct i2c_adapter *adap)
542{
543 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700544 normal_i2c[0] = addr;
545 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700548 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700549
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 if (adap->class & I2C_CLASS_TV_ANALOG)
551 return i2c_probe(adap, &addr_data, tuner_attach);
552 return 0;
553}
554
555static int tuner_detach(struct i2c_client *client)
556{
557 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700558 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700560 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700561 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700562 tuner_warn
563 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700564 return err;
565 }
566
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300567 if (t->release)
568 t->release(client);
Michael Krufkyb2083192007-05-29 22:54:06 -0300569 kfree(t->priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 kfree(t);
571 return 0;
572}
573
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700574/*
575 * Switch tuner to other mode. If tuner support both tv and radio,
576 * set another frequency to some value (This is needed for some pal
577 * tuners to avoid locking). Otherwise, just put second tuner in
578 * standby mode.
579 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700581static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
582{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800583 if (mode == t->mode)
584 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700585
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800586 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700587
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800588 if (check_mode(t, cmd) == EINVAL) {
589 t->mode = T_STANDBY;
590 if (t->standby)
591 t->standby (client);
592 return EINVAL;
593 }
594 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700595}
596
597#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800598 tuner_dbg("switching to v4l2\n"); \
599 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700600
601static inline int check_v4l2(struct tuner *t)
602{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300603 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
604 TV, v4l1 for radio), until that is fixed this code is disabled.
605 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
606 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700607 return 0;
608}
609
610static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611{
612 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Hans Verkuilf9195de2006-01-11 19:01:01 -0200614 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200615 v4l_i2c_print_ioctl(&(t->i2c),cmd);
616
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700617 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700619 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300620 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 -0700621 ((struct tuner_setup *)arg)->type,
622 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300623 ((struct tuner_setup *)arg)->mode_mask,
624 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700625
626 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700627 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200629 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
630 == EINVAL)
631 return 0;
632 if (t->radio_freq)
633 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700635 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200636 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
637 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300638 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200639 if (t->standby)
640 t->standby (client);
641 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300642#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700643 case VIDIOCSAUDIO:
644 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
645 return 0;
646 if (check_v4l2(t) == EINVAL)
647 return 0;
648
649 /* Should be implemented, since bttv calls it */
650 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700651 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700653 {
654 static const v4l2_std_id map[] = {
655 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
656 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
657 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
658 [4 /* bttv */ ] = V4L2_STD_PAL_M,
659 [5 /* bttv */ ] = V4L2_STD_PAL_N,
660 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
661 };
662 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700664 if (check_v4l2(t) == EINVAL)
665 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700666
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700667 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
668 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700670 if (vc->norm < ARRAY_SIZE(map))
671 t->std = map[vc->norm];
672 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200673 if (t->tv_freq)
674 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700675 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700676 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700677 case VIDIOCSFREQ:
678 {
679 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700680
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700681 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
682 return 0;
683 if (check_v4l2(t) == EINVAL)
684 return 0;
685
686 set_freq(client, *v);
687 return 0;
688 }
689 case VIDIOCGTUNER:
690 {
691 struct video_tuner *vt = arg;
692
693 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
694 return 0;
695 if (check_v4l2(t) == EINVAL)
696 return 0;
697
698 if (V4L2_TUNER_RADIO == t->mode) {
699 if (t->has_signal)
700 vt->signal = t->has_signal(client);
701 if (t->is_stereo) {
702 if (t->is_stereo(client))
703 vt->flags |=
704 VIDEO_TUNER_STEREO_ON;
705 else
706 vt->flags &=
707 ~VIDEO_TUNER_STEREO_ON;
708 }
709 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
710
711 vt->rangelow = radio_range[0] * 16000;
712 vt->rangehigh = radio_range[1] * 16000;
713
714 } else {
715 vt->rangelow = tv_range[0] * 16;
716 vt->rangehigh = tv_range[1] * 16;
717 }
718
719 return 0;
720 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700722 {
723 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700725 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
726 return 0;
727 if (check_v4l2(t) == EINVAL)
728 return 0;
729
730 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
731 va->mode = t->is_stereo(client)
732 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
733 return 0;
734 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300735#endif
736 case TDA9887_SET_CONFIG:
737 if (t->type == TUNER_TDA9887) {
738 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300740 t->tda9887_config = *i;
741 set_freq(client, t->tv_freq);
742 }
743 break;
744 /* --- v4l ioctls --- */
745 /* take care: bttv does userspace copying, we'll get a
746 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700748 {
749 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700751 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
752 == EINVAL)
753 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700754
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700755 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700757 t->std = *id;
758 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200759 if (t->tv_freq)
760 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700761 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700762 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700763 case VIDIOC_S_FREQUENCY:
764 {
765 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700766
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300767 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
768 == EINVAL)
769 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700770 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200771 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700772
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700773 break;
774 }
775 case VIDIOC_G_FREQUENCY:
776 {
777 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700778
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700779 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
780 return 0;
781 switch_v4l2();
782 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200783 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
784 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700785 break;
786 }
787 case VIDIOC_G_TUNER:
788 {
789 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700790
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700791 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
792 return 0;
793 switch_v4l2();
794
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200795 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300796 if (t->get_afc)
797 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300798 if (t->mode == V4L2_TUNER_ANALOG_TV)
799 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200800 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700801 tuner->rangelow = tv_range[0] * 16;
802 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200803 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700804 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200805
806 /* radio mode */
807 if (t->has_signal)
808 tuner->signal = t->has_signal(client);
809
810 tuner->rxsubchans =
811 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
812 if (t->is_stereo) {
813 tuner->rxsubchans = t->is_stereo(client) ?
814 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
815 }
816
817 tuner->capability |=
818 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
819 tuner->audmode = t->audmode;
820 tuner->rangelow = radio_range[0] * 16000;
821 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700822 break;
823 }
824 case VIDIOC_S_TUNER:
825 {
826 struct v4l2_tuner *tuner = arg;
827
828 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
829 return 0;
830
831 switch_v4l2();
832
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200833 /* do nothing unless we're a radio tuner */
834 if (t->mode != V4L2_TUNER_RADIO)
835 break;
836 t->audmode = tuner->audmode;
837 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700838 break;
839 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200840 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300841 if (t->tuner_status)
842 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200843 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 }
845
846 return 0;
847}
848
Jean Delvare21b48a72007-03-12 19:20:15 -0300849static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700851 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700853 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 /* FIXME: power down ??? */
855 return 0;
856}
857
Jean Delvare21b48a72007-03-12 19:20:15 -0300858static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700860 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700862 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200863 if (V4L2_TUNER_RADIO == t->mode) {
864 if (t->radio_freq)
865 set_freq(c, t->radio_freq);
866 } else {
867 if (t->tv_freq)
868 set_freq(c, t->tv_freq);
869 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 return 0;
871}
872
873/* ----------------------------------------------------------------------- */
874
875static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700876 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700877 .attach_adapter = tuner_probe,
878 .detach_client = tuner_detach,
879 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300880 .suspend = tuner_suspend,
881 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200883 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200884 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700886static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200887 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700888 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889};
890
891static int __init tuner_init_module(void)
892{
893 return i2c_add_driver(&driver);
894}
895
896static void __exit tuner_cleanup_module(void)
897{
898 i2c_del_driver(&driver);
899}
900
901module_init(tuner_init_module);
902module_exit(tuner_cleanup_module);
903
904/*
905 * Overrides for Emacs so that we follow Linus's tabbing style.
906 * ---------------------------------------------------------------------------
907 * Local variables:
908 * c-basic-offset: 8
909 * End:
910 */