blob: 41ff4d2604af91a7b51d91a3684f9ca756699d4a [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 Krufky4524c1ab2007-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) ||
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300229 ((t->i2c->addr < 0x64) || (t->i2c->addr > 0x6f)))
Michael Krufky293197c2007-08-28 17:20:42 -0300230 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",
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300236 t->i2c->name, t->i2c->addr);
Michael Krufky293197c2007-08-28 17:20:42 -0300237 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",
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300242 t->i2c->adapter->name, t->i2c->addr, t->type,
Michael Krufky293197c2007-08-28 17:20:42 -0300243 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 };
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300253 simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
Michael Krufky4adad282007-08-27 21:59:08 -0300254}
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:
Hans Verkuil1cba97d72007-09-14 05:13:54 -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 {
Michael Krufky8c125f22007-10-27 02:00:57 -0300299 tda829x_attach(t);
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300300 break;
301 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700302 case TUNER_TEA5767:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300303 if (tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700304 t->type = TUNER_ABSENT;
305 t->mode_mask = T_UNINITIALIZED;
306 return;
307 }
308 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700309 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300310 case TUNER_TEA5761:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300311 if (tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300312 t->type = TUNER_ABSENT;
313 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300314 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300315 }
316 t->mode_mask = T_RADIO;
317 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700318 case TUNER_PHILIPS_FMD1216ME_MK3:
319 buffer[0] = 0x0b;
320 buffer[1] = 0xdc;
321 buffer[2] = 0x9c;
322 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700323 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700324 mdelay(1);
325 buffer[2] = 0x86;
326 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700327 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300328 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700329 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800330 case TUNER_PHILIPS_TD1316:
331 buffer[0] = 0x0b;
332 buffer[1] = 0xdc;
333 buffer[2] = 0x86;
334 buffer[3] = 0xa4;
335 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300336 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200337 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300338 case TUNER_TDA9887:
Michael Krufky31c95842007-10-21 20:48:48 -0300339 tda9887_attach(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300340 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300342 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 break;
344 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700345
Michael Krufkye2be32a2007-10-21 14:35:21 -0300346 ops = t->fe.ops.analog_demod_ops;
347
Michael Krufky1dde7a42007-10-21 13:40:56 -0300348 if (((NULL == ops) ||
349 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
350 (fe_tuner_ops->set_analog_params)) {
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300351 strlcpy(t->i2c->name, fe_tuner_ops->info.name,
352 sizeof(t->i2c->name));
Michael Krufkye18f9442007-08-21 01:25:48 -0300353
Michael Krufky1dde7a42007-10-21 13:40:56 -0300354 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300355 t->fe.analog_demod_priv = t;
Michael Krufkye18f9442007-08-21 01:25:48 -0300356 }
357
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300358 tuner_info("type set to %s\n", t->i2c->name);
Michael Krufkye18f9442007-08-21 01:25:48 -0300359
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700360 if (t->mode_mask == T_UNINITIALIZED)
361 t->mode_mask = new_mode_mask;
362
Hans Verkuil27487d42006-01-15 15:04:52 -0200363 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700364 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100365 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700366 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300367 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368}
369
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700370/*
371 * This function apply tuner config to tuner specified
372 * by tun_setup structure. I addr is unset, then admin status
373 * and tun addr status is more precise then current status,
374 * it's applied. Otherwise status and type are applied only to
375 * tuner with exactly the same addr.
376*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700377
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700378static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700379{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700380 struct tuner *t = i2c_get_clientdata(c);
381
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300382 tuner_dbg("set addr for type %i\n", t->type);
383
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300384 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
385 (t->mode_mask & tun_setup->mode_mask))) ||
386 (tun_setup->addr == c->addr)) {
387 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300388 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700389 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700390}
391
392static inline int check_mode(struct tuner *t, char *cmd)
393{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700394 if ((1 << t->mode & t->mode_mask) == 0) {
395 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700396 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700397
398 switch (t->mode) {
399 case V4L2_TUNER_RADIO:
400 tuner_dbg("Cmd %s accepted for radio\n", cmd);
401 break;
402 case V4L2_TUNER_ANALOG_TV:
403 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
404 break;
405 case V4L2_TUNER_DIGITAL_TV:
406 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
407 break;
408 }
409 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700410}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700411
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700412/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413static int tuner_fixup_std(struct tuner *t)
414{
415 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300417 case '6':
418 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
419 t->std = V4L2_STD_PAL_60;
420 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 case 'b':
422 case 'B':
423 case 'g':
424 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700425 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 t->std = V4L2_STD_PAL_BG;
427 break;
428 case 'i':
429 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700430 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 t->std = V4L2_STD_PAL_I;
432 break;
433 case 'd':
434 case 'D':
435 case 'k':
436 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700437 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 t->std = V4L2_STD_PAL_DK;
439 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700440 case 'M':
441 case 'm':
442 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
443 t->std = V4L2_STD_PAL_M;
444 break;
445 case 'N':
446 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200447 if (pal[1] == 'c' || pal[1] == 'C') {
448 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
449 t->std = V4L2_STD_PAL_Nc;
450 } else {
451 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
452 t->std = V4L2_STD_PAL_N;
453 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700454 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700455 case '-':
456 /* default parameter, do nothing */
457 break;
458 default:
459 tuner_warn ("pal= argument not recognised\n");
460 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 }
462 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700463 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
464 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200465 case 'b':
466 case 'B':
467 case 'g':
468 case 'G':
469 case 'h':
470 case 'H':
471 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
472 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
473 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700474 case 'd':
475 case 'D':
476 case 'k':
477 case 'K':
478 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
479 t->std = V4L2_STD_SECAM_DK;
480 break;
481 case 'l':
482 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800483 if ((secam[1]=='C')||(secam[1]=='c')) {
484 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
485 t->std = V4L2_STD_SECAM_LC;
486 } else {
487 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
488 t->std = V4L2_STD_SECAM_L;
489 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700490 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700491 case '-':
492 /* default parameter, do nothing */
493 break;
494 default:
495 tuner_warn ("secam= argument not recognised\n");
496 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700497 }
498 }
499
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200500 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
501 switch (ntsc[0]) {
502 case 'm':
503 case 'M':
504 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
505 t->std = V4L2_STD_NTSC_M;
506 break;
507 case 'j':
508 case 'J':
509 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
510 t->std = V4L2_STD_NTSC_M_JP;
511 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200512 case 'k':
513 case 'K':
514 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
515 t->std = V4L2_STD_NTSC_M_KR;
516 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200517 case '-':
518 /* default parameter, do nothing */
519 break;
520 default:
521 tuner_info("ntsc= argument not recognised\n");
522 break;
523 }
524 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 return 0;
526}
527
Michael Krufky4e9154b2007-10-21 19:39:50 -0300528static void tuner_status(struct dvb_frontend *fe)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200529{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300530 struct tuner *t = fe->analog_demod_priv;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200531 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300532 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300533 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200534 const char *p;
535
536 switch (t->mode) {
537 case V4L2_TUNER_RADIO: p = "radio"; break;
538 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
539 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
540 default: p = "undefined"; break;
541 }
542 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200543 freq = t->radio_freq / 16000;
544 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200545 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200546 freq = t->tv_freq / 16;
547 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200548 }
549 tuner_info("Tuner mode: %s\n", p);
550 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300551 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200552 if (t->mode != V4L2_TUNER_RADIO)
553 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300554 if (fe_tuner_ops->get_status) {
555 u32 tuner_status;
556
557 fe_tuner_ops->get_status(&t->fe, &tuner_status);
558 if (tuner_status & TUNER_STATUS_LOCKED)
559 tuner_info("Tuner is locked.\n");
560 if (tuner_status & TUNER_STATUS_STEREO)
561 tuner_info("Stereo: yes\n");
562 }
Michael Krufky1b29ced2007-10-22 01:44:03 -0300563 if (ops) {
564 if (ops->has_signal)
565 tuner_info("Signal strength: %d\n",
566 ops->has_signal(fe));
567 if (ops->is_stereo)
568 tuner_info("Stereo: %s\n",
569 ops->is_stereo(fe) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200570 }
571}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200572
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573/* ---------------------------------------------------------------------- */
574
Hans Verkuilba8fc392006-06-25 15:34:39 -0300575/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700576static unsigned default_mode_mask;
577
578/* During client attach, set_type is called by adapter's attach_inform callback.
579 set_type must then be completed by tuner_attach.
580 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
582{
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300583 struct i2c_client *client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 struct tuner *t;
585
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300586 client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
587 if (NULL == client)
588 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
Panagiotis Issaris74081872006-01-11 19:40:56 -0200590 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300591 if (NULL == t) {
592 kfree(client);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700593 return -ENOMEM;
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300594 }
595 t->i2c = client;
596 client_template.adapter = adap;
597 client_template.addr = addr;
598 memcpy(client, &client_template, sizeof(struct i2c_client));
599 i2c_set_clientdata(client, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700600 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700601 t->audmode = V4L2_TUNER_MODE_STEREO;
602 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700604 if (show_i2c) {
605 unsigned char buffer[16];
606 int i,rc;
607
608 memset(buffer, 0, sizeof(buffer));
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300609 rc = i2c_master_recv(client, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800610 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700611 for (i=0;i<rc;i++)
612 printk("%02x ",buffer[i]);
613 printk("\n");
614 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300615 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
616 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
617 return -ENODEV;
618
Markus Rechberger257c6452006-01-23 17:11:11 -0200619 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700620 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800621 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300622 case 0x10:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300623 if (tea5761_autodetection(t->i2c->adapter, t->i2c->addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300624 t->type = TUNER_TEA5761;
625 t->mode_mask = T_RADIO;
626 t->mode = T_STANDBY;
627 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
628 default_mode_mask &= ~T_RADIO;
629
630 goto register_client;
631 }
632 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800633 case 0x42:
634 case 0x43:
635 case 0x4a:
636 case 0x4b:
637 /* If chip is not tda8290, don't register.
638 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300639 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300640 tuner_dbg("chip at addr %x is a tda8290\n", addr);
641 } else {
642 /* Default is being tda9887 */
643 t->type = TUNER_TDA9887;
644 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
645 t->mode = T_STANDBY;
646 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800647 }
648 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800649 case 0x60:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300650 if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700651 t->type = TUNER_TEA5767;
652 t->mode_mask = T_RADIO;
653 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200654 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700655 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700656
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800657 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700658 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800659 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700660 }
661 }
662
663 /* Initializes only the first adapter found */
664 if (default_mode_mask != T_UNINITIALIZED) {
665 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
666 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200667 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
668 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700669 default_mode_mask = T_UNINITIALIZED;
670 }
671
672 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800673register_client:
674 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300675 i2c_attach_client (client);
676 set_type (client,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return 0;
678}
679
680static int tuner_probe(struct i2c_adapter *adap)
681{
682 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700683 normal_i2c[0] = addr;
684 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
Michael Krufkya1dec512007-08-24 01:13:07 -0300687 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
688 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
689 * and an RTC at 0x6f which can get corrupted if probed.
690 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300691 if ((adap->id == I2C_HW_B_CX2388x) ||
692 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300693 unsigned int i = 0;
694
695 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
696 i += 2;
697 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
698 ignore[i+0] = adap->nr;
699 ignore[i+1] = 0x6b;
700 ignore[i+2] = adap->nr;
701 ignore[i+3] = 0x6f;
702 ignore[i+4] = I2C_CLIENT_END;
703 } else
704 printk(KERN_WARNING "tuner: "
705 "too many options specified "
706 "in i2c probe ignore list!\n");
707 }
708
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700709 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700710
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 if (adap->class & I2C_CLASS_TV_ANALOG)
712 return i2c_probe(adap, &addr_data, tuner_attach);
713 return 0;
714}
715
716static int tuner_detach(struct i2c_client *client)
717{
718 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300719 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700720 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300722 err = i2c_detach_client(t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700723 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700724 tuner_warn
725 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700726 return err;
727 }
728
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300729 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300730 ops->release(&t->fe);
Michael Krufky16f29162007-10-21 15:22:25 -0300731
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 kfree(t);
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300733 kfree(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 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 Krufkyaf3b0f32007-10-22 18:03:29 -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)
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300782 v4l_i2c_print_ioctl(client,cmd);
Michael Krufky5e453dc2006-01-09 15:32:31 -0200783
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 Krufkyaf3b0f32007-10-22 18:03:29 -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 Krufkyaf3b0f32007-10-22 18:03:29 -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 Krufkyaf3b0f32007-10-22 18:03:29 -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 Krufkyaf3b0f32007-10-22 18:03:29 -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 Krufkyaf3b0f32007-10-22 18:03:29 -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 Krufkyaf3b0f32007-10-22 18:03:29 -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 Krufkyaf3b0f32007-10-22 18:03:29 -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 Krufkyaf3b0f32007-10-22 18:03:29 -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 */