blob: 0cc190761e242f41ddc8cfdad0ed5603791eab32 [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>
Hans Verkuil9dd659d2007-11-04 11:03:36 -030022#include <media/v4l2-i2c-drv-legacy.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030023#include "tuner-driver.h"
Michael Krufky96c0b7c2007-08-27 21:23:08 -030024#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030025#include "tda8290.h"
Michael Krufky7ab10bf2007-08-27 21:23:40 -030026#include "tea5761.h"
Michael Krufky8d0936e2007-08-27 21:24:27 -030027#include "tea5767.h"
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030028#include "tuner-xc2028.h"
Michael Krufky4adad282007-08-27 21:59:08 -030029#include "tuner-simple.h"
Michael Krufky31c95842007-10-21 20:48:48 -030030#include "tda9887.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#define UNSET (-1U)
33
Hans Verkuil9dd659d2007-11-04 11:03:36 -030034#define PREFIX t->i2c->driver->driver.name
Michael Krufky241020d2007-10-30 09:46:10 -030035
Linus Torvalds1da177e2005-04-16 15:20:36 -070036/* standard i2c insmod options */
37static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030038#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030039 0x10,
40#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080041 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070042 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
43 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 I2C_CLIENT_END
45};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070046
Linus Torvalds1da177e2005-04-16 15:20:36 -070047I2C_CLIENT_INSMOD;
48
49/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070050static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070051static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070052static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070053
Linus Torvalds1da177e2005-04-16 15:20:36 -070054/* insmod options used at runtime => read/write */
Michael Krufkyab166052007-12-09 02:26:48 -030055static int tuner_debug;
56
57#define tuner_warn(fmt, arg...) do { \
58 printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
59 i2c_adapter_id(t->i2c->adapter), \
60 t->i2c->addr, ##arg); \
61 } while (0)
62
63#define tuner_info(fmt, arg...) do { \
64 printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \
65 i2c_adapter_id(t->i2c->adapter), \
66 t->i2c->addr, ##arg); \
67 } while (0)
68
69#define tuner_err(fmt, arg...) do { \
70 printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \
71 i2c_adapter_id(t->i2c->adapter), \
72 t->i2c->addr, ##arg); \
73 } while (0)
74
75#define tuner_dbg(fmt, arg...) do { \
76 if (tuner_debug) \
77 printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \
78 i2c_adapter_id(t->i2c->adapter), \
79 t->i2c->addr, ##arg); \
80 } while (0)
81
82/* ------------------------------------------------------------------------ */
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070084static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070085static unsigned int radio_range[2] = { 65, 108 };
86
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020087static char pal[] = "--";
88static char secam[] = "--";
89static char ntsc[] = "-";
90
Hans Verkuilf9195de2006-01-11 19:01:01 -020091
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020092module_param(addr, int, 0444);
93module_param(no_autodetect, int, 0444);
94module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020095module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020096module_param_string(pal, pal, sizeof(pal), 0644);
97module_param_string(secam, secam, sizeof(secam), 0644);
98module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070099module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100module_param_array(radio_range, int, NULL, 0644);
101
102MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
103MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
104MODULE_LICENSE("GPL");
105
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106/* ---------------------------------------------------------------------- */
107
Michael Krufkyc7919d52007-12-08 17:06:30 -0300108static void fe_set_params(struct dvb_frontend *fe,
109 struct analog_parameters *params)
Michael Krufkye18f9442007-08-21 01:25:48 -0300110{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300111 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
112 struct tuner *t = fe->analog_demod_priv;
Michael Krufkye18f9442007-08-21 01:25:48 -0300113
Michael Krufkye18f9442007-08-21 01:25:48 -0300114 if (NULL == fe_tuner_ops->set_analog_params) {
115 tuner_warn("Tuner frontend module has no way to set freq\n");
116 return;
117 }
Michael Krufkyc7919d52007-12-08 17:06:30 -0300118 fe_tuner_ops->set_analog_params(fe, params);
Michael Krufkye18f9442007-08-21 01:25:48 -0300119}
120
Michael Krufky4e9154b2007-10-21 19:39:50 -0300121static void fe_release(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300122{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300123 if (fe->ops.tuner_ops.release)
124 fe->ops.tuner_ops.release(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300125
Michael Krufky4e9154b2007-10-21 19:39:50 -0300126 fe->ops.analog_demod_ops = NULL;
Michael Krufky4524c1a2007-10-22 18:15:39 -0300127
128 /* DO NOT kfree(fe->analog_demod_priv)
129 *
130 * If we are in this function, analog_demod_priv contains a pointer
131 * to struct tuner *t. This will be kfree'd in tuner_detach().
132 *
133 * Otherwise, fe->ops.analog_demod_ops->release will
134 * handle the cleanup for analog demodulator modules.
135 */
Michael Krufky4e9154b2007-10-21 19:39:50 -0300136 fe->analog_demod_priv = NULL;
Michael Krufkye18f9442007-08-21 01:25:48 -0300137}
138
Michael Krufky4e9154b2007-10-21 19:39:50 -0300139static void fe_standby(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300140{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300141 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
Michael Krufkye18f9442007-08-21 01:25:48 -0300142
143 if (fe_tuner_ops->sleep)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300144 fe_tuner_ops->sleep(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300145}
146
Michael Krufky4e9154b2007-10-21 19:39:50 -0300147static int fe_has_signal(struct dvb_frontend *fe)
Michael Krufky1f5ef192007-08-31 17:38:02 -0300148{
Michael Krufky14196832007-10-14 18:11:53 -0300149 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300150
Michael Krufky4e9154b2007-10-21 19:39:50 -0300151 if (fe->ops.tuner_ops.get_rf_strength)
152 fe->ops.tuner_ops.get_rf_strength(fe, &strength);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300153
154 return strength;
155}
156
Michael Krufky4e9154b2007-10-21 19:39:50 -0300157static void tuner_status(struct dvb_frontend *fe);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300158
159static struct analog_tuner_ops tuner_core_ops = {
Michael Krufkyc7919d52007-12-08 17:06:30 -0300160 .set_params = fe_set_params,
Michael Krufky1dde7a42007-10-21 13:40:56 -0300161 .standby = fe_standby,
162 .release = fe_release,
163 .has_signal = fe_has_signal,
164 .tuner_status = tuner_status
165};
166
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700167/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168static void set_tv_freq(struct i2c_client *c, unsigned int freq)
169{
170 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300171 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
Michael Krufkyc7919d52007-12-08 17:06:30 -0300173 struct analog_parameters params = {
174 .mode = t->mode,
175 .audmode = t->audmode,
176 .std = t->std
177 };
178
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700180 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return;
182 }
Michael Krufkyc7919d52007-12-08 17:06:30 -0300183 if ((NULL == ops) || (NULL == ops->set_params)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700184 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 return;
186 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700187 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
188 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
189 freq / 16, freq % 16 * 100 / 16, tv_range[0],
190 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200191 /* V4L2 spec: if the freq is not possible then the closest
192 possible value should be selected */
193 if (freq < tv_range[0] * 16)
194 freq = tv_range[0] * 16;
195 else
196 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 }
Michael Krufkyc7919d52007-12-08 17:06:30 -0300198 params.frequency = freq;
199
200 ops->set_params(&t->fe, &params);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202
203static void set_radio_freq(struct i2c_client *c, unsigned int freq)
204{
205 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300206 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
Michael Krufkyc7919d52007-12-08 17:06:30 -0300208 struct analog_parameters params = {
209 .mode = t->mode,
210 .audmode = t->audmode,
211 .std = t->std
212 };
213
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700215 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 return;
217 }
Michael Krufkyc7919d52007-12-08 17:06:30 -0300218 if ((NULL == ops) || (NULL == ops->set_params)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700219 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 return;
221 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200222 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700223 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
224 freq / 16000, freq % 16000 * 100 / 16000,
225 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200226 /* V4L2 spec: if the freq is not possible then the closest
227 possible value should be selected */
228 if (freq < radio_range[0] * 16000)
229 freq = radio_range[0] * 16000;
230 else
231 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 }
Michael Krufkyc7919d52007-12-08 17:06:30 -0300233 params.frequency = freq;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700234
Michael Krufkyc7919d52007-12-08 17:06:30 -0300235 ops->set_params(&t->fe, &params);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236}
237
238static void set_freq(struct i2c_client *c, unsigned long freq)
239{
240 struct tuner *t = i2c_get_clientdata(c);
241
242 switch (t->mode) {
243 case V4L2_TUNER_RADIO:
244 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700245 freq / 16000, freq % 16000 * 100 / 16000);
246 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200247 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 break;
249 case V4L2_TUNER_ANALOG_TV:
250 case V4L2_TUNER_DIGITAL_TV:
251 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700252 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200254 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300256 default:
257 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259}
260
Michael Krufky293197c2007-08-28 17:20:42 -0300261static void tuner_i2c_address_check(struct tuner *t)
262{
263 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300264 ((t->i2c->addr < 0x64) || (t->i2c->addr > 0x6f)))
Michael Krufky293197c2007-08-28 17:20:42 -0300265 return;
266
267 tuner_warn("====================== WARNING! ======================\n");
268 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
269 tuner_warn("will soon be dropped. This message indicates that your\n");
270 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300271 t->i2c->name, t->i2c->addr);
Michael Krufky293197c2007-08-28 17:20:42 -0300272 tuner_warn("To ensure continued support for your device, please\n");
273 tuner_warn("send a copy of this message, along with full dmesg\n");
274 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
275 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
276 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300277 t->i2c->adapter->name, t->i2c->addr, t->type,
Michael Krufky293197c2007-08-28 17:20:42 -0300278 tuners[t->type].name);
279 tuner_warn("====================== WARNING! ======================\n");
280}
281
Michael Krufky4adad282007-08-27 21:59:08 -0300282static void attach_simple_tuner(struct tuner *t)
283{
284 struct simple_tuner_config cfg = {
285 .type = t->type,
286 .tun = &tuners[t->type]
287 };
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300288 simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
Michael Krufky4adad282007-08-27 21:59:08 -0300289}
290
Michael Krufkyab166052007-12-09 02:26:48 -0300291static void attach_tda829x(struct tuner *t)
292{
293 struct tda829x_config cfg = {
294 .lna_cfg = &t->config,
295 .tuner_callback = t->tuner_callback,
296 };
297 tda829x_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
298}
299
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700300static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300301 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300302 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303{
304 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300305 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300306 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700307 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700309 if (type == UNSET || type == TUNER_ABSENT) {
310 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 return;
312 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700314 if (type >= tuner_count) {
315 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
316 return;
317 }
318
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300320 t->config = new_config;
321 if (tuner_callback != NULL) {
322 tuner_dbg("defining GPIO callback\n");
323 t->tuner_callback = tuner_callback;
324 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300325
Mauro Carvalho Chehab48aa3362007-10-29 11:33:18 -0300326 if (t->mode == T_UNINITIALIZED) {
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300327 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
328
329 return;
330 }
331
Michael Krufkyb2083192007-05-29 22:54:06 -0300332 /* discard private data, in case set_type() was previously called */
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300333 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300334 ops->release(&t->fe);
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300335
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 switch (t->type) {
337 case TUNER_MT2032:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300338 microtune_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 break;
340 case TUNER_PHILIPS_TDA8290:
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300341 {
Michael Krufkyab166052007-12-09 02:26:48 -0300342 attach_tda829x(t);
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300343 break;
344 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700345 case TUNER_TEA5767:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300346 if (tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700347 t->type = TUNER_ABSENT;
348 t->mode_mask = T_UNINITIALIZED;
349 return;
350 }
351 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700352 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300353 case TUNER_TEA5761:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300354 if (tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300355 t->type = TUNER_ABSENT;
356 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300357 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300358 }
359 t->mode_mask = T_RADIO;
360 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700361 case TUNER_PHILIPS_FMD1216ME_MK3:
362 buffer[0] = 0x0b;
363 buffer[1] = 0xdc;
364 buffer[2] = 0x9c;
365 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700366 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700367 mdelay(1);
368 buffer[2] = 0x86;
369 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700370 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300371 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700372 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800373 case TUNER_PHILIPS_TD1316:
374 buffer[0] = 0x0b;
375 buffer[1] = 0xdc;
376 buffer[2] = 0x86;
377 buffer[3] = 0xa4;
378 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300379 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200380 break;
Mauro Carvalho Chehab690c5442007-10-29 11:33:18 -0300381 case TUNER_XC2028:
382 {
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300383 struct xc2028_config cfg = {
384 .i2c_adap = t->i2c->adapter,
385 .i2c_addr = t->i2c->addr,
386 .video_dev = c->adapter->algo_data,
387 .callback = t->tuner_callback,
388 };
389 if (!xc2028_attach(&t->fe, &cfg)) {
Mauro Carvalho Chehab690c5442007-10-29 11:33:18 -0300390 t->type = TUNER_ABSENT;
391 t->mode_mask = T_UNINITIALIZED;
392 return;
393 }
394 break;
395 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300396 case TUNER_TDA9887:
Michael Krufky31c95842007-10-21 20:48:48 -0300397 tda9887_attach(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300398 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300400 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 break;
402 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700403
Michael Krufkye2be32a2007-10-21 14:35:21 -0300404 ops = t->fe.ops.analog_demod_ops;
405
Michael Krufkyc7919d52007-12-08 17:06:30 -0300406 if (((NULL == ops) || (NULL == ops->set_params)) &&
Michael Krufky1dde7a42007-10-21 13:40:56 -0300407 (fe_tuner_ops->set_analog_params)) {
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300408 strlcpy(t->i2c->name, fe_tuner_ops->info.name,
409 sizeof(t->i2c->name));
Michael Krufkye18f9442007-08-21 01:25:48 -0300410
Michael Krufky1dde7a42007-10-21 13:40:56 -0300411 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300412 t->fe.analog_demod_priv = t;
Michael Krufkye18f9442007-08-21 01:25:48 -0300413 }
414
Michael Krufky49427442007-10-30 09:44:12 -0300415 tuner_dbg("type set to %s\n", t->i2c->name);
Michael Krufkye18f9442007-08-21 01:25:48 -0300416
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700417 if (t->mode_mask == T_UNINITIALIZED)
418 t->mode_mask = new_mode_mask;
419
Hans Verkuil27487d42006-01-15 15:04:52 -0200420 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700421 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100422 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700423 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300424 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425}
426
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700427/*
428 * This function apply tuner config to tuner specified
429 * by tun_setup structure. I addr is unset, then admin status
430 * and tun addr status is more precise then current status,
431 * it's applied. Otherwise status and type are applied only to
432 * tuner with exactly the same addr.
433*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700434
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700435static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700436{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700437 struct tuner *t = i2c_get_clientdata(c);
438
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300439 tuner_dbg("set addr for type %i\n", t->type);
440
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300441 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
442 (t->mode_mask & tun_setup->mode_mask))) ||
443 (tun_setup->addr == c->addr)) {
444 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300445 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700446 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700447}
448
449static inline int check_mode(struct tuner *t, char *cmd)
450{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700451 if ((1 << t->mode & t->mode_mask) == 0) {
452 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700453 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700454
455 switch (t->mode) {
456 case V4L2_TUNER_RADIO:
457 tuner_dbg("Cmd %s accepted for radio\n", cmd);
458 break;
459 case V4L2_TUNER_ANALOG_TV:
460 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
461 break;
462 case V4L2_TUNER_DIGITAL_TV:
463 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
464 break;
465 }
466 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700467}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700468
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700469/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470static int tuner_fixup_std(struct tuner *t)
471{
472 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300474 case '6':
475 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
476 t->std = V4L2_STD_PAL_60;
477 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 case 'b':
479 case 'B':
480 case 'g':
481 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700482 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 t->std = V4L2_STD_PAL_BG;
484 break;
485 case 'i':
486 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700487 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 t->std = V4L2_STD_PAL_I;
489 break;
490 case 'd':
491 case 'D':
492 case 'k':
493 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700494 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 t->std = V4L2_STD_PAL_DK;
496 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700497 case 'M':
498 case 'm':
499 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
500 t->std = V4L2_STD_PAL_M;
501 break;
502 case 'N':
503 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200504 if (pal[1] == 'c' || pal[1] == 'C') {
505 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
506 t->std = V4L2_STD_PAL_Nc;
507 } else {
508 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
509 t->std = V4L2_STD_PAL_N;
510 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700511 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700512 case '-':
513 /* default parameter, do nothing */
514 break;
515 default:
516 tuner_warn ("pal= argument not recognised\n");
517 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 }
519 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700520 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
521 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200522 case 'b':
523 case 'B':
524 case 'g':
525 case 'G':
526 case 'h':
527 case 'H':
528 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
529 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
530 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700531 case 'd':
532 case 'D':
533 case 'k':
534 case 'K':
535 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
536 t->std = V4L2_STD_SECAM_DK;
537 break;
538 case 'l':
539 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800540 if ((secam[1]=='C')||(secam[1]=='c')) {
541 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
542 t->std = V4L2_STD_SECAM_LC;
543 } else {
544 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
545 t->std = V4L2_STD_SECAM_L;
546 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700547 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700548 case '-':
549 /* default parameter, do nothing */
550 break;
551 default:
552 tuner_warn ("secam= argument not recognised\n");
553 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700554 }
555 }
556
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200557 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
558 switch (ntsc[0]) {
559 case 'm':
560 case 'M':
561 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
562 t->std = V4L2_STD_NTSC_M;
563 break;
564 case 'j':
565 case 'J':
566 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
567 t->std = V4L2_STD_NTSC_M_JP;
568 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200569 case 'k':
570 case 'K':
571 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
572 t->std = V4L2_STD_NTSC_M_KR;
573 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200574 case '-':
575 /* default parameter, do nothing */
576 break;
577 default:
578 tuner_info("ntsc= argument not recognised\n");
579 break;
580 }
581 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 return 0;
583}
584
Michael Krufky4e9154b2007-10-21 19:39:50 -0300585static void tuner_status(struct dvb_frontend *fe)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200586{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300587 struct tuner *t = fe->analog_demod_priv;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200588 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300589 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300590 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200591 const char *p;
592
593 switch (t->mode) {
594 case V4L2_TUNER_RADIO: p = "radio"; break;
595 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
596 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
597 default: p = "undefined"; break;
598 }
599 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200600 freq = t->radio_freq / 16000;
601 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200602 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200603 freq = t->tv_freq / 16;
604 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200605 }
606 tuner_info("Tuner mode: %s\n", p);
607 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300608 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200609 if (t->mode != V4L2_TUNER_RADIO)
610 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300611 if (fe_tuner_ops->get_status) {
612 u32 tuner_status;
613
614 fe_tuner_ops->get_status(&t->fe, &tuner_status);
615 if (tuner_status & TUNER_STATUS_LOCKED)
616 tuner_info("Tuner is locked.\n");
617 if (tuner_status & TUNER_STATUS_STEREO)
618 tuner_info("Stereo: yes\n");
619 }
Michael Krufky1b29ced2007-10-22 01:44:03 -0300620 if (ops) {
621 if (ops->has_signal)
622 tuner_info("Signal strength: %d\n",
623 ops->has_signal(fe));
624 if (ops->is_stereo)
625 tuner_info("Stereo: %s\n",
626 ops->is_stereo(fe) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200627 }
628}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630/* ---------------------------------------------------------------------- */
631
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700632/*
633 * Switch tuner to other mode. If tuner support both tv and radio,
634 * set another frequency to some value (This is needed for some pal
635 * tuners to avoid locking). Otherwise, just put second tuner in
636 * standby mode.
637 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700639static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
640{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300641 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
642
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800643 if (mode == t->mode)
644 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700645
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800646 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700647
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800648 if (check_mode(t, cmd) == EINVAL) {
649 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300650 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300651 ops->standby(&t->fe);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800652 return EINVAL;
653 }
654 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700655}
656
657#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800658 tuner_dbg("switching to v4l2\n"); \
659 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700660
661static inline int check_v4l2(struct tuner *t)
662{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300663 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
664 TV, v4l1 for radio), until that is fixed this code is disabled.
665 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
666 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700667 return 0;
668}
669
670static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671{
672 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300673 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300674 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Hans Verkuilf9195de2006-01-11 19:01:01 -0200676 if (tuner_debug>1)
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300677 v4l_i2c_print_ioctl(client,cmd);
Michael Krufky5e453dc2006-01-09 15:32:31 -0200678
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700679 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700681 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300682 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 -0700683 ((struct tuner_setup *)arg)->type,
684 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300685 ((struct tuner_setup *)arg)->mode_mask,
686 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700687
688 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700689 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200691 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
692 == EINVAL)
693 return 0;
694 if (t->radio_freq)
695 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700697 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200698 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
699 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300700 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300701 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300702 ops->standby(&t->fe);
Hans Verkuil27487d42006-01-15 15:04:52 -0200703 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300704#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700705 case VIDIOCSAUDIO:
706 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
707 return 0;
708 if (check_v4l2(t) == EINVAL)
709 return 0;
710
711 /* Should be implemented, since bttv calls it */
712 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700713 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700715 {
716 static const v4l2_std_id map[] = {
717 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
718 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
719 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
720 [4 /* bttv */ ] = V4L2_STD_PAL_M,
721 [5 /* bttv */ ] = V4L2_STD_PAL_N,
722 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
723 };
724 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700726 if (check_v4l2(t) == EINVAL)
727 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700728
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700729 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
730 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700732 if (vc->norm < ARRAY_SIZE(map))
733 t->std = map[vc->norm];
734 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200735 if (t->tv_freq)
736 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700737 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700738 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700739 case VIDIOCSFREQ:
740 {
741 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700742
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700743 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
744 return 0;
745 if (check_v4l2(t) == EINVAL)
746 return 0;
747
748 set_freq(client, *v);
749 return 0;
750 }
751 case VIDIOCGTUNER:
752 {
753 struct video_tuner *vt = arg;
754
755 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
756 return 0;
757 if (check_v4l2(t) == EINVAL)
758 return 0;
759
760 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300761 if (fe_tuner_ops->get_status) {
762 u32 tuner_status;
763
764 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300765 if (tuner_status & TUNER_STATUS_STEREO)
766 vt->flags |= VIDEO_TUNER_STEREO_ON;
767 else
768 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300769 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300770 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300771 if (ops->is_stereo(&t->fe))
Michael Krufkye18f9442007-08-21 01:25:48 -0300772 vt->flags |=
773 VIDEO_TUNER_STEREO_ON;
774 else
775 vt->flags &=
776 ~VIDEO_TUNER_STEREO_ON;
777 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700778 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300779 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300780 vt->signal = ops->has_signal(&t->fe);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300781
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700782 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
783
784 vt->rangelow = radio_range[0] * 16000;
785 vt->rangehigh = radio_range[1] * 16000;
786
787 } else {
788 vt->rangelow = tv_range[0] * 16;
789 vt->rangehigh = tv_range[1] * 16;
790 }
791
792 return 0;
793 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700795 {
796 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700798 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
799 return 0;
800 if (check_v4l2(t) == EINVAL)
801 return 0;
802
Michael Krufkye18f9442007-08-21 01:25:48 -0300803 if (V4L2_TUNER_RADIO == t->mode) {
804 if (fe_tuner_ops->get_status) {
805 u32 tuner_status;
806
807 fe_tuner_ops->get_status(&t->fe, &tuner_status);
808 va->mode = (tuner_status & TUNER_STATUS_STEREO)
809 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300810 } else if (ops && ops->is_stereo)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300811 va->mode = ops->is_stereo(&t->fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300812 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
813 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700814 return 0;
815 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300816#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300817 case TUNER_SET_CONFIG:
818 {
819 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
820 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300822 if (t->type != cfg->tuner)
823 break;
824
825 if (t->type == TUNER_TDA9887) {
826 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300827 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300828 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300829 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300830
831 if (NULL == fe_tuner_ops->set_config) {
832 tuner_warn("Tuner frontend module has no way to "
833 "set config\n");
834 break;
835 }
836 fe_tuner_ops->set_config(&t->fe, cfg->priv);
837
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300838 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300839 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300840 /* --- v4l ioctls --- */
841 /* take care: bttv does userspace copying, we'll get a
842 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700844 {
845 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700847 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
848 == EINVAL)
849 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700850
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700851 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700853 t->std = *id;
854 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200855 if (t->tv_freq)
856 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700857 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700858 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700859 case VIDIOC_S_FREQUENCY:
860 {
861 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700862
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300863 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
864 == EINVAL)
865 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700866 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200867 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700868
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700869 break;
870 }
871 case VIDIOC_G_FREQUENCY:
872 {
873 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700874
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700875 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
876 return 0;
877 switch_v4l2();
878 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300879 if (fe_tuner_ops->get_frequency) {
880 u32 abs_freq;
881
882 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
883 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
884 (abs_freq * 2 + 125/2) / 125 :
885 (abs_freq + 62500/2) / 62500;
886 break;
887 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200888 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
889 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700890 break;
891 }
892 case VIDIOC_G_TUNER:
893 {
894 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700895
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700896 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
897 return 0;
898 switch_v4l2();
899
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200900 tuner->type = t->mode;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300901 if (ops && ops->get_afc)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300902 tuner->afc = ops->get_afc(&t->fe);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300903 if (t->mode == V4L2_TUNER_ANALOG_TV)
904 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200905 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700906 tuner->rangelow = tv_range[0] * 16;
907 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200908 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700909 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200910
911 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200912 tuner->rxsubchans =
913 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300914 if (fe_tuner_ops->get_status) {
915 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200916
Michael Krufkye18f9442007-08-21 01:25:48 -0300917 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky4e9154b2007-10-21 19:39:50 -0300918 tuner->rxsubchans =
919 (tuner_status & TUNER_STATUS_STEREO) ?
920 V4L2_TUNER_SUB_STEREO :
921 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300922 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300923 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300924 tuner->rxsubchans =
925 ops->is_stereo(&t->fe) ?
926 V4L2_TUNER_SUB_STEREO :
927 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300928 }
Michael Krufkye18f9442007-08-21 01:25:48 -0300929 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300930 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300931 tuner->signal = ops->has_signal(&t->fe);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200932 tuner->capability |=
933 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
934 tuner->audmode = t->audmode;
935 tuner->rangelow = radio_range[0] * 16000;
936 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700937 break;
938 }
939 case VIDIOC_S_TUNER:
940 {
941 struct v4l2_tuner *tuner = arg;
942
943 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
944 return 0;
945
946 switch_v4l2();
947
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200948 /* do nothing unless we're a radio tuner */
949 if (t->mode != V4L2_TUNER_RADIO)
950 break;
951 t->audmode = tuner->audmode;
952 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700953 break;
954 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200955 case VIDIOC_LOG_STATUS:
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300956 if (ops && ops->tuner_status)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300957 ops->tuner_status(&t->fe);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200958 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 }
960
961 return 0;
962}
963
Jean Delvare21b48a72007-03-12 19:20:15 -0300964static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965{
Hans Verkuil9dd659d2007-11-04 11:03:36 -0300966 struct tuner *t = i2c_get_clientdata(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967
Hans Verkuil9dd659d2007-11-04 11:03:36 -0300968 tuner_dbg("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 /* FIXME: power down ??? */
970 return 0;
971}
972
Jean Delvare21b48a72007-03-12 19:20:15 -0300973static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974{
Hans Verkuil9dd659d2007-11-04 11:03:36 -0300975 struct tuner *t = i2c_get_clientdata(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
Hans Verkuil9dd659d2007-11-04 11:03:36 -0300977 tuner_dbg("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200978 if (V4L2_TUNER_RADIO == t->mode) {
979 if (t->radio_freq)
980 set_freq(c, t->radio_freq);
981 } else {
982 if (t->tv_freq)
983 set_freq(c, t->tv_freq);
984 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 return 0;
986}
987
Hans Verkuil92de1f12007-11-04 10:53:09 -0300988/* ---------------------------------------------------------------------- */
989
990LIST_HEAD(tuner_list);
991
992/* Search for existing radio and/or TV tuners on the given I2C adapter.
Hans Verkuil9dd659d2007-11-04 11:03:36 -0300993 Note that when this function is called from tuner_probe you can be
Hans Verkuil92de1f12007-11-04 10:53:09 -0300994 certain no other devices will be added/deleted at the same time, I2C
995 core protects against that. */
996static void tuner_lookup(struct i2c_adapter *adap,
997 struct tuner **radio, struct tuner **tv)
998{
999 struct tuner *pos;
1000
1001 *radio = NULL;
1002 *tv = NULL;
1003
1004 list_for_each_entry(pos, &tuner_list, list) {
1005 int mode_mask;
1006
1007 if (pos->i2c->adapter != adap ||
1008 pos->i2c->driver->id != I2C_DRIVERID_TUNER)
1009 continue;
1010
1011 mode_mask = pos->mode_mask & ~T_STANDBY;
1012 if (*radio == NULL && mode_mask == T_RADIO)
1013 *radio = pos;
1014 /* Note: currently TDA9887 is the only demod-only
1015 device. If other devices appear then we need to
1016 make this test more general. */
1017 else if (*tv == NULL && pos->type != TUNER_TDA9887 &&
1018 (pos->mode_mask & (T_ANALOG_TV | T_DIGITAL_TV)))
1019 *tv = pos;
1020 }
1021}
1022
1023/* During client attach, set_type is called by adapter's attach_inform callback.
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001024 set_type must then be completed by tuner_probe.
Hans Verkuil92de1f12007-11-04 10:53:09 -03001025 */
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001026static int tuner_probe(struct i2c_client *client)
Hans Verkuil92de1f12007-11-04 10:53:09 -03001027{
Hans Verkuil92de1f12007-11-04 10:53:09 -03001028 struct tuner *t;
1029 struct tuner *radio;
1030 struct tuner *tv;
1031
Hans Verkuil92de1f12007-11-04 10:53:09 -03001032 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001033 if (NULL == t)
Hans Verkuil92de1f12007-11-04 10:53:09 -03001034 return -ENOMEM;
Hans Verkuil92de1f12007-11-04 10:53:09 -03001035 t->i2c = client;
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001036 strlcpy(client->name, "(tuner unset)", sizeof(client->name));
Hans Verkuil92de1f12007-11-04 10:53:09 -03001037 i2c_set_clientdata(client, t);
1038 t->type = UNSET;
1039 t->audmode = V4L2_TUNER_MODE_STEREO;
1040 t->mode_mask = T_UNINITIALIZED;
1041
1042 if (show_i2c) {
1043 unsigned char buffer[16];
1044 int i, rc;
1045
1046 memset(buffer, 0, sizeof(buffer));
1047 rc = i2c_master_recv(client, buffer, sizeof(buffer));
1048 tuner_info("I2C RECV = ");
1049 for (i = 0; i < rc; i++)
1050 printk(KERN_CONT "%02x ", buffer[i]);
1051 printk("\n");
1052 }
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001053 /* HACK: This test was added to avoid tuner to probe tda9840 and
Hans Verkuil92de1f12007-11-04 10:53:09 -03001054 tea6415c on the MXB card */
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001055 if (client->adapter->id == I2C_HW_SAA7146 && client->addr < 0x4a) {
1056 kfree(t);
Hans Verkuil92de1f12007-11-04 10:53:09 -03001057 return -ENODEV;
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001058 }
Hans Verkuil92de1f12007-11-04 10:53:09 -03001059
1060 /* autodetection code based on the i2c addr */
1061 if (!no_autodetect) {
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001062 switch (client->addr) {
Hans Verkuil92de1f12007-11-04 10:53:09 -03001063 case 0x10:
1064 if (tea5761_autodetection(t->i2c->adapter, t->i2c->addr)
1065 != EINVAL) {
1066 t->type = TUNER_TEA5761;
1067 t->mode_mask = T_RADIO;
1068 t->mode = T_STANDBY;
1069 /* Sets freq to FM range */
1070 t->radio_freq = 87.5 * 16000;
1071 tuner_lookup(t->i2c->adapter, &radio, &tv);
1072 if (tv)
1073 tv->mode_mask &= ~T_RADIO;
1074
1075 goto register_client;
1076 }
1077 break;
1078 case 0x42:
1079 case 0x43:
1080 case 0x4a:
1081 case 0x4b:
1082 /* If chip is not tda8290, don't register.
1083 since it can be tda9887*/
Michael Krufkyab166052007-12-09 02:26:48 -03001084 if (tda829x_probe(t->i2c->adapter,
1085 t->i2c->addr) == 0) {
Hans Verkuil92de1f12007-11-04 10:53:09 -03001086 tuner_dbg("tda829x detected\n");
1087 } else {
1088 /* Default is being tda9887 */
1089 t->type = TUNER_TDA9887;
1090 t->mode_mask = T_RADIO | T_ANALOG_TV |
1091 T_DIGITAL_TV;
1092 t->mode = T_STANDBY;
1093 goto register_client;
1094 }
1095 break;
1096 case 0x60:
1097 if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr)
1098 != EINVAL) {
1099 t->type = TUNER_TEA5767;
1100 t->mode_mask = T_RADIO;
1101 t->mode = T_STANDBY;
1102 /* Sets freq to FM range */
1103 t->radio_freq = 87.5 * 16000;
1104 tuner_lookup(t->i2c->adapter, &radio, &tv);
1105 if (tv)
1106 tv->mode_mask &= ~T_RADIO;
1107
1108 goto register_client;
1109 }
1110 break;
1111 }
1112 }
1113
1114 /* Initializes only the first TV tuner on this adapter. Why only the
1115 first? Because there are some devices (notably the ones with TI
1116 tuners) that have more than one i2c address for the *same* device.
1117 Experience shows that, except for just one case, the first
1118 address is the right one. The exception is a Russian tuner
1119 (ACORP_Y878F). So, the desired behavior is just to enable the
1120 first found TV tuner. */
1121 tuner_lookup(t->i2c->adapter, &radio, &tv);
1122 if (tv == NULL) {
1123 t->mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
1124 if (radio == NULL)
1125 t->mode_mask |= T_RADIO;
1126 tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask);
1127 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
1128 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
1129 }
1130
1131 /* Should be just before return */
1132register_client:
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001133 tuner_info("chip found @ 0x%x (%s)\n", client->addr << 1,
1134 client->adapter->name);
Hans Verkuil92de1f12007-11-04 10:53:09 -03001135
1136 /* Sets a default mode */
1137 if (t->mode_mask & T_ANALOG_TV) {
1138 t->mode = T_ANALOG_TV;
1139 } else if (t->mode_mask & T_RADIO) {
1140 t->mode = T_RADIO;
1141 } else {
1142 t->mode = T_DIGITAL_TV;
1143 }
Hans Verkuil92de1f12007-11-04 10:53:09 -03001144 set_type(client, t->type, t->mode_mask, t->config, t->tuner_callback);
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001145 list_add_tail(&t->list, &tuner_list);
Hans Verkuil92de1f12007-11-04 10:53:09 -03001146 return 0;
1147}
1148
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001149static int tuner_legacy_probe(struct i2c_adapter *adap)
Hans Verkuil92de1f12007-11-04 10:53:09 -03001150{
1151 if (0 != addr) {
1152 normal_i2c[0] = addr;
1153 normal_i2c[1] = I2C_CLIENT_END;
1154 }
1155
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001156 if ((adap->class & I2C_CLASS_TV_ANALOG) == 0)
1157 return 0;
1158
Hans Verkuil92de1f12007-11-04 10:53:09 -03001159 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
1160 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
1161 * and an RTC at 0x6f which can get corrupted if probed.
1162 */
1163 if ((adap->id == I2C_HW_B_CX2388x) ||
1164 (adap->id == I2C_HW_B_CX23885)) {
1165 unsigned int i = 0;
1166
1167 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
1168 i += 2;
1169 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
1170 ignore[i+0] = adap->nr;
1171 ignore[i+1] = 0x6b;
1172 ignore[i+2] = adap->nr;
1173 ignore[i+3] = 0x6f;
1174 ignore[i+4] = I2C_CLIENT_END;
1175 } else
1176 printk(KERN_WARNING "tuner: "
1177 "too many options specified "
1178 "in i2c probe ignore list!\n");
1179 }
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001180 return 1;
Hans Verkuil92de1f12007-11-04 10:53:09 -03001181}
1182
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001183static int tuner_remove(struct i2c_client *client)
Hans Verkuil92de1f12007-11-04 10:53:09 -03001184{
1185 struct tuner *t = i2c_get_clientdata(client);
1186 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Hans Verkuil92de1f12007-11-04 10:53:09 -03001187
1188 if (ops && ops->release)
1189 ops->release(&t->fe);
1190
1191 list_del(&t->list);
1192 kfree(t);
Hans Verkuil92de1f12007-11-04 10:53:09 -03001193 return 0;
1194}
1195
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196/* ----------------------------------------------------------------------- */
1197
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001198static struct v4l2_i2c_driver_data v4l2_i2c_data = {
1199 .name = "tuner",
1200 .driverid = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001201 .command = tuner_command,
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001202 .probe = tuner_probe,
1203 .remove = tuner_remove,
Jean Delvare21b48a72007-03-12 19:20:15 -03001204 .suspend = tuner_suspend,
Hans Verkuil9dd659d2007-11-04 11:03:36 -03001205 .resume = tuner_resume,
1206 .legacy_probe = tuner_legacy_probe,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207};
1208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209
1210/*
1211 * Overrides for Emacs so that we follow Linus's tabbing style.
1212 * ---------------------------------------------------------------------------
1213 * Local variables:
1214 * c-basic-offset: 8
1215 * End:
1216 */