blob: f31de98bbfa54187f20d7252763bfd52885956a1 [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 Krufky4adad282007-08-27 21:59:08 -030020#include <media/tuner-types.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020021#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030022#include "tuner-driver.h"
Michael Krufky96c0b7c2007-08-27 21:23:08 -030023#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030024#include "tda8290.h"
Michael Krufky7ab10bf2007-08-27 21:23:40 -030025#include "tea5761.h"
Michael Krufky8d0936e2007-08-27 21:24:27 -030026#include "tea5767.h"
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030027#include "tuner-xc2028.h"
Michael Krufky4adad282007-08-27 21:59:08 -030028#include "tuner-simple.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#define UNSET (-1U)
31
32/* standard i2c insmod options */
33static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030034#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030035 0x10,
36#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080037 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070038 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
39 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 I2C_CLIENT_END
41};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043I2C_CLIENT_INSMOD;
44
45/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070046static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070047static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070048static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070049
Linus Torvalds1da177e2005-04-16 15:20:36 -070050/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020051int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070053static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070054static unsigned int radio_range[2] = { 65, 108 };
55
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020056static char pal[] = "--";
57static char secam[] = "--";
58static char ntsc[] = "-";
59
Hans Verkuilf9195de2006-01-11 19:01:01 -020060
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020061module_param(addr, int, 0444);
62module_param(no_autodetect, int, 0444);
63module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020064module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020065module_param_string(pal, pal, sizeof(pal), 0644);
66module_param_string(secam, secam, sizeof(secam), 0644);
67module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070068module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069module_param_array(radio_range, int, NULL, 0644);
70
71MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
72MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
73MODULE_LICENSE("GPL");
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static struct i2c_driver driver;
76static struct i2c_client client_template;
77
78/* ---------------------------------------------------------------------- */
79
Michael Krufkye18f9442007-08-21 01:25:48 -030080static void fe_set_freq(struct tuner *t, unsigned int freq)
81{
82 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
83
84 struct analog_parameters params = {
85 .frequency = freq,
86 .mode = t->mode,
87 .audmode = t->audmode,
88 .std = t->std
89 };
90
91 if (NULL == fe_tuner_ops->set_analog_params) {
92 tuner_warn("Tuner frontend module has no way to set freq\n");
93 return;
94 }
95 fe_tuner_ops->set_analog_params(&t->fe, &params);
96}
97
98static void fe_release(struct tuner *t)
99{
100 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
101
102 if (fe_tuner_ops->release)
103 fe_tuner_ops->release(&t->fe);
104}
105
106static void fe_standby(struct tuner *t)
107{
108 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
109
110 if (fe_tuner_ops->sleep)
111 fe_tuner_ops->sleep(&t->fe);
112}
113
Michael Krufky1f5ef192007-08-31 17:38:02 -0300114static int fe_has_signal(struct tuner *t)
115{
116 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky14196832007-10-14 18:11:53 -0300117 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300118
119 if (fe_tuner_ops->get_rf_strength)
120 fe_tuner_ops->get_rf_strength(&t->fe, &strength);
121
122 return strength;
123}
124
Michael Krufky1dde7a42007-10-21 13:40:56 -0300125static void tuner_status(struct tuner *t);
126
127static struct analog_tuner_ops tuner_core_ops = {
128 .set_tv_freq = fe_set_freq,
129 .set_radio_freq = fe_set_freq,
130 .standby = fe_standby,
131 .release = fe_release,
132 .has_signal = fe_has_signal,
133 .tuner_status = tuner_status
134};
135
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700136/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137static void set_tv_freq(struct i2c_client *c, unsigned int freq)
138{
139 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300140 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
142 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700143 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 return;
145 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300146 if ((NULL == ops) || (NULL == ops->set_tv_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700147 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 return;
149 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700150 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
151 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
152 freq / 16, freq % 16 * 100 / 16, tv_range[0],
153 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200154 /* V4L2 spec: if the freq is not possible then the closest
155 possible value should be selected */
156 if (freq < tv_range[0] * 16)
157 freq = tv_range[0] * 16;
158 else
159 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300161 ops->set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
164static void set_radio_freq(struct i2c_client *c, unsigned int freq)
165{
166 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300167 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
169 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700170 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 return;
172 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300173 if ((NULL == ops) || (NULL == ops->set_radio_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700174 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 return;
176 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200177 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700178 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
179 freq / 16000, freq % 16000 * 100 / 16000,
180 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200181 /* V4L2 spec: if the freq is not possible then the closest
182 possible value should be selected */
183 if (freq < radio_range[0] * 16000)
184 freq = radio_range[0] * 16000;
185 else
186 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700188
Michael Krufky1dde7a42007-10-21 13:40:56 -0300189 ops->set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190}
191
192static void set_freq(struct i2c_client *c, unsigned long freq)
193{
194 struct tuner *t = i2c_get_clientdata(c);
195
196 switch (t->mode) {
197 case V4L2_TUNER_RADIO:
198 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700199 freq / 16000, freq % 16000 * 100 / 16000);
200 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200201 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 break;
203 case V4L2_TUNER_ANALOG_TV:
204 case V4L2_TUNER_DIGITAL_TV:
205 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700206 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200208 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300210 default:
211 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213}
214
Michael Krufky293197c2007-08-28 17:20:42 -0300215static void tuner_i2c_address_check(struct tuner *t)
216{
217 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
218 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
219 return;
220
221 tuner_warn("====================== WARNING! ======================\n");
222 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
223 tuner_warn("will soon be dropped. This message indicates that your\n");
224 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
225 t->i2c.name, t->i2c.addr);
226 tuner_warn("To ensure continued support for your device, please\n");
227 tuner_warn("send a copy of this message, along with full dmesg\n");
228 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
229 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
230 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
231 t->i2c.adapter->name, t->i2c.addr, t->type,
232 tuners[t->type].name);
233 tuner_warn("====================== WARNING! ======================\n");
234}
235
Michael Krufky4adad282007-08-27 21:59:08 -0300236static void attach_simple_tuner(struct tuner *t)
237{
238 struct simple_tuner_config cfg = {
239 .type = t->type,
240 .tun = &tuners[t->type]
241 };
242 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
243}
244
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700245static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300246 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300247 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248{
249 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300250 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300251 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700252 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700254 if (type == UNSET || type == TUNER_ABSENT) {
255 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 return;
257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700259 if (type >= tuner_count) {
260 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
261 return;
262 }
263
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300265 t->config = new_config;
266 if (tuner_callback != NULL) {
267 tuner_dbg("defining GPIO callback\n");
268 t->tuner_callback = tuner_callback;
269 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300270
271 /* This code detects calls by card attach_inform */
272 if (NULL == t->i2c.dev.driver) {
273 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
274
275 return;
276 }
277
Michael Krufkyb2083192007-05-29 22:54:06 -0300278 /* discard private data, in case set_type() was previously called */
Michael Krufky1dde7a42007-10-21 13:40:56 -0300279 if ((ops) && (ops->release))
280 ops->release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300281 else {
282 kfree(t->priv);
283 t->priv = NULL;
284 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300285
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 switch (t->type) {
287 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300288 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 break;
290 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300291 {
Michael Krufky746d97322007-08-25 19:08:45 -0300292 tda8290_attach(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300294 }
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300295 case TUNER_PHILIPS_TDA8295:
296 {
297 tda8295_attach(t);
298 break;
299 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700300 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300301 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700302 t->type = TUNER_ABSENT;
303 t->mode_mask = T_UNINITIALIZED;
304 return;
305 }
306 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700307 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300308 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300309 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300310 t->type = TUNER_ABSENT;
311 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300312 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300313 }
314 t->mode_mask = T_RADIO;
315 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700316 case TUNER_PHILIPS_FMD1216ME_MK3:
317 buffer[0] = 0x0b;
318 buffer[1] = 0xdc;
319 buffer[2] = 0x9c;
320 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700321 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700322 mdelay(1);
323 buffer[2] = 0x86;
324 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700325 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300326 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700327 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800328 case TUNER_PHILIPS_TD1316:
329 buffer[0] = 0x0b;
330 buffer[1] = 0xdc;
331 buffer[2] = 0x86;
332 buffer[3] = 0xa4;
333 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300334 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200335 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300336 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300337 {
338 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
339 &c->dev, c->adapter->algo_data,
340 t->tuner_callback);
341 if (rc<0) {
342 t->type = TUNER_ABSENT;
343 t->mode_mask = T_UNINITIALIZED;
344 return;
345 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300346 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300347 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300348 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300349 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300350 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300352 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 break;
354 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700355
Michael Krufky1dde7a42007-10-21 13:40:56 -0300356 if (((NULL == ops) ||
357 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
358 (fe_tuner_ops->set_analog_params)) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300359 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
360
Michael Krufky1dde7a42007-10-21 13:40:56 -0300361 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufkye18f9442007-08-21 01:25:48 -0300362 }
363
364 tuner_info("type set to %s\n", t->i2c.name);
365
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700366 if (t->mode_mask == T_UNINITIALIZED)
367 t->mode_mask = new_mode_mask;
368
Hans Verkuil27487d42006-01-15 15:04:52 -0200369 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700370 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100371 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700372 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300373 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374}
375
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700376/*
377 * This function apply tuner config to tuner specified
378 * by tun_setup structure. I addr is unset, then admin status
379 * and tun addr status is more precise then current status,
380 * it's applied. Otherwise status and type are applied only to
381 * tuner with exactly the same addr.
382*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700383
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700384static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700385{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700386 struct tuner *t = i2c_get_clientdata(c);
387
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300388 tuner_dbg("set addr for type %i\n", t->type);
389
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300390 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
391 (t->mode_mask & tun_setup->mode_mask))) ||
392 (tun_setup->addr == c->addr)) {
393 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300394 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700395 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700396}
397
398static inline int check_mode(struct tuner *t, char *cmd)
399{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700400 if ((1 << t->mode & t->mode_mask) == 0) {
401 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700402 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700403
404 switch (t->mode) {
405 case V4L2_TUNER_RADIO:
406 tuner_dbg("Cmd %s accepted for radio\n", cmd);
407 break;
408 case V4L2_TUNER_ANALOG_TV:
409 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
410 break;
411 case V4L2_TUNER_DIGITAL_TV:
412 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
413 break;
414 }
415 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700416}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700417
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700418/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419static int tuner_fixup_std(struct tuner *t)
420{
421 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300423 case '6':
424 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
425 t->std = V4L2_STD_PAL_60;
426 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 case 'b':
428 case 'B':
429 case 'g':
430 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700431 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 t->std = V4L2_STD_PAL_BG;
433 break;
434 case 'i':
435 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700436 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 t->std = V4L2_STD_PAL_I;
438 break;
439 case 'd':
440 case 'D':
441 case 'k':
442 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700443 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 t->std = V4L2_STD_PAL_DK;
445 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700446 case 'M':
447 case 'm':
448 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
449 t->std = V4L2_STD_PAL_M;
450 break;
451 case 'N':
452 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200453 if (pal[1] == 'c' || pal[1] == 'C') {
454 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
455 t->std = V4L2_STD_PAL_Nc;
456 } else {
457 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
458 t->std = V4L2_STD_PAL_N;
459 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700460 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700461 case '-':
462 /* default parameter, do nothing */
463 break;
464 default:
465 tuner_warn ("pal= argument not recognised\n");
466 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 }
468 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700469 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
470 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200471 case 'b':
472 case 'B':
473 case 'g':
474 case 'G':
475 case 'h':
476 case 'H':
477 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
478 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
479 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700480 case 'd':
481 case 'D':
482 case 'k':
483 case 'K':
484 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
485 t->std = V4L2_STD_SECAM_DK;
486 break;
487 case 'l':
488 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800489 if ((secam[1]=='C')||(secam[1]=='c')) {
490 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
491 t->std = V4L2_STD_SECAM_LC;
492 } else {
493 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
494 t->std = V4L2_STD_SECAM_L;
495 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700496 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700497 case '-':
498 /* default parameter, do nothing */
499 break;
500 default:
501 tuner_warn ("secam= argument not recognised\n");
502 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700503 }
504 }
505
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200506 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
507 switch (ntsc[0]) {
508 case 'm':
509 case 'M':
510 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
511 t->std = V4L2_STD_NTSC_M;
512 break;
513 case 'j':
514 case 'J':
515 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
516 t->std = V4L2_STD_NTSC_M_JP;
517 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200518 case 'k':
519 case 'K':
520 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
521 t->std = V4L2_STD_NTSC_M_KR;
522 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200523 case '-':
524 /* default parameter, do nothing */
525 break;
526 default:
527 tuner_info("ntsc= argument not recognised\n");
528 break;
529 }
530 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 return 0;
532}
533
Michael Krufkydb8a6952007-08-21 01:24:42 -0300534static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200535{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200536 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300537 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300538 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200539 const char *p;
540
541 switch (t->mode) {
542 case V4L2_TUNER_RADIO: p = "radio"; break;
543 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
544 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
545 default: p = "undefined"; break;
546 }
547 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200548 freq = t->radio_freq / 16000;
549 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200550 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200551 freq = t->tv_freq / 16;
552 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200553 }
554 tuner_info("Tuner mode: %s\n", p);
555 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300556 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200557 if (t->mode != V4L2_TUNER_RADIO)
558 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300559 if (fe_tuner_ops->get_status) {
560 u32 tuner_status;
561
562 fe_tuner_ops->get_status(&t->fe, &tuner_status);
563 if (tuner_status & TUNER_STATUS_LOCKED)
564 tuner_info("Tuner is locked.\n");
565 if (tuner_status & TUNER_STATUS_STEREO)
566 tuner_info("Stereo: yes\n");
567 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300568 if ((ops) && (ops->has_signal)) {
569 tuner_info("Signal strength: %d\n", ops->has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200570 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300571 if ((ops) && (ops->is_stereo)) {
572 tuner_info("Stereo: %s\n", ops->is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200573 }
574}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200575
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576/* ---------------------------------------------------------------------- */
577
Hans Verkuilba8fc392006-06-25 15:34:39 -0300578/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700579static unsigned default_mode_mask;
580
581/* During client attach, set_type is called by adapter's attach_inform callback.
582 set_type must then be completed by tuner_attach.
583 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
585{
586 struct tuner *t;
587
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700588 client_template.adapter = adap;
589 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Panagiotis Issaris74081872006-01-11 19:40:56 -0200591 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700592 if (NULL == t)
593 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700594 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700596 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700597 t->audmode = V4L2_TUNER_MODE_STEREO;
598 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700600 if (show_i2c) {
601 unsigned char buffer[16];
602 int i,rc;
603
604 memset(buffer, 0, sizeof(buffer));
605 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800606 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700607 for (i=0;i<rc;i++)
608 printk("%02x ",buffer[i]);
609 printk("\n");
610 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300611 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
612 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
613 return -ENODEV;
614
Markus Rechberger257c6452006-01-23 17:11:11 -0200615 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700616 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800617 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300618 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300619 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300620 t->type = TUNER_TEA5761;
621 t->mode_mask = T_RADIO;
622 t->mode = T_STANDBY;
623 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
624 default_mode_mask &= ~T_RADIO;
625
626 goto register_client;
627 }
628 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800629 case 0x42:
630 case 0x43:
631 case 0x4a:
632 case 0x4b:
633 /* If chip is not tda8290, don't register.
634 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300635 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300636 tuner_dbg("chip at addr %x is a tda8290\n", addr);
637 } else {
638 /* Default is being tda9887 */
639 t->type = TUNER_TDA9887;
640 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
641 t->mode = T_STANDBY;
642 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800643 }
644 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800645 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300646 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700647 t->type = TUNER_TEA5767;
648 t->mode_mask = T_RADIO;
649 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200650 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700651 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700652
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800653 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700654 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800655 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700656 }
657 }
658
659 /* Initializes only the first adapter found */
660 if (default_mode_mask != T_UNINITIALIZED) {
661 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
662 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200663 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
664 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700665 default_mode_mask = T_UNINITIALIZED;
666 }
667
668 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800669register_client:
670 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700671 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300672 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 return 0;
674}
675
676static int tuner_probe(struct i2c_adapter *adap)
677{
678 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700679 normal_i2c[0] = addr;
680 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
Michael Krufkya1dec512007-08-24 01:13:07 -0300683 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
684 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
685 * and an RTC at 0x6f which can get corrupted if probed.
686 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300687 if ((adap->id == I2C_HW_B_CX2388x) ||
688 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300689 unsigned int i = 0;
690
691 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
692 i += 2;
693 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
694 ignore[i+0] = adap->nr;
695 ignore[i+1] = 0x6b;
696 ignore[i+2] = adap->nr;
697 ignore[i+3] = 0x6f;
698 ignore[i+4] = I2C_CLIENT_END;
699 } else
700 printk(KERN_WARNING "tuner: "
701 "too many options specified "
702 "in i2c probe ignore list!\n");
703 }
704
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700705 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700706
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 if (adap->class & I2C_CLASS_TV_ANALOG)
708 return i2c_probe(adap, &addr_data, tuner_attach);
709 return 0;
710}
711
712static int tuner_detach(struct i2c_client *client)
713{
714 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300715 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700716 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700718 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700719 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700720 tuner_warn
721 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700722 return err;
723 }
724
Michael Krufky1dde7a42007-10-21 13:40:56 -0300725 if ((ops) && (ops->release))
726 ops->release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300727 else {
728 kfree(t->priv);
729 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 kfree(t);
731 return 0;
732}
733
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700734/*
735 * Switch tuner to other mode. If tuner support both tv and radio,
736 * set another frequency to some value (This is needed for some pal
737 * tuners to avoid locking). Otherwise, just put second tuner in
738 * standby mode.
739 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700741static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
742{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300743 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
744
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800745 if (mode == t->mode)
746 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700747
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800748 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700749
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800750 if (check_mode(t, cmd) == EINVAL) {
751 t->mode = T_STANDBY;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300752 if ((ops) && (ops->standby))
753 ops->standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800754 return EINVAL;
755 }
756 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700757}
758
759#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800760 tuner_dbg("switching to v4l2\n"); \
761 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700762
763static inline int check_v4l2(struct tuner *t)
764{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300765 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
766 TV, v4l1 for radio), until that is fixed this code is disabled.
767 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
768 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700769 return 0;
770}
771
772static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773{
774 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300775 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300776 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
Hans Verkuilf9195de2006-01-11 19:01:01 -0200778 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200779 v4l_i2c_print_ioctl(&(t->i2c),cmd);
780
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700781 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700783 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300784 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 -0700785 ((struct tuner_setup *)arg)->type,
786 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300787 ((struct tuner_setup *)arg)->mode_mask,
788 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700789
790 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700791 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200793 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
794 == EINVAL)
795 return 0;
796 if (t->radio_freq)
797 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700799 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200800 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
801 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300802 t->mode = T_STANDBY;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300803 if ((ops) && (ops->standby))
804 ops->standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200805 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300806#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700807 case VIDIOCSAUDIO:
808 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
809 return 0;
810 if (check_v4l2(t) == EINVAL)
811 return 0;
812
813 /* Should be implemented, since bttv calls it */
814 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700815 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700817 {
818 static const v4l2_std_id map[] = {
819 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
820 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
821 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
822 [4 /* bttv */ ] = V4L2_STD_PAL_M,
823 [5 /* bttv */ ] = V4L2_STD_PAL_N,
824 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
825 };
826 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700828 if (check_v4l2(t) == EINVAL)
829 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700830
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700831 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
832 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700834 if (vc->norm < ARRAY_SIZE(map))
835 t->std = map[vc->norm];
836 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200837 if (t->tv_freq)
838 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700839 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700840 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700841 case VIDIOCSFREQ:
842 {
843 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700844
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700845 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
846 return 0;
847 if (check_v4l2(t) == EINVAL)
848 return 0;
849
850 set_freq(client, *v);
851 return 0;
852 }
853 case VIDIOCGTUNER:
854 {
855 struct video_tuner *vt = arg;
856
857 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
858 return 0;
859 if (check_v4l2(t) == EINVAL)
860 return 0;
861
862 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300863 if (fe_tuner_ops->get_status) {
864 u32 tuner_status;
865
866 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300867 if (tuner_status & TUNER_STATUS_STEREO)
868 vt->flags |= VIDEO_TUNER_STEREO_ON;
869 else
870 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300871 } else {
Michael Krufky1dde7a42007-10-21 13:40:56 -0300872 if ((ops) && (ops->is_stereo)) {
873 if (ops->is_stereo(t))
Michael Krufkye18f9442007-08-21 01:25:48 -0300874 vt->flags |=
875 VIDEO_TUNER_STEREO_ON;
876 else
877 vt->flags &=
878 ~VIDEO_TUNER_STEREO_ON;
879 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700880 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300881 if ((ops) && (ops->has_signal))
882 vt->signal = ops->has_signal(t);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300883
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700884 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
885
886 vt->rangelow = radio_range[0] * 16000;
887 vt->rangehigh = radio_range[1] * 16000;
888
889 } else {
890 vt->rangelow = tv_range[0] * 16;
891 vt->rangehigh = tv_range[1] * 16;
892 }
893
894 return 0;
895 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700897 {
898 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700900 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
901 return 0;
902 if (check_v4l2(t) == EINVAL)
903 return 0;
904
Michael Krufkye18f9442007-08-21 01:25:48 -0300905 if (V4L2_TUNER_RADIO == t->mode) {
906 if (fe_tuner_ops->get_status) {
907 u32 tuner_status;
908
909 fe_tuner_ops->get_status(&t->fe, &tuner_status);
910 va->mode = (tuner_status & TUNER_STATUS_STEREO)
911 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300912 } else if ((ops) && (ops->is_stereo))
913 va->mode = ops->is_stereo(t)
Michael Krufkye18f9442007-08-21 01:25:48 -0300914 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
915 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700916 return 0;
917 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300918#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300919 case TUNER_SET_CONFIG:
920 {
921 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
922 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300924 if (t->type != cfg->tuner)
925 break;
926
927 if (t->type == TUNER_TDA9887) {
928 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300929 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300930 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300931 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300932
933 if (NULL == fe_tuner_ops->set_config) {
934 tuner_warn("Tuner frontend module has no way to "
935 "set config\n");
936 break;
937 }
938 fe_tuner_ops->set_config(&t->fe, cfg->priv);
939
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300940 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300941 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300942 /* --- v4l ioctls --- */
943 /* take care: bttv does userspace copying, we'll get a
944 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700946 {
947 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700949 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
950 == EINVAL)
951 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700952
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700953 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700955 t->std = *id;
956 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200957 if (t->tv_freq)
958 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700959 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700960 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700961 case VIDIOC_S_FREQUENCY:
962 {
963 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700964
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300965 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
966 == EINVAL)
967 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700968 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200969 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700970
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700971 break;
972 }
973 case VIDIOC_G_FREQUENCY:
974 {
975 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700976
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700977 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
978 return 0;
979 switch_v4l2();
980 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300981 if (fe_tuner_ops->get_frequency) {
982 u32 abs_freq;
983
984 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
985 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
986 (abs_freq * 2 + 125/2) / 125 :
987 (abs_freq + 62500/2) / 62500;
988 break;
989 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200990 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
991 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700992 break;
993 }
994 case VIDIOC_G_TUNER:
995 {
996 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700997
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700998 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
999 return 0;
1000 switch_v4l2();
1001
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001002 tuner->type = t->mode;
Michael Krufky1dde7a42007-10-21 13:40:56 -03001003 if ((ops) && (ops->get_afc))
1004 tuner->afc = ops->get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001005 if (t->mode == V4L2_TUNER_ANALOG_TV)
1006 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001007 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001008 tuner->rangelow = tv_range[0] * 16;
1009 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001010 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001011 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001012
1013 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001014 tuner->rxsubchans =
1015 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001016 if (fe_tuner_ops->get_status) {
1017 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001018
Michael Krufkye18f9442007-08-21 01:25:48 -03001019 fe_tuner_ops->get_status(&t->fe, &tuner_status);
1020 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
1021 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001022 } else {
Michael Krufky1dde7a42007-10-21 13:40:56 -03001023 if ((ops) && (ops->is_stereo)) {
1024 tuner->rxsubchans = ops->is_stereo(t) ?
Michael Krufkye18f9442007-08-21 01:25:48 -03001025 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
1026 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001027 }
Michael Krufky1dde7a42007-10-21 13:40:56 -03001028 if ((ops) && (ops->has_signal))
1029 tuner->signal = ops->has_signal(t);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001030 tuner->capability |=
1031 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1032 tuner->audmode = t->audmode;
1033 tuner->rangelow = radio_range[0] * 16000;
1034 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001035 break;
1036 }
1037 case VIDIOC_S_TUNER:
1038 {
1039 struct v4l2_tuner *tuner = arg;
1040
1041 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1042 return 0;
1043
1044 switch_v4l2();
1045
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001046 /* do nothing unless we're a radio tuner */
1047 if (t->mode != V4L2_TUNER_RADIO)
1048 break;
1049 t->audmode = tuner->audmode;
1050 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001051 break;
1052 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001053 case VIDIOC_LOG_STATUS:
Michael Krufky1dde7a42007-10-21 13:40:56 -03001054 if ((ops) && (ops->tuner_status))
1055 ops->tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001056 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 }
1058
1059 return 0;
1060}
1061
Jean Delvare21b48a72007-03-12 19:20:15 -03001062static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001064 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001066 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 /* FIXME: power down ??? */
1068 return 0;
1069}
1070
Jean Delvare21b48a72007-03-12 19:20:15 -03001071static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001073 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001075 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001076 if (V4L2_TUNER_RADIO == t->mode) {
1077 if (t->radio_freq)
1078 set_freq(c, t->radio_freq);
1079 } else {
1080 if (t->tv_freq)
1081 set_freq(c, t->tv_freq);
1082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 return 0;
1084}
1085
1086/* ----------------------------------------------------------------------- */
1087
1088static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001089 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001090 .attach_adapter = tuner_probe,
1091 .detach_client = tuner_detach,
1092 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001093 .suspend = tuner_suspend,
1094 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001096 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001097 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001099static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001100 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001101 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102};
1103
1104static int __init tuner_init_module(void)
1105{
1106 return i2c_add_driver(&driver);
1107}
1108
1109static void __exit tuner_cleanup_module(void)
1110{
1111 i2c_del_driver(&driver);
1112}
1113
1114module_init(tuner_init_module);
1115module_exit(tuner_cleanup_module);
1116
1117/*
1118 * Overrides for Emacs so that we follow Linus's tabbing style.
1119 * ---------------------------------------------------------------------------
1120 * Local variables:
1121 * c-basic-offset: 8
1122 * End:
1123 */