blob: f37fe83b199513c5d8cf53622cf0491545562911 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/string.h>
10#include <linux/timer.h>
11#include <linux/delay.h>
12#include <linux/errno.h>
13#include <linux/slab.h>
14#include <linux/poll.h>
15#include <linux/i2c.h>
16#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/init.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030018#include <linux/videodev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <media/tuner.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020020#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030021#include "tuner-driver.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030022#include "tda8290.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
Michael Krufkye18f9442007-08-21 01:25:48 -030074static void fe_set_freq(struct tuner *t, unsigned int freq)
75{
76 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
77
78 struct analog_parameters params = {
79 .frequency = freq,
80 .mode = t->mode,
81 .audmode = t->audmode,
82 .std = t->std
83 };
84
85 if (NULL == fe_tuner_ops->set_analog_params) {
86 tuner_warn("Tuner frontend module has no way to set freq\n");
87 return;
88 }
89 fe_tuner_ops->set_analog_params(&t->fe, &params);
90}
91
92static void fe_release(struct tuner *t)
93{
94 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
95
96 if (fe_tuner_ops->release)
97 fe_tuner_ops->release(&t->fe);
98}
99
100static void fe_standby(struct tuner *t)
101{
102 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
103
104 if (fe_tuner_ops->sleep)
105 fe_tuner_ops->sleep(&t->fe);
106}
107
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700108/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109static void set_tv_freq(struct i2c_client *c, unsigned int freq)
110{
111 struct tuner *t = i2c_get_clientdata(c);
112
113 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700114 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 return;
116 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300117 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700118 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 return;
120 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700121 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
122 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
123 freq / 16, freq % 16 * 100 / 16, tv_range[0],
124 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200125 /* V4L2 spec: if the freq is not possible then the closest
126 possible value should be selected */
127 if (freq < tv_range[0] * 16)
128 freq = tv_range[0] * 16;
129 else
130 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300132 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133}
134
135static void set_radio_freq(struct i2c_client *c, unsigned int freq)
136{
137 struct tuner *t = i2c_get_clientdata(c);
138
139 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700140 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 return;
142 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300143 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700144 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 return;
146 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200147 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700148 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
149 freq / 16000, freq % 16000 * 100 / 16000,
150 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200151 /* V4L2 spec: if the freq is not possible then the closest
152 possible value should be selected */
153 if (freq < radio_range[0] * 16000)
154 freq = radio_range[0] * 16000;
155 else
156 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700158
Michael Krufkydb8a6952007-08-21 01:24:42 -0300159 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160}
161
162static void set_freq(struct i2c_client *c, unsigned long freq)
163{
164 struct tuner *t = i2c_get_clientdata(c);
165
166 switch (t->mode) {
167 case V4L2_TUNER_RADIO:
168 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700169 freq / 16000, freq % 16000 * 100 / 16000);
170 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200171 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 break;
173 case V4L2_TUNER_ANALOG_TV:
174 case V4L2_TUNER_DIGITAL_TV:
175 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700176 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200178 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 break;
180 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181}
182
Michael Krufky293197c2007-08-28 17:20:42 -0300183static void tuner_i2c_address_check(struct tuner *t)
184{
185 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
186 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
187 return;
188
189 tuner_warn("====================== WARNING! ======================\n");
190 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
191 tuner_warn("will soon be dropped. This message indicates that your\n");
192 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
193 t->i2c.name, t->i2c.addr);
194 tuner_warn("To ensure continued support for your device, please\n");
195 tuner_warn("send a copy of this message, along with full dmesg\n");
196 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
197 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
198 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
199 t->i2c.adapter->name, t->i2c.addr, t->type,
200 tuners[t->type].name);
201 tuner_warn("====================== WARNING! ======================\n");
202}
203
Michael Krufky910bb3e2007-08-27 21:22:20 -0300204static void attach_tda8290(struct tuner *t)
205{
206 struct tda8290_config cfg = {
207 .lna_cfg = &t->config,
208 .tuner_callback = t->tuner_callback
209 };
210 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
211}
212
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700213static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300214 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300215 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
217 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300218 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700219 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700221 if (type == UNSET || type == TUNER_ABSENT) {
222 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 return;
224 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700226 if (type >= tuner_count) {
227 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
228 return;
229 }
230
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300232 t->config = new_config;
233 if (tuner_callback != NULL) {
234 tuner_dbg("defining GPIO callback\n");
235 t->tuner_callback = tuner_callback;
236 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300237
238 /* This code detects calls by card attach_inform */
239 if (NULL == t->i2c.dev.driver) {
240 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
241
242 return;
243 }
244
Michael Krufkyb2083192007-05-29 22:54:06 -0300245 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300246 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300247 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300248 else {
249 kfree(t->priv);
250 t->priv = NULL;
251 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300252
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 switch (t->type) {
254 case TUNER_MT2032:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300255 microtune_init(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 break;
257 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300258 {
259 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300261 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700262 case TUNER_TEA5767:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300263 if (tea5767_tuner_init(t) == EINVAL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700264 t->type = TUNER_ABSENT;
265 t->mode_mask = T_UNINITIALIZED;
266 return;
267 }
268 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700269 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300270#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300271 case TUNER_TEA5761:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300272 if (tea5761_tuner_init(t) == EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300273 t->type = TUNER_ABSENT;
274 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300275 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300276 }
277 t->mode_mask = T_RADIO;
278 break;
279#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700280 case TUNER_PHILIPS_FMD1216ME_MK3:
281 buffer[0] = 0x0b;
282 buffer[1] = 0xdc;
283 buffer[2] = 0x9c;
284 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700285 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700286 mdelay(1);
287 buffer[2] = 0x86;
288 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700289 i2c_master_send(c, buffer, 4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300290 default_tuner_init(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700291 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800292 case TUNER_PHILIPS_TD1316:
293 buffer[0] = 0x0b;
294 buffer[1] = 0xdc;
295 buffer[2] = 0x86;
296 buffer[3] = 0xa4;
297 i2c_master_send(c,buffer,4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300298 default_tuner_init(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200299 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300300 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300301 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300302 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 default:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300304 default_tuner_init(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 break;
306 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700307
Michael Krufkye18f9442007-08-21 01:25:48 -0300308 if (fe_tuner_ops->set_analog_params) {
309 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
310
311 t->ops.set_tv_freq = fe_set_freq;
312 t->ops.set_radio_freq = fe_set_freq;
313 t->ops.standby = fe_standby;
314 t->ops.release = fe_release;
315 }
316
317 tuner_info("type set to %s\n", t->i2c.name);
318
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700319 if (t->mode_mask == T_UNINITIALIZED)
320 t->mode_mask = new_mode_mask;
321
Hans Verkuil27487d42006-01-15 15:04:52 -0200322 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700323 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100324 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700325 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300326 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327}
328
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700329/*
330 * This function apply tuner config to tuner specified
331 * by tun_setup structure. I addr is unset, then admin status
332 * and tun addr status is more precise then current status,
333 * it's applied. Otherwise status and type are applied only to
334 * tuner with exactly the same addr.
335*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700336
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700337static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700338{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700339 struct tuner *t = i2c_get_clientdata(c);
340
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300341 tuner_dbg("set addr for type %i\n", t->type);
342
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300343 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
344 (t->mode_mask & tun_setup->mode_mask))) ||
345 (tun_setup->addr == c->addr)) {
346 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300347 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700348 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700349}
350
351static inline int check_mode(struct tuner *t, char *cmd)
352{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700353 if ((1 << t->mode & t->mode_mask) == 0) {
354 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700355 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700356
357 switch (t->mode) {
358 case V4L2_TUNER_RADIO:
359 tuner_dbg("Cmd %s accepted for radio\n", cmd);
360 break;
361 case V4L2_TUNER_ANALOG_TV:
362 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
363 break;
364 case V4L2_TUNER_DIGITAL_TV:
365 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
366 break;
367 }
368 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700369}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700370
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700371/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372static int tuner_fixup_std(struct tuner *t)
373{
374 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300376 case '6':
377 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
378 t->std = V4L2_STD_PAL_60;
379 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 case 'b':
381 case 'B':
382 case 'g':
383 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700384 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 t->std = V4L2_STD_PAL_BG;
386 break;
387 case 'i':
388 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700389 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 t->std = V4L2_STD_PAL_I;
391 break;
392 case 'd':
393 case 'D':
394 case 'k':
395 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700396 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 t->std = V4L2_STD_PAL_DK;
398 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700399 case 'M':
400 case 'm':
401 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
402 t->std = V4L2_STD_PAL_M;
403 break;
404 case 'N':
405 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200406 if (pal[1] == 'c' || pal[1] == 'C') {
407 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
408 t->std = V4L2_STD_PAL_Nc;
409 } else {
410 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
411 t->std = V4L2_STD_PAL_N;
412 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700413 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700414 case '-':
415 /* default parameter, do nothing */
416 break;
417 default:
418 tuner_warn ("pal= argument not recognised\n");
419 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 }
421 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700422 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
423 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200424 case 'b':
425 case 'B':
426 case 'g':
427 case 'G':
428 case 'h':
429 case 'H':
430 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
431 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
432 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700433 case 'd':
434 case 'D':
435 case 'k':
436 case 'K':
437 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
438 t->std = V4L2_STD_SECAM_DK;
439 break;
440 case 'l':
441 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800442 if ((secam[1]=='C')||(secam[1]=='c')) {
443 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
444 t->std = V4L2_STD_SECAM_LC;
445 } else {
446 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
447 t->std = V4L2_STD_SECAM_L;
448 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700449 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700450 case '-':
451 /* default parameter, do nothing */
452 break;
453 default:
454 tuner_warn ("secam= argument not recognised\n");
455 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700456 }
457 }
458
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200459 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
460 switch (ntsc[0]) {
461 case 'm':
462 case 'M':
463 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
464 t->std = V4L2_STD_NTSC_M;
465 break;
466 case 'j':
467 case 'J':
468 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
469 t->std = V4L2_STD_NTSC_M_JP;
470 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200471 case 'k':
472 case 'K':
473 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
474 t->std = V4L2_STD_NTSC_M_KR;
475 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200476 case '-':
477 /* default parameter, do nothing */
478 break;
479 default:
480 tuner_info("ntsc= argument not recognised\n");
481 break;
482 }
483 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 return 0;
485}
486
Michael Krufkydb8a6952007-08-21 01:24:42 -0300487static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200488{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200489 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300490 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200491 const char *p;
492
493 switch (t->mode) {
494 case V4L2_TUNER_RADIO: p = "radio"; break;
495 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
496 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
497 default: p = "undefined"; break;
498 }
499 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200500 freq = t->radio_freq / 16000;
501 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200502 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200503 freq = t->tv_freq / 16;
504 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200505 }
506 tuner_info("Tuner mode: %s\n", p);
507 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300508 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200509 if (t->mode != V4L2_TUNER_RADIO)
510 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300511 if (fe_tuner_ops->get_status) {
512 u32 tuner_status;
513
514 fe_tuner_ops->get_status(&t->fe, &tuner_status);
515 if (tuner_status & TUNER_STATUS_LOCKED)
516 tuner_info("Tuner is locked.\n");
517 if (tuner_status & TUNER_STATUS_STEREO)
518 tuner_info("Stereo: yes\n");
519 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300520 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300521 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200522 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300523 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300524 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200525 }
526}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200527
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528/* ---------------------------------------------------------------------- */
529
Hans Verkuilba8fc392006-06-25 15:34:39 -0300530/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700531static unsigned default_mode_mask;
532
533/* During client attach, set_type is called by adapter's attach_inform callback.
534 set_type must then be completed by tuner_attach.
535 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
537{
538 struct tuner *t;
539
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700540 client_template.adapter = adap;
541 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Panagiotis Issaris74081872006-01-11 19:40:56 -0200543 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700544 if (NULL == t)
545 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700546 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700548 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700549 t->audmode = V4L2_TUNER_MODE_STEREO;
550 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300551 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700553 if (show_i2c) {
554 unsigned char buffer[16];
555 int i,rc;
556
557 memset(buffer, 0, sizeof(buffer));
558 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800559 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700560 for (i=0;i<rc;i++)
561 printk("%02x ",buffer[i]);
562 printk("\n");
563 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300564 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
565 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
566 return -ENODEV;
567
Markus Rechberger257c6452006-01-23 17:11:11 -0200568 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700569 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800570 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300571#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300572 case 0x10:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300573 if (tea5761_autodetection(t) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300574 t->type = TUNER_TEA5761;
575 t->mode_mask = T_RADIO;
576 t->mode = T_STANDBY;
577 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
578 default_mode_mask &= ~T_RADIO;
579
580 goto register_client;
581 }
582 break;
583#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800584 case 0x42:
585 case 0x43:
586 case 0x4a:
587 case 0x4b:
588 /* If chip is not tda8290, don't register.
589 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300590 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300591 tuner_dbg("chip at addr %x is a tda8290\n", addr);
592 } else {
593 /* Default is being tda9887 */
594 t->type = TUNER_TDA9887;
595 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
596 t->mode = T_STANDBY;
597 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800598 }
599 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800600 case 0x60:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300601 if (tea5767_autodetection(t) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700602 t->type = TUNER_TEA5767;
603 t->mode_mask = T_RADIO;
604 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200605 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700606 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700607
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800608 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700609 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800610 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700611 }
612 }
613
614 /* Initializes only the first adapter found */
615 if (default_mode_mask != T_UNINITIALIZED) {
616 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
617 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200618 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
619 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700620 default_mode_mask = T_UNINITIALIZED;
621 }
622
623 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800624register_client:
625 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700626 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300627 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 return 0;
629}
630
631static int tuner_probe(struct i2c_adapter *adap)
632{
633 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700634 normal_i2c[0] = addr;
635 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700638 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700639
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 if (adap->class & I2C_CLASS_TV_ANALOG)
641 return i2c_probe(adap, &addr_data, tuner_attach);
642 return 0;
643}
644
645static int tuner_detach(struct i2c_client *client)
646{
647 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700648 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700650 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700651 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700652 tuner_warn
653 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700654 return err;
655 }
656
Michael Krufky7a91a802007-06-06 16:10:39 -0300657 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300658 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300659 else {
660 kfree(t->priv);
661 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 kfree(t);
663 return 0;
664}
665
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700666/*
667 * Switch tuner to other mode. If tuner support both tv and radio,
668 * set another frequency to some value (This is needed for some pal
669 * tuners to avoid locking). Otherwise, just put second tuner in
670 * standby mode.
671 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700673static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
674{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800675 if (mode == t->mode)
676 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700677
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800678 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700679
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800680 if (check_mode(t, cmd) == EINVAL) {
681 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300682 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300683 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800684 return EINVAL;
685 }
686 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700687}
688
689#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800690 tuner_dbg("switching to v4l2\n"); \
691 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700692
693static inline int check_v4l2(struct tuner *t)
694{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300695 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
696 TV, v4l1 for radio), until that is fixed this code is disabled.
697 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
698 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700699 return 0;
700}
701
702static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703{
704 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300705 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706
Hans Verkuilf9195de2006-01-11 19:01:01 -0200707 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200708 v4l_i2c_print_ioctl(&(t->i2c),cmd);
709
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700710 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700712 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300713 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 -0700714 ((struct tuner_setup *)arg)->type,
715 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300716 ((struct tuner_setup *)arg)->mode_mask,
717 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700718
719 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700720 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200722 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
723 == EINVAL)
724 return 0;
725 if (t->radio_freq)
726 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700728 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200729 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
730 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300731 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300732 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300733 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200734 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300735#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700736 case VIDIOCSAUDIO:
737 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
738 return 0;
739 if (check_v4l2(t) == EINVAL)
740 return 0;
741
742 /* Should be implemented, since bttv calls it */
743 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700744 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700746 {
747 static const v4l2_std_id map[] = {
748 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
749 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
750 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
751 [4 /* bttv */ ] = V4L2_STD_PAL_M,
752 [5 /* bttv */ ] = V4L2_STD_PAL_N,
753 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
754 };
755 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700757 if (check_v4l2(t) == EINVAL)
758 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700759
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
761 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700763 if (vc->norm < ARRAY_SIZE(map))
764 t->std = map[vc->norm];
765 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200766 if (t->tv_freq)
767 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700768 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700769 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700770 case VIDIOCSFREQ:
771 {
772 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700773
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700774 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
775 return 0;
776 if (check_v4l2(t) == EINVAL)
777 return 0;
778
779 set_freq(client, *v);
780 return 0;
781 }
782 case VIDIOCGTUNER:
783 {
784 struct video_tuner *vt = arg;
785
786 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
787 return 0;
788 if (check_v4l2(t) == EINVAL)
789 return 0;
790
791 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300792 if (fe_tuner_ops->get_status) {
793 u32 tuner_status;
794
795 fe_tuner_ops->get_status(&t->fe, &tuner_status);
796 if (tuner_status & TUNER_STATUS_STEREO)
797 vt->flags |= VIDEO_TUNER_STEREO_ON;
798 else
799 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
800 vt->signal = tuner_status & TUNER_STATUS_LOCKED
801 ? 65535 : 0;
802 } else {
803 if (t->ops.is_stereo) {
804 if (t->ops.is_stereo(t))
805 vt->flags |=
806 VIDEO_TUNER_STEREO_ON;
807 else
808 vt->flags &=
809 ~VIDEO_TUNER_STEREO_ON;
810 }
811 if (t->ops.has_signal)
812 vt->signal = t->ops.has_signal(t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700813 }
814 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
815
816 vt->rangelow = radio_range[0] * 16000;
817 vt->rangehigh = radio_range[1] * 16000;
818
819 } else {
820 vt->rangelow = tv_range[0] * 16;
821 vt->rangehigh = tv_range[1] * 16;
822 }
823
824 return 0;
825 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700827 {
828 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700830 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
831 return 0;
832 if (check_v4l2(t) == EINVAL)
833 return 0;
834
Michael Krufkye18f9442007-08-21 01:25:48 -0300835 if (V4L2_TUNER_RADIO == t->mode) {
836 if (fe_tuner_ops->get_status) {
837 u32 tuner_status;
838
839 fe_tuner_ops->get_status(&t->fe, &tuner_status);
840 va->mode = (tuner_status & TUNER_STATUS_STEREO)
841 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
842 } else if (t->ops.is_stereo)
843 va->mode = t->ops.is_stereo(t)
844 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
845 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700846 return 0;
847 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300848#endif
849 case TDA9887_SET_CONFIG:
850 if (t->type == TUNER_TDA9887) {
851 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300853 t->tda9887_config = *i;
854 set_freq(client, t->tv_freq);
855 }
856 break;
857 /* --- v4l ioctls --- */
858 /* take care: bttv does userspace copying, we'll get a
859 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700861 {
862 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700864 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
865 == EINVAL)
866 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700867
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700868 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700870 t->std = *id;
871 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200872 if (t->tv_freq)
873 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700874 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700875 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700876 case VIDIOC_S_FREQUENCY:
877 {
878 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700879
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300880 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
881 == EINVAL)
882 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700883 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200884 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700885
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700886 break;
887 }
888 case VIDIOC_G_FREQUENCY:
889 {
890 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700891
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700892 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
893 return 0;
894 switch_v4l2();
895 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300896 if (fe_tuner_ops->get_frequency) {
897 u32 abs_freq;
898
899 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
900 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
901 (abs_freq * 2 + 125/2) / 125 :
902 (abs_freq + 62500/2) / 62500;
903 break;
904 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200905 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
906 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700907 break;
908 }
909 case VIDIOC_G_TUNER:
910 {
911 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700912
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700913 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
914 return 0;
915 switch_v4l2();
916
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200917 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300918 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300919 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300920 if (t->mode == V4L2_TUNER_ANALOG_TV)
921 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200922 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700923 tuner->rangelow = tv_range[0] * 16;
924 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200925 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700926 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200927
928 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200929 tuner->rxsubchans =
930 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300931 if (fe_tuner_ops->get_status) {
932 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200933
Michael Krufkye18f9442007-08-21 01:25:48 -0300934 fe_tuner_ops->get_status(&t->fe, &tuner_status);
935 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
936 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
937 tuner->signal = tuner_status & TUNER_STATUS_LOCKED ? 65535 : 0;
938 } else {
939 if (t->ops.is_stereo) {
940 tuner->rxsubchans = t->ops.is_stereo(t) ?
941 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
942 }
943 if (t->ops.has_signal)
944 tuner->signal = t->ops.has_signal(t);
945 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200946 tuner->capability |=
947 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
948 tuner->audmode = t->audmode;
949 tuner->rangelow = radio_range[0] * 16000;
950 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700951 break;
952 }
953 case VIDIOC_S_TUNER:
954 {
955 struct v4l2_tuner *tuner = arg;
956
957 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
958 return 0;
959
960 switch_v4l2();
961
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200962 /* do nothing unless we're a radio tuner */
963 if (t->mode != V4L2_TUNER_RADIO)
964 break;
965 t->audmode = tuner->audmode;
966 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700967 break;
968 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200969 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300970 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300971 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200972 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 }
974
975 return 0;
976}
977
Jean Delvare21b48a72007-03-12 19:20:15 -0300978static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700980 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700982 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 /* FIXME: power down ??? */
984 return 0;
985}
986
Jean Delvare21b48a72007-03-12 19:20:15 -0300987static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700989 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700991 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200992 if (V4L2_TUNER_RADIO == t->mode) {
993 if (t->radio_freq)
994 set_freq(c, t->radio_freq);
995 } else {
996 if (t->tv_freq)
997 set_freq(c, t->tv_freq);
998 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 return 0;
1000}
1001
1002/* ----------------------------------------------------------------------- */
1003
1004static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001005 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001006 .attach_adapter = tuner_probe,
1007 .detach_client = tuner_detach,
1008 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001009 .suspend = tuner_suspend,
1010 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001012 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001013 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001015static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001016 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001017 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018};
1019
1020static int __init tuner_init_module(void)
1021{
1022 return i2c_add_driver(&driver);
1023}
1024
1025static void __exit tuner_cleanup_module(void)
1026{
1027 i2c_del_driver(&driver);
1028}
1029
1030module_init(tuner_init_module);
1031module_exit(tuner_cleanup_module);
1032
1033/*
1034 * Overrides for Emacs so that we follow Linus's tabbing style.
1035 * ---------------------------------------------------------------------------
1036 * Local variables:
1037 * c-basic-offset: 8
1038 * End:
1039 */