blob: 805a2bd29e7fbb9d00c6fa072acfc9fd28c0f57c [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#define UNSET (-1U)
31
32/* standard i2c insmod options */
33static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030034#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030035 0x10,
36#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080037 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070038 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
39 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 I2C_CLIENT_END
41};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043I2C_CLIENT_INSMOD;
44
45/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070046static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070047static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070048static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070049
Linus Torvalds1da177e2005-04-16 15:20:36 -070050/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020051int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070053static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070054static unsigned int radio_range[2] = { 65, 108 };
55
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020056static char pal[] = "--";
57static char secam[] = "--";
58static char ntsc[] = "-";
59
Hans Verkuilf9195de2006-01-11 19:01:01 -020060
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020061module_param(addr, int, 0444);
62module_param(no_autodetect, int, 0444);
63module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020064module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020065module_param_string(pal, pal, sizeof(pal), 0644);
66module_param_string(secam, secam, sizeof(secam), 0644);
67module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070068module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069module_param_array(radio_range, int, NULL, 0644);
70
71MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
72MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
73MODULE_LICENSE("GPL");
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static struct i2c_driver driver;
76static struct i2c_client client_template;
77
78/* ---------------------------------------------------------------------- */
79
Michael Krufky4e9154b2007-10-21 19:39:50 -030080static void fe_set_freq(struct dvb_frontend *fe, unsigned int freq)
Michael Krufkye18f9442007-08-21 01:25:48 -030081{
Michael Krufky4e9154b2007-10-21 19:39:50 -030082 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
83 struct tuner *t = fe->analog_demod_priv;
Michael Krufkye18f9442007-08-21 01:25:48 -030084
85 struct analog_parameters params = {
86 .frequency = freq,
87 .mode = t->mode,
88 .audmode = t->audmode,
89 .std = t->std
90 };
91
92 if (NULL == fe_tuner_ops->set_analog_params) {
93 tuner_warn("Tuner frontend module has no way to set freq\n");
94 return;
95 }
Michael Krufky4e9154b2007-10-21 19:39:50 -030096 fe_tuner_ops->set_analog_params(fe, &params);
Michael Krufkye18f9442007-08-21 01:25:48 -030097}
98
Michael Krufky4e9154b2007-10-21 19:39:50 -030099static void fe_release(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300100{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300101 if (fe->ops.tuner_ops.release)
102 fe->ops.tuner_ops.release(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300103
Michael Krufky4e9154b2007-10-21 19:39:50 -0300104 fe->ops.analog_demod_ops = NULL;
105 /* DO NOT kfree(t->fe.analog_demod_priv) */
106 fe->analog_demod_priv = NULL;
Michael Krufkye18f9442007-08-21 01:25:48 -0300107}
108
Michael Krufky4e9154b2007-10-21 19:39:50 -0300109static void fe_standby(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300110{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300111 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
Michael Krufkye18f9442007-08-21 01:25:48 -0300112
113 if (fe_tuner_ops->sleep)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300114 fe_tuner_ops->sleep(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300115}
116
Michael Krufky4e9154b2007-10-21 19:39:50 -0300117static int fe_has_signal(struct dvb_frontend *fe)
Michael Krufky1f5ef192007-08-31 17:38:02 -0300118{
Michael Krufky14196832007-10-14 18:11:53 -0300119 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300120
Michael Krufky4e9154b2007-10-21 19:39:50 -0300121 if (fe->ops.tuner_ops.get_rf_strength)
122 fe->ops.tuner_ops.get_rf_strength(fe, &strength);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300123
124 return strength;
125}
126
Michael Krufky4e9154b2007-10-21 19:39:50 -0300127static void tuner_status(struct dvb_frontend *fe);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300128
129static struct analog_tuner_ops tuner_core_ops = {
130 .set_tv_freq = fe_set_freq,
131 .set_radio_freq = fe_set_freq,
132 .standby = fe_standby,
133 .release = fe_release,
134 .has_signal = fe_has_signal,
135 .tuner_status = tuner_status
136};
137
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700138/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139static void set_tv_freq(struct i2c_client *c, unsigned int freq)
140{
141 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300142 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700145 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 return;
147 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300148 if ((NULL == ops) || (NULL == ops->set_tv_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700149 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 return;
151 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700152 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
153 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
154 freq / 16, freq % 16 * 100 / 16, tv_range[0],
155 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200156 /* V4L2 spec: if the freq is not possible then the closest
157 possible value should be selected */
158 if (freq < tv_range[0] * 16)
159 freq = tv_range[0] * 16;
160 else
161 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 }
Michael Krufky4e9154b2007-10-21 19:39:50 -0300163 ops->set_tv_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164}
165
166static void set_radio_freq(struct i2c_client *c, unsigned int freq)
167{
168 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300169 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
171 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700172 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 return;
174 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300175 if ((NULL == ops) || (NULL == ops->set_radio_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700176 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 return;
178 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200179 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700180 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
181 freq / 16000, freq % 16000 * 100 / 16000,
182 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200183 /* V4L2 spec: if the freq is not possible then the closest
184 possible value should be selected */
185 if (freq < radio_range[0] * 16000)
186 freq = radio_range[0] * 16000;
187 else
188 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700190
Michael Krufky4e9154b2007-10-21 19:39:50 -0300191 ops->set_radio_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192}
193
194static void set_freq(struct i2c_client *c, unsigned long freq)
195{
196 struct tuner *t = i2c_get_clientdata(c);
197
198 switch (t->mode) {
199 case V4L2_TUNER_RADIO:
200 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700201 freq / 16000, freq % 16000 * 100 / 16000);
202 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200203 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 break;
205 case V4L2_TUNER_ANALOG_TV:
206 case V4L2_TUNER_DIGITAL_TV:
207 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700208 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200210 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300212 default:
213 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215}
216
Michael Krufky293197c2007-08-28 17:20:42 -0300217static void tuner_i2c_address_check(struct tuner *t)
218{
219 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
220 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
221 return;
222
223 tuner_warn("====================== WARNING! ======================\n");
224 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
225 tuner_warn("will soon be dropped. This message indicates that your\n");
226 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
227 t->i2c.name, t->i2c.addr);
228 tuner_warn("To ensure continued support for your device, please\n");
229 tuner_warn("send a copy of this message, along with full dmesg\n");
230 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
231 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
232 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
233 t->i2c.adapter->name, t->i2c.addr, t->type,
234 tuners[t->type].name);
235 tuner_warn("====================== WARNING! ======================\n");
236}
237
Michael Krufky4adad282007-08-27 21:59:08 -0300238static void attach_simple_tuner(struct tuner *t)
239{
240 struct simple_tuner_config cfg = {
241 .type = t->type,
242 .tun = &tuners[t->type]
243 };
244 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
245}
246
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700247static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300248 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300249 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
251 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300252 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300253 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700254 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700256 if (type == UNSET || type == TUNER_ABSENT) {
257 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 return;
259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700261 if (type >= tuner_count) {
262 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
263 return;
264 }
265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300267 t->config = new_config;
268 if (tuner_callback != NULL) {
269 tuner_dbg("defining GPIO callback\n");
270 t->tuner_callback = tuner_callback;
271 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300272
273 /* This code detects calls by card attach_inform */
274 if (NULL == t->i2c.dev.driver) {
275 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
276
277 return;
278 }
279
Michael Krufkyb2083192007-05-29 22:54:06 -0300280 /* discard private data, in case set_type() was previously called */
Michael Krufky1dde7a42007-10-21 13:40:56 -0300281 if ((ops) && (ops->release))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300282 ops->release(&t->fe);
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300283
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 switch (t->type) {
285 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300286 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 break;
288 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300289 {
Michael Krufky746d97322007-08-25 19:08:45 -0300290 tda8290_attach(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300292 }
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300293 case TUNER_PHILIPS_TDA8295:
294 {
295 tda8295_attach(t);
296 break;
297 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700298 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300299 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700300 t->type = TUNER_ABSENT;
301 t->mode_mask = T_UNINITIALIZED;
302 return;
303 }
304 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700305 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300306 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300307 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300308 t->type = TUNER_ABSENT;
309 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300310 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300311 }
312 t->mode_mask = T_RADIO;
313 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700314 case TUNER_PHILIPS_FMD1216ME_MK3:
315 buffer[0] = 0x0b;
316 buffer[1] = 0xdc;
317 buffer[2] = 0x9c;
318 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700319 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700320 mdelay(1);
321 buffer[2] = 0x86;
322 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700323 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300324 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700325 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800326 case TUNER_PHILIPS_TD1316:
327 buffer[0] = 0x0b;
328 buffer[1] = 0xdc;
329 buffer[2] = 0x86;
330 buffer[3] = 0xa4;
331 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300332 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200333 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300334 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300335 {
336 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
337 &c->dev, c->adapter->algo_data,
338 t->tuner_callback);
339 if (rc<0) {
340 t->type = TUNER_ABSENT;
341 t->mode_mask = T_UNINITIALIZED;
342 return;
343 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300344 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300345 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300346 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300347 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300348 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300350 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 break;
352 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700353
Michael Krufkye2be32a2007-10-21 14:35:21 -0300354 ops = t->fe.ops.analog_demod_ops;
355
Michael Krufky1dde7a42007-10-21 13:40:56 -0300356 if (((NULL == ops) ||
357 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
358 (fe_tuner_ops->set_analog_params)) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300359 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
360
Michael Krufky1dde7a42007-10-21 13:40:56 -0300361 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300362 t->fe.analog_demod_priv = t;
Michael Krufkye18f9442007-08-21 01:25:48 -0300363 }
364
365 tuner_info("type set to %s\n", t->i2c.name);
366
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700367 if (t->mode_mask == T_UNINITIALIZED)
368 t->mode_mask = new_mode_mask;
369
Hans Verkuil27487d42006-01-15 15:04:52 -0200370 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700371 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100372 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700373 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300374 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375}
376
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700377/*
378 * This function apply tuner config to tuner specified
379 * by tun_setup structure. I addr is unset, then admin status
380 * and tun addr status is more precise then current status,
381 * it's applied. Otherwise status and type are applied only to
382 * tuner with exactly the same addr.
383*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700384
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700385static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700386{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700387 struct tuner *t = i2c_get_clientdata(c);
388
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300389 tuner_dbg("set addr for type %i\n", t->type);
390
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300391 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
392 (t->mode_mask & tun_setup->mode_mask))) ||
393 (tun_setup->addr == c->addr)) {
394 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300395 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700396 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700397}
398
399static inline int check_mode(struct tuner *t, char *cmd)
400{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700401 if ((1 << t->mode & t->mode_mask) == 0) {
402 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700403 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700404
405 switch (t->mode) {
406 case V4L2_TUNER_RADIO:
407 tuner_dbg("Cmd %s accepted for radio\n", cmd);
408 break;
409 case V4L2_TUNER_ANALOG_TV:
410 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
411 break;
412 case V4L2_TUNER_DIGITAL_TV:
413 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
414 break;
415 }
416 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700417}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700418
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700419/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420static int tuner_fixup_std(struct tuner *t)
421{
422 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300424 case '6':
425 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
426 t->std = V4L2_STD_PAL_60;
427 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 case 'b':
429 case 'B':
430 case 'g':
431 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700432 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 t->std = V4L2_STD_PAL_BG;
434 break;
435 case 'i':
436 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700437 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 t->std = V4L2_STD_PAL_I;
439 break;
440 case 'd':
441 case 'D':
442 case 'k':
443 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700444 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 t->std = V4L2_STD_PAL_DK;
446 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700447 case 'M':
448 case 'm':
449 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
450 t->std = V4L2_STD_PAL_M;
451 break;
452 case 'N':
453 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200454 if (pal[1] == 'c' || pal[1] == 'C') {
455 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
456 t->std = V4L2_STD_PAL_Nc;
457 } else {
458 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
459 t->std = V4L2_STD_PAL_N;
460 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700461 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700462 case '-':
463 /* default parameter, do nothing */
464 break;
465 default:
466 tuner_warn ("pal= argument not recognised\n");
467 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 }
469 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700470 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
471 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200472 case 'b':
473 case 'B':
474 case 'g':
475 case 'G':
476 case 'h':
477 case 'H':
478 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
479 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
480 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700481 case 'd':
482 case 'D':
483 case 'k':
484 case 'K':
485 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
486 t->std = V4L2_STD_SECAM_DK;
487 break;
488 case 'l':
489 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800490 if ((secam[1]=='C')||(secam[1]=='c')) {
491 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
492 t->std = V4L2_STD_SECAM_LC;
493 } else {
494 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
495 t->std = V4L2_STD_SECAM_L;
496 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700497 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700498 case '-':
499 /* default parameter, do nothing */
500 break;
501 default:
502 tuner_warn ("secam= argument not recognised\n");
503 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700504 }
505 }
506
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200507 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
508 switch (ntsc[0]) {
509 case 'm':
510 case 'M':
511 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
512 t->std = V4L2_STD_NTSC_M;
513 break;
514 case 'j':
515 case 'J':
516 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
517 t->std = V4L2_STD_NTSC_M_JP;
518 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200519 case 'k':
520 case 'K':
521 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
522 t->std = V4L2_STD_NTSC_M_KR;
523 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200524 case '-':
525 /* default parameter, do nothing */
526 break;
527 default:
528 tuner_info("ntsc= argument not recognised\n");
529 break;
530 }
531 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 return 0;
533}
534
Michael Krufky4e9154b2007-10-21 19:39:50 -0300535static void tuner_status(struct dvb_frontend *fe)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200536{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300537 struct tuner *t = fe->analog_demod_priv;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200538 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300539 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300540 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200541 const char *p;
542
543 switch (t->mode) {
544 case V4L2_TUNER_RADIO: p = "radio"; break;
545 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
546 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
547 default: p = "undefined"; break;
548 }
549 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200550 freq = t->radio_freq / 16000;
551 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200552 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200553 freq = t->tv_freq / 16;
554 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200555 }
556 tuner_info("Tuner mode: %s\n", p);
557 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300558 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200559 if (t->mode != V4L2_TUNER_RADIO)
560 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300561 if (fe_tuner_ops->get_status) {
562 u32 tuner_status;
563
564 fe_tuner_ops->get_status(&t->fe, &tuner_status);
565 if (tuner_status & TUNER_STATUS_LOCKED)
566 tuner_info("Tuner is locked.\n");
567 if (tuner_status & TUNER_STATUS_STEREO)
568 tuner_info("Stereo: yes\n");
569 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300570 if ((ops) && (ops->has_signal)) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300571 tuner_info("Signal strength: %d\n", ops->has_signal(fe));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200572 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300573 if ((ops) && (ops->is_stereo)) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300574 tuner_info("Stereo: %s\n", ops->is_stereo(fe) ?
575 "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200576 }
577}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579/* ---------------------------------------------------------------------- */
580
Hans Verkuilba8fc392006-06-25 15:34:39 -0300581/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700582static unsigned default_mode_mask;
583
584/* During client attach, set_type is called by adapter's attach_inform callback.
585 set_type must then be completed by tuner_attach.
586 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
588{
589 struct tuner *t;
590
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700591 client_template.adapter = adap;
592 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Panagiotis Issaris74081872006-01-11 19:40:56 -0200594 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700595 if (NULL == t)
596 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700597 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700599 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700600 t->audmode = V4L2_TUNER_MODE_STEREO;
601 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700603 if (show_i2c) {
604 unsigned char buffer[16];
605 int i,rc;
606
607 memset(buffer, 0, sizeof(buffer));
608 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800609 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700610 for (i=0;i<rc;i++)
611 printk("%02x ",buffer[i]);
612 printk("\n");
613 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300614 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
615 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
616 return -ENODEV;
617
Markus Rechberger257c6452006-01-23 17:11:11 -0200618 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700619 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800620 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300621 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300622 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300623 t->type = TUNER_TEA5761;
624 t->mode_mask = T_RADIO;
625 t->mode = T_STANDBY;
626 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
627 default_mode_mask &= ~T_RADIO;
628
629 goto register_client;
630 }
631 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800632 case 0x42:
633 case 0x43:
634 case 0x4a:
635 case 0x4b:
636 /* If chip is not tda8290, don't register.
637 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300638 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300639 tuner_dbg("chip at addr %x is a tda8290\n", addr);
640 } else {
641 /* Default is being tda9887 */
642 t->type = TUNER_TDA9887;
643 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
644 t->mode = T_STANDBY;
645 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800646 }
647 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800648 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300649 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700650 t->type = TUNER_TEA5767;
651 t->mode_mask = T_RADIO;
652 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200653 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700654 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700655
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800656 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700657 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800658 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700659 }
660 }
661
662 /* Initializes only the first adapter found */
663 if (default_mode_mask != T_UNINITIALIZED) {
664 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
665 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200666 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
667 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700668 default_mode_mask = T_UNINITIALIZED;
669 }
670
671 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800672register_client:
673 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700674 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300675 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 return 0;
677}
678
679static int tuner_probe(struct i2c_adapter *adap)
680{
681 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700682 normal_i2c[0] = addr;
683 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
Michael Krufkya1dec512007-08-24 01:13:07 -0300686 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
687 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
688 * and an RTC at 0x6f which can get corrupted if probed.
689 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300690 if ((adap->id == I2C_HW_B_CX2388x) ||
691 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300692 unsigned int i = 0;
693
694 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
695 i += 2;
696 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
697 ignore[i+0] = adap->nr;
698 ignore[i+1] = 0x6b;
699 ignore[i+2] = adap->nr;
700 ignore[i+3] = 0x6f;
701 ignore[i+4] = I2C_CLIENT_END;
702 } else
703 printk(KERN_WARNING "tuner: "
704 "too many options specified "
705 "in i2c probe ignore list!\n");
706 }
707
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700708 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700709
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 if (adap->class & I2C_CLASS_TV_ANALOG)
711 return i2c_probe(adap, &addr_data, tuner_attach);
712 return 0;
713}
714
715static int tuner_detach(struct i2c_client *client)
716{
717 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300718 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700719 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700721 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700722 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700723 tuner_warn
724 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700725 return err;
726 }
727
Michael Krufky1dde7a42007-10-21 13:40:56 -0300728 if ((ops) && (ops->release))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300729 ops->release(&t->fe);
Michael Krufky16f29162007-10-21 15:22:25 -0300730
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 kfree(t);
732 return 0;
733}
734
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700735/*
736 * Switch tuner to other mode. If tuner support both tv and radio,
737 * set another frequency to some value (This is needed for some pal
738 * tuners to avoid locking). Otherwise, just put second tuner in
739 * standby mode.
740 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700742static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
743{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300744 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
745
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800746 if (mode == t->mode)
747 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700748
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800749 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700750
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800751 if (check_mode(t, cmd) == EINVAL) {
752 t->mode = T_STANDBY;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300753 if ((ops) && (ops->standby))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300754 ops->standby(&t->fe);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800755 return EINVAL;
756 }
757 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758}
759
760#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800761 tuner_dbg("switching to v4l2\n"); \
762 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700763
764static inline int check_v4l2(struct tuner *t)
765{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300766 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
767 TV, v4l1 for radio), until that is fixed this code is disabled.
768 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
769 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700770 return 0;
771}
772
773static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
775 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300776 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300777 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778
Hans Verkuilf9195de2006-01-11 19:01:01 -0200779 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200780 v4l_i2c_print_ioctl(&(t->i2c),cmd);
781
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700782 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700784 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300785 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 -0700786 ((struct tuner_setup *)arg)->type,
787 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300788 ((struct tuner_setup *)arg)->mode_mask,
789 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700790
791 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700792 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200794 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
795 == EINVAL)
796 return 0;
797 if (t->radio_freq)
798 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700800 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200801 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
802 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300803 t->mode = T_STANDBY;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300804 if ((ops) && (ops->standby))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300805 ops->standby(&t->fe);
Hans Verkuil27487d42006-01-15 15:04:52 -0200806 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300807#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700808 case VIDIOCSAUDIO:
809 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
810 return 0;
811 if (check_v4l2(t) == EINVAL)
812 return 0;
813
814 /* Should be implemented, since bttv calls it */
815 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700816 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700818 {
819 static const v4l2_std_id map[] = {
820 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
821 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
822 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
823 [4 /* bttv */ ] = V4L2_STD_PAL_M,
824 [5 /* bttv */ ] = V4L2_STD_PAL_N,
825 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
826 };
827 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700829 if (check_v4l2(t) == EINVAL)
830 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700831
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700832 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
833 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700835 if (vc->norm < ARRAY_SIZE(map))
836 t->std = map[vc->norm];
837 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200838 if (t->tv_freq)
839 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700840 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700841 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700842 case VIDIOCSFREQ:
843 {
844 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700845
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700846 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
847 return 0;
848 if (check_v4l2(t) == EINVAL)
849 return 0;
850
851 set_freq(client, *v);
852 return 0;
853 }
854 case VIDIOCGTUNER:
855 {
856 struct video_tuner *vt = arg;
857
858 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
859 return 0;
860 if (check_v4l2(t) == EINVAL)
861 return 0;
862
863 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300864 if (fe_tuner_ops->get_status) {
865 u32 tuner_status;
866
867 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300868 if (tuner_status & TUNER_STATUS_STEREO)
869 vt->flags |= VIDEO_TUNER_STEREO_ON;
870 else
871 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300872 } else {
Michael Krufky1dde7a42007-10-21 13:40:56 -0300873 if ((ops) && (ops->is_stereo)) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300874 if (ops->is_stereo(&t->fe))
Michael Krufkye18f9442007-08-21 01:25:48 -0300875 vt->flags |=
876 VIDEO_TUNER_STEREO_ON;
877 else
878 vt->flags &=
879 ~VIDEO_TUNER_STEREO_ON;
880 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700881 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300882 if ((ops) && (ops->has_signal))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300883 vt->signal = ops->has_signal(&t->fe);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300884
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700885 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
886
887 vt->rangelow = radio_range[0] * 16000;
888 vt->rangehigh = radio_range[1] * 16000;
889
890 } else {
891 vt->rangelow = tv_range[0] * 16;
892 vt->rangehigh = tv_range[1] * 16;
893 }
894
895 return 0;
896 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700898 {
899 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700901 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
902 return 0;
903 if (check_v4l2(t) == EINVAL)
904 return 0;
905
Michael Krufkye18f9442007-08-21 01:25:48 -0300906 if (V4L2_TUNER_RADIO == t->mode) {
907 if (fe_tuner_ops->get_status) {
908 u32 tuner_status;
909
910 fe_tuner_ops->get_status(&t->fe, &tuner_status);
911 va->mode = (tuner_status & TUNER_STATUS_STEREO)
912 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300913 } else if ((ops) && (ops->is_stereo))
Michael Krufky4e9154b2007-10-21 19:39:50 -0300914 va->mode = ops->is_stereo(&t->fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300915 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
916 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700917 return 0;
918 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300919#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300920 case TUNER_SET_CONFIG:
921 {
922 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
923 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300925 if (t->type != cfg->tuner)
926 break;
927
928 if (t->type == TUNER_TDA9887) {
929 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300930 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300931 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300932 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300933
934 if (NULL == fe_tuner_ops->set_config) {
935 tuner_warn("Tuner frontend module has no way to "
936 "set config\n");
937 break;
938 }
939 fe_tuner_ops->set_config(&t->fe, cfg->priv);
940
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300941 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300942 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300943 /* --- v4l ioctls --- */
944 /* take care: bttv does userspace copying, we'll get a
945 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700947 {
948 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700950 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
951 == EINVAL)
952 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700953
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700954 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700956 t->std = *id;
957 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200958 if (t->tv_freq)
959 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700960 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700961 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700962 case VIDIOC_S_FREQUENCY:
963 {
964 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700965
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300966 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
967 == EINVAL)
968 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700969 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200970 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700971
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700972 break;
973 }
974 case VIDIOC_G_FREQUENCY:
975 {
976 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700977
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700978 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
979 return 0;
980 switch_v4l2();
981 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300982 if (fe_tuner_ops->get_frequency) {
983 u32 abs_freq;
984
985 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
986 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
987 (abs_freq * 2 + 125/2) / 125 :
988 (abs_freq + 62500/2) / 62500;
989 break;
990 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200991 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
992 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700993 break;
994 }
995 case VIDIOC_G_TUNER:
996 {
997 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700998
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700999 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
1000 return 0;
1001 switch_v4l2();
1002
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001003 tuner->type = t->mode;
Michael Krufky1dde7a42007-10-21 13:40:56 -03001004 if ((ops) && (ops->get_afc))
Michael Krufky4e9154b2007-10-21 19:39:50 -03001005 tuner->afc = ops->get_afc(&t->fe);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001006 if (t->mode == V4L2_TUNER_ANALOG_TV)
1007 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001008 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001009 tuner->rangelow = tv_range[0] * 16;
1010 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001011 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001012 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001013
1014 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001015 tuner->rxsubchans =
1016 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001017 if (fe_tuner_ops->get_status) {
1018 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001019
Michael Krufkye18f9442007-08-21 01:25:48 -03001020 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky4e9154b2007-10-21 19:39:50 -03001021 tuner->rxsubchans =
1022 (tuner_status & TUNER_STATUS_STEREO) ?
1023 V4L2_TUNER_SUB_STEREO :
1024 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001025 } else {
Michael Krufky1dde7a42007-10-21 13:40:56 -03001026 if ((ops) && (ops->is_stereo)) {
Michael Krufky4e9154b2007-10-21 19:39:50 -03001027 tuner->rxsubchans =
1028 ops->is_stereo(&t->fe) ?
1029 V4L2_TUNER_SUB_STEREO :
1030 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001031 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001032 }
Michael Krufky1dde7a42007-10-21 13:40:56 -03001033 if ((ops) && (ops->has_signal))
Michael Krufky4e9154b2007-10-21 19:39:50 -03001034 tuner->signal = ops->has_signal(&t->fe);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001035 tuner->capability |=
1036 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1037 tuner->audmode = t->audmode;
1038 tuner->rangelow = radio_range[0] * 16000;
1039 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001040 break;
1041 }
1042 case VIDIOC_S_TUNER:
1043 {
1044 struct v4l2_tuner *tuner = arg;
1045
1046 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1047 return 0;
1048
1049 switch_v4l2();
1050
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001051 /* do nothing unless we're a radio tuner */
1052 if (t->mode != V4L2_TUNER_RADIO)
1053 break;
1054 t->audmode = tuner->audmode;
1055 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001056 break;
1057 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001058 case VIDIOC_LOG_STATUS:
Michael Krufky1dde7a42007-10-21 13:40:56 -03001059 if ((ops) && (ops->tuner_status))
Michael Krufky4e9154b2007-10-21 19:39:50 -03001060 ops->tuner_status(&t->fe);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001061 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 }
1063
1064 return 0;
1065}
1066
Jean Delvare21b48a72007-03-12 19:20:15 -03001067static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001069 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001071 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 /* FIXME: power down ??? */
1073 return 0;
1074}
1075
Jean Delvare21b48a72007-03-12 19:20:15 -03001076static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001078 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001080 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001081 if (V4L2_TUNER_RADIO == t->mode) {
1082 if (t->radio_freq)
1083 set_freq(c, t->radio_freq);
1084 } else {
1085 if (t->tv_freq)
1086 set_freq(c, t->tv_freq);
1087 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 return 0;
1089}
1090
1091/* ----------------------------------------------------------------------- */
1092
1093static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001094 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001095 .attach_adapter = tuner_probe,
1096 .detach_client = tuner_detach,
1097 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001098 .suspend = tuner_suspend,
1099 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001101 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001102 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001104static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001105 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001106 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107};
1108
1109static int __init tuner_init_module(void)
1110{
1111 return i2c_add_driver(&driver);
1112}
1113
1114static void __exit tuner_cleanup_module(void)
1115{
1116 i2c_del_driver(&driver);
1117}
1118
1119module_init(tuner_init_module);
1120module_exit(tuner_cleanup_module);
1121
1122/*
1123 * Overrides for Emacs so that we follow Linus's tabbing style.
1124 * ---------------------------------------------------------------------------
1125 * Local variables:
1126 * c-basic-offset: 8
1127 * End:
1128 */