blob: da16bf998fbe052fd163d2f104ecfe5b77560991 [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;
106 /* DO NOT kfree(t->fe.analog_demod_priv) */
107 fe->analog_demod_priv = NULL;
Michael Krufkye18f9442007-08-21 01:25:48 -0300108}
109
Michael Krufky4e9154b2007-10-21 19:39:50 -0300110static void fe_standby(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300111{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300112 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
Michael Krufkye18f9442007-08-21 01:25:48 -0300113
114 if (fe_tuner_ops->sleep)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300115 fe_tuner_ops->sleep(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300116}
117
Michael Krufky4e9154b2007-10-21 19:39:50 -0300118static int fe_has_signal(struct dvb_frontend *fe)
Michael Krufky1f5ef192007-08-31 17:38:02 -0300119{
Michael Krufky14196832007-10-14 18:11:53 -0300120 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300121
Michael Krufky4e9154b2007-10-21 19:39:50 -0300122 if (fe->ops.tuner_ops.get_rf_strength)
123 fe->ops.tuner_ops.get_rf_strength(fe, &strength);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300124
125 return strength;
126}
127
Michael Krufky4e9154b2007-10-21 19:39:50 -0300128static void tuner_status(struct dvb_frontend *fe);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300129
130static struct analog_tuner_ops tuner_core_ops = {
131 .set_tv_freq = fe_set_freq,
132 .set_radio_freq = fe_set_freq,
133 .standby = fe_standby,
134 .release = fe_release,
135 .has_signal = fe_has_signal,
136 .tuner_status = tuner_status
137};
138
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700139/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140static void set_tv_freq(struct i2c_client *c, unsigned int freq)
141{
142 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300143 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
145 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700146 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 return;
148 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300149 if ((NULL == ops) || (NULL == ops->set_tv_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700150 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 return;
152 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700153 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
154 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
155 freq / 16, freq % 16 * 100 / 16, tv_range[0],
156 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200157 /* V4L2 spec: if the freq is not possible then the closest
158 possible value should be selected */
159 if (freq < tv_range[0] * 16)
160 freq = tv_range[0] * 16;
161 else
162 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 }
Michael Krufky4e9154b2007-10-21 19:39:50 -0300164 ops->set_tv_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165}
166
167static void set_radio_freq(struct i2c_client *c, unsigned int freq)
168{
169 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300170 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
172 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700173 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 return;
175 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300176 if ((NULL == ops) || (NULL == ops->set_radio_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700177 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 return;
179 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200180 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700181 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
182 freq / 16000, freq % 16000 * 100 / 16000,
183 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200184 /* V4L2 spec: if the freq is not possible then the closest
185 possible value should be selected */
186 if (freq < radio_range[0] * 16000)
187 freq = radio_range[0] * 16000;
188 else
189 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700191
Michael Krufky4e9154b2007-10-21 19:39:50 -0300192 ops->set_radio_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193}
194
195static void set_freq(struct i2c_client *c, unsigned long freq)
196{
197 struct tuner *t = i2c_get_clientdata(c);
198
199 switch (t->mode) {
200 case V4L2_TUNER_RADIO:
201 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700202 freq / 16000, freq % 16000 * 100 / 16000);
203 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200204 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 break;
206 case V4L2_TUNER_ANALOG_TV:
207 case V4L2_TUNER_DIGITAL_TV:
208 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700209 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200211 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300213 default:
214 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216}
217
Michael Krufky293197c2007-08-28 17:20:42 -0300218static void tuner_i2c_address_check(struct tuner *t)
219{
220 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
221 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
222 return;
223
224 tuner_warn("====================== WARNING! ======================\n");
225 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
226 tuner_warn("will soon be dropped. This message indicates that your\n");
227 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
228 t->i2c.name, t->i2c.addr);
229 tuner_warn("To ensure continued support for your device, please\n");
230 tuner_warn("send a copy of this message, along with full dmesg\n");
231 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
232 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
233 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
234 t->i2c.adapter->name, t->i2c.addr, t->type,
235 tuners[t->type].name);
236 tuner_warn("====================== WARNING! ======================\n");
237}
238
Michael Krufky4adad282007-08-27 21:59:08 -0300239static void attach_simple_tuner(struct tuner *t)
240{
241 struct simple_tuner_config cfg = {
242 .type = t->type,
243 .tun = &tuners[t->type]
244 };
245 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
246}
247
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700248static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300249 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300250 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251{
252 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300253 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300254 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700255 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700257 if (type == UNSET || type == TUNER_ABSENT) {
258 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 return;
260 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700262 if (type >= tuner_count) {
263 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
264 return;
265 }
266
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300268 t->config = new_config;
269 if (tuner_callback != NULL) {
270 tuner_dbg("defining GPIO callback\n");
271 t->tuner_callback = tuner_callback;
272 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300273
274 /* This code detects calls by card attach_inform */
275 if (NULL == t->i2c.dev.driver) {
276 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
277
278 return;
279 }
280
Michael Krufkyb2083192007-05-29 22:54:06 -0300281 /* discard private data, in case set_type() was previously called */
Michael Krufky1dde7a42007-10-21 13:40:56 -0300282 if ((ops) && (ops->release))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300283 ops->release(&t->fe);
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300284
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 switch (t->type) {
286 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300287 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 break;
289 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300290 {
Michael Krufky746d97322007-08-25 19:08:45 -0300291 tda8290_attach(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300293 }
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300294 case TUNER_PHILIPS_TDA8295:
295 {
296 tda8295_attach(t);
297 break;
298 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700299 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300300 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700301 t->type = TUNER_ABSENT;
302 t->mode_mask = T_UNINITIALIZED;
303 return;
304 }
305 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700306 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300307 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300308 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300309 t->type = TUNER_ABSENT;
310 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300311 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300312 }
313 t->mode_mask = T_RADIO;
314 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700315 case TUNER_PHILIPS_FMD1216ME_MK3:
316 buffer[0] = 0x0b;
317 buffer[1] = 0xdc;
318 buffer[2] = 0x9c;
319 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700320 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700321 mdelay(1);
322 buffer[2] = 0x86;
323 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700324 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300325 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700326 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800327 case TUNER_PHILIPS_TD1316:
328 buffer[0] = 0x0b;
329 buffer[1] = 0xdc;
330 buffer[2] = 0x86;
331 buffer[3] = 0xa4;
332 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300333 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200334 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300335 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300336 {
337 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
338 &c->dev, c->adapter->algo_data,
339 t->tuner_callback);
340 if (rc<0) {
341 t->type = TUNER_ABSENT;
342 t->mode_mask = T_UNINITIALIZED;
343 return;
344 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300345 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300346 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300347 case TUNER_TDA9887:
Michael Krufky31c95842007-10-21 20:48:48 -0300348 tda9887_attach(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300349 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300351 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 break;
353 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700354
Michael Krufkye2be32a2007-10-21 14:35:21 -0300355 ops = t->fe.ops.analog_demod_ops;
356
Michael Krufky1dde7a42007-10-21 13:40:56 -0300357 if (((NULL == ops) ||
358 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
359 (fe_tuner_ops->set_analog_params)) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300360 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
361
Michael Krufky1dde7a42007-10-21 13:40:56 -0300362 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300363 t->fe.analog_demod_priv = t;
Michael Krufkye18f9442007-08-21 01:25:48 -0300364 }
365
366 tuner_info("type set to %s\n", t->i2c.name);
367
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700368 if (t->mode_mask == T_UNINITIALIZED)
369 t->mode_mask = new_mode_mask;
370
Hans Verkuil27487d42006-01-15 15:04:52 -0200371 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700372 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100373 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700374 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300375 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700378/*
379 * This function apply tuner config to tuner specified
380 * by tun_setup structure. I addr is unset, then admin status
381 * and tun addr status is more precise then current status,
382 * it's applied. Otherwise status and type are applied only to
383 * tuner with exactly the same addr.
384*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700385
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700386static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700387{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700388 struct tuner *t = i2c_get_clientdata(c);
389
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300390 tuner_dbg("set addr for type %i\n", t->type);
391
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300392 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
393 (t->mode_mask & tun_setup->mode_mask))) ||
394 (tun_setup->addr == c->addr)) {
395 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300396 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700397 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700398}
399
400static inline int check_mode(struct tuner *t, char *cmd)
401{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700402 if ((1 << t->mode & t->mode_mask) == 0) {
403 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700404 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700405
406 switch (t->mode) {
407 case V4L2_TUNER_RADIO:
408 tuner_dbg("Cmd %s accepted for radio\n", cmd);
409 break;
410 case V4L2_TUNER_ANALOG_TV:
411 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
412 break;
413 case V4L2_TUNER_DIGITAL_TV:
414 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
415 break;
416 }
417 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700418}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700419
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700420/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421static int tuner_fixup_std(struct tuner *t)
422{
423 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300425 case '6':
426 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
427 t->std = V4L2_STD_PAL_60;
428 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 case 'b':
430 case 'B':
431 case 'g':
432 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700433 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 t->std = V4L2_STD_PAL_BG;
435 break;
436 case 'i':
437 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700438 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 t->std = V4L2_STD_PAL_I;
440 break;
441 case 'd':
442 case 'D':
443 case 'k':
444 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700445 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 t->std = V4L2_STD_PAL_DK;
447 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700448 case 'M':
449 case 'm':
450 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
451 t->std = V4L2_STD_PAL_M;
452 break;
453 case 'N':
454 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200455 if (pal[1] == 'c' || pal[1] == 'C') {
456 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
457 t->std = V4L2_STD_PAL_Nc;
458 } else {
459 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
460 t->std = V4L2_STD_PAL_N;
461 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700462 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700463 case '-':
464 /* default parameter, do nothing */
465 break;
466 default:
467 tuner_warn ("pal= argument not recognised\n");
468 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
470 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700471 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
472 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200473 case 'b':
474 case 'B':
475 case 'g':
476 case 'G':
477 case 'h':
478 case 'H':
479 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
480 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
481 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700482 case 'd':
483 case 'D':
484 case 'k':
485 case 'K':
486 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
487 t->std = V4L2_STD_SECAM_DK;
488 break;
489 case 'l':
490 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800491 if ((secam[1]=='C')||(secam[1]=='c')) {
492 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
493 t->std = V4L2_STD_SECAM_LC;
494 } else {
495 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
496 t->std = V4L2_STD_SECAM_L;
497 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700498 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700499 case '-':
500 /* default parameter, do nothing */
501 break;
502 default:
503 tuner_warn ("secam= argument not recognised\n");
504 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700505 }
506 }
507
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200508 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
509 switch (ntsc[0]) {
510 case 'm':
511 case 'M':
512 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
513 t->std = V4L2_STD_NTSC_M;
514 break;
515 case 'j':
516 case 'J':
517 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
518 t->std = V4L2_STD_NTSC_M_JP;
519 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200520 case 'k':
521 case 'K':
522 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
523 t->std = V4L2_STD_NTSC_M_KR;
524 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200525 case '-':
526 /* default parameter, do nothing */
527 break;
528 default:
529 tuner_info("ntsc= argument not recognised\n");
530 break;
531 }
532 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 return 0;
534}
535
Michael Krufky4e9154b2007-10-21 19:39:50 -0300536static void tuner_status(struct dvb_frontend *fe)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200537{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300538 struct tuner *t = fe->analog_demod_priv;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200539 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300540 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300541 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200542 const char *p;
543
544 switch (t->mode) {
545 case V4L2_TUNER_RADIO: p = "radio"; break;
546 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
547 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
548 default: p = "undefined"; break;
549 }
550 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200551 freq = t->radio_freq / 16000;
552 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200553 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200554 freq = t->tv_freq / 16;
555 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200556 }
557 tuner_info("Tuner mode: %s\n", p);
558 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300559 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200560 if (t->mode != V4L2_TUNER_RADIO)
561 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300562 if (fe_tuner_ops->get_status) {
563 u32 tuner_status;
564
565 fe_tuner_ops->get_status(&t->fe, &tuner_status);
566 if (tuner_status & TUNER_STATUS_LOCKED)
567 tuner_info("Tuner is locked.\n");
568 if (tuner_status & TUNER_STATUS_STEREO)
569 tuner_info("Stereo: yes\n");
570 }
Michael Krufky1b29ced2007-10-22 01:44:03 -0300571 if (ops) {
572 if (ops->has_signal)
573 tuner_info("Signal strength: %d\n",
574 ops->has_signal(fe));
575 if (ops->is_stereo)
576 tuner_info("Stereo: %s\n",
577 ops->is_stereo(fe) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200578 }
579}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200580
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581/* ---------------------------------------------------------------------- */
582
Hans Verkuilba8fc392006-06-25 15:34:39 -0300583/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700584static unsigned default_mode_mask;
585
586/* During client attach, set_type is called by adapter's attach_inform callback.
587 set_type must then be completed by tuner_attach.
588 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
590{
591 struct tuner *t;
592
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700593 client_template.adapter = adap;
594 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Panagiotis Issaris74081872006-01-11 19:40:56 -0200596 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700597 if (NULL == t)
598 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700599 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700601 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700602 t->audmode = V4L2_TUNER_MODE_STEREO;
603 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700605 if (show_i2c) {
606 unsigned char buffer[16];
607 int i,rc;
608
609 memset(buffer, 0, sizeof(buffer));
610 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800611 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700612 for (i=0;i<rc;i++)
613 printk("%02x ",buffer[i]);
614 printk("\n");
615 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300616 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
617 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
618 return -ENODEV;
619
Markus Rechberger257c6452006-01-23 17:11:11 -0200620 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700621 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800622 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300623 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300624 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300625 t->type = TUNER_TEA5761;
626 t->mode_mask = T_RADIO;
627 t->mode = T_STANDBY;
628 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
629 default_mode_mask &= ~T_RADIO;
630
631 goto register_client;
632 }
633 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800634 case 0x42:
635 case 0x43:
636 case 0x4a:
637 case 0x4b:
638 /* If chip is not tda8290, don't register.
639 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300640 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300641 tuner_dbg("chip at addr %x is a tda8290\n", addr);
642 } else {
643 /* Default is being tda9887 */
644 t->type = TUNER_TDA9887;
645 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
646 t->mode = T_STANDBY;
647 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800648 }
649 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800650 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300651 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700652 t->type = TUNER_TEA5767;
653 t->mode_mask = T_RADIO;
654 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200655 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700656 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700657
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800658 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700659 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800660 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700661 }
662 }
663
664 /* Initializes only the first adapter found */
665 if (default_mode_mask != T_UNINITIALIZED) {
666 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
667 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200668 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
669 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700670 default_mode_mask = T_UNINITIALIZED;
671 }
672
673 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800674register_client:
675 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700676 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300677 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 return 0;
679}
680
681static int tuner_probe(struct i2c_adapter *adap)
682{
683 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700684 normal_i2c[0] = addr;
685 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
Michael Krufkya1dec512007-08-24 01:13:07 -0300688 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
689 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
690 * and an RTC at 0x6f which can get corrupted if probed.
691 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300692 if ((adap->id == I2C_HW_B_CX2388x) ||
693 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300694 unsigned int i = 0;
695
696 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
697 i += 2;
698 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
699 ignore[i+0] = adap->nr;
700 ignore[i+1] = 0x6b;
701 ignore[i+2] = adap->nr;
702 ignore[i+3] = 0x6f;
703 ignore[i+4] = I2C_CLIENT_END;
704 } else
705 printk(KERN_WARNING "tuner: "
706 "too many options specified "
707 "in i2c probe ignore list!\n");
708 }
709
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700710 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700711
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 if (adap->class & I2C_CLASS_TV_ANALOG)
713 return i2c_probe(adap, &addr_data, tuner_attach);
714 return 0;
715}
716
717static int tuner_detach(struct i2c_client *client)
718{
719 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300720 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700721 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700723 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700724 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700725 tuner_warn
726 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700727 return err;
728 }
729
Michael Krufky1dde7a42007-10-21 13:40:56 -0300730 if ((ops) && (ops->release))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300731 ops->release(&t->fe);
Michael Krufky16f29162007-10-21 15:22:25 -0300732
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 kfree(t);
734 return 0;
735}
736
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700737/*
738 * Switch tuner to other mode. If tuner support both tv and radio,
739 * set another frequency to some value (This is needed for some pal
740 * tuners to avoid locking). Otherwise, just put second tuner in
741 * standby mode.
742 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700744static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
745{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300746 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
747
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800748 if (mode == t->mode)
749 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700750
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800751 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700752
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800753 if (check_mode(t, cmd) == EINVAL) {
754 t->mode = T_STANDBY;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300755 if ((ops) && (ops->standby))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300756 ops->standby(&t->fe);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800757 return EINVAL;
758 }
759 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760}
761
762#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800763 tuner_dbg("switching to v4l2\n"); \
764 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700765
766static inline int check_v4l2(struct tuner *t)
767{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300768 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
769 TV, v4l1 for radio), until that is fixed this code is disabled.
770 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
771 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700772 return 0;
773}
774
775static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776{
777 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300778 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300779 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
Hans Verkuilf9195de2006-01-11 19:01:01 -0200781 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200782 v4l_i2c_print_ioctl(&(t->i2c),cmd);
783
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700784 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700786 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300787 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 -0700788 ((struct tuner_setup *)arg)->type,
789 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300790 ((struct tuner_setup *)arg)->mode_mask,
791 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700792
793 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700794 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200796 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
797 == EINVAL)
798 return 0;
799 if (t->radio_freq)
800 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700802 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200803 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
804 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300805 t->mode = T_STANDBY;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300806 if ((ops) && (ops->standby))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300807 ops->standby(&t->fe);
Hans Verkuil27487d42006-01-15 15:04:52 -0200808 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300809#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700810 case VIDIOCSAUDIO:
811 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
812 return 0;
813 if (check_v4l2(t) == EINVAL)
814 return 0;
815
816 /* Should be implemented, since bttv calls it */
817 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700818 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700820 {
821 static const v4l2_std_id map[] = {
822 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
823 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
824 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
825 [4 /* bttv */ ] = V4L2_STD_PAL_M,
826 [5 /* bttv */ ] = V4L2_STD_PAL_N,
827 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
828 };
829 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700831 if (check_v4l2(t) == EINVAL)
832 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700833
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700834 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
835 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700837 if (vc->norm < ARRAY_SIZE(map))
838 t->std = map[vc->norm];
839 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200840 if (t->tv_freq)
841 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700842 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700843 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700844 case VIDIOCSFREQ:
845 {
846 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700847
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700848 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
849 return 0;
850 if (check_v4l2(t) == EINVAL)
851 return 0;
852
853 set_freq(client, *v);
854 return 0;
855 }
856 case VIDIOCGTUNER:
857 {
858 struct video_tuner *vt = arg;
859
860 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
861 return 0;
862 if (check_v4l2(t) == EINVAL)
863 return 0;
864
865 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300866 if (fe_tuner_ops->get_status) {
867 u32 tuner_status;
868
869 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300870 if (tuner_status & TUNER_STATUS_STEREO)
871 vt->flags |= VIDEO_TUNER_STEREO_ON;
872 else
873 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300874 } else {
Michael Krufky1dde7a42007-10-21 13:40:56 -0300875 if ((ops) && (ops->is_stereo)) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300876 if (ops->is_stereo(&t->fe))
Michael Krufkye18f9442007-08-21 01:25:48 -0300877 vt->flags |=
878 VIDEO_TUNER_STEREO_ON;
879 else
880 vt->flags &=
881 ~VIDEO_TUNER_STEREO_ON;
882 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700883 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300884 if ((ops) && (ops->has_signal))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300885 vt->signal = ops->has_signal(&t->fe);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300886
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700887 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
888
889 vt->rangelow = radio_range[0] * 16000;
890 vt->rangehigh = radio_range[1] * 16000;
891
892 } else {
893 vt->rangelow = tv_range[0] * 16;
894 vt->rangehigh = tv_range[1] * 16;
895 }
896
897 return 0;
898 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700900 {
901 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700903 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
904 return 0;
905 if (check_v4l2(t) == EINVAL)
906 return 0;
907
Michael Krufkye18f9442007-08-21 01:25:48 -0300908 if (V4L2_TUNER_RADIO == t->mode) {
909 if (fe_tuner_ops->get_status) {
910 u32 tuner_status;
911
912 fe_tuner_ops->get_status(&t->fe, &tuner_status);
913 va->mode = (tuner_status & TUNER_STATUS_STEREO)
914 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300915 } else if ((ops) && (ops->is_stereo))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300916 va->mode = ops->is_stereo(&t->fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300917 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
918 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700919 return 0;
920 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300921#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300922 case TUNER_SET_CONFIG:
923 {
924 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
925 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300927 if (t->type != cfg->tuner)
928 break;
929
930 if (t->type == TUNER_TDA9887) {
931 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300932 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300933 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300934 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300935
936 if (NULL == fe_tuner_ops->set_config) {
937 tuner_warn("Tuner frontend module has no way to "
938 "set config\n");
939 break;
940 }
941 fe_tuner_ops->set_config(&t->fe, cfg->priv);
942
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300943 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300944 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300945 /* --- v4l ioctls --- */
946 /* take care: bttv does userspace copying, we'll get a
947 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700949 {
950 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700952 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
953 == EINVAL)
954 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700955
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700956 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700958 t->std = *id;
959 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200960 if (t->tv_freq)
961 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700962 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700963 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700964 case VIDIOC_S_FREQUENCY:
965 {
966 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700967
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300968 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
969 == EINVAL)
970 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700971 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200972 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700973
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700974 break;
975 }
976 case VIDIOC_G_FREQUENCY:
977 {
978 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700979
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700980 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
981 return 0;
982 switch_v4l2();
983 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300984 if (fe_tuner_ops->get_frequency) {
985 u32 abs_freq;
986
987 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
988 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
989 (abs_freq * 2 + 125/2) / 125 :
990 (abs_freq + 62500/2) / 62500;
991 break;
992 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200993 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
994 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700995 break;
996 }
997 case VIDIOC_G_TUNER:
998 {
999 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -07001000
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001001 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
1002 return 0;
1003 switch_v4l2();
1004
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001005 tuner->type = t->mode;
Michael Krufky1dde7a42007-10-21 13:40:56 -03001006 if ((ops) && (ops->get_afc))
Michael Krufky4e9154b2007-10-21 19:39:50 -03001007 tuner->afc = ops->get_afc(&t->fe);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001008 if (t->mode == V4L2_TUNER_ANALOG_TV)
1009 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001010 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001011 tuner->rangelow = tv_range[0] * 16;
1012 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001013 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001014 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001015
1016 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001017 tuner->rxsubchans =
1018 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001019 if (fe_tuner_ops->get_status) {
1020 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001021
Michael Krufkye18f9442007-08-21 01:25:48 -03001022 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky4e9154b2007-10-21 19:39:50 -03001023 tuner->rxsubchans =
1024 (tuner_status & TUNER_STATUS_STEREO) ?
1025 V4L2_TUNER_SUB_STEREO :
1026 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001027 } else {
Michael Krufky1dde7a42007-10-21 13:40:56 -03001028 if ((ops) && (ops->is_stereo)) {
Michael Krufky4e9154b2007-10-21 19:39:50 -03001029 tuner->rxsubchans =
1030 ops->is_stereo(&t->fe) ?
1031 V4L2_TUNER_SUB_STEREO :
1032 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001033 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001034 }
Michael Krufky1dde7a42007-10-21 13:40:56 -03001035 if ((ops) && (ops->has_signal))
Michael Krufky4e9154b2007-10-21 19:39:50 -03001036 tuner->signal = ops->has_signal(&t->fe);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001037 tuner->capability |=
1038 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1039 tuner->audmode = t->audmode;
1040 tuner->rangelow = radio_range[0] * 16000;
1041 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001042 break;
1043 }
1044 case VIDIOC_S_TUNER:
1045 {
1046 struct v4l2_tuner *tuner = arg;
1047
1048 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1049 return 0;
1050
1051 switch_v4l2();
1052
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001053 /* do nothing unless we're a radio tuner */
1054 if (t->mode != V4L2_TUNER_RADIO)
1055 break;
1056 t->audmode = tuner->audmode;
1057 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001058 break;
1059 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001060 case VIDIOC_LOG_STATUS:
Michael Krufky1dde7a42007-10-21 13:40:56 -03001061 if ((ops) && (ops->tuner_status))
Michael Krufky4e9154b2007-10-21 19:39:50 -03001062 ops->tuner_status(&t->fe);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001063 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 }
1065
1066 return 0;
1067}
1068
Jean Delvare21b48a72007-03-12 19:20:15 -03001069static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001071 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001073 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 /* FIXME: power down ??? */
1075 return 0;
1076}
1077
Jean Delvare21b48a72007-03-12 19:20:15 -03001078static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001080 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001082 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001083 if (V4L2_TUNER_RADIO == t->mode) {
1084 if (t->radio_freq)
1085 set_freq(c, t->radio_freq);
1086 } else {
1087 if (t->tv_freq)
1088 set_freq(c, t->tv_freq);
1089 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 return 0;
1091}
1092
1093/* ----------------------------------------------------------------------- */
1094
1095static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001096 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001097 .attach_adapter = tuner_probe,
1098 .detach_client = tuner_detach,
1099 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001100 .suspend = tuner_suspend,
1101 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001103 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001104 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001106static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001107 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001108 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109};
1110
1111static int __init tuner_init_module(void)
1112{
1113 return i2c_add_driver(&driver);
1114}
1115
1116static void __exit tuner_cleanup_module(void)
1117{
1118 i2c_del_driver(&driver);
1119}
1120
1121module_init(tuner_init_module);
1122module_exit(tuner_cleanup_module);
1123
1124/*
1125 * Overrides for Emacs so that we follow Linus's tabbing style.
1126 * ---------------------------------------------------------------------------
1127 * Local variables:
1128 * c-basic-offset: 8
1129 * End:
1130 */