blob: b92efe0460f34d322b236206aa8386b9a03b385d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * i2c tv tuner chip device driver
4 * core core, i.e. kernel interfaces, registering and so on
5 */
6
7#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/string.h>
10#include <linux/timer.h>
11#include <linux/delay.h>
12#include <linux/errno.h>
13#include <linux/slab.h>
14#include <linux/poll.h>
15#include <linux/i2c.h>
16#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/init.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030018#include <linux/videodev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <media/tuner.h>
Michael Krufky4adad282007-08-27 21:59:08 -030020#include <media/tuner-types.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020021#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030022#include "tuner-driver.h"
Michael Krufky96c0b7c2007-08-27 21:23:08 -030023#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030024#include "tda8290.h"
Michael Krufky7ab10bf2007-08-27 21:23:40 -030025#include "tea5761.h"
Michael Krufky8d0936e2007-08-27 21:24:27 -030026#include "tea5767.h"
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030027#include "tuner-xc2028.h"
Michael Krufky4adad282007-08-27 21:59:08 -030028#include "tuner-simple.h"
Michael Krufky31c95842007-10-21 20:48:48 -030029#include "tda9887.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31#define UNSET (-1U)
32
33/* standard i2c insmod options */
34static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030035#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030036 0x10,
37#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080038 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070039 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
40 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 I2C_CLIENT_END
42};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070043
Linus Torvalds1da177e2005-04-16 15:20:36 -070044I2C_CLIENT_INSMOD;
45
46/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070047static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070048static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070049static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070050
Linus Torvalds1da177e2005-04-16 15:20:36 -070051/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020052int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070054static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070055static unsigned int radio_range[2] = { 65, 108 };
56
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020057static char pal[] = "--";
58static char secam[] = "--";
59static char ntsc[] = "-";
60
Hans Verkuilf9195de2006-01-11 19:01:01 -020061
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020062module_param(addr, int, 0444);
63module_param(no_autodetect, int, 0444);
64module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020065module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020066module_param_string(pal, pal, sizeof(pal), 0644);
67module_param_string(secam, secam, sizeof(secam), 0644);
68module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070069module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070module_param_array(radio_range, int, NULL, 0644);
71
72MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
73MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
74MODULE_LICENSE("GPL");
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076static struct i2c_driver driver;
77static struct i2c_client client_template;
78
79/* ---------------------------------------------------------------------- */
80
Michael Krufky4e9154b2007-10-21 19:39:50 -030081static void fe_set_freq(struct dvb_frontend *fe, unsigned int freq)
Michael Krufkye18f9442007-08-21 01:25:48 -030082{
Michael Krufky4e9154b2007-10-21 19:39:50 -030083 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
84 struct tuner *t = fe->analog_demod_priv;
Michael Krufkye18f9442007-08-21 01:25:48 -030085
86 struct analog_parameters params = {
87 .frequency = freq,
88 .mode = t->mode,
89 .audmode = t->audmode,
90 .std = t->std
91 };
92
93 if (NULL == fe_tuner_ops->set_analog_params) {
94 tuner_warn("Tuner frontend module has no way to set freq\n");
95 return;
96 }
Michael Krufky4e9154b2007-10-21 19:39:50 -030097 fe_tuner_ops->set_analog_params(fe, &params);
Michael Krufkye18f9442007-08-21 01:25:48 -030098}
99
Michael Krufky4e9154b2007-10-21 19:39:50 -0300100static void fe_release(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300101{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300102 if (fe->ops.tuner_ops.release)
103 fe->ops.tuner_ops.release(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300104
Michael Krufky4e9154b2007-10-21 19:39:50 -0300105 fe->ops.analog_demod_ops = NULL;
Michael Krufky4524c1a2007-10-22 18:15:39 -0300106
107 /* DO NOT kfree(fe->analog_demod_priv)
108 *
109 * If we are in this function, analog_demod_priv contains a pointer
110 * to struct tuner *t. This will be kfree'd in tuner_detach().
111 *
112 * Otherwise, fe->ops.analog_demod_ops->release will
113 * handle the cleanup for analog demodulator modules.
114 */
Michael Krufky4e9154b2007-10-21 19:39:50 -0300115 fe->analog_demod_priv = NULL;
Michael Krufkye18f9442007-08-21 01:25:48 -0300116}
117
Michael Krufky4e9154b2007-10-21 19:39:50 -0300118static void fe_standby(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300119{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300120 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
Michael Krufkye18f9442007-08-21 01:25:48 -0300121
122 if (fe_tuner_ops->sleep)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300123 fe_tuner_ops->sleep(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300124}
125
Michael Krufky4e9154b2007-10-21 19:39:50 -0300126static int fe_has_signal(struct dvb_frontend *fe)
Michael Krufky1f5ef192007-08-31 17:38:02 -0300127{
Michael Krufky14196832007-10-14 18:11:53 -0300128 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300129
Michael Krufky4e9154b2007-10-21 19:39:50 -0300130 if (fe->ops.tuner_ops.get_rf_strength)
131 fe->ops.tuner_ops.get_rf_strength(fe, &strength);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300132
133 return strength;
134}
135
Michael Krufky4e9154b2007-10-21 19:39:50 -0300136static void tuner_status(struct dvb_frontend *fe);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300137
138static struct analog_tuner_ops tuner_core_ops = {
139 .set_tv_freq = fe_set_freq,
140 .set_radio_freq = fe_set_freq,
141 .standby = fe_standby,
142 .release = fe_release,
143 .has_signal = fe_has_signal,
144 .tuner_status = tuner_status
145};
146
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700147/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148static void set_tv_freq(struct i2c_client *c, unsigned int freq)
149{
150 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300151 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700154 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 return;
156 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300157 if ((NULL == ops) || (NULL == ops->set_tv_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700158 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 return;
160 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700161 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
162 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
163 freq / 16, freq % 16 * 100 / 16, tv_range[0],
164 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200165 /* V4L2 spec: if the freq is not possible then the closest
166 possible value should be selected */
167 if (freq < tv_range[0] * 16)
168 freq = tv_range[0] * 16;
169 else
170 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 }
Michael Krufky4e9154b2007-10-21 19:39:50 -0300172 ops->set_tv_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
175static void set_radio_freq(struct i2c_client *c, unsigned int freq)
176{
177 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300178 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
180 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700181 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 return;
183 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300184 if ((NULL == ops) || (NULL == ops->set_radio_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700185 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 return;
187 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200188 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700189 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
190 freq / 16000, freq % 16000 * 100 / 16000,
191 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200192 /* V4L2 spec: if the freq is not possible then the closest
193 possible value should be selected */
194 if (freq < radio_range[0] * 16000)
195 freq = radio_range[0] * 16000;
196 else
197 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700199
Michael Krufky4e9154b2007-10-21 19:39:50 -0300200 ops->set_radio_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202
203static void set_freq(struct i2c_client *c, unsigned long freq)
204{
205 struct tuner *t = i2c_get_clientdata(c);
206
207 switch (t->mode) {
208 case V4L2_TUNER_RADIO:
209 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700210 freq / 16000, freq % 16000 * 100 / 16000);
211 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200212 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 break;
214 case V4L2_TUNER_ANALOG_TV:
215 case V4L2_TUNER_DIGITAL_TV:
216 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700217 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200219 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300221 default:
222 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224}
225
Michael Krufky293197c2007-08-28 17:20:42 -0300226static void tuner_i2c_address_check(struct tuner *t)
227{
228 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
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
Mauro Carvalho Chehab48aa3362007-10-29 11:33:18 -0300282 if (t->mode == T_UNINITIALIZED) {
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300283 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
284
285 return;
286 }
287
Michael Krufkyb2083192007-05-29 22:54:06 -0300288 /* discard private data, in case set_type() was previously called */
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300289 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300290 ops->release(&t->fe);
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300291
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 switch (t->type) {
293 case TUNER_MT2032:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300294 microtune_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 break;
296 case TUNER_PHILIPS_TDA8290:
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300297 {
Michael Krufky8c125f2c2007-10-27 02:00:57 -0300298 tda829x_attach(t);
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300299 break;
300 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700301 case TUNER_TEA5767:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300302 if (tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700303 t->type = TUNER_ABSENT;
304 t->mode_mask = T_UNINITIALIZED;
305 return;
306 }
307 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700308 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300309 case TUNER_TEA5761:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300310 if (tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300311 t->type = TUNER_ABSENT;
312 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300313 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300314 }
315 t->mode_mask = T_RADIO;
316 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700317 case TUNER_PHILIPS_FMD1216ME_MK3:
318 buffer[0] = 0x0b;
319 buffer[1] = 0xdc;
320 buffer[2] = 0x9c;
321 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700322 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700323 mdelay(1);
324 buffer[2] = 0x86;
325 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700326 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300327 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700328 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800329 case TUNER_PHILIPS_TD1316:
330 buffer[0] = 0x0b;
331 buffer[1] = 0xdc;
332 buffer[2] = 0x86;
333 buffer[3] = 0xa4;
334 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300335 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200336 break;
Mauro Carvalho Chehab690c5442007-10-29 11:33:18 -0300337 case TUNER_XC2028:
338 {
339 int rc=xc2028_attach(&t->fe, t->i2c->adapter, t->i2c->addr,
340 &c->dev, c->adapter->algo_data,
341 t->tuner_callback);
342 if (rc<0) {
343 t->type = TUNER_ABSENT;
344 t->mode_mask = T_UNINITIALIZED;
345 return;
346 }
347 break;
348 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300349 case TUNER_TDA9887:
Michael Krufky31c95842007-10-21 20:48:48 -0300350 tda9887_attach(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300351 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300353 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 break;
355 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700356
Michael Krufkye2be32a2007-10-21 14:35:21 -0300357 ops = t->fe.ops.analog_demod_ops;
358
Michael Krufky1dde7a42007-10-21 13:40:56 -0300359 if (((NULL == ops) ||
360 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
361 (fe_tuner_ops->set_analog_params)) {
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300362 strlcpy(t->i2c->name, fe_tuner_ops->info.name,
363 sizeof(t->i2c->name));
Michael Krufkye18f9442007-08-21 01:25:48 -0300364
Michael Krufky1dde7a42007-10-21 13:40:56 -0300365 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300366 t->fe.analog_demod_priv = t;
Michael Krufkye18f9442007-08-21 01:25:48 -0300367 }
368
Michael Krufky49427442007-10-30 09:44:12 -0300369 tuner_dbg("type set to %s\n", t->i2c->name);
Michael Krufkye18f9442007-08-21 01:25:48 -0300370
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700371 if (t->mode_mask == T_UNINITIALIZED)
372 t->mode_mask = new_mode_mask;
373
Hans Verkuil27487d42006-01-15 15:04:52 -0200374 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700375 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100376 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700377 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300378 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
380
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700381/*
382 * This function apply tuner config to tuner specified
383 * by tun_setup structure. I addr is unset, then admin status
384 * and tun addr status is more precise then current status,
385 * it's applied. Otherwise status and type are applied only to
386 * tuner with exactly the same addr.
387*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700388
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700389static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700390{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700391 struct tuner *t = i2c_get_clientdata(c);
392
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300393 tuner_dbg("set addr for type %i\n", t->type);
394
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300395 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
396 (t->mode_mask & tun_setup->mode_mask))) ||
397 (tun_setup->addr == c->addr)) {
398 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300399 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700400 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700401}
402
403static inline int check_mode(struct tuner *t, char *cmd)
404{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700405 if ((1 << t->mode & t->mode_mask) == 0) {
406 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700407 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700408
409 switch (t->mode) {
410 case V4L2_TUNER_RADIO:
411 tuner_dbg("Cmd %s accepted for radio\n", cmd);
412 break;
413 case V4L2_TUNER_ANALOG_TV:
414 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
415 break;
416 case V4L2_TUNER_DIGITAL_TV:
417 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
418 break;
419 }
420 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700421}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700422
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700423/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424static int tuner_fixup_std(struct tuner *t)
425{
426 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300428 case '6':
429 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
430 t->std = V4L2_STD_PAL_60;
431 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 case 'b':
433 case 'B':
434 case 'g':
435 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700436 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 t->std = V4L2_STD_PAL_BG;
438 break;
439 case 'i':
440 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700441 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 t->std = V4L2_STD_PAL_I;
443 break;
444 case 'd':
445 case 'D':
446 case 'k':
447 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700448 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 t->std = V4L2_STD_PAL_DK;
450 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700451 case 'M':
452 case 'm':
453 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
454 t->std = V4L2_STD_PAL_M;
455 break;
456 case 'N':
457 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200458 if (pal[1] == 'c' || pal[1] == 'C') {
459 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
460 t->std = V4L2_STD_PAL_Nc;
461 } else {
462 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
463 t->std = V4L2_STD_PAL_N;
464 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700465 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700466 case '-':
467 /* default parameter, do nothing */
468 break;
469 default:
470 tuner_warn ("pal= argument not recognised\n");
471 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 }
473 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700474 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
475 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200476 case 'b':
477 case 'B':
478 case 'g':
479 case 'G':
480 case 'h':
481 case 'H':
482 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
483 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
484 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700485 case 'd':
486 case 'D':
487 case 'k':
488 case 'K':
489 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
490 t->std = V4L2_STD_SECAM_DK;
491 break;
492 case 'l':
493 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800494 if ((secam[1]=='C')||(secam[1]=='c')) {
495 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
496 t->std = V4L2_STD_SECAM_LC;
497 } else {
498 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
499 t->std = V4L2_STD_SECAM_L;
500 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700501 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700502 case '-':
503 /* default parameter, do nothing */
504 break;
505 default:
506 tuner_warn ("secam= argument not recognised\n");
507 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700508 }
509 }
510
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200511 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
512 switch (ntsc[0]) {
513 case 'm':
514 case 'M':
515 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
516 t->std = V4L2_STD_NTSC_M;
517 break;
518 case 'j':
519 case 'J':
520 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
521 t->std = V4L2_STD_NTSC_M_JP;
522 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200523 case 'k':
524 case 'K':
525 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
526 t->std = V4L2_STD_NTSC_M_KR;
527 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200528 case '-':
529 /* default parameter, do nothing */
530 break;
531 default:
532 tuner_info("ntsc= argument not recognised\n");
533 break;
534 }
535 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 return 0;
537}
538
Michael Krufky4e9154b2007-10-21 19:39:50 -0300539static void tuner_status(struct dvb_frontend *fe)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200540{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300541 struct tuner *t = fe->analog_demod_priv;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200542 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300543 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300544 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200545 const char *p;
546
547 switch (t->mode) {
548 case V4L2_TUNER_RADIO: p = "radio"; break;
549 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
550 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
551 default: p = "undefined"; break;
552 }
553 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200554 freq = t->radio_freq / 16000;
555 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200556 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200557 freq = t->tv_freq / 16;
558 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200559 }
560 tuner_info("Tuner mode: %s\n", p);
561 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300562 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200563 if (t->mode != V4L2_TUNER_RADIO)
564 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300565 if (fe_tuner_ops->get_status) {
566 u32 tuner_status;
567
568 fe_tuner_ops->get_status(&t->fe, &tuner_status);
569 if (tuner_status & TUNER_STATUS_LOCKED)
570 tuner_info("Tuner is locked.\n");
571 if (tuner_status & TUNER_STATUS_STEREO)
572 tuner_info("Stereo: yes\n");
573 }
Michael Krufky1b29ced2007-10-22 01:44:03 -0300574 if (ops) {
575 if (ops->has_signal)
576 tuner_info("Signal strength: %d\n",
577 ops->has_signal(fe));
578 if (ops->is_stereo)
579 tuner_info("Stereo: %s\n",
580 ops->is_stereo(fe) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200581 }
582}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200583
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584/* ---------------------------------------------------------------------- */
585
Hans Verkuilba8fc392006-06-25 15:34:39 -0300586/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700587static unsigned default_mode_mask;
588
589/* During client attach, set_type is called by adapter's attach_inform callback.
590 set_type must then be completed by tuner_attach.
591 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
593{
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300594 struct i2c_client *client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 struct tuner *t;
596
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300597 client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
598 if (NULL == client)
599 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
Panagiotis Issaris74081872006-01-11 19:40:56 -0200601 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300602 if (NULL == t) {
603 kfree(client);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700604 return -ENOMEM;
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300605 }
606 t->i2c = client;
607 client_template.adapter = adap;
608 client_template.addr = addr;
609 memcpy(client, &client_template, sizeof(struct i2c_client));
610 i2c_set_clientdata(client, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700611 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700612 t->audmode = V4L2_TUNER_MODE_STEREO;
613 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700615 if (show_i2c) {
616 unsigned char buffer[16];
617 int i,rc;
618
619 memset(buffer, 0, sizeof(buffer));
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300620 rc = i2c_master_recv(client, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800621 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700622 for (i=0;i<rc;i++)
623 printk("%02x ",buffer[i]);
624 printk("\n");
625 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300626 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
627 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
628 return -ENODEV;
629
Markus Rechberger257c6452006-01-23 17:11:11 -0200630 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700631 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800632 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300633 case 0x10:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300634 if (tea5761_autodetection(t->i2c->adapter, t->i2c->addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300635 t->type = TUNER_TEA5761;
636 t->mode_mask = T_RADIO;
637 t->mode = T_STANDBY;
638 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
639 default_mode_mask &= ~T_RADIO;
640
641 goto register_client;
642 }
643 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800644 case 0x42:
645 case 0x43:
646 case 0x4a:
647 case 0x4b:
648 /* If chip is not tda8290, don't register.
649 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300650 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300651 tuner_dbg("chip at addr %x is a tda8290\n", addr);
652 } else {
653 /* Default is being tda9887 */
654 t->type = TUNER_TDA9887;
655 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
656 t->mode = T_STANDBY;
657 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800658 }
659 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800660 case 0x60:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300661 if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700662 t->type = TUNER_TEA5767;
663 t->mode_mask = T_RADIO;
664 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200665 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700666 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700667
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800668 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700669 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800670 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700671 }
672 }
673
674 /* Initializes only the first adapter found */
675 if (default_mode_mask != T_UNINITIALIZED) {
676 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
677 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200678 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
679 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680 default_mode_mask = T_UNINITIALIZED;
681 }
682
683 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800684register_client:
685 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehab48aa3362007-10-29 11:33:18 -0300686
687 /* Sets a default mode */
688 if (t->mode_mask & T_ANALOG_TV) {
689 t->mode = T_ANALOG_TV;
690 } else if (t->mode_mask & T_RADIO) {
691 t->mode = T_RADIO;
692 } else {
693 t->mode = T_DIGITAL_TV;
694 }
695
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300696 i2c_attach_client (client);
697 set_type (client,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 return 0;
699}
700
701static int tuner_probe(struct i2c_adapter *adap)
702{
703 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700704 normal_i2c[0] = addr;
705 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707
Michael Krufkya1dec512007-08-24 01:13:07 -0300708 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
709 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
710 * and an RTC at 0x6f which can get corrupted if probed.
711 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300712 if ((adap->id == I2C_HW_B_CX2388x) ||
713 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300714 unsigned int i = 0;
715
716 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
717 i += 2;
718 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
719 ignore[i+0] = adap->nr;
720 ignore[i+1] = 0x6b;
721 ignore[i+2] = adap->nr;
722 ignore[i+3] = 0x6f;
723 ignore[i+4] = I2C_CLIENT_END;
724 } else
725 printk(KERN_WARNING "tuner: "
726 "too many options specified "
727 "in i2c probe ignore list!\n");
728 }
729
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700730 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700731
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 if (adap->class & I2C_CLASS_TV_ANALOG)
733 return i2c_probe(adap, &addr_data, tuner_attach);
734 return 0;
735}
736
737static int tuner_detach(struct i2c_client *client)
738{
739 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300740 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700741 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300743 err = i2c_detach_client(t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700744 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700745 tuner_warn
746 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700747 return err;
748 }
749
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300750 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300751 ops->release(&t->fe);
Michael Krufky16f29162007-10-21 15:22:25 -0300752
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 kfree(t);
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300754 kfree(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 return 0;
756}
757
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758/*
759 * Switch tuner to other mode. If tuner support both tv and radio,
760 * set another frequency to some value (This is needed for some pal
761 * tuners to avoid locking). Otherwise, just put second tuner in
762 * standby mode.
763 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700765static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
766{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300767 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
768
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800769 if (mode == t->mode)
770 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700771
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800772 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700773
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800774 if (check_mode(t, cmd) == EINVAL) {
775 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300776 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300777 ops->standby(&t->fe);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800778 return EINVAL;
779 }
780 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700781}
782
783#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800784 tuner_dbg("switching to v4l2\n"); \
785 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700786
787static inline int check_v4l2(struct tuner *t)
788{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300789 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
790 TV, v4l1 for radio), until that is fixed this code is disabled.
791 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
792 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700793 return 0;
794}
795
796static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797{
798 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300799 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300800 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Hans Verkuilf9195de2006-01-11 19:01:01 -0200802 if (tuner_debug>1)
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300803 v4l_i2c_print_ioctl(client,cmd);
Michael Krufky5e453dc2006-01-09 15:32:31 -0200804
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700805 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700807 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300808 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 -0700809 ((struct tuner_setup *)arg)->type,
810 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300811 ((struct tuner_setup *)arg)->mode_mask,
812 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700813
814 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700815 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200817 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
818 == EINVAL)
819 return 0;
820 if (t->radio_freq)
821 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700823 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200824 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
825 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300826 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300827 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300828 ops->standby(&t->fe);
Hans Verkuil27487d42006-01-15 15:04:52 -0200829 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300830#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700831 case VIDIOCSAUDIO:
832 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
833 return 0;
834 if (check_v4l2(t) == EINVAL)
835 return 0;
836
837 /* Should be implemented, since bttv calls it */
838 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700839 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700841 {
842 static const v4l2_std_id map[] = {
843 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
844 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
845 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
846 [4 /* bttv */ ] = V4L2_STD_PAL_M,
847 [5 /* bttv */ ] = V4L2_STD_PAL_N,
848 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
849 };
850 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700852 if (check_v4l2(t) == EINVAL)
853 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700854
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700855 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
856 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700858 if (vc->norm < ARRAY_SIZE(map))
859 t->std = map[vc->norm];
860 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200861 if (t->tv_freq)
862 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700863 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700864 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700865 case VIDIOCSFREQ:
866 {
867 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700868
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700869 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
870 return 0;
871 if (check_v4l2(t) == EINVAL)
872 return 0;
873
874 set_freq(client, *v);
875 return 0;
876 }
877 case VIDIOCGTUNER:
878 {
879 struct video_tuner *vt = arg;
880
881 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
882 return 0;
883 if (check_v4l2(t) == EINVAL)
884 return 0;
885
886 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300887 if (fe_tuner_ops->get_status) {
888 u32 tuner_status;
889
890 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300891 if (tuner_status & TUNER_STATUS_STEREO)
892 vt->flags |= VIDEO_TUNER_STEREO_ON;
893 else
894 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300895 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300896 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300897 if (ops->is_stereo(&t->fe))
Michael Krufkye18f9442007-08-21 01:25:48 -0300898 vt->flags |=
899 VIDEO_TUNER_STEREO_ON;
900 else
901 vt->flags &=
902 ~VIDEO_TUNER_STEREO_ON;
903 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700904 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300905 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300906 vt->signal = ops->has_signal(&t->fe);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300907
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700908 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
909
910 vt->rangelow = radio_range[0] * 16000;
911 vt->rangehigh = radio_range[1] * 16000;
912
913 } else {
914 vt->rangelow = tv_range[0] * 16;
915 vt->rangehigh = tv_range[1] * 16;
916 }
917
918 return 0;
919 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700921 {
922 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700924 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
925 return 0;
926 if (check_v4l2(t) == EINVAL)
927 return 0;
928
Michael Krufkye18f9442007-08-21 01:25:48 -0300929 if (V4L2_TUNER_RADIO == t->mode) {
930 if (fe_tuner_ops->get_status) {
931 u32 tuner_status;
932
933 fe_tuner_ops->get_status(&t->fe, &tuner_status);
934 va->mode = (tuner_status & TUNER_STATUS_STEREO)
935 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300936 } else if (ops && ops->is_stereo)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300937 va->mode = ops->is_stereo(&t->fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300938 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
939 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700940 return 0;
941 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300942#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300943 case TUNER_SET_CONFIG:
944 {
945 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
946 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300948 if (t->type != cfg->tuner)
949 break;
950
951 if (t->type == TUNER_TDA9887) {
952 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300953 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300954 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300955 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300956
957 if (NULL == fe_tuner_ops->set_config) {
958 tuner_warn("Tuner frontend module has no way to "
959 "set config\n");
960 break;
961 }
962 fe_tuner_ops->set_config(&t->fe, cfg->priv);
963
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300964 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300965 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300966 /* --- v4l ioctls --- */
967 /* take care: bttv does userspace copying, we'll get a
968 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700970 {
971 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700973 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
974 == EINVAL)
975 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700976
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700977 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700979 t->std = *id;
980 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200981 if (t->tv_freq)
982 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700983 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700984 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700985 case VIDIOC_S_FREQUENCY:
986 {
987 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700988
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300989 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
990 == EINVAL)
991 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700992 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200993 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700994
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700995 break;
996 }
997 case VIDIOC_G_FREQUENCY:
998 {
999 struct v4l2_frequency *f = 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_FREQUENCY") == EINVAL)
1002 return 0;
1003 switch_v4l2();
1004 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -03001005 if (fe_tuner_ops->get_frequency) {
1006 u32 abs_freq;
1007
1008 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
1009 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
1010 (abs_freq * 2 + 125/2) / 125 :
1011 (abs_freq + 62500/2) / 62500;
1012 break;
1013 }
Hans Verkuil27487d42006-01-15 15:04:52 -02001014 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
1015 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001016 break;
1017 }
1018 case VIDIOC_G_TUNER:
1019 {
1020 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -07001021
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001022 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
1023 return 0;
1024 switch_v4l2();
1025
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001026 tuner->type = t->mode;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001027 if (ops && ops->get_afc)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001028 tuner->afc = ops->get_afc(&t->fe);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001029 if (t->mode == V4L2_TUNER_ANALOG_TV)
1030 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001031 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001032 tuner->rangelow = tv_range[0] * 16;
1033 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001034 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001035 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001036
1037 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001038 tuner->rxsubchans =
1039 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001040 if (fe_tuner_ops->get_status) {
1041 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001042
Michael Krufkye18f9442007-08-21 01:25:48 -03001043 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky4e9154b2007-10-21 19:39:50 -03001044 tuner->rxsubchans =
1045 (tuner_status & TUNER_STATUS_STEREO) ?
1046 V4L2_TUNER_SUB_STEREO :
1047 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001048 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001049 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -03001050 tuner->rxsubchans =
1051 ops->is_stereo(&t->fe) ?
1052 V4L2_TUNER_SUB_STEREO :
1053 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001054 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001055 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001056 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001057 tuner->signal = ops->has_signal(&t->fe);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001058 tuner->capability |=
1059 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1060 tuner->audmode = t->audmode;
1061 tuner->rangelow = radio_range[0] * 16000;
1062 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001063 break;
1064 }
1065 case VIDIOC_S_TUNER:
1066 {
1067 struct v4l2_tuner *tuner = arg;
1068
1069 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1070 return 0;
1071
1072 switch_v4l2();
1073
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001074 /* do nothing unless we're a radio tuner */
1075 if (t->mode != V4L2_TUNER_RADIO)
1076 break;
1077 t->audmode = tuner->audmode;
1078 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001079 break;
1080 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001081 case VIDIOC_LOG_STATUS:
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001082 if (ops && ops->tuner_status)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001083 ops->tuner_status(&t->fe);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001084 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 }
1086
1087 return 0;
1088}
1089
Jean Delvare21b48a72007-03-12 19:20:15 -03001090static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001092 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001094 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 /* FIXME: power down ??? */
1096 return 0;
1097}
1098
Jean Delvare21b48a72007-03-12 19:20:15 -03001099static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001101 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001103 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001104 if (V4L2_TUNER_RADIO == t->mode) {
1105 if (t->radio_freq)
1106 set_freq(c, t->radio_freq);
1107 } else {
1108 if (t->tv_freq)
1109 set_freq(c, t->tv_freq);
1110 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 return 0;
1112}
1113
1114/* ----------------------------------------------------------------------- */
1115
1116static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001117 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001118 .attach_adapter = tuner_probe,
1119 .detach_client = tuner_detach,
1120 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001121 .suspend = tuner_suspend,
1122 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001124 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001125 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001127static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001128 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001129 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130};
1131
1132static int __init tuner_init_module(void)
1133{
1134 return i2c_add_driver(&driver);
1135}
1136
1137static void __exit tuner_cleanup_module(void)
1138{
1139 i2c_del_driver(&driver);
1140}
1141
1142module_init(tuner_init_module);
1143module_exit(tuner_cleanup_module);
1144
1145/*
1146 * Overrides for Emacs so that we follow Linus's tabbing style.
1147 * ---------------------------------------------------------------------------
1148 * Local variables:
1149 * c-basic-offset: 8
1150 * End:
1151 */