blob: 505591a7abe970dbaa694c9c714e32a660159ca9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * i2c tv tuner chip device driver
4 * core core, i.e. kernel interfaces, registering and so on
5 */
6
7#include <linux/module.h>
8#include <linux/moduleparam.h>
9#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <linux/string.h>
11#include <linux/timer.h>
12#include <linux/delay.h>
13#include <linux/errno.h>
14#include <linux/slab.h>
15#include <linux/poll.h>
16#include <linux/i2c.h>
17#include <linux/types.h>
18#include <linux/videodev.h>
19#include <linux/init.h>
20
21#include <media/tuner.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020022#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
24#define UNSET (-1U)
25
26/* standard i2c insmod options */
27static unsigned short normal_i2c[] = {
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080028 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070029 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
30 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070031 I2C_CLIENT_END
32};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070033
Linus Torvalds1da177e2005-04-16 15:20:36 -070034I2C_CLIENT_INSMOD;
35
36/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070037static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070038static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070039static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070040
Linus Torvalds1da177e2005-04-16 15:20:36 -070041/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020042int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070044static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070045static unsigned int radio_range[2] = { 65, 108 };
46
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020047static char pal[] = "--";
48static char secam[] = "--";
49static char ntsc[] = "-";
50
Hans Verkuilf9195de2006-01-11 19:01:01 -020051
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020052module_param(addr, int, 0444);
53module_param(no_autodetect, int, 0444);
54module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020055module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020056module_param_string(pal, pal, sizeof(pal), 0644);
57module_param_string(secam, secam, sizeof(secam), 0644);
58module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070059module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070060module_param_array(radio_range, int, NULL, 0644);
61
62MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
63MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
64MODULE_LICENSE("GPL");
65
Linus Torvalds1da177e2005-04-16 15:20:36 -070066static struct i2c_driver driver;
67static struct i2c_client client_template;
68
69/* ---------------------------------------------------------------------- */
70
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070071/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -070072static void set_tv_freq(struct i2c_client *c, unsigned int freq)
73{
74 struct tuner *t = i2c_get_clientdata(c);
75
76 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070077 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 return;
79 }
Hans Verkuil27487d42006-01-15 15:04:52 -020080 if (NULL == t->set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070081 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 return;
83 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070084 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
85 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
86 freq / 16, freq % 16 * 100 / 16, tv_range[0],
87 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -020088 /* V4L2 spec: if the freq is not possible then the closest
89 possible value should be selected */
90 if (freq < tv_range[0] * 16)
91 freq = tv_range[0] * 16;
92 else
93 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 }
Hans Verkuil27487d42006-01-15 15:04:52 -020095 t->set_tv_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97
98static void set_radio_freq(struct i2c_client *c, unsigned int freq)
99{
100 struct tuner *t = i2c_get_clientdata(c);
101
102 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700103 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 return;
105 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200106 if (NULL == t->set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700107 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 return;
109 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200110 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700111 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
112 freq / 16000, freq % 16000 * 100 / 16000,
113 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200114 /* V4L2 spec: if the freq is not possible then the closest
115 possible value should be selected */
116 if (freq < radio_range[0] * 16000)
117 freq = radio_range[0] * 16000;
118 else
119 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700121
Hans Verkuil27487d42006-01-15 15:04:52 -0200122 t->set_radio_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123}
124
125static void set_freq(struct i2c_client *c, unsigned long freq)
126{
127 struct tuner *t = i2c_get_clientdata(c);
128
129 switch (t->mode) {
130 case V4L2_TUNER_RADIO:
131 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700132 freq / 16000, freq % 16000 * 100 / 16000);
133 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200134 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 break;
136 case V4L2_TUNER_ANALOG_TV:
137 case V4L2_TUNER_DIGITAL_TV:
138 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700139 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200141 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 break;
143 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144}
145
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700146static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300147 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300148 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
150 struct tuner *t = i2c_get_clientdata(c);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700151 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700153 if (type == UNSET || type == TUNER_ABSENT) {
154 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 return;
156 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700158 if (type >= tuner_count) {
159 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
160 return;
161 }
162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300164 t->config = new_config;
165 if (tuner_callback != NULL) {
166 tuner_dbg("defining GPIO callback\n");
167 t->tuner_callback = tuner_callback;
168 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300169
170 /* This code detects calls by card attach_inform */
171 if (NULL == t->i2c.dev.driver) {
172 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
173
174 return;
175 }
176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 switch (t->type) {
178 case TUNER_MT2032:
179 microtune_init(c);
180 break;
181 case TUNER_PHILIPS_TDA8290:
182 tda8290_init(c);
183 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700184 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700185 if (tea5767_tuner_init(c) == EINVAL) {
186 t->type = TUNER_ABSENT;
187 t->mode_mask = T_UNINITIALIZED;
188 return;
189 }
190 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700191 break;
192 case TUNER_PHILIPS_FMD1216ME_MK3:
193 buffer[0] = 0x0b;
194 buffer[1] = 0xdc;
195 buffer[2] = 0x9c;
196 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700197 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700198 mdelay(1);
199 buffer[2] = 0x86;
200 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700201 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700202 default_tuner_init(c);
203 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800204 case TUNER_PHILIPS_TD1316:
205 buffer[0] = 0x0b;
206 buffer[1] = 0xdc;
207 buffer[2] = 0x86;
208 buffer[3] = 0xa4;
209 i2c_master_send(c,buffer,4);
210 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200211 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300212 case TUNER_TDA9887:
213 tda9887_tuner_init(c);
214 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 default:
216 default_tuner_init(c);
217 break;
218 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700219
220 if (t->mode_mask == T_UNINITIALIZED)
221 t->mode_mask = new_mode_mask;
222
Hans Verkuil27487d42006-01-15 15:04:52 -0200223 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700224 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100225 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700226 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227}
228
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700229/*
230 * This function apply tuner config to tuner specified
231 * by tun_setup structure. I addr is unset, then admin status
232 * and tun addr status is more precise then current status,
233 * it's applied. Otherwise status and type are applied only to
234 * tuner with exactly the same addr.
235*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700236
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700237static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700238{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700239 struct tuner *t = i2c_get_clientdata(c);
240
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300241 tuner_dbg("set addr for type %i\n", t->type);
242
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300243 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
244 (t->mode_mask & tun_setup->mode_mask))) ||
245 (tun_setup->addr == c->addr)) {
246 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300247 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700248 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700249}
250
251static inline int check_mode(struct tuner *t, char *cmd)
252{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700253 if ((1 << t->mode & t->mode_mask) == 0) {
254 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700255 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700256
257 switch (t->mode) {
258 case V4L2_TUNER_RADIO:
259 tuner_dbg("Cmd %s accepted for radio\n", cmd);
260 break;
261 case V4L2_TUNER_ANALOG_TV:
262 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
263 break;
264 case V4L2_TUNER_DIGITAL_TV:
265 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
266 break;
267 }
268 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700269}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700270
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700271/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272static int tuner_fixup_std(struct tuner *t)
273{
274 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300276 case '6':
277 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
278 t->std = V4L2_STD_PAL_60;
279 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 case 'b':
281 case 'B':
282 case 'g':
283 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700284 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 t->std = V4L2_STD_PAL_BG;
286 break;
287 case 'i':
288 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700289 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 t->std = V4L2_STD_PAL_I;
291 break;
292 case 'd':
293 case 'D':
294 case 'k':
295 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700296 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 t->std = V4L2_STD_PAL_DK;
298 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700299 case 'M':
300 case 'm':
301 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
302 t->std = V4L2_STD_PAL_M;
303 break;
304 case 'N':
305 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200306 if (pal[1] == 'c' || pal[1] == 'C') {
307 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
308 t->std = V4L2_STD_PAL_Nc;
309 } else {
310 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
311 t->std = V4L2_STD_PAL_N;
312 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700313 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700314 case '-':
315 /* default parameter, do nothing */
316 break;
317 default:
318 tuner_warn ("pal= argument not recognised\n");
319 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 }
321 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700322 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
323 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200324 case 'b':
325 case 'B':
326 case 'g':
327 case 'G':
328 case 'h':
329 case 'H':
330 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
331 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
332 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700333 case 'd':
334 case 'D':
335 case 'k':
336 case 'K':
337 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
338 t->std = V4L2_STD_SECAM_DK;
339 break;
340 case 'l':
341 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800342 if ((secam[1]=='C')||(secam[1]=='c')) {
343 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
344 t->std = V4L2_STD_SECAM_LC;
345 } else {
346 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
347 t->std = V4L2_STD_SECAM_L;
348 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700349 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700350 case '-':
351 /* default parameter, do nothing */
352 break;
353 default:
354 tuner_warn ("secam= argument not recognised\n");
355 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700356 }
357 }
358
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200359 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
360 switch (ntsc[0]) {
361 case 'm':
362 case 'M':
363 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
364 t->std = V4L2_STD_NTSC_M;
365 break;
366 case 'j':
367 case 'J':
368 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
369 t->std = V4L2_STD_NTSC_M_JP;
370 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200371 case 'k':
372 case 'K':
373 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
374 t->std = V4L2_STD_NTSC_M_KR;
375 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200376 case '-':
377 /* default parameter, do nothing */
378 break;
379 default:
380 tuner_info("ntsc= argument not recognised\n");
381 break;
382 }
383 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 return 0;
385}
386
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200387static void tuner_status(struct i2c_client *client)
388{
389 struct tuner *t = i2c_get_clientdata(client);
390 unsigned long freq, freq_fraction;
391 const char *p;
392
393 switch (t->mode) {
394 case V4L2_TUNER_RADIO: p = "radio"; break;
395 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
396 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
397 default: p = "undefined"; break;
398 }
399 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200400 freq = t->radio_freq / 16000;
401 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200402 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200403 freq = t->tv_freq / 16;
404 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200405 }
406 tuner_info("Tuner mode: %s\n", p);
407 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300408 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200409 if (t->mode != V4L2_TUNER_RADIO)
410 return;
411 if (t->has_signal) {
412 tuner_info("Signal strength: %d\n", t->has_signal(client));
413 }
414 if (t->is_stereo) {
415 tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200416 }
417}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200418
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419/* ---------------------------------------------------------------------- */
420
Hans Verkuilba8fc392006-06-25 15:34:39 -0300421/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700422static unsigned default_mode_mask;
423
424/* During client attach, set_type is called by adapter's attach_inform callback.
425 set_type must then be completed by tuner_attach.
426 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
428{
429 struct tuner *t;
430
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700431 client_template.adapter = adap;
432 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
Panagiotis Issaris74081872006-01-11 19:40:56 -0200434 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700435 if (NULL == t)
436 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700437 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700439 t->type = UNSET;
440 t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
441 t->audmode = V4L2_TUNER_MODE_STEREO;
442 t->mode_mask = T_UNINITIALIZED;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300443 t->tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700445 if (show_i2c) {
446 unsigned char buffer[16];
447 int i,rc;
448
449 memset(buffer, 0, sizeof(buffer));
450 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800451 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700452 for (i=0;i<rc;i++)
453 printk("%02x ",buffer[i]);
454 printk("\n");
455 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300456 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
457 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
458 return -ENODEV;
459
Markus Rechberger257c6452006-01-23 17:11:11 -0200460 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700461 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800462 switch (addr) {
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800463 case 0x42:
464 case 0x43:
465 case 0x4a:
466 case 0x4b:
467 /* If chip is not tda8290, don't register.
468 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300469 if (tda8290_probe(&t->i2c) == 0) {
470 tuner_dbg("chip at addr %x is a tda8290\n", addr);
471 } else {
472 /* Default is being tda9887 */
473 t->type = TUNER_TDA9887;
474 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
475 t->mode = T_STANDBY;
476 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800477 }
478 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800479 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700480 if (tea5767_autodetection(&t->i2c) != EINVAL) {
481 t->type = TUNER_TEA5767;
482 t->mode_mask = T_RADIO;
483 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200484 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700485 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700486
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800487 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700488 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800489 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700490 }
491 }
492
493 /* Initializes only the first adapter found */
494 if (default_mode_mask != T_UNINITIALIZED) {
495 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
496 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200497 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
498 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700499 default_mode_mask = T_UNINITIALIZED;
500 }
501
502 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800503register_client:
504 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700505 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300506 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 return 0;
508}
509
510static int tuner_probe(struct i2c_adapter *adap)
511{
512 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700513 normal_i2c[0] = addr;
514 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700517 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700518
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 if (adap->class & I2C_CLASS_TV_ANALOG)
520 return i2c_probe(adap, &addr_data, tuner_attach);
521 return 0;
522}
523
524static int tuner_detach(struct i2c_client *client)
525{
526 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700527 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700529 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700530 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700531 tuner_warn
532 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700533 return err;
534 }
535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 kfree(t);
537 return 0;
538}
539
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700540/*
541 * Switch tuner to other mode. If tuner support both tv and radio,
542 * set another frequency to some value (This is needed for some pal
543 * tuners to avoid locking). Otherwise, just put second tuner in
544 * standby mode.
545 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700547static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
548{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800549 if (mode == t->mode)
550 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700551
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800552 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700553
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800554 if (check_mode(t, cmd) == EINVAL) {
555 t->mode = T_STANDBY;
556 if (t->standby)
557 t->standby (client);
558 return EINVAL;
559 }
560 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700561}
562
563#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800564 tuner_dbg("switching to v4l2\n"); \
565 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700566
567static inline int check_v4l2(struct tuner *t)
568{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300569 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
570 TV, v4l1 for radio), until that is fixed this code is disabled.
571 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
572 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700573 return 0;
574}
575
576static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577{
578 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Hans Verkuilf9195de2006-01-11 19:01:01 -0200580 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200581 v4l_i2c_print_ioctl(&(t->i2c),cmd);
582
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700583 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700585 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300586 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 -0700587 ((struct tuner_setup *)arg)->type,
588 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300589 ((struct tuner_setup *)arg)->mode_mask,
590 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700591
592 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700593 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200595 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
596 == EINVAL)
597 return 0;
598 if (t->radio_freq)
599 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700601 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200602 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
603 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300604 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200605 if (t->standby)
606 t->standby (client);
607 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300608#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700609 case VIDIOCSAUDIO:
610 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
611 return 0;
612 if (check_v4l2(t) == EINVAL)
613 return 0;
614
615 /* Should be implemented, since bttv calls it */
616 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700617 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700619 {
620 static const v4l2_std_id map[] = {
621 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
622 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
623 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
624 [4 /* bttv */ ] = V4L2_STD_PAL_M,
625 [5 /* bttv */ ] = V4L2_STD_PAL_N,
626 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
627 };
628 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700630 if (check_v4l2(t) == EINVAL)
631 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700632
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700633 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
634 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700636 if (vc->norm < ARRAY_SIZE(map))
637 t->std = map[vc->norm];
638 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200639 if (t->tv_freq)
640 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700641 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700642 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700643 case VIDIOCSFREQ:
644 {
645 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700646
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700647 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
648 return 0;
649 if (check_v4l2(t) == EINVAL)
650 return 0;
651
652 set_freq(client, *v);
653 return 0;
654 }
655 case VIDIOCGTUNER:
656 {
657 struct video_tuner *vt = arg;
658
659 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
660 return 0;
661 if (check_v4l2(t) == EINVAL)
662 return 0;
663
664 if (V4L2_TUNER_RADIO == t->mode) {
665 if (t->has_signal)
666 vt->signal = t->has_signal(client);
667 if (t->is_stereo) {
668 if (t->is_stereo(client))
669 vt->flags |=
670 VIDEO_TUNER_STEREO_ON;
671 else
672 vt->flags &=
673 ~VIDEO_TUNER_STEREO_ON;
674 }
675 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
676
677 vt->rangelow = radio_range[0] * 16000;
678 vt->rangehigh = radio_range[1] * 16000;
679
680 } else {
681 vt->rangelow = tv_range[0] * 16;
682 vt->rangehigh = tv_range[1] * 16;
683 }
684
685 return 0;
686 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700688 {
689 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700691 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
692 return 0;
693 if (check_v4l2(t) == EINVAL)
694 return 0;
695
696 if (V4L2_TUNER_RADIO == t->mode && t->is_stereo)
697 va->mode = t->is_stereo(client)
698 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
699 return 0;
700 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300701#endif
702 case TDA9887_SET_CONFIG:
703 if (t->type == TUNER_TDA9887) {
704 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300706 t->tda9887_config = *i;
707 set_freq(client, t->tv_freq);
708 }
709 break;
710 /* --- v4l ioctls --- */
711 /* take care: bttv does userspace copying, we'll get a
712 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700714 {
715 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700717 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
718 == EINVAL)
719 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700720
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700721 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700723 t->std = *id;
724 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200725 if (t->tv_freq)
726 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700727 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700728 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700729 case VIDIOC_S_FREQUENCY:
730 {
731 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700732
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300733 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
734 == EINVAL)
735 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700736 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200737 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700738
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700739 break;
740 }
741 case VIDIOC_G_FREQUENCY:
742 {
743 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700744
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700745 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
746 return 0;
747 switch_v4l2();
748 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200749 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
750 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700751 break;
752 }
753 case VIDIOC_G_TUNER:
754 {
755 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700756
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700757 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
758 return 0;
759 switch_v4l2();
760
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200761 tuner->type = t->mode;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300762 if (t->get_afc)
763 tuner->afc=t->get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300764 if (t->mode == V4L2_TUNER_ANALOG_TV)
765 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200766 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700767 tuner->rangelow = tv_range[0] * 16;
768 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200769 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700770 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200771
772 /* radio mode */
773 if (t->has_signal)
774 tuner->signal = t->has_signal(client);
775
776 tuner->rxsubchans =
777 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
778 if (t->is_stereo) {
779 tuner->rxsubchans = t->is_stereo(client) ?
780 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
781 }
782
783 tuner->capability |=
784 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
785 tuner->audmode = t->audmode;
786 tuner->rangelow = radio_range[0] * 16000;
787 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700788 break;
789 }
790 case VIDIOC_S_TUNER:
791 {
792 struct v4l2_tuner *tuner = arg;
793
794 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
795 return 0;
796
797 switch_v4l2();
798
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200799 /* do nothing unless we're a radio tuner */
800 if (t->mode != V4L2_TUNER_RADIO)
801 break;
802 t->audmode = tuner->audmode;
803 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700804 break;
805 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200806 case VIDIOC_LOG_STATUS:
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300807 if (t->tuner_status)
808 t->tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200809 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 }
811
812 return 0;
813}
814
Jean Delvare21b48a72007-03-12 19:20:15 -0300815static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700817 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700819 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 /* FIXME: power down ??? */
821 return 0;
822}
823
Jean Delvare21b48a72007-03-12 19:20:15 -0300824static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700826 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700828 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200829 if (V4L2_TUNER_RADIO == t->mode) {
830 if (t->radio_freq)
831 set_freq(c, t->radio_freq);
832 } else {
833 if (t->tv_freq)
834 set_freq(c, t->tv_freq);
835 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 return 0;
837}
838
839/* ----------------------------------------------------------------------- */
840
841static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700842 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700843 .attach_adapter = tuner_probe,
844 .detach_client = tuner_detach,
845 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300846 .suspend = tuner_suspend,
847 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200849 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200850 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700852static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200853 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700854 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855};
856
857static int __init tuner_init_module(void)
858{
859 return i2c_add_driver(&driver);
860}
861
862static void __exit tuner_cleanup_module(void)
863{
864 i2c_del_driver(&driver);
865}
866
867module_init(tuner_init_module);
868module_exit(tuner_cleanup_module);
869
870/*
871 * Overrides for Emacs so that we follow Linus's tabbing style.
872 * ---------------------------------------------------------------------------
873 * Local variables:
874 * c-basic-offset: 8
875 * End:
876 */