blob: 6ab57ec3f5a3307ce40f32695549a27b675d3df5 [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 Krufky5bea1cd2007-10-22 09:56:38 -0300298 case TUNER_PHILIPS_TDA8295:
299 {
Michael Krufky8c125f2c2007-10-27 02:00:57 -0300300 tda829x_attach(t);
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300301 break;
302 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700303 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300304 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700305 t->type = TUNER_ABSENT;
306 t->mode_mask = T_UNINITIALIZED;
307 return;
308 }
309 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700310 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300311 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300312 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300313 t->type = TUNER_ABSENT;
314 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300315 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300316 }
317 t->mode_mask = T_RADIO;
318 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700319 case TUNER_PHILIPS_FMD1216ME_MK3:
320 buffer[0] = 0x0b;
321 buffer[1] = 0xdc;
322 buffer[2] = 0x9c;
323 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700324 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700325 mdelay(1);
326 buffer[2] = 0x86;
327 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700328 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300329 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700330 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800331 case TUNER_PHILIPS_TD1316:
332 buffer[0] = 0x0b;
333 buffer[1] = 0xdc;
334 buffer[2] = 0x86;
335 buffer[3] = 0xa4;
336 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300337 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200338 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300339 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300340 {
341 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
342 &c->dev, c->adapter->algo_data,
343 t->tuner_callback);
344 if (rc<0) {
345 t->type = TUNER_ABSENT;
346 t->mode_mask = T_UNINITIALIZED;
347 return;
348 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300349 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300350 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300351 case TUNER_TDA9887:
Michael Krufky31c95842007-10-21 20:48:48 -0300352 tda9887_attach(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300353 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300355 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 break;
357 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700358
Michael Krufkye2be32a2007-10-21 14:35:21 -0300359 ops = t->fe.ops.analog_demod_ops;
360
Michael Krufky1dde7a42007-10-21 13:40:56 -0300361 if (((NULL == ops) ||
362 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
363 (fe_tuner_ops->set_analog_params)) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300364 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
365
Michael Krufky1dde7a42007-10-21 13:40:56 -0300366 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300367 t->fe.analog_demod_priv = t;
Michael Krufkye18f9442007-08-21 01:25:48 -0300368 }
369
370 tuner_info("type set to %s\n", t->i2c.name);
371
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700372 if (t->mode_mask == T_UNINITIALIZED)
373 t->mode_mask = new_mode_mask;
374
Hans Verkuil27487d42006-01-15 15:04:52 -0200375 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700376 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100377 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700378 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300379 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380}
381
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700382/*
383 * This function apply tuner config to tuner specified
384 * by tun_setup structure. I addr is unset, then admin status
385 * and tun addr status is more precise then current status,
386 * it's applied. Otherwise status and type are applied only to
387 * tuner with exactly the same addr.
388*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700389
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700390static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700391{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700392 struct tuner *t = i2c_get_clientdata(c);
393
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300394 tuner_dbg("set addr for type %i\n", t->type);
395
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300396 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
397 (t->mode_mask & tun_setup->mode_mask))) ||
398 (tun_setup->addr == c->addr)) {
399 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300400 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700401 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700402}
403
404static inline int check_mode(struct tuner *t, char *cmd)
405{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700406 if ((1 << t->mode & t->mode_mask) == 0) {
407 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700408 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700409
410 switch (t->mode) {
411 case V4L2_TUNER_RADIO:
412 tuner_dbg("Cmd %s accepted for radio\n", cmd);
413 break;
414 case V4L2_TUNER_ANALOG_TV:
415 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
416 break;
417 case V4L2_TUNER_DIGITAL_TV:
418 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
419 break;
420 }
421 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700422}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700423
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700424/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425static int tuner_fixup_std(struct tuner *t)
426{
427 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300429 case '6':
430 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
431 t->std = V4L2_STD_PAL_60;
432 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 case 'b':
434 case 'B':
435 case 'g':
436 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700437 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 t->std = V4L2_STD_PAL_BG;
439 break;
440 case 'i':
441 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700442 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 t->std = V4L2_STD_PAL_I;
444 break;
445 case 'd':
446 case 'D':
447 case 'k':
448 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700449 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 t->std = V4L2_STD_PAL_DK;
451 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700452 case 'M':
453 case 'm':
454 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
455 t->std = V4L2_STD_PAL_M;
456 break;
457 case 'N':
458 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200459 if (pal[1] == 'c' || pal[1] == 'C') {
460 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
461 t->std = V4L2_STD_PAL_Nc;
462 } else {
463 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
464 t->std = V4L2_STD_PAL_N;
465 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700466 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700467 case '-':
468 /* default parameter, do nothing */
469 break;
470 default:
471 tuner_warn ("pal= argument not recognised\n");
472 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 }
474 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700475 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
476 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200477 case 'b':
478 case 'B':
479 case 'g':
480 case 'G':
481 case 'h':
482 case 'H':
483 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
484 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
485 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700486 case 'd':
487 case 'D':
488 case 'k':
489 case 'K':
490 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
491 t->std = V4L2_STD_SECAM_DK;
492 break;
493 case 'l':
494 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800495 if ((secam[1]=='C')||(secam[1]=='c')) {
496 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
497 t->std = V4L2_STD_SECAM_LC;
498 } else {
499 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
500 t->std = V4L2_STD_SECAM_L;
501 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700502 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700503 case '-':
504 /* default parameter, do nothing */
505 break;
506 default:
507 tuner_warn ("secam= argument not recognised\n");
508 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700509 }
510 }
511
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200512 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
513 switch (ntsc[0]) {
514 case 'm':
515 case 'M':
516 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
517 t->std = V4L2_STD_NTSC_M;
518 break;
519 case 'j':
520 case 'J':
521 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
522 t->std = V4L2_STD_NTSC_M_JP;
523 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200524 case 'k':
525 case 'K':
526 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
527 t->std = V4L2_STD_NTSC_M_KR;
528 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200529 case '-':
530 /* default parameter, do nothing */
531 break;
532 default:
533 tuner_info("ntsc= argument not recognised\n");
534 break;
535 }
536 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 return 0;
538}
539
Michael Krufky4e9154b2007-10-21 19:39:50 -0300540static void tuner_status(struct dvb_frontend *fe)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200541{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300542 struct tuner *t = fe->analog_demod_priv;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200543 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300544 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300545 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200546 const char *p;
547
548 switch (t->mode) {
549 case V4L2_TUNER_RADIO: p = "radio"; break;
550 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
551 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
552 default: p = "undefined"; break;
553 }
554 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200555 freq = t->radio_freq / 16000;
556 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200557 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200558 freq = t->tv_freq / 16;
559 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200560 }
561 tuner_info("Tuner mode: %s\n", p);
562 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300563 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200564 if (t->mode != V4L2_TUNER_RADIO)
565 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300566 if (fe_tuner_ops->get_status) {
567 u32 tuner_status;
568
569 fe_tuner_ops->get_status(&t->fe, &tuner_status);
570 if (tuner_status & TUNER_STATUS_LOCKED)
571 tuner_info("Tuner is locked.\n");
572 if (tuner_status & TUNER_STATUS_STEREO)
573 tuner_info("Stereo: yes\n");
574 }
Michael Krufky1b29ced2007-10-22 01:44:03 -0300575 if (ops) {
576 if (ops->has_signal)
577 tuner_info("Signal strength: %d\n",
578 ops->has_signal(fe));
579 if (ops->is_stereo)
580 tuner_info("Stereo: %s\n",
581 ops->is_stereo(fe) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200582 }
583}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200584
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585/* ---------------------------------------------------------------------- */
586
Hans Verkuilba8fc392006-06-25 15:34:39 -0300587/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700588static unsigned default_mode_mask;
589
590/* During client attach, set_type is called by adapter's attach_inform callback.
591 set_type must then be completed by tuner_attach.
592 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
594{
595 struct tuner *t;
596
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700597 client_template.adapter = adap;
598 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Panagiotis Issaris74081872006-01-11 19:40:56 -0200600 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700601 if (NULL == t)
602 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700603 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700605 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700606 t->audmode = V4L2_TUNER_MODE_STEREO;
607 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700609 if (show_i2c) {
610 unsigned char buffer[16];
611 int i,rc;
612
613 memset(buffer, 0, sizeof(buffer));
614 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800615 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700616 for (i=0;i<rc;i++)
617 printk("%02x ",buffer[i]);
618 printk("\n");
619 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300620 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
621 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
622 return -ENODEV;
623
Markus Rechberger257c6452006-01-23 17:11:11 -0200624 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700625 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800626 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300627 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300628 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300629 t->type = TUNER_TEA5761;
630 t->mode_mask = T_RADIO;
631 t->mode = T_STANDBY;
632 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
633 default_mode_mask &= ~T_RADIO;
634
635 goto register_client;
636 }
637 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800638 case 0x42:
639 case 0x43:
640 case 0x4a:
641 case 0x4b:
642 /* If chip is not tda8290, don't register.
643 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300644 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300645 tuner_dbg("chip at addr %x is a tda8290\n", addr);
646 } else {
647 /* Default is being tda9887 */
648 t->type = TUNER_TDA9887;
649 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
650 t->mode = T_STANDBY;
651 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800652 }
653 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800654 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300655 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700656 t->type = TUNER_TEA5767;
657 t->mode_mask = T_RADIO;
658 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200659 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700660 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700661
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800662 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700663 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800664 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700665 }
666 }
667
668 /* Initializes only the first adapter found */
669 if (default_mode_mask != T_UNINITIALIZED) {
670 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
671 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200672 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
673 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700674 default_mode_mask = T_UNINITIALIZED;
675 }
676
677 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800678register_client:
679 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300681 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 return 0;
683}
684
685static int tuner_probe(struct i2c_adapter *adap)
686{
687 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700688 normal_i2c[0] = addr;
689 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
Michael Krufkya1dec512007-08-24 01:13:07 -0300692 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
693 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
694 * and an RTC at 0x6f which can get corrupted if probed.
695 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300696 if ((adap->id == I2C_HW_B_CX2388x) ||
697 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300698 unsigned int i = 0;
699
700 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
701 i += 2;
702 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
703 ignore[i+0] = adap->nr;
704 ignore[i+1] = 0x6b;
705 ignore[i+2] = adap->nr;
706 ignore[i+3] = 0x6f;
707 ignore[i+4] = I2C_CLIENT_END;
708 } else
709 printk(KERN_WARNING "tuner: "
710 "too many options specified "
711 "in i2c probe ignore list!\n");
712 }
713
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700714 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700715
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 if (adap->class & I2C_CLASS_TV_ANALOG)
717 return i2c_probe(adap, &addr_data, tuner_attach);
718 return 0;
719}
720
721static int tuner_detach(struct i2c_client *client)
722{
723 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300724 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700725 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700727 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700728 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700729 tuner_warn
730 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700731 return err;
732 }
733
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300734 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300735 ops->release(&t->fe);
Michael Krufky16f29162007-10-21 15:22:25 -0300736
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 kfree(t);
738 return 0;
739}
740
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700741/*
742 * Switch tuner to other mode. If tuner support both tv and radio,
743 * set another frequency to some value (This is needed for some pal
744 * tuners to avoid locking). Otherwise, just put second tuner in
745 * standby mode.
746 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700748static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
749{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300750 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
751
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800752 if (mode == t->mode)
753 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700754
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800755 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700756
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800757 if (check_mode(t, cmd) == EINVAL) {
758 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300759 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300760 ops->standby(&t->fe);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800761 return EINVAL;
762 }
763 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700764}
765
766#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800767 tuner_dbg("switching to v4l2\n"); \
768 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700769
770static inline int check_v4l2(struct tuner *t)
771{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300772 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
773 TV, v4l1 for radio), until that is fixed this code is disabled.
774 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
775 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700776 return 0;
777}
778
779static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780{
781 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300782 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300783 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Hans Verkuilf9195de2006-01-11 19:01:01 -0200785 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200786 v4l_i2c_print_ioctl(&(t->i2c),cmd);
787
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700788 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700790 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300791 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 -0700792 ((struct tuner_setup *)arg)->type,
793 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300794 ((struct tuner_setup *)arg)->mode_mask,
795 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700796
797 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700798 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200800 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
801 == EINVAL)
802 return 0;
803 if (t->radio_freq)
804 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700806 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200807 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
808 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300809 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300810 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300811 ops->standby(&t->fe);
Hans Verkuil27487d42006-01-15 15:04:52 -0200812 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300813#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700814 case VIDIOCSAUDIO:
815 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
816 return 0;
817 if (check_v4l2(t) == EINVAL)
818 return 0;
819
820 /* Should be implemented, since bttv calls it */
821 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700822 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700824 {
825 static const v4l2_std_id map[] = {
826 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
827 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
828 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
829 [4 /* bttv */ ] = V4L2_STD_PAL_M,
830 [5 /* bttv */ ] = V4L2_STD_PAL_N,
831 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
832 };
833 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700835 if (check_v4l2(t) == EINVAL)
836 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700837
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700838 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
839 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700841 if (vc->norm < ARRAY_SIZE(map))
842 t->std = map[vc->norm];
843 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200844 if (t->tv_freq)
845 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700846 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700847 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700848 case VIDIOCSFREQ:
849 {
850 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700851
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700852 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
853 return 0;
854 if (check_v4l2(t) == EINVAL)
855 return 0;
856
857 set_freq(client, *v);
858 return 0;
859 }
860 case VIDIOCGTUNER:
861 {
862 struct video_tuner *vt = arg;
863
864 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
865 return 0;
866 if (check_v4l2(t) == EINVAL)
867 return 0;
868
869 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300870 if (fe_tuner_ops->get_status) {
871 u32 tuner_status;
872
873 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300874 if (tuner_status & TUNER_STATUS_STEREO)
875 vt->flags |= VIDEO_TUNER_STEREO_ON;
876 else
877 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300878 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300879 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300880 if (ops->is_stereo(&t->fe))
Michael Krufkye18f9442007-08-21 01:25:48 -0300881 vt->flags |=
882 VIDEO_TUNER_STEREO_ON;
883 else
884 vt->flags &=
885 ~VIDEO_TUNER_STEREO_ON;
886 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700887 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300888 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300889 vt->signal = ops->has_signal(&t->fe);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300890
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700891 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
892
893 vt->rangelow = radio_range[0] * 16000;
894 vt->rangehigh = radio_range[1] * 16000;
895
896 } else {
897 vt->rangelow = tv_range[0] * 16;
898 vt->rangehigh = tv_range[1] * 16;
899 }
900
901 return 0;
902 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700904 {
905 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700907 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
908 return 0;
909 if (check_v4l2(t) == EINVAL)
910 return 0;
911
Michael Krufkye18f9442007-08-21 01:25:48 -0300912 if (V4L2_TUNER_RADIO == t->mode) {
913 if (fe_tuner_ops->get_status) {
914 u32 tuner_status;
915
916 fe_tuner_ops->get_status(&t->fe, &tuner_status);
917 va->mode = (tuner_status & TUNER_STATUS_STEREO)
918 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300919 } else if (ops && ops->is_stereo)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300920 va->mode = ops->is_stereo(&t->fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300921 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
922 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700923 return 0;
924 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300925#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300926 case TUNER_SET_CONFIG:
927 {
928 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
929 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300931 if (t->type != cfg->tuner)
932 break;
933
934 if (t->type == TUNER_TDA9887) {
935 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300936 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300937 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300938 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300939
940 if (NULL == fe_tuner_ops->set_config) {
941 tuner_warn("Tuner frontend module has no way to "
942 "set config\n");
943 break;
944 }
945 fe_tuner_ops->set_config(&t->fe, cfg->priv);
946
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300947 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300948 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300949 /* --- v4l ioctls --- */
950 /* take care: bttv does userspace copying, we'll get a
951 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700953 {
954 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700956 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
957 == EINVAL)
958 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700959
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700960 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700962 t->std = *id;
963 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200964 if (t->tv_freq)
965 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700966 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700967 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700968 case VIDIOC_S_FREQUENCY:
969 {
970 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700971
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300972 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
973 == EINVAL)
974 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700975 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200976 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700977
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700978 break;
979 }
980 case VIDIOC_G_FREQUENCY:
981 {
982 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700983
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700984 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
985 return 0;
986 switch_v4l2();
987 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300988 if (fe_tuner_ops->get_frequency) {
989 u32 abs_freq;
990
991 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
992 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
993 (abs_freq * 2 + 125/2) / 125 :
994 (abs_freq + 62500/2) / 62500;
995 break;
996 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200997 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
998 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700999 break;
1000 }
1001 case VIDIOC_G_TUNER:
1002 {
1003 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -07001004
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001005 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
1006 return 0;
1007 switch_v4l2();
1008
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001009 tuner->type = t->mode;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001010 if (ops && ops->get_afc)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001011 tuner->afc = ops->get_afc(&t->fe);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001012 if (t->mode == V4L2_TUNER_ANALOG_TV)
1013 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001014 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001015 tuner->rangelow = tv_range[0] * 16;
1016 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001017 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001018 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001019
1020 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001021 tuner->rxsubchans =
1022 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001023 if (fe_tuner_ops->get_status) {
1024 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001025
Michael Krufkye18f9442007-08-21 01:25:48 -03001026 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky4e9154b2007-10-21 19:39:50 -03001027 tuner->rxsubchans =
1028 (tuner_status & TUNER_STATUS_STEREO) ?
1029 V4L2_TUNER_SUB_STEREO :
1030 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001031 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001032 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -03001033 tuner->rxsubchans =
1034 ops->is_stereo(&t->fe) ?
1035 V4L2_TUNER_SUB_STEREO :
1036 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001037 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001038 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001039 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001040 tuner->signal = ops->has_signal(&t->fe);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001041 tuner->capability |=
1042 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1043 tuner->audmode = t->audmode;
1044 tuner->rangelow = radio_range[0] * 16000;
1045 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001046 break;
1047 }
1048 case VIDIOC_S_TUNER:
1049 {
1050 struct v4l2_tuner *tuner = arg;
1051
1052 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1053 return 0;
1054
1055 switch_v4l2();
1056
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001057 /* do nothing unless we're a radio tuner */
1058 if (t->mode != V4L2_TUNER_RADIO)
1059 break;
1060 t->audmode = tuner->audmode;
1061 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001062 break;
1063 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001064 case VIDIOC_LOG_STATUS:
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001065 if (ops && ops->tuner_status)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001066 ops->tuner_status(&t->fe);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001067 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 }
1069
1070 return 0;
1071}
1072
Jean Delvare21b48a72007-03-12 19:20:15 -03001073static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001075 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001077 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 /* FIXME: power down ??? */
1079 return 0;
1080}
1081
Jean Delvare21b48a72007-03-12 19:20:15 -03001082static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001084 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001086 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001087 if (V4L2_TUNER_RADIO == t->mode) {
1088 if (t->radio_freq)
1089 set_freq(c, t->radio_freq);
1090 } else {
1091 if (t->tv_freq)
1092 set_freq(c, t->tv_freq);
1093 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 return 0;
1095}
1096
1097/* ----------------------------------------------------------------------- */
1098
1099static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001100 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001101 .attach_adapter = tuner_probe,
1102 .detach_client = tuner_detach,
1103 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001104 .suspend = tuner_suspend,
1105 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001107 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001108 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001110static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001111 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001112 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113};
1114
1115static int __init tuner_init_module(void)
1116{
1117 return i2c_add_driver(&driver);
1118}
1119
1120static void __exit tuner_cleanup_module(void)
1121{
1122 i2c_del_driver(&driver);
1123}
1124
1125module_init(tuner_init_module);
1126module_exit(tuner_cleanup_module);
1127
1128/*
1129 * Overrides for Emacs so that we follow Linus's tabbing style.
1130 * ---------------------------------------------------------------------------
1131 * Local variables:
1132 * c-basic-offset: 8
1133 * End:
1134 */