blob: 5f378265d8c5bd5f0eeb878d56dee2ebb922134a [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/init.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030019#include <linux/videodev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <media/tuner.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020021#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030022#include "tuner-driver.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 }
Michael Krufky7a91a802007-06-06 16:10:39 -030083 if (NULL == t->ops.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 }
Michael Krufky7a91a802007-06-06 16:10:39 -030098 t->ops.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 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300109 if (NULL == t->ops.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
Michael Krufky7a91a802007-06-06 16:10:39 -0300125 t->ops.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 Krufky7a91a802007-06-06 16:10:39 -0300181 if (t->ops.release)
182 t->ops.release(c);
Michael Krufky052c50d2007-06-04 16:00:45 -0300183 else {
184 kfree(t->priv);
185 t->priv = NULL;
186 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300187
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 switch (t->type) {
189 case TUNER_MT2032:
190 microtune_init(c);
191 break;
192 case TUNER_PHILIPS_TDA8290:
193 tda8290_init(c);
194 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700195 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700196 if (tea5767_tuner_init(c) == EINVAL) {
197 t->type = TUNER_ABSENT;
198 t->mode_mask = T_UNINITIALIZED;
199 return;
200 }
201 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700202 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300203#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300204 case TUNER_TEA5761:
205 if (tea5761_tuner_init(c) == EINVAL) {
206 t->type = TUNER_ABSENT;
207 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300208 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300209 }
210 t->mode_mask = T_RADIO;
211 break;
212#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700213 case TUNER_PHILIPS_FMD1216ME_MK3:
214 buffer[0] = 0x0b;
215 buffer[1] = 0xdc;
216 buffer[2] = 0x9c;
217 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700218 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700219 mdelay(1);
220 buffer[2] = 0x86;
221 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700222 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700223 default_tuner_init(c);
224 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800225 case TUNER_PHILIPS_TD1316:
226 buffer[0] = 0x0b;
227 buffer[1] = 0xdc;
228 buffer[2] = 0x86;
229 buffer[3] = 0xa4;
230 i2c_master_send(c,buffer,4);
231 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200232 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300233 case TUNER_TDA9887:
234 tda9887_tuner_init(c);
235 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 default:
237 default_tuner_init(c);
238 break;
239 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700240
241 if (t->mode_mask == T_UNINITIALIZED)
242 t->mode_mask = new_mode_mask;
243
Hans Verkuil27487d42006-01-15 15:04:52 -0200244 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700245 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100246 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700247 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}
249
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700250/*
251 * This function apply tuner config to tuner specified
252 * by tun_setup structure. I addr is unset, then admin status
253 * and tun addr status is more precise then current status,
254 * it's applied. Otherwise status and type are applied only to
255 * tuner with exactly the same addr.
256*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700257
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700258static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700259{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700260 struct tuner *t = i2c_get_clientdata(c);
261
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300262 tuner_dbg("set addr for type %i\n", t->type);
263
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300264 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
265 (t->mode_mask & tun_setup->mode_mask))) ||
266 (tun_setup->addr == c->addr)) {
267 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300268 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700269 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700270}
271
272static inline int check_mode(struct tuner *t, char *cmd)
273{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700274 if ((1 << t->mode & t->mode_mask) == 0) {
275 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700276 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700277
278 switch (t->mode) {
279 case V4L2_TUNER_RADIO:
280 tuner_dbg("Cmd %s accepted for radio\n", cmd);
281 break;
282 case V4L2_TUNER_ANALOG_TV:
283 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
284 break;
285 case V4L2_TUNER_DIGITAL_TV:
286 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
287 break;
288 }
289 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700290}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700291
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700292/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293static int tuner_fixup_std(struct tuner *t)
294{
295 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300297 case '6':
298 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
299 t->std = V4L2_STD_PAL_60;
300 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 case 'b':
302 case 'B':
303 case 'g':
304 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700305 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 t->std = V4L2_STD_PAL_BG;
307 break;
308 case 'i':
309 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700310 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 t->std = V4L2_STD_PAL_I;
312 break;
313 case 'd':
314 case 'D':
315 case 'k':
316 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700317 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 t->std = V4L2_STD_PAL_DK;
319 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700320 case 'M':
321 case 'm':
322 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
323 t->std = V4L2_STD_PAL_M;
324 break;
325 case 'N':
326 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200327 if (pal[1] == 'c' || pal[1] == 'C') {
328 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
329 t->std = V4L2_STD_PAL_Nc;
330 } else {
331 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
332 t->std = V4L2_STD_PAL_N;
333 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700334 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700335 case '-':
336 /* default parameter, do nothing */
337 break;
338 default:
339 tuner_warn ("pal= argument not recognised\n");
340 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 }
342 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700343 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
344 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200345 case 'b':
346 case 'B':
347 case 'g':
348 case 'G':
349 case 'h':
350 case 'H':
351 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
352 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
353 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700354 case 'd':
355 case 'D':
356 case 'k':
357 case 'K':
358 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
359 t->std = V4L2_STD_SECAM_DK;
360 break;
361 case 'l':
362 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800363 if ((secam[1]=='C')||(secam[1]=='c')) {
364 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
365 t->std = V4L2_STD_SECAM_LC;
366 } else {
367 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
368 t->std = V4L2_STD_SECAM_L;
369 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700370 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700371 case '-':
372 /* default parameter, do nothing */
373 break;
374 default:
375 tuner_warn ("secam= argument not recognised\n");
376 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700377 }
378 }
379
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200380 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
381 switch (ntsc[0]) {
382 case 'm':
383 case 'M':
384 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
385 t->std = V4L2_STD_NTSC_M;
386 break;
387 case 'j':
388 case 'J':
389 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
390 t->std = V4L2_STD_NTSC_M_JP;
391 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200392 case 'k':
393 case 'K':
394 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
395 t->std = V4L2_STD_NTSC_M_KR;
396 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200397 case '-':
398 /* default parameter, do nothing */
399 break;
400 default:
401 tuner_info("ntsc= argument not recognised\n");
402 break;
403 }
404 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 return 0;
406}
407
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200408static void tuner_status(struct i2c_client *client)
409{
410 struct tuner *t = i2c_get_clientdata(client);
411 unsigned long freq, freq_fraction;
412 const char *p;
413
414 switch (t->mode) {
415 case V4L2_TUNER_RADIO: p = "radio"; break;
416 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
417 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
418 default: p = "undefined"; break;
419 }
420 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200421 freq = t->radio_freq / 16000;
422 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200423 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200424 freq = t->tv_freq / 16;
425 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200426 }
427 tuner_info("Tuner mode: %s\n", p);
428 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300429 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200430 if (t->mode != V4L2_TUNER_RADIO)
431 return;
Michael Krufky7a91a802007-06-06 16:10:39 -0300432 if (t->ops.has_signal) {
433 tuner_info("Signal strength: %d\n", t->ops.has_signal(client));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200434 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300435 if (t->ops.is_stereo) {
436 tuner_info("Stereo: %s\n", t->ops.is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200437 }
438}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200439
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440/* ---------------------------------------------------------------------- */
441
Hans Verkuilba8fc392006-06-25 15:34:39 -0300442/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700443static unsigned default_mode_mask;
444
445/* During client attach, set_type is called by adapter's attach_inform callback.
446 set_type must then be completed by tuner_attach.
447 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
449{
450 struct tuner *t;
451
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700452 client_template.adapter = adap;
453 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Panagiotis Issaris74081872006-01-11 19:40:56 -0200455 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700456 if (NULL == t)
457 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700458 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700460 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700461 t->audmode = V4L2_TUNER_MODE_STEREO;
462 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300463 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700465 if (show_i2c) {
466 unsigned char buffer[16];
467 int i,rc;
468
469 memset(buffer, 0, sizeof(buffer));
470 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800471 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700472 for (i=0;i<rc;i++)
473 printk("%02x ",buffer[i]);
474 printk("\n");
475 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300476 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
477 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
478 return -ENODEV;
479
Markus Rechberger257c6452006-01-23 17:11:11 -0200480 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700481 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800482 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300483#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300484 case 0x10:
485 if (tea5761_autodetection(&t->i2c) != EINVAL) {
486 t->type = TUNER_TEA5761;
487 t->mode_mask = T_RADIO;
488 t->mode = T_STANDBY;
489 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
490 default_mode_mask &= ~T_RADIO;
491
492 goto register_client;
493 }
494 break;
495#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800496 case 0x42:
497 case 0x43:
498 case 0x4a:
499 case 0x4b:
500 /* If chip is not tda8290, don't register.
501 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300502 if (tda8290_probe(&t->i2c) == 0) {
503 tuner_dbg("chip at addr %x is a tda8290\n", addr);
504 } else {
505 /* Default is being tda9887 */
506 t->type = TUNER_TDA9887;
507 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
508 t->mode = T_STANDBY;
509 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800510 }
511 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800512 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700513 if (tea5767_autodetection(&t->i2c) != EINVAL) {
514 t->type = TUNER_TEA5767;
515 t->mode_mask = T_RADIO;
516 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200517 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700518 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700519
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800520 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700521 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800522 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700523 }
524 }
525
526 /* Initializes only the first adapter found */
527 if (default_mode_mask != T_UNINITIALIZED) {
528 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
529 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200530 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
531 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700532 default_mode_mask = T_UNINITIALIZED;
533 }
534
535 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800536register_client:
537 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700538 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300539 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 return 0;
541}
542
543static int tuner_probe(struct i2c_adapter *adap)
544{
545 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700546 normal_i2c[0] = addr;
547 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700550 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700551
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 if (adap->class & I2C_CLASS_TV_ANALOG)
553 return i2c_probe(adap, &addr_data, tuner_attach);
554 return 0;
555}
556
557static int tuner_detach(struct i2c_client *client)
558{
559 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700560 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700562 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700563 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700564 tuner_warn
565 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700566 return err;
567 }
568
Michael Krufky7a91a802007-06-06 16:10:39 -0300569 if (t->ops.release)
570 t->ops.release(client);
Michael Krufky052c50d2007-06-04 16:00:45 -0300571 else {
572 kfree(t->priv);
573 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 kfree(t);
575 return 0;
576}
577
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700578/*
579 * Switch tuner to other mode. If tuner support both tv and radio,
580 * set another frequency to some value (This is needed for some pal
581 * tuners to avoid locking). Otherwise, just put second tuner in
582 * standby mode.
583 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700585static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
586{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800587 if (mode == t->mode)
588 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700589
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800590 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700591
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800592 if (check_mode(t, cmd) == EINVAL) {
593 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300594 if (t->ops.standby)
595 t->ops.standby (client);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800596 return EINVAL;
597 }
598 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700599}
600
601#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800602 tuner_dbg("switching to v4l2\n"); \
603 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700604
605static inline int check_v4l2(struct tuner *t)
606{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300607 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
608 TV, v4l1 for radio), until that is fixed this code is disabled.
609 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
610 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700611 return 0;
612}
613
614static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615{
616 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617
Hans Verkuilf9195de2006-01-11 19:01:01 -0200618 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200619 v4l_i2c_print_ioctl(&(t->i2c),cmd);
620
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700621 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700623 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300624 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 -0700625 ((struct tuner_setup *)arg)->type,
626 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300627 ((struct tuner_setup *)arg)->mode_mask,
628 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700629
630 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700631 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200633 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
634 == EINVAL)
635 return 0;
636 if (t->radio_freq)
637 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700639 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200640 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
641 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300642 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300643 if (t->ops.standby)
644 t->ops.standby (client);
Hans Verkuil27487d42006-01-15 15:04:52 -0200645 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300646#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700647 case VIDIOCSAUDIO:
648 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
649 return 0;
650 if (check_v4l2(t) == EINVAL)
651 return 0;
652
653 /* Should be implemented, since bttv calls it */
654 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700655 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700657 {
658 static const v4l2_std_id map[] = {
659 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
660 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
661 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
662 [4 /* bttv */ ] = V4L2_STD_PAL_M,
663 [5 /* bttv */ ] = V4L2_STD_PAL_N,
664 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
665 };
666 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700668 if (check_v4l2(t) == EINVAL)
669 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700670
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700671 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
672 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700674 if (vc->norm < ARRAY_SIZE(map))
675 t->std = map[vc->norm];
676 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200677 if (t->tv_freq)
678 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700679 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700680 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700681 case VIDIOCSFREQ:
682 {
683 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700684
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700685 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
686 return 0;
687 if (check_v4l2(t) == EINVAL)
688 return 0;
689
690 set_freq(client, *v);
691 return 0;
692 }
693 case VIDIOCGTUNER:
694 {
695 struct video_tuner *vt = arg;
696
697 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
698 return 0;
699 if (check_v4l2(t) == EINVAL)
700 return 0;
701
702 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufky7a91a802007-06-06 16:10:39 -0300703 if (t->ops.has_signal)
704 vt->signal = t->ops.has_signal(client);
705 if (t->ops.is_stereo) {
706 if (t->ops.is_stereo(client))
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700707 vt->flags |=
708 VIDEO_TUNER_STEREO_ON;
709 else
710 vt->flags &=
711 ~VIDEO_TUNER_STEREO_ON;
712 }
713 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
714
715 vt->rangelow = radio_range[0] * 16000;
716 vt->rangehigh = radio_range[1] * 16000;
717
718 } else {
719 vt->rangelow = tv_range[0] * 16;
720 vt->rangehigh = tv_range[1] * 16;
721 }
722
723 return 0;
724 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700726 {
727 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700729 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
730 return 0;
731 if (check_v4l2(t) == EINVAL)
732 return 0;
733
Michael Krufky7a91a802007-06-06 16:10:39 -0300734 if (V4L2_TUNER_RADIO == t->mode && t->ops.is_stereo)
735 va->mode = t->ops.is_stereo(client)
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700736 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
737 return 0;
738 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300739#endif
740 case TDA9887_SET_CONFIG:
741 if (t->type == TUNER_TDA9887) {
742 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300744 t->tda9887_config = *i;
745 set_freq(client, t->tv_freq);
746 }
747 break;
748 /* --- v4l ioctls --- */
749 /* take care: bttv does userspace copying, we'll get a
750 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700752 {
753 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700755 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
756 == EINVAL)
757 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700758
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700759 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700761 t->std = *id;
762 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200763 if (t->tv_freq)
764 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700765 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700766 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700767 case VIDIOC_S_FREQUENCY:
768 {
769 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700770
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300771 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
772 == EINVAL)
773 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700774 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200775 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700776
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700777 break;
778 }
779 case VIDIOC_G_FREQUENCY:
780 {
781 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700782
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700783 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
784 return 0;
785 switch_v4l2();
786 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200787 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
788 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700789 break;
790 }
791 case VIDIOC_G_TUNER:
792 {
793 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700794
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700795 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
796 return 0;
797 switch_v4l2();
798
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200799 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300800 if (t->ops.get_afc)
801 tuner->afc=t->ops.get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300802 if (t->mode == V4L2_TUNER_ANALOG_TV)
803 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200804 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700805 tuner->rangelow = tv_range[0] * 16;
806 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200807 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700808 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200809
810 /* radio mode */
Michael Krufky7a91a802007-06-06 16:10:39 -0300811 if (t->ops.has_signal)
812 tuner->signal = t->ops.has_signal(client);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200813
814 tuner->rxsubchans =
815 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufky7a91a802007-06-06 16:10:39 -0300816 if (t->ops.is_stereo) {
817 tuner->rxsubchans = t->ops.is_stereo(client) ?
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200818 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
819 }
820
821 tuner->capability |=
822 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
823 tuner->audmode = t->audmode;
824 tuner->rangelow = radio_range[0] * 16000;
825 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700826 break;
827 }
828 case VIDIOC_S_TUNER:
829 {
830 struct v4l2_tuner *tuner = arg;
831
832 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
833 return 0;
834
835 switch_v4l2();
836
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200837 /* do nothing unless we're a radio tuner */
838 if (t->mode != V4L2_TUNER_RADIO)
839 break;
840 t->audmode = tuner->audmode;
841 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700842 break;
843 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200844 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300845 if (t->ops.tuner_status)
846 t->ops.tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200847 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 }
849
850 return 0;
851}
852
Jean Delvare21b48a72007-03-12 19:20:15 -0300853static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700855 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700857 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 /* FIXME: power down ??? */
859 return 0;
860}
861
Jean Delvare21b48a72007-03-12 19:20:15 -0300862static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700864 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700866 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200867 if (V4L2_TUNER_RADIO == t->mode) {
868 if (t->radio_freq)
869 set_freq(c, t->radio_freq);
870 } else {
871 if (t->tv_freq)
872 set_freq(c, t->tv_freq);
873 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 return 0;
875}
876
877/* ----------------------------------------------------------------------- */
878
879static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700880 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700881 .attach_adapter = tuner_probe,
882 .detach_client = tuner_detach,
883 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300884 .suspend = tuner_suspend,
885 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200887 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200888 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700890static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200891 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700892 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893};
894
895static int __init tuner_init_module(void)
896{
897 return i2c_add_driver(&driver);
898}
899
900static void __exit tuner_cleanup_module(void)
901{
902 i2c_del_driver(&driver);
903}
904
905module_init(tuner_init_module);
906module_exit(tuner_cleanup_module);
907
908/*
909 * Overrides for Emacs so that we follow Linus's tabbing style.
910 * ---------------------------------------------------------------------------
911 * Local variables:
912 * c-basic-offset: 8
913 * End:
914 */