blob: 88db8b33c864d6d7ca791992fb4dd390bec69ec1 [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"
Michael Krufky31c95842007-10-21 20:48:48 -030029#include "tda9887.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31#define UNSET (-1U)
32
33/* standard i2c insmod options */
34static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030035#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030036 0x10,
37#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080038 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070039 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
40 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 I2C_CLIENT_END
42};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070043
Linus Torvalds1da177e2005-04-16 15:20:36 -070044I2C_CLIENT_INSMOD;
45
46/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070047static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070048static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070049static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070050
Linus Torvalds1da177e2005-04-16 15:20:36 -070051/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020052int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070054static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070055static unsigned int radio_range[2] = { 65, 108 };
56
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020057static char pal[] = "--";
58static char secam[] = "--";
59static char ntsc[] = "-";
60
Hans Verkuilf9195de2006-01-11 19:01:01 -020061
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020062module_param(addr, int, 0444);
63module_param(no_autodetect, int, 0444);
64module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020065module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020066module_param_string(pal, pal, sizeof(pal), 0644);
67module_param_string(secam, secam, sizeof(secam), 0644);
68module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070069module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070module_param_array(radio_range, int, NULL, 0644);
71
72MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
73MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
74MODULE_LICENSE("GPL");
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076static struct i2c_driver driver;
77static struct i2c_client client_template;
78
79/* ---------------------------------------------------------------------- */
80
Michael Krufky4e9154b2007-10-21 19:39:50 -030081static void fe_set_freq(struct dvb_frontend *fe, unsigned int freq)
Michael Krufkye18f9442007-08-21 01:25:48 -030082{
Michael Krufky4e9154b2007-10-21 19:39:50 -030083 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
84 struct tuner *t = fe->analog_demod_priv;
Michael Krufkye18f9442007-08-21 01:25:48 -030085
86 struct analog_parameters params = {
87 .frequency = freq,
88 .mode = t->mode,
89 .audmode = t->audmode,
90 .std = t->std
91 };
92
93 if (NULL == fe_tuner_ops->set_analog_params) {
94 tuner_warn("Tuner frontend module has no way to set freq\n");
95 return;
96 }
Michael Krufky4e9154b2007-10-21 19:39:50 -030097 fe_tuner_ops->set_analog_params(fe, &params);
Michael Krufkye18f9442007-08-21 01:25:48 -030098}
99
Michael Krufky4e9154b2007-10-21 19:39:50 -0300100static void fe_release(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300101{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300102 if (fe->ops.tuner_ops.release)
103 fe->ops.tuner_ops.release(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300104
Michael Krufky4e9154b2007-10-21 19:39:50 -0300105 fe->ops.analog_demod_ops = NULL;
Michael Krufky4524c1a2007-10-22 18:15:39 -0300106
107 /* DO NOT kfree(fe->analog_demod_priv)
108 *
109 * If we are in this function, analog_demod_priv contains a pointer
110 * to struct tuner *t. This will be kfree'd in tuner_detach().
111 *
112 * Otherwise, fe->ops.analog_demod_ops->release will
113 * handle the cleanup for analog demodulator modules.
114 */
Michael Krufky4e9154b2007-10-21 19:39:50 -0300115 fe->analog_demod_priv = NULL;
Michael Krufkye18f9442007-08-21 01:25:48 -0300116}
117
Michael Krufky4e9154b2007-10-21 19:39:50 -0300118static void fe_standby(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300119{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300120 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
Michael Krufkye18f9442007-08-21 01:25:48 -0300121
122 if (fe_tuner_ops->sleep)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300123 fe_tuner_ops->sleep(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300124}
125
Michael Krufky4e9154b2007-10-21 19:39:50 -0300126static int fe_has_signal(struct dvb_frontend *fe)
Michael Krufky1f5ef192007-08-31 17:38:02 -0300127{
Michael Krufky14196832007-10-14 18:11:53 -0300128 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300129
Michael Krufky4e9154b2007-10-21 19:39:50 -0300130 if (fe->ops.tuner_ops.get_rf_strength)
131 fe->ops.tuner_ops.get_rf_strength(fe, &strength);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300132
133 return strength;
134}
135
Michael Krufky4e9154b2007-10-21 19:39:50 -0300136static void tuner_status(struct dvb_frontend *fe);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300137
138static struct analog_tuner_ops tuner_core_ops = {
139 .set_tv_freq = fe_set_freq,
140 .set_radio_freq = fe_set_freq,
141 .standby = fe_standby,
142 .release = fe_release,
143 .has_signal = fe_has_signal,
144 .tuner_status = tuner_status
145};
146
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700147/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148static void set_tv_freq(struct i2c_client *c, unsigned int freq)
149{
150 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300151 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700154 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 return;
156 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300157 if ((NULL == ops) || (NULL == ops->set_tv_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700158 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 return;
160 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700161 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
162 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
163 freq / 16, freq % 16 * 100 / 16, tv_range[0],
164 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200165 /* V4L2 spec: if the freq is not possible then the closest
166 possible value should be selected */
167 if (freq < tv_range[0] * 16)
168 freq = tv_range[0] * 16;
169 else
170 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 }
Michael Krufky4e9154b2007-10-21 19:39:50 -0300172 ops->set_tv_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
175static void set_radio_freq(struct i2c_client *c, unsigned int freq)
176{
177 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300178 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
180 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700181 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 return;
183 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300184 if ((NULL == ops) || (NULL == ops->set_radio_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700185 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 return;
187 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200188 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700189 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
190 freq / 16000, freq % 16000 * 100 / 16000,
191 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200192 /* V4L2 spec: if the freq is not possible then the closest
193 possible value should be selected */
194 if (freq < radio_range[0] * 16000)
195 freq = radio_range[0] * 16000;
196 else
197 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700199
Michael Krufky4e9154b2007-10-21 19:39:50 -0300200 ops->set_radio_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202
203static void set_freq(struct i2c_client *c, unsigned long freq)
204{
205 struct tuner *t = i2c_get_clientdata(c);
206
207 switch (t->mode) {
208 case V4L2_TUNER_RADIO:
209 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700210 freq / 16000, freq % 16000 * 100 / 16000);
211 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200212 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 break;
214 case V4L2_TUNER_ANALOG_TV:
215 case V4L2_TUNER_DIGITAL_TV:
216 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700217 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200219 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300221 default:
222 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224}
225
Michael Krufky293197c2007-08-28 17:20:42 -0300226static void tuner_i2c_address_check(struct tuner *t)
227{
228 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
229 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
230 return;
231
232 tuner_warn("====================== WARNING! ======================\n");
233 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
234 tuner_warn("will soon be dropped. This message indicates that your\n");
235 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
236 t->i2c.name, t->i2c.addr);
237 tuner_warn("To ensure continued support for your device, please\n");
238 tuner_warn("send a copy of this message, along with full dmesg\n");
239 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
240 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
241 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
242 t->i2c.adapter->name, t->i2c.addr, t->type,
243 tuners[t->type].name);
244 tuner_warn("====================== WARNING! ======================\n");
245}
246
Michael Krufky4adad282007-08-27 21:59:08 -0300247static void attach_simple_tuner(struct tuner *t)
248{
249 struct simple_tuner_config cfg = {
250 .type = t->type,
251 .tun = &tuners[t->type]
252 };
253 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
254}
255
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700256static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300257 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300258 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
260 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300261 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300262 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700263 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700265 if (type == UNSET || type == TUNER_ABSENT) {
266 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 return;
268 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700270 if (type >= tuner_count) {
271 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
272 return;
273 }
274
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300276 t->config = new_config;
277 if (tuner_callback != NULL) {
278 tuner_dbg("defining GPIO callback\n");
279 t->tuner_callback = tuner_callback;
280 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300281
282 /* This code detects calls by card attach_inform */
283 if (NULL == t->i2c.dev.driver) {
284 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
285
286 return;
287 }
288
Michael Krufkyb2083192007-05-29 22:54:06 -0300289 /* discard private data, in case set_type() was previously called */
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300290 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300291 ops->release(&t->fe);
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300292
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 switch (t->type) {
294 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300295 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 break;
297 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300298 {
Michael Krufky746d97322007-08-25 19:08:45 -0300299 tda8290_attach(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300301 }
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300302 case TUNER_PHILIPS_TDA8295:
303 {
304 tda8295_attach(t);
305 break;
306 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700307 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300308 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700309 t->type = TUNER_ABSENT;
310 t->mode_mask = T_UNINITIALIZED;
311 return;
312 }
313 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700314 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300315 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300316 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300317 t->type = TUNER_ABSENT;
318 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300319 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300320 }
321 t->mode_mask = T_RADIO;
322 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700323 case TUNER_PHILIPS_FMD1216ME_MK3:
324 buffer[0] = 0x0b;
325 buffer[1] = 0xdc;
326 buffer[2] = 0x9c;
327 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700328 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700329 mdelay(1);
330 buffer[2] = 0x86;
331 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700332 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300333 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700334 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800335 case TUNER_PHILIPS_TD1316:
336 buffer[0] = 0x0b;
337 buffer[1] = 0xdc;
338 buffer[2] = 0x86;
339 buffer[3] = 0xa4;
340 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300341 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200342 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300343 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300344 {
345 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
346 &c->dev, c->adapter->algo_data,
347 t->tuner_callback);
348 if (rc<0) {
349 t->type = TUNER_ABSENT;
350 t->mode_mask = T_UNINITIALIZED;
351 return;
352 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300353 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300354 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300355 case TUNER_TDA9887:
Michael Krufky31c95842007-10-21 20:48:48 -0300356 tda9887_attach(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300357 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300359 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 break;
361 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700362
Michael Krufkye2be32a2007-10-21 14:35:21 -0300363 ops = t->fe.ops.analog_demod_ops;
364
Michael Krufky1dde7a42007-10-21 13:40:56 -0300365 if (((NULL == ops) ||
366 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
367 (fe_tuner_ops->set_analog_params)) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300368 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
369
Michael Krufky1dde7a42007-10-21 13:40:56 -0300370 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300371 t->fe.analog_demod_priv = t;
Michael Krufkye18f9442007-08-21 01:25:48 -0300372 }
373
374 tuner_info("type set to %s\n", t->i2c.name);
375
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700376 if (t->mode_mask == T_UNINITIALIZED)
377 t->mode_mask = new_mode_mask;
378
Hans Verkuil27487d42006-01-15 15:04:52 -0200379 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700380 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100381 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700382 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300383 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384}
385
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700386/*
387 * This function apply tuner config to tuner specified
388 * by tun_setup structure. I addr is unset, then admin status
389 * and tun addr status is more precise then current status,
390 * it's applied. Otherwise status and type are applied only to
391 * tuner with exactly the same addr.
392*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700393
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700394static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700395{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700396 struct tuner *t = i2c_get_clientdata(c);
397
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300398 tuner_dbg("set addr for type %i\n", t->type);
399
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300400 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
401 (t->mode_mask & tun_setup->mode_mask))) ||
402 (tun_setup->addr == c->addr)) {
403 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300404 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700405 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700406}
407
408static inline int check_mode(struct tuner *t, char *cmd)
409{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700410 if ((1 << t->mode & t->mode_mask) == 0) {
411 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700412 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700413
414 switch (t->mode) {
415 case V4L2_TUNER_RADIO:
416 tuner_dbg("Cmd %s accepted for radio\n", cmd);
417 break;
418 case V4L2_TUNER_ANALOG_TV:
419 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
420 break;
421 case V4L2_TUNER_DIGITAL_TV:
422 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
423 break;
424 }
425 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700426}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700427
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700428/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429static int tuner_fixup_std(struct tuner *t)
430{
431 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300433 case '6':
434 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
435 t->std = V4L2_STD_PAL_60;
436 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 case 'b':
438 case 'B':
439 case 'g':
440 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700441 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 t->std = V4L2_STD_PAL_BG;
443 break;
444 case 'i':
445 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700446 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 t->std = V4L2_STD_PAL_I;
448 break;
449 case 'd':
450 case 'D':
451 case 'k':
452 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700453 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 t->std = V4L2_STD_PAL_DK;
455 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700456 case 'M':
457 case 'm':
458 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
459 t->std = V4L2_STD_PAL_M;
460 break;
461 case 'N':
462 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200463 if (pal[1] == 'c' || pal[1] == 'C') {
464 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
465 t->std = V4L2_STD_PAL_Nc;
466 } else {
467 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
468 t->std = V4L2_STD_PAL_N;
469 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700470 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700471 case '-':
472 /* default parameter, do nothing */
473 break;
474 default:
475 tuner_warn ("pal= argument not recognised\n");
476 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 }
478 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700479 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
480 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200481 case 'b':
482 case 'B':
483 case 'g':
484 case 'G':
485 case 'h':
486 case 'H':
487 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
488 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
489 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700490 case 'd':
491 case 'D':
492 case 'k':
493 case 'K':
494 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
495 t->std = V4L2_STD_SECAM_DK;
496 break;
497 case 'l':
498 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800499 if ((secam[1]=='C')||(secam[1]=='c')) {
500 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
501 t->std = V4L2_STD_SECAM_LC;
502 } else {
503 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
504 t->std = V4L2_STD_SECAM_L;
505 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700506 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700507 case '-':
508 /* default parameter, do nothing */
509 break;
510 default:
511 tuner_warn ("secam= argument not recognised\n");
512 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700513 }
514 }
515
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200516 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
517 switch (ntsc[0]) {
518 case 'm':
519 case 'M':
520 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
521 t->std = V4L2_STD_NTSC_M;
522 break;
523 case 'j':
524 case 'J':
525 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
526 t->std = V4L2_STD_NTSC_M_JP;
527 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200528 case 'k':
529 case 'K':
530 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
531 t->std = V4L2_STD_NTSC_M_KR;
532 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200533 case '-':
534 /* default parameter, do nothing */
535 break;
536 default:
537 tuner_info("ntsc= argument not recognised\n");
538 break;
539 }
540 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 return 0;
542}
543
Michael Krufky4e9154b2007-10-21 19:39:50 -0300544static void tuner_status(struct dvb_frontend *fe)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200545{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300546 struct tuner *t = fe->analog_demod_priv;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200547 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300548 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300549 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200550 const char *p;
551
552 switch (t->mode) {
553 case V4L2_TUNER_RADIO: p = "radio"; break;
554 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
555 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
556 default: p = "undefined"; break;
557 }
558 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200559 freq = t->radio_freq / 16000;
560 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200561 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200562 freq = t->tv_freq / 16;
563 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200564 }
565 tuner_info("Tuner mode: %s\n", p);
566 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300567 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200568 if (t->mode != V4L2_TUNER_RADIO)
569 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300570 if (fe_tuner_ops->get_status) {
571 u32 tuner_status;
572
573 fe_tuner_ops->get_status(&t->fe, &tuner_status);
574 if (tuner_status & TUNER_STATUS_LOCKED)
575 tuner_info("Tuner is locked.\n");
576 if (tuner_status & TUNER_STATUS_STEREO)
577 tuner_info("Stereo: yes\n");
578 }
Michael Krufky1b29ced2007-10-22 01:44:03 -0300579 if (ops) {
580 if (ops->has_signal)
581 tuner_info("Signal strength: %d\n",
582 ops->has_signal(fe));
583 if (ops->is_stereo)
584 tuner_info("Stereo: %s\n",
585 ops->is_stereo(fe) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200586 }
587}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200588
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589/* ---------------------------------------------------------------------- */
590
Hans Verkuilba8fc392006-06-25 15:34:39 -0300591/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700592static unsigned default_mode_mask;
593
594/* During client attach, set_type is called by adapter's attach_inform callback.
595 set_type must then be completed by tuner_attach.
596 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
598{
599 struct tuner *t;
600
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700601 client_template.adapter = adap;
602 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Panagiotis Issaris74081872006-01-11 19:40:56 -0200604 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700605 if (NULL == t)
606 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700607 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700609 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700610 t->audmode = V4L2_TUNER_MODE_STEREO;
611 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700613 if (show_i2c) {
614 unsigned char buffer[16];
615 int i,rc;
616
617 memset(buffer, 0, sizeof(buffer));
618 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800619 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700620 for (i=0;i<rc;i++)
621 printk("%02x ",buffer[i]);
622 printk("\n");
623 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300624 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
625 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
626 return -ENODEV;
627
Markus Rechberger257c6452006-01-23 17:11:11 -0200628 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700629 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800630 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300631 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300632 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300633 t->type = TUNER_TEA5761;
634 t->mode_mask = T_RADIO;
635 t->mode = T_STANDBY;
636 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
637 default_mode_mask &= ~T_RADIO;
638
639 goto register_client;
640 }
641 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800642 case 0x42:
643 case 0x43:
644 case 0x4a:
645 case 0x4b:
646 /* If chip is not tda8290, don't register.
647 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300648 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300649 tuner_dbg("chip at addr %x is a tda8290\n", addr);
650 } else {
651 /* Default is being tda9887 */
652 t->type = TUNER_TDA9887;
653 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
654 t->mode = T_STANDBY;
655 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800656 }
657 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800658 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300659 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700660 t->type = TUNER_TEA5767;
661 t->mode_mask = T_RADIO;
662 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200663 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700664 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700665
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800666 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700667 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800668 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700669 }
670 }
671
672 /* Initializes only the first adapter found */
673 if (default_mode_mask != T_UNINITIALIZED) {
674 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
675 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200676 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
677 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700678 default_mode_mask = T_UNINITIALIZED;
679 }
680
681 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800682register_client:
683 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700684 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300685 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 return 0;
687}
688
689static int tuner_probe(struct i2c_adapter *adap)
690{
691 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700692 normal_i2c[0] = addr;
693 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Michael Krufkya1dec512007-08-24 01:13:07 -0300696 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
697 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
698 * and an RTC at 0x6f which can get corrupted if probed.
699 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300700 if ((adap->id == I2C_HW_B_CX2388x) ||
701 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300702 unsigned int i = 0;
703
704 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
705 i += 2;
706 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
707 ignore[i+0] = adap->nr;
708 ignore[i+1] = 0x6b;
709 ignore[i+2] = adap->nr;
710 ignore[i+3] = 0x6f;
711 ignore[i+4] = I2C_CLIENT_END;
712 } else
713 printk(KERN_WARNING "tuner: "
714 "too many options specified "
715 "in i2c probe ignore list!\n");
716 }
717
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700718 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700719
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 if (adap->class & I2C_CLASS_TV_ANALOG)
721 return i2c_probe(adap, &addr_data, tuner_attach);
722 return 0;
723}
724
725static int tuner_detach(struct i2c_client *client)
726{
727 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300728 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700729 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700731 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700732 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700733 tuner_warn
734 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700735 return err;
736 }
737
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300738 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300739 ops->release(&t->fe);
Michael Krufky16f29162007-10-21 15:22:25 -0300740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 kfree(t);
742 return 0;
743}
744
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700745/*
746 * Switch tuner to other mode. If tuner support both tv and radio,
747 * set another frequency to some value (This is needed for some pal
748 * tuners to avoid locking). Otherwise, just put second tuner in
749 * standby mode.
750 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700752static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
753{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300754 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
755
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800756 if (mode == t->mode)
757 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800759 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700760
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800761 if (check_mode(t, cmd) == EINVAL) {
762 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300763 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300764 ops->standby(&t->fe);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800765 return EINVAL;
766 }
767 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700768}
769
770#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800771 tuner_dbg("switching to v4l2\n"); \
772 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700773
774static inline int check_v4l2(struct tuner *t)
775{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300776 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
777 TV, v4l1 for radio), until that is fixed this code is disabled.
778 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
779 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700780 return 0;
781}
782
783static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784{
785 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300786 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300787 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Hans Verkuilf9195de2006-01-11 19:01:01 -0200789 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200790 v4l_i2c_print_ioctl(&(t->i2c),cmd);
791
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700792 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700794 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300795 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 -0700796 ((struct tuner_setup *)arg)->type,
797 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300798 ((struct tuner_setup *)arg)->mode_mask,
799 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700800
801 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700802 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200804 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
805 == EINVAL)
806 return 0;
807 if (t->radio_freq)
808 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700810 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200811 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
812 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300813 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300814 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300815 ops->standby(&t->fe);
Hans Verkuil27487d42006-01-15 15:04:52 -0200816 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300817#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700818 case VIDIOCSAUDIO:
819 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
820 return 0;
821 if (check_v4l2(t) == EINVAL)
822 return 0;
823
824 /* Should be implemented, since bttv calls it */
825 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700826 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700828 {
829 static const v4l2_std_id map[] = {
830 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
831 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
832 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
833 [4 /* bttv */ ] = V4L2_STD_PAL_M,
834 [5 /* bttv */ ] = V4L2_STD_PAL_N,
835 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
836 };
837 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700839 if (check_v4l2(t) == EINVAL)
840 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700841
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700842 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
843 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700845 if (vc->norm < ARRAY_SIZE(map))
846 t->std = map[vc->norm];
847 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200848 if (t->tv_freq)
849 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700850 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700851 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700852 case VIDIOCSFREQ:
853 {
854 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700855
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700856 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
857 return 0;
858 if (check_v4l2(t) == EINVAL)
859 return 0;
860
861 set_freq(client, *v);
862 return 0;
863 }
864 case VIDIOCGTUNER:
865 {
866 struct video_tuner *vt = arg;
867
868 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
869 return 0;
870 if (check_v4l2(t) == EINVAL)
871 return 0;
872
873 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300874 if (fe_tuner_ops->get_status) {
875 u32 tuner_status;
876
877 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300878 if (tuner_status & TUNER_STATUS_STEREO)
879 vt->flags |= VIDEO_TUNER_STEREO_ON;
880 else
881 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300882 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300883 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300884 if (ops->is_stereo(&t->fe))
Michael Krufkye18f9442007-08-21 01:25:48 -0300885 vt->flags |=
886 VIDEO_TUNER_STEREO_ON;
887 else
888 vt->flags &=
889 ~VIDEO_TUNER_STEREO_ON;
890 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700891 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300892 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300893 vt->signal = ops->has_signal(&t->fe);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300894
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700895 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
896
897 vt->rangelow = radio_range[0] * 16000;
898 vt->rangehigh = radio_range[1] * 16000;
899
900 } else {
901 vt->rangelow = tv_range[0] * 16;
902 vt->rangehigh = tv_range[1] * 16;
903 }
904
905 return 0;
906 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700908 {
909 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700911 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
912 return 0;
913 if (check_v4l2(t) == EINVAL)
914 return 0;
915
Michael Krufkye18f9442007-08-21 01:25:48 -0300916 if (V4L2_TUNER_RADIO == t->mode) {
917 if (fe_tuner_ops->get_status) {
918 u32 tuner_status;
919
920 fe_tuner_ops->get_status(&t->fe, &tuner_status);
921 va->mode = (tuner_status & TUNER_STATUS_STEREO)
922 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300923 } else if (ops && ops->is_stereo)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300924 va->mode = ops->is_stereo(&t->fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300925 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
926 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700927 return 0;
928 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300929#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300930 case TUNER_SET_CONFIG:
931 {
932 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
933 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300935 if (t->type != cfg->tuner)
936 break;
937
938 if (t->type == TUNER_TDA9887) {
939 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300940 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300941 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300942 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300943
944 if (NULL == fe_tuner_ops->set_config) {
945 tuner_warn("Tuner frontend module has no way to "
946 "set config\n");
947 break;
948 }
949 fe_tuner_ops->set_config(&t->fe, cfg->priv);
950
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300951 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300952 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300953 /* --- v4l ioctls --- */
954 /* take care: bttv does userspace copying, we'll get a
955 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700957 {
958 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700960 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
961 == EINVAL)
962 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700963
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700964 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700966 t->std = *id;
967 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200968 if (t->tv_freq)
969 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700970 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700971 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700972 case VIDIOC_S_FREQUENCY:
973 {
974 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700975
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300976 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
977 == EINVAL)
978 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700979 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200980 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700981
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700982 break;
983 }
984 case VIDIOC_G_FREQUENCY:
985 {
986 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700987
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700988 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
989 return 0;
990 switch_v4l2();
991 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300992 if (fe_tuner_ops->get_frequency) {
993 u32 abs_freq;
994
995 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
996 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
997 (abs_freq * 2 + 125/2) / 125 :
998 (abs_freq + 62500/2) / 62500;
999 break;
1000 }
Hans Verkuil27487d42006-01-15 15:04:52 -02001001 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
1002 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001003 break;
1004 }
1005 case VIDIOC_G_TUNER:
1006 {
1007 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -07001008
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001009 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
1010 return 0;
1011 switch_v4l2();
1012
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001013 tuner->type = t->mode;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001014 if (ops && ops->get_afc)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001015 tuner->afc = ops->get_afc(&t->fe);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001016 if (t->mode == V4L2_TUNER_ANALOG_TV)
1017 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001018 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001019 tuner->rangelow = tv_range[0] * 16;
1020 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001021 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001022 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001023
1024 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001025 tuner->rxsubchans =
1026 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001027 if (fe_tuner_ops->get_status) {
1028 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001029
Michael Krufkye18f9442007-08-21 01:25:48 -03001030 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky4e9154b2007-10-21 19:39:50 -03001031 tuner->rxsubchans =
1032 (tuner_status & TUNER_STATUS_STEREO) ?
1033 V4L2_TUNER_SUB_STEREO :
1034 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001035 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001036 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -03001037 tuner->rxsubchans =
1038 ops->is_stereo(&t->fe) ?
1039 V4L2_TUNER_SUB_STEREO :
1040 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001041 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001042 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001043 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001044 tuner->signal = ops->has_signal(&t->fe);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001045 tuner->capability |=
1046 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1047 tuner->audmode = t->audmode;
1048 tuner->rangelow = radio_range[0] * 16000;
1049 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001050 break;
1051 }
1052 case VIDIOC_S_TUNER:
1053 {
1054 struct v4l2_tuner *tuner = arg;
1055
1056 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1057 return 0;
1058
1059 switch_v4l2();
1060
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001061 /* do nothing unless we're a radio tuner */
1062 if (t->mode != V4L2_TUNER_RADIO)
1063 break;
1064 t->audmode = tuner->audmode;
1065 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001066 break;
1067 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001068 case VIDIOC_LOG_STATUS:
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001069 if (ops && ops->tuner_status)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001070 ops->tuner_status(&t->fe);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001071 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 }
1073
1074 return 0;
1075}
1076
Jean Delvare21b48a72007-03-12 19:20:15 -03001077static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001079 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001081 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 /* FIXME: power down ??? */
1083 return 0;
1084}
1085
Jean Delvare21b48a72007-03-12 19:20:15 -03001086static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001088 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001090 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001091 if (V4L2_TUNER_RADIO == t->mode) {
1092 if (t->radio_freq)
1093 set_freq(c, t->radio_freq);
1094 } else {
1095 if (t->tv_freq)
1096 set_freq(c, t->tv_freq);
1097 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 return 0;
1099}
1100
1101/* ----------------------------------------------------------------------- */
1102
1103static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001104 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001105 .attach_adapter = tuner_probe,
1106 .detach_client = tuner_detach,
1107 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001108 .suspend = tuner_suspend,
1109 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001111 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001112 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001114static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001115 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001116 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117};
1118
1119static int __init tuner_init_module(void)
1120{
1121 return i2c_add_driver(&driver);
1122}
1123
1124static void __exit tuner_cleanup_module(void)
1125{
1126 i2c_del_driver(&driver);
1127}
1128
1129module_init(tuner_init_module);
1130module_exit(tuner_cleanup_module);
1131
1132/*
1133 * Overrides for Emacs so that we follow Linus's tabbing style.
1134 * ---------------------------------------------------------------------------
1135 * Local variables:
1136 * c-basic-offset: 8
1137 * End:
1138 */