blob: 848ee6420c5c3c55e2960c9229d04053f227df47 [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 Krufky96c0b7c2007-08-27 21:23:08 -030022#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030023#include "tda8290.h"
Michael Krufky7ab10bf2007-08-27 21:23:40 -030024#include "tea5761.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26#define UNSET (-1U)
27
28/* standard i2c insmod options */
29static unsigned short normal_i2c[] = {
Adrian Bunk9ee476a2007-06-05 05:22:00 -030030#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030031 0x10,
32#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080033 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070034 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
35 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 I2C_CLIENT_END
37};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070038
Linus Torvalds1da177e2005-04-16 15:20:36 -070039I2C_CLIENT_INSMOD;
40
41/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070042static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070043static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070044static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070045
Linus Torvalds1da177e2005-04-16 15:20:36 -070046/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020047int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070049static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070050static unsigned int radio_range[2] = { 65, 108 };
51
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020052static char pal[] = "--";
53static char secam[] = "--";
54static char ntsc[] = "-";
55
Hans Verkuilf9195de2006-01-11 19:01:01 -020056
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020057module_param(addr, int, 0444);
58module_param(no_autodetect, int, 0444);
59module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020060module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020061module_param_string(pal, pal, sizeof(pal), 0644);
62module_param_string(secam, secam, sizeof(secam), 0644);
63module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070064module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070065module_param_array(radio_range, int, NULL, 0644);
66
67MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
68MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
69MODULE_LICENSE("GPL");
70
Linus Torvalds1da177e2005-04-16 15:20:36 -070071static struct i2c_driver driver;
72static struct i2c_client client_template;
73
74/* ---------------------------------------------------------------------- */
75
Michael Krufkye18f9442007-08-21 01:25:48 -030076static void fe_set_freq(struct tuner *t, unsigned int freq)
77{
78 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
79
80 struct analog_parameters params = {
81 .frequency = freq,
82 .mode = t->mode,
83 .audmode = t->audmode,
84 .std = t->std
85 };
86
87 if (NULL == fe_tuner_ops->set_analog_params) {
88 tuner_warn("Tuner frontend module has no way to set freq\n");
89 return;
90 }
91 fe_tuner_ops->set_analog_params(&t->fe, &params);
92}
93
94static void fe_release(struct tuner *t)
95{
96 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
97
98 if (fe_tuner_ops->release)
99 fe_tuner_ops->release(&t->fe);
100}
101
102static void fe_standby(struct tuner *t)
103{
104 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
105
106 if (fe_tuner_ops->sleep)
107 fe_tuner_ops->sleep(&t->fe);
108}
109
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700110/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111static void set_tv_freq(struct i2c_client *c, unsigned int freq)
112{
113 struct tuner *t = i2c_get_clientdata(c);
114
115 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700116 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 return;
118 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300119 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700120 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 return;
122 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700123 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
124 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
125 freq / 16, freq % 16 * 100 / 16, tv_range[0],
126 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200127 /* V4L2 spec: if the freq is not possible then the closest
128 possible value should be selected */
129 if (freq < tv_range[0] * 16)
130 freq = tv_range[0] * 16;
131 else
132 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300134 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135}
136
137static void set_radio_freq(struct i2c_client *c, unsigned int freq)
138{
139 struct tuner *t = i2c_get_clientdata(c);
140
141 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700142 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 return;
144 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300145 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700146 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 return;
148 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200149 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700150 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
151 freq / 16000, freq % 16000 * 100 / 16000,
152 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200153 /* V4L2 spec: if the freq is not possible then the closest
154 possible value should be selected */
155 if (freq < radio_range[0] * 16000)
156 freq = radio_range[0] * 16000;
157 else
158 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700160
Michael Krufkydb8a6952007-08-21 01:24:42 -0300161 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
164static void set_freq(struct i2c_client *c, unsigned long freq)
165{
166 struct tuner *t = i2c_get_clientdata(c);
167
168 switch (t->mode) {
169 case V4L2_TUNER_RADIO:
170 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700171 freq / 16000, freq % 16000 * 100 / 16000);
172 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200173 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 break;
175 case V4L2_TUNER_ANALOG_TV:
176 case V4L2_TUNER_DIGITAL_TV:
177 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700178 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200180 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 break;
182 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
184
Michael Krufky293197c2007-08-28 17:20:42 -0300185static void tuner_i2c_address_check(struct tuner *t)
186{
187 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
188 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
189 return;
190
191 tuner_warn("====================== WARNING! ======================\n");
192 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
193 tuner_warn("will soon be dropped. This message indicates that your\n");
194 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
195 t->i2c.name, t->i2c.addr);
196 tuner_warn("To ensure continued support for your device, please\n");
197 tuner_warn("send a copy of this message, along with full dmesg\n");
198 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
199 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
200 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
201 t->i2c.adapter->name, t->i2c.addr, t->type,
202 tuners[t->type].name);
203 tuner_warn("====================== WARNING! ======================\n");
204}
205
Michael Krufky910bb3e2007-08-27 21:22:20 -0300206static void attach_tda8290(struct tuner *t)
207{
208 struct tda8290_config cfg = {
209 .lna_cfg = &t->config,
210 .tuner_callback = t->tuner_callback
211 };
212 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
213}
214
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700215static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300216 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300217 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218{
219 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300220 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700221 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700223 if (type == UNSET || type == TUNER_ABSENT) {
224 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 return;
226 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700228 if (type >= tuner_count) {
229 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
230 return;
231 }
232
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300234 t->config = new_config;
235 if (tuner_callback != NULL) {
236 tuner_dbg("defining GPIO callback\n");
237 t->tuner_callback = tuner_callback;
238 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300239
240 /* This code detects calls by card attach_inform */
241 if (NULL == t->i2c.dev.driver) {
242 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
243
244 return;
245 }
246
Michael Krufkyb2083192007-05-29 22:54:06 -0300247 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300248 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300249 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300250 else {
251 kfree(t->priv);
252 t->priv = NULL;
253 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300254
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 switch (t->type) {
256 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300257 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 break;
259 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300260 {
261 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300263 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700264 case TUNER_TEA5767:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300265 if (tea5767_tuner_init(t) == EINVAL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700266 t->type = TUNER_ABSENT;
267 t->mode_mask = T_UNINITIALIZED;
268 return;
269 }
270 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700271 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300272#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300273 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300274 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300275 t->type = TUNER_ABSENT;
276 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300277 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300278 }
279 t->mode_mask = T_RADIO;
280 break;
281#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700282 case TUNER_PHILIPS_FMD1216ME_MK3:
283 buffer[0] = 0x0b;
284 buffer[1] = 0xdc;
285 buffer[2] = 0x9c;
286 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700287 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700288 mdelay(1);
289 buffer[2] = 0x86;
290 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700291 i2c_master_send(c, buffer, 4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300292 default_tuner_init(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700293 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800294 case TUNER_PHILIPS_TD1316:
295 buffer[0] = 0x0b;
296 buffer[1] = 0xdc;
297 buffer[2] = 0x86;
298 buffer[3] = 0xa4;
299 i2c_master_send(c,buffer,4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300300 default_tuner_init(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200301 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300302 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300303 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300304 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 default:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300306 default_tuner_init(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 break;
308 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700309
Michael Krufkye18f9442007-08-21 01:25:48 -0300310 if (fe_tuner_ops->set_analog_params) {
311 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
312
313 t->ops.set_tv_freq = fe_set_freq;
314 t->ops.set_radio_freq = fe_set_freq;
315 t->ops.standby = fe_standby;
316 t->ops.release = fe_release;
317 }
318
319 tuner_info("type set to %s\n", t->i2c.name);
320
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700321 if (t->mode_mask == T_UNINITIALIZED)
322 t->mode_mask = new_mode_mask;
323
Hans Verkuil27487d42006-01-15 15:04:52 -0200324 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700325 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100326 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700327 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300328 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700331/*
332 * This function apply tuner config to tuner specified
333 * by tun_setup structure. I addr is unset, then admin status
334 * and tun addr status is more precise then current status,
335 * it's applied. Otherwise status and type are applied only to
336 * tuner with exactly the same addr.
337*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700338
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700339static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700340{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700341 struct tuner *t = i2c_get_clientdata(c);
342
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300343 tuner_dbg("set addr for type %i\n", t->type);
344
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300345 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
346 (t->mode_mask & tun_setup->mode_mask))) ||
347 (tun_setup->addr == c->addr)) {
348 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300349 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700350 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700351}
352
353static inline int check_mode(struct tuner *t, char *cmd)
354{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700355 if ((1 << t->mode & t->mode_mask) == 0) {
356 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700357 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700358
359 switch (t->mode) {
360 case V4L2_TUNER_RADIO:
361 tuner_dbg("Cmd %s accepted for radio\n", cmd);
362 break;
363 case V4L2_TUNER_ANALOG_TV:
364 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
365 break;
366 case V4L2_TUNER_DIGITAL_TV:
367 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
368 break;
369 }
370 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700371}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700372
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700373/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374static int tuner_fixup_std(struct tuner *t)
375{
376 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300378 case '6':
379 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
380 t->std = V4L2_STD_PAL_60;
381 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 case 'b':
383 case 'B':
384 case 'g':
385 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700386 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 t->std = V4L2_STD_PAL_BG;
388 break;
389 case 'i':
390 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700391 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 t->std = V4L2_STD_PAL_I;
393 break;
394 case 'd':
395 case 'D':
396 case 'k':
397 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700398 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 t->std = V4L2_STD_PAL_DK;
400 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700401 case 'M':
402 case 'm':
403 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
404 t->std = V4L2_STD_PAL_M;
405 break;
406 case 'N':
407 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200408 if (pal[1] == 'c' || pal[1] == 'C') {
409 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
410 t->std = V4L2_STD_PAL_Nc;
411 } else {
412 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
413 t->std = V4L2_STD_PAL_N;
414 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700415 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700416 case '-':
417 /* default parameter, do nothing */
418 break;
419 default:
420 tuner_warn ("pal= argument not recognised\n");
421 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 }
423 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700424 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
425 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200426 case 'b':
427 case 'B':
428 case 'g':
429 case 'G':
430 case 'h':
431 case 'H':
432 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
433 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
434 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700435 case 'd':
436 case 'D':
437 case 'k':
438 case 'K':
439 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
440 t->std = V4L2_STD_SECAM_DK;
441 break;
442 case 'l':
443 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800444 if ((secam[1]=='C')||(secam[1]=='c')) {
445 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
446 t->std = V4L2_STD_SECAM_LC;
447 } else {
448 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
449 t->std = V4L2_STD_SECAM_L;
450 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700451 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700452 case '-':
453 /* default parameter, do nothing */
454 break;
455 default:
456 tuner_warn ("secam= argument not recognised\n");
457 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700458 }
459 }
460
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200461 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
462 switch (ntsc[0]) {
463 case 'm':
464 case 'M':
465 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
466 t->std = V4L2_STD_NTSC_M;
467 break;
468 case 'j':
469 case 'J':
470 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
471 t->std = V4L2_STD_NTSC_M_JP;
472 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200473 case 'k':
474 case 'K':
475 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
476 t->std = V4L2_STD_NTSC_M_KR;
477 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200478 case '-':
479 /* default parameter, do nothing */
480 break;
481 default:
482 tuner_info("ntsc= argument not recognised\n");
483 break;
484 }
485 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 return 0;
487}
488
Michael Krufkydb8a6952007-08-21 01:24:42 -0300489static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200490{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200491 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300492 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200493 const char *p;
494
495 switch (t->mode) {
496 case V4L2_TUNER_RADIO: p = "radio"; break;
497 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
498 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
499 default: p = "undefined"; break;
500 }
501 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200502 freq = t->radio_freq / 16000;
503 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200504 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200505 freq = t->tv_freq / 16;
506 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200507 }
508 tuner_info("Tuner mode: %s\n", p);
509 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300510 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200511 if (t->mode != V4L2_TUNER_RADIO)
512 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300513 if (fe_tuner_ops->get_status) {
514 u32 tuner_status;
515
516 fe_tuner_ops->get_status(&t->fe, &tuner_status);
517 if (tuner_status & TUNER_STATUS_LOCKED)
518 tuner_info("Tuner is locked.\n");
519 if (tuner_status & TUNER_STATUS_STEREO)
520 tuner_info("Stereo: yes\n");
521 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300522 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300523 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200524 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300525 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300526 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200527 }
528}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530/* ---------------------------------------------------------------------- */
531
Hans Verkuilba8fc392006-06-25 15:34:39 -0300532/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700533static unsigned default_mode_mask;
534
535/* During client attach, set_type is called by adapter's attach_inform callback.
536 set_type must then be completed by tuner_attach.
537 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
539{
540 struct tuner *t;
541
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700542 client_template.adapter = adap;
543 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544
Panagiotis Issaris74081872006-01-11 19:40:56 -0200545 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700546 if (NULL == t)
547 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700548 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700550 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700551 t->audmode = V4L2_TUNER_MODE_STEREO;
552 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300553 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700555 if (show_i2c) {
556 unsigned char buffer[16];
557 int i,rc;
558
559 memset(buffer, 0, sizeof(buffer));
560 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800561 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700562 for (i=0;i<rc;i++)
563 printk("%02x ",buffer[i]);
564 printk("\n");
565 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300566 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
567 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
568 return -ENODEV;
569
Markus Rechberger257c6452006-01-23 17:11:11 -0200570 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700571 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800572 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300573#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300574 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300575 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300576 t->type = TUNER_TEA5761;
577 t->mode_mask = T_RADIO;
578 t->mode = T_STANDBY;
579 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
580 default_mode_mask &= ~T_RADIO;
581
582 goto register_client;
583 }
584 break;
585#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800586 case 0x42:
587 case 0x43:
588 case 0x4a:
589 case 0x4b:
590 /* If chip is not tda8290, don't register.
591 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300592 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300593 tuner_dbg("chip at addr %x is a tda8290\n", addr);
594 } else {
595 /* Default is being tda9887 */
596 t->type = TUNER_TDA9887;
597 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
598 t->mode = T_STANDBY;
599 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800600 }
601 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800602 case 0x60:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300603 if (tea5767_autodetection(t) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700604 t->type = TUNER_TEA5767;
605 t->mode_mask = T_RADIO;
606 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200607 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700608 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700609
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800610 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700611 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800612 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700613 }
614 }
615
616 /* Initializes only the first adapter found */
617 if (default_mode_mask != T_UNINITIALIZED) {
618 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
619 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200620 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
621 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700622 default_mode_mask = T_UNINITIALIZED;
623 }
624
625 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800626register_client:
627 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700628 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300629 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return 0;
631}
632
633static int tuner_probe(struct i2c_adapter *adap)
634{
635 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700636 normal_i2c[0] = addr;
637 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700640 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 if (adap->class & I2C_CLASS_TV_ANALOG)
643 return i2c_probe(adap, &addr_data, tuner_attach);
644 return 0;
645}
646
647static int tuner_detach(struct i2c_client *client)
648{
649 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700650 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700652 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700653 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700654 tuner_warn
655 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700656 return err;
657 }
658
Michael Krufky7a91a802007-06-06 16:10:39 -0300659 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300660 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300661 else {
662 kfree(t->priv);
663 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 kfree(t);
665 return 0;
666}
667
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700668/*
669 * Switch tuner to other mode. If tuner support both tv and radio,
670 * set another frequency to some value (This is needed for some pal
671 * tuners to avoid locking). Otherwise, just put second tuner in
672 * standby mode.
673 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700675static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
676{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800677 if (mode == t->mode)
678 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700679
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800680 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700681
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800682 if (check_mode(t, cmd) == EINVAL) {
683 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300684 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300685 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800686 return EINVAL;
687 }
688 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700689}
690
691#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800692 tuner_dbg("switching to v4l2\n"); \
693 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700694
695static inline int check_v4l2(struct tuner *t)
696{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300697 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
698 TV, v4l1 for radio), until that is fixed this code is disabled.
699 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
700 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700701 return 0;
702}
703
704static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705{
706 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300707 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
Hans Verkuilf9195de2006-01-11 19:01:01 -0200709 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200710 v4l_i2c_print_ioctl(&(t->i2c),cmd);
711
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700712 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700714 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300715 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 -0700716 ((struct tuner_setup *)arg)->type,
717 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300718 ((struct tuner_setup *)arg)->mode_mask,
719 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700720
721 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700722 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200724 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
725 == EINVAL)
726 return 0;
727 if (t->radio_freq)
728 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700730 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200731 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
732 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300733 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300734 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300735 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200736 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300737#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700738 case VIDIOCSAUDIO:
739 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
740 return 0;
741 if (check_v4l2(t) == EINVAL)
742 return 0;
743
744 /* Should be implemented, since bttv calls it */
745 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700746 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700748 {
749 static const v4l2_std_id map[] = {
750 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
751 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
752 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
753 [4 /* bttv */ ] = V4L2_STD_PAL_M,
754 [5 /* bttv */ ] = V4L2_STD_PAL_N,
755 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
756 };
757 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700759 if (check_v4l2(t) == EINVAL)
760 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700761
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700762 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
763 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700765 if (vc->norm < ARRAY_SIZE(map))
766 t->std = map[vc->norm];
767 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200768 if (t->tv_freq)
769 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700770 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700771 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700772 case VIDIOCSFREQ:
773 {
774 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700775
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700776 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
777 return 0;
778 if (check_v4l2(t) == EINVAL)
779 return 0;
780
781 set_freq(client, *v);
782 return 0;
783 }
784 case VIDIOCGTUNER:
785 {
786 struct video_tuner *vt = arg;
787
788 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
789 return 0;
790 if (check_v4l2(t) == EINVAL)
791 return 0;
792
793 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300794 if (fe_tuner_ops->get_status) {
795 u32 tuner_status;
796
797 fe_tuner_ops->get_status(&t->fe, &tuner_status);
798 if (tuner_status & TUNER_STATUS_STEREO)
799 vt->flags |= VIDEO_TUNER_STEREO_ON;
800 else
801 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
802 vt->signal = tuner_status & TUNER_STATUS_LOCKED
803 ? 65535 : 0;
804 } else {
805 if (t->ops.is_stereo) {
806 if (t->ops.is_stereo(t))
807 vt->flags |=
808 VIDEO_TUNER_STEREO_ON;
809 else
810 vt->flags &=
811 ~VIDEO_TUNER_STEREO_ON;
812 }
813 if (t->ops.has_signal)
814 vt->signal = t->ops.has_signal(t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700815 }
816 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
817
818 vt->rangelow = radio_range[0] * 16000;
819 vt->rangehigh = radio_range[1] * 16000;
820
821 } else {
822 vt->rangelow = tv_range[0] * 16;
823 vt->rangehigh = tv_range[1] * 16;
824 }
825
826 return 0;
827 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700829 {
830 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700832 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
833 return 0;
834 if (check_v4l2(t) == EINVAL)
835 return 0;
836
Michael Krufkye18f9442007-08-21 01:25:48 -0300837 if (V4L2_TUNER_RADIO == t->mode) {
838 if (fe_tuner_ops->get_status) {
839 u32 tuner_status;
840
841 fe_tuner_ops->get_status(&t->fe, &tuner_status);
842 va->mode = (tuner_status & TUNER_STATUS_STEREO)
843 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
844 } else if (t->ops.is_stereo)
845 va->mode = t->ops.is_stereo(t)
846 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
847 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700848 return 0;
849 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300850#endif
851 case TDA9887_SET_CONFIG:
852 if (t->type == TUNER_TDA9887) {
853 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300855 t->tda9887_config = *i;
856 set_freq(client, t->tv_freq);
857 }
858 break;
859 /* --- v4l ioctls --- */
860 /* take care: bttv does userspace copying, we'll get a
861 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700863 {
864 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700866 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
867 == EINVAL)
868 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700869
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700870 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700872 t->std = *id;
873 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200874 if (t->tv_freq)
875 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700876 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700877 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700878 case VIDIOC_S_FREQUENCY:
879 {
880 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700881
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300882 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
883 == EINVAL)
884 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700885 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200886 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700887
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700888 break;
889 }
890 case VIDIOC_G_FREQUENCY:
891 {
892 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700893
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700894 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
895 return 0;
896 switch_v4l2();
897 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300898 if (fe_tuner_ops->get_frequency) {
899 u32 abs_freq;
900
901 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
902 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
903 (abs_freq * 2 + 125/2) / 125 :
904 (abs_freq + 62500/2) / 62500;
905 break;
906 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200907 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
908 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700909 break;
910 }
911 case VIDIOC_G_TUNER:
912 {
913 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700914
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700915 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
916 return 0;
917 switch_v4l2();
918
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200919 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300920 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300921 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300922 if (t->mode == V4L2_TUNER_ANALOG_TV)
923 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200924 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700925 tuner->rangelow = tv_range[0] * 16;
926 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200927 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700928 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200929
930 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200931 tuner->rxsubchans =
932 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300933 if (fe_tuner_ops->get_status) {
934 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200935
Michael Krufkye18f9442007-08-21 01:25:48 -0300936 fe_tuner_ops->get_status(&t->fe, &tuner_status);
937 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
938 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
939 tuner->signal = tuner_status & TUNER_STATUS_LOCKED ? 65535 : 0;
940 } else {
941 if (t->ops.is_stereo) {
942 tuner->rxsubchans = t->ops.is_stereo(t) ?
943 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
944 }
945 if (t->ops.has_signal)
946 tuner->signal = t->ops.has_signal(t);
947 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200948 tuner->capability |=
949 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
950 tuner->audmode = t->audmode;
951 tuner->rangelow = radio_range[0] * 16000;
952 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700953 break;
954 }
955 case VIDIOC_S_TUNER:
956 {
957 struct v4l2_tuner *tuner = arg;
958
959 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
960 return 0;
961
962 switch_v4l2();
963
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200964 /* do nothing unless we're a radio tuner */
965 if (t->mode != V4L2_TUNER_RADIO)
966 break;
967 t->audmode = tuner->audmode;
968 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700969 break;
970 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200971 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300972 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300973 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200974 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 }
976
977 return 0;
978}
979
Jean Delvare21b48a72007-03-12 19:20:15 -0300980static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700982 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700984 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 /* FIXME: power down ??? */
986 return 0;
987}
988
Jean Delvare21b48a72007-03-12 19:20:15 -0300989static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700991 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700993 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200994 if (V4L2_TUNER_RADIO == t->mode) {
995 if (t->radio_freq)
996 set_freq(c, t->radio_freq);
997 } else {
998 if (t->tv_freq)
999 set_freq(c, t->tv_freq);
1000 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 return 0;
1002}
1003
1004/* ----------------------------------------------------------------------- */
1005
1006static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001007 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001008 .attach_adapter = tuner_probe,
1009 .detach_client = tuner_detach,
1010 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001011 .suspend = tuner_suspend,
1012 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001014 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001015 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001017static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001018 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001019 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020};
1021
1022static int __init tuner_init_module(void)
1023{
1024 return i2c_add_driver(&driver);
1025}
1026
1027static void __exit tuner_cleanup_module(void)
1028{
1029 i2c_del_driver(&driver);
1030}
1031
1032module_init(tuner_init_module);
1033module_exit(tuner_cleanup_module);
1034
1035/*
1036 * Overrides for Emacs so that we follow Linus's tabbing style.
1037 * ---------------------------------------------------------------------------
1038 * Local variables:
1039 * c-basic-offset: 8
1040 * End:
1041 */