blob: e646465464a1e8c7e29a63823fac88fa14b478b1 [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>
Michael Krufky8218b0b2007-06-26 13:12:08 -030023#include "tuner-driver.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
25#define UNSET (-1U)
26
27/* standard i2c insmod options */
28static unsigned short normal_i2c[] = {
Adrian Bunk9ee476a2007-06-05 05:22:00 -030029#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030030 0x10,
31#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080032 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070033 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
34 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 I2C_CLIENT_END
36};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070037
Linus Torvalds1da177e2005-04-16 15:20:36 -070038I2C_CLIENT_INSMOD;
39
40/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070041static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070042static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070043static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070044
Linus Torvalds1da177e2005-04-16 15:20:36 -070045/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020046int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070048static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070049static unsigned int radio_range[2] = { 65, 108 };
50
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020051static char pal[] = "--";
52static char secam[] = "--";
53static char ntsc[] = "-";
54
Hans Verkuilf9195de2006-01-11 19:01:01 -020055
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020056module_param(addr, int, 0444);
57module_param(no_autodetect, int, 0444);
58module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020059module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020060module_param_string(pal, pal, sizeof(pal), 0644);
61module_param_string(secam, secam, sizeof(secam), 0644);
62module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070063module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064module_param_array(radio_range, int, NULL, 0644);
65
66MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
67MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
68MODULE_LICENSE("GPL");
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070static struct i2c_driver driver;
71static struct i2c_client client_template;
72
73/* ---------------------------------------------------------------------- */
74
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070075/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -070076static void set_tv_freq(struct i2c_client *c, unsigned int freq)
77{
78 struct tuner *t = i2c_get_clientdata(c);
79
80 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070081 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 return;
83 }
Michael Krufky7a91a802007-06-06 16:10:39 -030084 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070085 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 return;
87 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070088 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
89 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
90 freq / 16, freq % 16 * 100 / 16, tv_range[0],
91 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -020092 /* V4L2 spec: if the freq is not possible then the closest
93 possible value should be selected */
94 if (freq < tv_range[0] * 16)
95 freq = tv_range[0] * 16;
96 else
97 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 }
Michael Krufky7a91a802007-06-06 16:10:39 -030099 t->ops.set_tv_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100}
101
102static void set_radio_freq(struct i2c_client *c, unsigned int freq)
103{
104 struct tuner *t = i2c_get_clientdata(c);
105
106 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700107 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 return;
109 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300110 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700111 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 return;
113 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200114 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700115 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
116 freq / 16000, freq % 16000 * 100 / 16000,
117 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200118 /* V4L2 spec: if the freq is not possible then the closest
119 possible value should be selected */
120 if (freq < radio_range[0] * 16000)
121 freq = radio_range[0] * 16000;
122 else
123 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700125
Michael Krufky7a91a802007-06-06 16:10:39 -0300126 t->ops.set_radio_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127}
128
129static void set_freq(struct i2c_client *c, unsigned long freq)
130{
131 struct tuner *t = i2c_get_clientdata(c);
132
133 switch (t->mode) {
134 case V4L2_TUNER_RADIO:
135 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700136 freq / 16000, freq % 16000 * 100 / 16000);
137 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200138 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 break;
140 case V4L2_TUNER_ANALOG_TV:
141 case V4L2_TUNER_DIGITAL_TV:
142 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700143 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200145 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 break;
147 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700150static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300151 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300152 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
154 struct tuner *t = i2c_get_clientdata(c);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700155 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700157 if (type == UNSET || type == TUNER_ABSENT) {
158 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 return;
160 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700162 if (type >= tuner_count) {
163 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
164 return;
165 }
166
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300168 t->config = new_config;
169 if (tuner_callback != NULL) {
170 tuner_dbg("defining GPIO callback\n");
171 t->tuner_callback = tuner_callback;
172 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300173
174 /* This code detects calls by card attach_inform */
175 if (NULL == t->i2c.dev.driver) {
176 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
177
178 return;
179 }
180
Michael Krufkyb2083192007-05-29 22:54:06 -0300181 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300182 if (t->ops.release)
183 t->ops.release(c);
Michael Krufky052c50d2007-06-04 16:00:45 -0300184 else {
185 kfree(t->priv);
186 t->priv = NULL;
187 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 switch (t->type) {
190 case TUNER_MT2032:
191 microtune_init(c);
192 break;
193 case TUNER_PHILIPS_TDA8290:
194 tda8290_init(c);
195 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700196 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700197 if (tea5767_tuner_init(c) == EINVAL) {
198 t->type = TUNER_ABSENT;
199 t->mode_mask = T_UNINITIALIZED;
200 return;
201 }
202 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700203 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300204#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300205 case TUNER_TEA5761:
206 if (tea5761_tuner_init(c) == EINVAL) {
207 t->type = TUNER_ABSENT;
208 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300209 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300210 }
211 t->mode_mask = T_RADIO;
212 break;
213#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700214 case TUNER_PHILIPS_FMD1216ME_MK3:
215 buffer[0] = 0x0b;
216 buffer[1] = 0xdc;
217 buffer[2] = 0x9c;
218 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700219 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700220 mdelay(1);
221 buffer[2] = 0x86;
222 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700223 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700224 default_tuner_init(c);
225 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800226 case TUNER_PHILIPS_TD1316:
227 buffer[0] = 0x0b;
228 buffer[1] = 0xdc;
229 buffer[2] = 0x86;
230 buffer[3] = 0xa4;
231 i2c_master_send(c,buffer,4);
232 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200233 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300234 case TUNER_TDA9887:
235 tda9887_tuner_init(c);
236 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 default:
238 default_tuner_init(c);
239 break;
240 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700241
242 if (t->mode_mask == T_UNINITIALIZED)
243 t->mode_mask = new_mode_mask;
244
Hans Verkuil27487d42006-01-15 15:04:52 -0200245 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700246 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100247 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700248 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249}
250
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700251/*
252 * This function apply tuner config to tuner specified
253 * by tun_setup structure. I addr is unset, then admin status
254 * and tun addr status is more precise then current status,
255 * it's applied. Otherwise status and type are applied only to
256 * tuner with exactly the same addr.
257*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700258
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700259static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700260{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700261 struct tuner *t = i2c_get_clientdata(c);
262
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300263 tuner_dbg("set addr for type %i\n", t->type);
264
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300265 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
266 (t->mode_mask & tun_setup->mode_mask))) ||
267 (tun_setup->addr == c->addr)) {
268 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300269 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700270 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700271}
272
273static inline int check_mode(struct tuner *t, char *cmd)
274{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700275 if ((1 << t->mode & t->mode_mask) == 0) {
276 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700277 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700278
279 switch (t->mode) {
280 case V4L2_TUNER_RADIO:
281 tuner_dbg("Cmd %s accepted for radio\n", cmd);
282 break;
283 case V4L2_TUNER_ANALOG_TV:
284 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
285 break;
286 case V4L2_TUNER_DIGITAL_TV:
287 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
288 break;
289 }
290 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700291}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700292
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700293/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294static int tuner_fixup_std(struct tuner *t)
295{
296 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300298 case '6':
299 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
300 t->std = V4L2_STD_PAL_60;
301 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 case 'b':
303 case 'B':
304 case 'g':
305 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700306 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 t->std = V4L2_STD_PAL_BG;
308 break;
309 case 'i':
310 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700311 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 t->std = V4L2_STD_PAL_I;
313 break;
314 case 'd':
315 case 'D':
316 case 'k':
317 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700318 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 t->std = V4L2_STD_PAL_DK;
320 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700321 case 'M':
322 case 'm':
323 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
324 t->std = V4L2_STD_PAL_M;
325 break;
326 case 'N':
327 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200328 if (pal[1] == 'c' || pal[1] == 'C') {
329 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
330 t->std = V4L2_STD_PAL_Nc;
331 } else {
332 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
333 t->std = V4L2_STD_PAL_N;
334 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700335 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700336 case '-':
337 /* default parameter, do nothing */
338 break;
339 default:
340 tuner_warn ("pal= argument not recognised\n");
341 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 }
343 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700344 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
345 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200346 case 'b':
347 case 'B':
348 case 'g':
349 case 'G':
350 case 'h':
351 case 'H':
352 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
353 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
354 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700355 case 'd':
356 case 'D':
357 case 'k':
358 case 'K':
359 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
360 t->std = V4L2_STD_SECAM_DK;
361 break;
362 case 'l':
363 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800364 if ((secam[1]=='C')||(secam[1]=='c')) {
365 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
366 t->std = V4L2_STD_SECAM_LC;
367 } else {
368 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
369 t->std = V4L2_STD_SECAM_L;
370 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700371 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700372 case '-':
373 /* default parameter, do nothing */
374 break;
375 default:
376 tuner_warn ("secam= argument not recognised\n");
377 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700378 }
379 }
380
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200381 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
382 switch (ntsc[0]) {
383 case 'm':
384 case 'M':
385 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
386 t->std = V4L2_STD_NTSC_M;
387 break;
388 case 'j':
389 case 'J':
390 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
391 t->std = V4L2_STD_NTSC_M_JP;
392 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200393 case 'k':
394 case 'K':
395 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
396 t->std = V4L2_STD_NTSC_M_KR;
397 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200398 case '-':
399 /* default parameter, do nothing */
400 break;
401 default:
402 tuner_info("ntsc= argument not recognised\n");
403 break;
404 }
405 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 return 0;
407}
408
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200409static void tuner_status(struct i2c_client *client)
410{
411 struct tuner *t = i2c_get_clientdata(client);
412 unsigned long freq, freq_fraction;
413 const char *p;
414
415 switch (t->mode) {
416 case V4L2_TUNER_RADIO: p = "radio"; break;
417 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
418 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
419 default: p = "undefined"; break;
420 }
421 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200422 freq = t->radio_freq / 16000;
423 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200424 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200425 freq = t->tv_freq / 16;
426 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200427 }
428 tuner_info("Tuner mode: %s\n", p);
429 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300430 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200431 if (t->mode != V4L2_TUNER_RADIO)
432 return;
Michael Krufky7a91a802007-06-06 16:10:39 -0300433 if (t->ops.has_signal) {
434 tuner_info("Signal strength: %d\n", t->ops.has_signal(client));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200435 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300436 if (t->ops.is_stereo) {
437 tuner_info("Stereo: %s\n", t->ops.is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200438 }
439}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200440
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441/* ---------------------------------------------------------------------- */
442
Hans Verkuilba8fc392006-06-25 15:34:39 -0300443/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700444static unsigned default_mode_mask;
445
446/* During client attach, set_type is called by adapter's attach_inform callback.
447 set_type must then be completed by tuner_attach.
448 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
450{
451 struct tuner *t;
452
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700453 client_template.adapter = adap;
454 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
Panagiotis Issaris74081872006-01-11 19:40:56 -0200456 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700457 if (NULL == t)
458 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700459 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700461 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700462 t->audmode = V4L2_TUNER_MODE_STEREO;
463 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300464 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700466 if (show_i2c) {
467 unsigned char buffer[16];
468 int i,rc;
469
470 memset(buffer, 0, sizeof(buffer));
471 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800472 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700473 for (i=0;i<rc;i++)
474 printk("%02x ",buffer[i]);
475 printk("\n");
476 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300477 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
478 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
479 return -ENODEV;
480
Markus Rechberger257c6452006-01-23 17:11:11 -0200481 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700482 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800483 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300484#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300485 case 0x10:
486 if (tea5761_autodetection(&t->i2c) != EINVAL) {
487 t->type = TUNER_TEA5761;
488 t->mode_mask = T_RADIO;
489 t->mode = T_STANDBY;
490 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
491 default_mode_mask &= ~T_RADIO;
492
493 goto register_client;
494 }
495 break;
496#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800497 case 0x42:
498 case 0x43:
499 case 0x4a:
500 case 0x4b:
501 /* If chip is not tda8290, don't register.
502 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300503 if (tda8290_probe(&t->i2c) == 0) {
504 tuner_dbg("chip at addr %x is a tda8290\n", addr);
505 } else {
506 /* Default is being tda9887 */
507 t->type = TUNER_TDA9887;
508 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
509 t->mode = T_STANDBY;
510 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800511 }
512 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800513 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700514 if (tea5767_autodetection(&t->i2c) != EINVAL) {
515 t->type = TUNER_TEA5767;
516 t->mode_mask = T_RADIO;
517 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200518 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700519 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700520
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800521 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700522 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800523 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700524 }
525 }
526
527 /* Initializes only the first adapter found */
528 if (default_mode_mask != T_UNINITIALIZED) {
529 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
530 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200531 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
532 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700533 default_mode_mask = T_UNINITIALIZED;
534 }
535
536 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800537register_client:
538 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700539 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300540 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 return 0;
542}
543
544static int tuner_probe(struct i2c_adapter *adap)
545{
546 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700547 normal_i2c[0] = addr;
548 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700551 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700552
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 if (adap->class & I2C_CLASS_TV_ANALOG)
554 return i2c_probe(adap, &addr_data, tuner_attach);
555 return 0;
556}
557
558static int tuner_detach(struct i2c_client *client)
559{
560 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700561 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700563 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700564 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700565 tuner_warn
566 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700567 return err;
568 }
569
Michael Krufky7a91a802007-06-06 16:10:39 -0300570 if (t->ops.release)
571 t->ops.release(client);
Michael Krufky052c50d2007-06-04 16:00:45 -0300572 else {
573 kfree(t->priv);
574 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 kfree(t);
576 return 0;
577}
578
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700579/*
580 * Switch tuner to other mode. If tuner support both tv and radio,
581 * set another frequency to some value (This is needed for some pal
582 * tuners to avoid locking). Otherwise, just put second tuner in
583 * standby mode.
584 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700586static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
587{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800588 if (mode == t->mode)
589 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700590
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800591 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700592
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800593 if (check_mode(t, cmd) == EINVAL) {
594 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300595 if (t->ops.standby)
596 t->ops.standby (client);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800597 return EINVAL;
598 }
599 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700600}
601
602#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800603 tuner_dbg("switching to v4l2\n"); \
604 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700605
606static inline int check_v4l2(struct tuner *t)
607{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300608 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
609 TV, v4l1 for radio), until that is fixed this code is disabled.
610 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
611 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700612 return 0;
613}
614
615static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616{
617 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
Hans Verkuilf9195de2006-01-11 19:01:01 -0200619 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200620 v4l_i2c_print_ioctl(&(t->i2c),cmd);
621
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700622 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700624 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300625 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 -0700626 ((struct tuner_setup *)arg)->type,
627 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300628 ((struct tuner_setup *)arg)->mode_mask,
629 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700630
631 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700632 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200634 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
635 == EINVAL)
636 return 0;
637 if (t->radio_freq)
638 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700640 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200641 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
642 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300643 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300644 if (t->ops.standby)
645 t->ops.standby (client);
Hans Verkuil27487d42006-01-15 15:04:52 -0200646 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300647#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700648 case VIDIOCSAUDIO:
649 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
650 return 0;
651 if (check_v4l2(t) == EINVAL)
652 return 0;
653
654 /* Should be implemented, since bttv calls it */
655 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700656 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700658 {
659 static const v4l2_std_id map[] = {
660 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
661 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
662 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
663 [4 /* bttv */ ] = V4L2_STD_PAL_M,
664 [5 /* bttv */ ] = V4L2_STD_PAL_N,
665 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
666 };
667 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700669 if (check_v4l2(t) == EINVAL)
670 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700671
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700672 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
673 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700675 if (vc->norm < ARRAY_SIZE(map))
676 t->std = map[vc->norm];
677 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200678 if (t->tv_freq)
679 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700681 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700682 case VIDIOCSFREQ:
683 {
684 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700685
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700686 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
687 return 0;
688 if (check_v4l2(t) == EINVAL)
689 return 0;
690
691 set_freq(client, *v);
692 return 0;
693 }
694 case VIDIOCGTUNER:
695 {
696 struct video_tuner *vt = arg;
697
698 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
699 return 0;
700 if (check_v4l2(t) == EINVAL)
701 return 0;
702
703 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufky7a91a802007-06-06 16:10:39 -0300704 if (t->ops.has_signal)
705 vt->signal = t->ops.has_signal(client);
706 if (t->ops.is_stereo) {
707 if (t->ops.is_stereo(client))
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700708 vt->flags |=
709 VIDEO_TUNER_STEREO_ON;
710 else
711 vt->flags &=
712 ~VIDEO_TUNER_STEREO_ON;
713 }
714 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
715
716 vt->rangelow = radio_range[0] * 16000;
717 vt->rangehigh = radio_range[1] * 16000;
718
719 } else {
720 vt->rangelow = tv_range[0] * 16;
721 vt->rangehigh = tv_range[1] * 16;
722 }
723
724 return 0;
725 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700727 {
728 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700730 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
731 return 0;
732 if (check_v4l2(t) == EINVAL)
733 return 0;
734
Michael Krufky7a91a802007-06-06 16:10:39 -0300735 if (V4L2_TUNER_RADIO == t->mode && t->ops.is_stereo)
736 va->mode = t->ops.is_stereo(client)
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700737 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
738 return 0;
739 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300740#endif
741 case TDA9887_SET_CONFIG:
742 if (t->type == TUNER_TDA9887) {
743 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300745 t->tda9887_config = *i;
746 set_freq(client, t->tv_freq);
747 }
748 break;
749 /* --- v4l ioctls --- */
750 /* take care: bttv does userspace copying, we'll get a
751 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700753 {
754 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700756 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
757 == EINVAL)
758 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700759
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700762 t->std = *id;
763 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200764 if (t->tv_freq)
765 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700766 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700767 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700768 case VIDIOC_S_FREQUENCY:
769 {
770 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700771
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300772 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
773 == EINVAL)
774 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700775 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200776 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700777
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700778 break;
779 }
780 case VIDIOC_G_FREQUENCY:
781 {
782 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700783
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700784 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
785 return 0;
786 switch_v4l2();
787 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200788 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
789 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700790 break;
791 }
792 case VIDIOC_G_TUNER:
793 {
794 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700795
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700796 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
797 return 0;
798 switch_v4l2();
799
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200800 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300801 if (t->ops.get_afc)
802 tuner->afc=t->ops.get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300803 if (t->mode == V4L2_TUNER_ANALOG_TV)
804 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200805 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700806 tuner->rangelow = tv_range[0] * 16;
807 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200808 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700809 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200810
811 /* radio mode */
Michael Krufky7a91a802007-06-06 16:10:39 -0300812 if (t->ops.has_signal)
813 tuner->signal = t->ops.has_signal(client);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200814
815 tuner->rxsubchans =
816 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufky7a91a802007-06-06 16:10:39 -0300817 if (t->ops.is_stereo) {
818 tuner->rxsubchans = t->ops.is_stereo(client) ?
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200819 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
820 }
821
822 tuner->capability |=
823 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
824 tuner->audmode = t->audmode;
825 tuner->rangelow = radio_range[0] * 16000;
826 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700827 break;
828 }
829 case VIDIOC_S_TUNER:
830 {
831 struct v4l2_tuner *tuner = arg;
832
833 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
834 return 0;
835
836 switch_v4l2();
837
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200838 /* do nothing unless we're a radio tuner */
839 if (t->mode != V4L2_TUNER_RADIO)
840 break;
841 t->audmode = tuner->audmode;
842 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700843 break;
844 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200845 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300846 if (t->ops.tuner_status)
847 t->ops.tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200848 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 }
850
851 return 0;
852}
853
Jean Delvare21b48a72007-03-12 19:20:15 -0300854static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700856 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700858 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 /* FIXME: power down ??? */
860 return 0;
861}
862
Jean Delvare21b48a72007-03-12 19:20:15 -0300863static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700865 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700867 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200868 if (V4L2_TUNER_RADIO == t->mode) {
869 if (t->radio_freq)
870 set_freq(c, t->radio_freq);
871 } else {
872 if (t->tv_freq)
873 set_freq(c, t->tv_freq);
874 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 return 0;
876}
877
878/* ----------------------------------------------------------------------- */
879
880static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700881 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700882 .attach_adapter = tuner_probe,
883 .detach_client = tuner_detach,
884 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300885 .suspend = tuner_suspend,
886 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200888 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200889 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700891static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200892 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700893 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894};
895
896static int __init tuner_init_module(void)
897{
898 return i2c_add_driver(&driver);
899}
900
901static void __exit tuner_cleanup_module(void)
902{
903 i2c_del_driver(&driver);
904}
905
906module_init(tuner_init_module);
907module_exit(tuner_cleanup_module);
908
909/*
910 * Overrides for Emacs so that we follow Linus's tabbing style.
911 * ---------------------------------------------------------------------------
912 * Local variables:
913 * c-basic-offset: 8
914 * End:
915 */