blob: 17c873c869afc4d7533db9955526c7cdff65dbf1 [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
Michael Krufky241020d2007-10-30 09:46:10 -030033#define PREFIX "tuner "
34
Linus Torvalds1da177e2005-04-16 15:20:36 -070035/* standard i2c insmod options */
36static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030037#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030038 0x10,
39#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080040 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070041 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
42 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 I2C_CLIENT_END
44};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070045
Linus Torvalds1da177e2005-04-16 15:20:36 -070046I2C_CLIENT_INSMOD;
47
48/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070049static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070050static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070051static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070052
Linus Torvalds1da177e2005-04-16 15:20:36 -070053/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020054int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070056static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070057static unsigned int radio_range[2] = { 65, 108 };
58
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020059static char pal[] = "--";
60static char secam[] = "--";
61static char ntsc[] = "-";
62
Hans Verkuilf9195de2006-01-11 19:01:01 -020063
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020064module_param(addr, int, 0444);
65module_param(no_autodetect, int, 0444);
66module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020067module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020068module_param_string(pal, pal, sizeof(pal), 0644);
69module_param_string(secam, secam, sizeof(secam), 0644);
70module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070071module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072module_param_array(radio_range, int, NULL, 0644);
73
74MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
75MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
76MODULE_LICENSE("GPL");
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078static struct i2c_driver driver;
79static struct i2c_client client_template;
80
81/* ---------------------------------------------------------------------- */
82
Michael Krufky4e9154b2007-10-21 19:39:50 -030083static void fe_set_freq(struct dvb_frontend *fe, unsigned int freq)
Michael Krufkye18f9442007-08-21 01:25:48 -030084{
Michael Krufky4e9154b2007-10-21 19:39:50 -030085 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
86 struct tuner *t = fe->analog_demod_priv;
Michael Krufkye18f9442007-08-21 01:25:48 -030087
88 struct analog_parameters params = {
89 .frequency = freq,
90 .mode = t->mode,
91 .audmode = t->audmode,
92 .std = t->std
93 };
94
95 if (NULL == fe_tuner_ops->set_analog_params) {
96 tuner_warn("Tuner frontend module has no way to set freq\n");
97 return;
98 }
Michael Krufky4e9154b2007-10-21 19:39:50 -030099 fe_tuner_ops->set_analog_params(fe, &params);
Michael Krufkye18f9442007-08-21 01:25:48 -0300100}
101
Michael Krufky4e9154b2007-10-21 19:39:50 -0300102static void fe_release(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300103{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300104 if (fe->ops.tuner_ops.release)
105 fe->ops.tuner_ops.release(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300106
Michael Krufky4e9154b2007-10-21 19:39:50 -0300107 fe->ops.analog_demod_ops = NULL;
Michael Krufky4524c1a2007-10-22 18:15:39 -0300108
109 /* DO NOT kfree(fe->analog_demod_priv)
110 *
111 * If we are in this function, analog_demod_priv contains a pointer
112 * to struct tuner *t. This will be kfree'd in tuner_detach().
113 *
114 * Otherwise, fe->ops.analog_demod_ops->release will
115 * handle the cleanup for analog demodulator modules.
116 */
Michael Krufky4e9154b2007-10-21 19:39:50 -0300117 fe->analog_demod_priv = NULL;
Michael Krufkye18f9442007-08-21 01:25:48 -0300118}
119
Michael Krufky4e9154b2007-10-21 19:39:50 -0300120static void fe_standby(struct dvb_frontend *fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300121{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300122 struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
Michael Krufkye18f9442007-08-21 01:25:48 -0300123
124 if (fe_tuner_ops->sleep)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300125 fe_tuner_ops->sleep(fe);
Michael Krufkye18f9442007-08-21 01:25:48 -0300126}
127
Michael Krufky4e9154b2007-10-21 19:39:50 -0300128static int fe_has_signal(struct dvb_frontend *fe)
Michael Krufky1f5ef192007-08-31 17:38:02 -0300129{
Michael Krufky14196832007-10-14 18:11:53 -0300130 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300131
Michael Krufky4e9154b2007-10-21 19:39:50 -0300132 if (fe->ops.tuner_ops.get_rf_strength)
133 fe->ops.tuner_ops.get_rf_strength(fe, &strength);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300134
135 return strength;
136}
137
Michael Krufky4e9154b2007-10-21 19:39:50 -0300138static void tuner_status(struct dvb_frontend *fe);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300139
140static struct analog_tuner_ops tuner_core_ops = {
141 .set_tv_freq = fe_set_freq,
142 .set_radio_freq = fe_set_freq,
143 .standby = fe_standby,
144 .release = fe_release,
145 .has_signal = fe_has_signal,
146 .tuner_status = tuner_status
147};
148
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700149/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150static void set_tv_freq(struct i2c_client *c, unsigned int freq)
151{
152 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300153 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700156 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 return;
158 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300159 if ((NULL == ops) || (NULL == ops->set_tv_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700160 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 return;
162 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700163 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
164 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
165 freq / 16, freq % 16 * 100 / 16, tv_range[0],
166 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200167 /* V4L2 spec: if the freq is not possible then the closest
168 possible value should be selected */
169 if (freq < tv_range[0] * 16)
170 freq = tv_range[0] * 16;
171 else
172 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 }
Michael Krufky4e9154b2007-10-21 19:39:50 -0300174 ops->set_tv_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175}
176
177static void set_radio_freq(struct i2c_client *c, unsigned int freq)
178{
179 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300180 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
182 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700183 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 return;
185 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300186 if ((NULL == ops) || (NULL == ops->set_radio_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700187 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 return;
189 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200190 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700191 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
192 freq / 16000, freq % 16000 * 100 / 16000,
193 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200194 /* V4L2 spec: if the freq is not possible then the closest
195 possible value should be selected */
196 if (freq < radio_range[0] * 16000)
197 freq = radio_range[0] * 16000;
198 else
199 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700201
Michael Krufky4e9154b2007-10-21 19:39:50 -0300202 ops->set_radio_freq(&t->fe, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203}
204
205static void set_freq(struct i2c_client *c, unsigned long freq)
206{
207 struct tuner *t = i2c_get_clientdata(c);
208
209 switch (t->mode) {
210 case V4L2_TUNER_RADIO:
211 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700212 freq / 16000, freq % 16000 * 100 / 16000);
213 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200214 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 break;
216 case V4L2_TUNER_ANALOG_TV:
217 case V4L2_TUNER_DIGITAL_TV:
218 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700219 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200221 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300223 default:
224 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226}
227
Michael Krufky293197c2007-08-28 17:20:42 -0300228static void tuner_i2c_address_check(struct tuner *t)
229{
230 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300231 ((t->i2c->addr < 0x64) || (t->i2c->addr > 0x6f)))
Michael Krufky293197c2007-08-28 17:20:42 -0300232 return;
233
234 tuner_warn("====================== WARNING! ======================\n");
235 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
236 tuner_warn("will soon be dropped. This message indicates that your\n");
237 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300238 t->i2c->name, t->i2c->addr);
Michael Krufky293197c2007-08-28 17:20:42 -0300239 tuner_warn("To ensure continued support for your device, please\n");
240 tuner_warn("send a copy of this message, along with full dmesg\n");
241 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
242 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
243 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300244 t->i2c->adapter->name, t->i2c->addr, t->type,
Michael Krufky293197c2007-08-28 17:20:42 -0300245 tuners[t->type].name);
246 tuner_warn("====================== WARNING! ======================\n");
247}
248
Michael Krufky4adad282007-08-27 21:59:08 -0300249static void attach_simple_tuner(struct tuner *t)
250{
251 struct simple_tuner_config cfg = {
252 .type = t->type,
253 .tun = &tuners[t->type]
254 };
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300255 simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
Michael Krufky4adad282007-08-27 21:59:08 -0300256}
257
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700258static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300259 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300260 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261{
262 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300263 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300264 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700265 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700267 if (type == UNSET || type == TUNER_ABSENT) {
268 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 return;
270 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700272 if (type >= tuner_count) {
273 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
274 return;
275 }
276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300278 t->config = new_config;
279 if (tuner_callback != NULL) {
280 tuner_dbg("defining GPIO callback\n");
281 t->tuner_callback = tuner_callback;
282 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300283
Mauro Carvalho Chehab48aa3362007-10-29 11:33:18 -0300284 if (t->mode == T_UNINITIALIZED) {
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300285 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
286
287 return;
288 }
289
Michael Krufkyb2083192007-05-29 22:54:06 -0300290 /* discard private data, in case set_type() was previously called */
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300291 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300292 ops->release(&t->fe);
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300293
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 switch (t->type) {
295 case TUNER_MT2032:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300296 microtune_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 break;
298 case TUNER_PHILIPS_TDA8290:
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300299 {
Michael Krufky8c125f2c2007-10-27 02:00:57 -0300300 tda829x_attach(t);
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300301 break;
302 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700303 case TUNER_TEA5767:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300304 if (tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700305 t->type = TUNER_ABSENT;
306 t->mode_mask = T_UNINITIALIZED;
307 return;
308 }
309 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700310 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300311 case TUNER_TEA5761:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300312 if (tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300313 t->type = TUNER_ABSENT;
314 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300315 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300316 }
317 t->mode_mask = T_RADIO;
318 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700319 case TUNER_PHILIPS_FMD1216ME_MK3:
320 buffer[0] = 0x0b;
321 buffer[1] = 0xdc;
322 buffer[2] = 0x9c;
323 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700324 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700325 mdelay(1);
326 buffer[2] = 0x86;
327 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700328 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300329 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700330 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800331 case TUNER_PHILIPS_TD1316:
332 buffer[0] = 0x0b;
333 buffer[1] = 0xdc;
334 buffer[2] = 0x86;
335 buffer[3] = 0xa4;
336 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300337 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200338 break;
Mauro Carvalho Chehab690c5442007-10-29 11:33:18 -0300339 case TUNER_XC2028:
340 {
341 int rc=xc2028_attach(&t->fe, t->i2c->adapter, t->i2c->addr,
342 &c->dev, c->adapter->algo_data,
343 t->tuner_callback);
344 if (rc<0) {
345 t->type = TUNER_ABSENT;
346 t->mode_mask = T_UNINITIALIZED;
347 return;
348 }
349 break;
350 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300351 case TUNER_TDA9887:
Michael Krufky31c95842007-10-21 20:48:48 -0300352 tda9887_attach(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300353 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300355 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 break;
357 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700358
Michael Krufkye2be32a2007-10-21 14:35:21 -0300359 ops = t->fe.ops.analog_demod_ops;
360
Michael Krufky1dde7a42007-10-21 13:40:56 -0300361 if (((NULL == ops) ||
362 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
363 (fe_tuner_ops->set_analog_params)) {
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300364 strlcpy(t->i2c->name, fe_tuner_ops->info.name,
365 sizeof(t->i2c->name));
Michael Krufkye18f9442007-08-21 01:25:48 -0300366
Michael Krufky1dde7a42007-10-21 13:40:56 -0300367 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300368 t->fe.analog_demod_priv = t;
Michael Krufkye18f9442007-08-21 01:25:48 -0300369 }
370
Michael Krufky49427442007-10-30 09:44:12 -0300371 tuner_dbg("type set to %s\n", t->i2c->name);
Michael Krufkye18f9442007-08-21 01:25:48 -0300372
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700373 if (t->mode_mask == T_UNINITIALIZED)
374 t->mode_mask = new_mode_mask;
375
Hans Verkuil27487d42006-01-15 15:04:52 -0200376 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700377 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100378 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700379 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300380 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381}
382
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700383/*
384 * This function apply tuner config to tuner specified
385 * by tun_setup structure. I addr is unset, then admin status
386 * and tun addr status is more precise then current status,
387 * it's applied. Otherwise status and type are applied only to
388 * tuner with exactly the same addr.
389*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700390
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700391static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700392{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700393 struct tuner *t = i2c_get_clientdata(c);
394
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300395 tuner_dbg("set addr for type %i\n", t->type);
396
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300397 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
398 (t->mode_mask & tun_setup->mode_mask))) ||
399 (tun_setup->addr == c->addr)) {
400 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300401 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700402 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700403}
404
405static inline int check_mode(struct tuner *t, char *cmd)
406{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700407 if ((1 << t->mode & t->mode_mask) == 0) {
408 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700409 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700410
411 switch (t->mode) {
412 case V4L2_TUNER_RADIO:
413 tuner_dbg("Cmd %s accepted for radio\n", cmd);
414 break;
415 case V4L2_TUNER_ANALOG_TV:
416 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
417 break;
418 case V4L2_TUNER_DIGITAL_TV:
419 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
420 break;
421 }
422 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700423}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700424
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700425/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426static int tuner_fixup_std(struct tuner *t)
427{
428 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300430 case '6':
431 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
432 t->std = V4L2_STD_PAL_60;
433 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 case 'b':
435 case 'B':
436 case 'g':
437 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700438 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 t->std = V4L2_STD_PAL_BG;
440 break;
441 case 'i':
442 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700443 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 t->std = V4L2_STD_PAL_I;
445 break;
446 case 'd':
447 case 'D':
448 case 'k':
449 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700450 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 t->std = V4L2_STD_PAL_DK;
452 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700453 case 'M':
454 case 'm':
455 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
456 t->std = V4L2_STD_PAL_M;
457 break;
458 case 'N':
459 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200460 if (pal[1] == 'c' || pal[1] == 'C') {
461 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
462 t->std = V4L2_STD_PAL_Nc;
463 } else {
464 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
465 t->std = V4L2_STD_PAL_N;
466 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700467 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700468 case '-':
469 /* default parameter, do nothing */
470 break;
471 default:
472 tuner_warn ("pal= argument not recognised\n");
473 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 }
475 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700476 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
477 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200478 case 'b':
479 case 'B':
480 case 'g':
481 case 'G':
482 case 'h':
483 case 'H':
484 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
485 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
486 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700487 case 'd':
488 case 'D':
489 case 'k':
490 case 'K':
491 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
492 t->std = V4L2_STD_SECAM_DK;
493 break;
494 case 'l':
495 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800496 if ((secam[1]=='C')||(secam[1]=='c')) {
497 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
498 t->std = V4L2_STD_SECAM_LC;
499 } else {
500 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
501 t->std = V4L2_STD_SECAM_L;
502 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700503 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700504 case '-':
505 /* default parameter, do nothing */
506 break;
507 default:
508 tuner_warn ("secam= argument not recognised\n");
509 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700510 }
511 }
512
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200513 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
514 switch (ntsc[0]) {
515 case 'm':
516 case 'M':
517 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
518 t->std = V4L2_STD_NTSC_M;
519 break;
520 case 'j':
521 case 'J':
522 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
523 t->std = V4L2_STD_NTSC_M_JP;
524 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200525 case 'k':
526 case 'K':
527 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
528 t->std = V4L2_STD_NTSC_M_KR;
529 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200530 case '-':
531 /* default parameter, do nothing */
532 break;
533 default:
534 tuner_info("ntsc= argument not recognised\n");
535 break;
536 }
537 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 return 0;
539}
540
Michael Krufky4e9154b2007-10-21 19:39:50 -0300541static void tuner_status(struct dvb_frontend *fe)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200542{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300543 struct tuner *t = fe->analog_demod_priv;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200544 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300545 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300546 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200547 const char *p;
548
549 switch (t->mode) {
550 case V4L2_TUNER_RADIO: p = "radio"; break;
551 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
552 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
553 default: p = "undefined"; break;
554 }
555 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200556 freq = t->radio_freq / 16000;
557 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200558 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200559 freq = t->tv_freq / 16;
560 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200561 }
562 tuner_info("Tuner mode: %s\n", p);
563 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300564 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200565 if (t->mode != V4L2_TUNER_RADIO)
566 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300567 if (fe_tuner_ops->get_status) {
568 u32 tuner_status;
569
570 fe_tuner_ops->get_status(&t->fe, &tuner_status);
571 if (tuner_status & TUNER_STATUS_LOCKED)
572 tuner_info("Tuner is locked.\n");
573 if (tuner_status & TUNER_STATUS_STEREO)
574 tuner_info("Stereo: yes\n");
575 }
Michael Krufky1b29ced2007-10-22 01:44:03 -0300576 if (ops) {
577 if (ops->has_signal)
578 tuner_info("Signal strength: %d\n",
579 ops->has_signal(fe));
580 if (ops->is_stereo)
581 tuner_info("Stereo: %s\n",
582 ops->is_stereo(fe) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200583 }
584}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200585
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586/* ---------------------------------------------------------------------- */
587
Hans Verkuilba8fc392006-06-25 15:34:39 -0300588/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700589static unsigned default_mode_mask;
590
591/* During client attach, set_type is called by adapter's attach_inform callback.
592 set_type must then be completed by tuner_attach.
593 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
595{
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300596 struct i2c_client *client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 struct tuner *t;
598
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300599 client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
600 if (NULL == client)
601 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Panagiotis Issaris74081872006-01-11 19:40:56 -0200603 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300604 if (NULL == t) {
605 kfree(client);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700606 return -ENOMEM;
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300607 }
608 t->i2c = client;
609 client_template.adapter = adap;
610 client_template.addr = addr;
611 memcpy(client, &client_template, sizeof(struct i2c_client));
612 i2c_set_clientdata(client, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700613 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700614 t->audmode = V4L2_TUNER_MODE_STEREO;
615 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700617 if (show_i2c) {
618 unsigned char buffer[16];
619 int i,rc;
620
621 memset(buffer, 0, sizeof(buffer));
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300622 rc = i2c_master_recv(client, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800623 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700624 for (i=0;i<rc;i++)
625 printk("%02x ",buffer[i]);
626 printk("\n");
627 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300628 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
629 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
630 return -ENODEV;
631
Markus Rechberger257c6452006-01-23 17:11:11 -0200632 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700633 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800634 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300635 case 0x10:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300636 if (tea5761_autodetection(t->i2c->adapter, t->i2c->addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300637 t->type = TUNER_TEA5761;
638 t->mode_mask = T_RADIO;
639 t->mode = T_STANDBY;
640 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
641 default_mode_mask &= ~T_RADIO;
642
643 goto register_client;
644 }
645 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800646 case 0x42:
647 case 0x43:
648 case 0x4a:
649 case 0x4b:
650 /* If chip is not tda8290, don't register.
651 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300652 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300653 tuner_dbg("chip at addr %x is a tda8290\n", addr);
654 } else {
655 /* Default is being tda9887 */
656 t->type = TUNER_TDA9887;
657 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
658 t->mode = T_STANDBY;
659 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800660 }
661 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800662 case 0x60:
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300663 if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700664 t->type = TUNER_TEA5767;
665 t->mode_mask = T_RADIO;
666 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200667 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700668 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700669
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800670 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700671 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800672 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700673 }
674 }
675
676 /* Initializes only the first adapter found */
677 if (default_mode_mask != T_UNINITIALIZED) {
678 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
679 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200680 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
681 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700682 default_mode_mask = T_UNINITIALIZED;
683 }
684
685 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800686register_client:
687 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehab48aa3362007-10-29 11:33:18 -0300688
689 /* Sets a default mode */
690 if (t->mode_mask & T_ANALOG_TV) {
691 t->mode = T_ANALOG_TV;
692 } else if (t->mode_mask & T_RADIO) {
693 t->mode = T_RADIO;
694 } else {
695 t->mode = T_DIGITAL_TV;
696 }
697
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300698 i2c_attach_client (client);
699 set_type (client,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 return 0;
701}
702
703static int tuner_probe(struct i2c_adapter *adap)
704{
705 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700706 normal_i2c[0] = addr;
707 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
Michael Krufkya1dec512007-08-24 01:13:07 -0300710 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
711 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
712 * and an RTC at 0x6f which can get corrupted if probed.
713 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300714 if ((adap->id == I2C_HW_B_CX2388x) ||
715 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300716 unsigned int i = 0;
717
718 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
719 i += 2;
720 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
721 ignore[i+0] = adap->nr;
722 ignore[i+1] = 0x6b;
723 ignore[i+2] = adap->nr;
724 ignore[i+3] = 0x6f;
725 ignore[i+4] = I2C_CLIENT_END;
726 } else
727 printk(KERN_WARNING "tuner: "
728 "too many options specified "
729 "in i2c probe ignore list!\n");
730 }
731
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700732 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700733
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 if (adap->class & I2C_CLASS_TV_ANALOG)
735 return i2c_probe(adap, &addr_data, tuner_attach);
736 return 0;
737}
738
739static int tuner_detach(struct i2c_client *client)
740{
741 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300742 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700743 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300745 err = i2c_detach_client(t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700746 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700747 tuner_warn
748 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700749 return err;
750 }
751
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300752 if (ops && ops->release)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300753 ops->release(&t->fe);
Michael Krufky16f29162007-10-21 15:22:25 -0300754
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 kfree(t);
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300756 kfree(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 return 0;
758}
759
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760/*
761 * Switch tuner to other mode. If tuner support both tv and radio,
762 * set another frequency to some value (This is needed for some pal
763 * tuners to avoid locking). Otherwise, just put second tuner in
764 * standby mode.
765 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700767static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
768{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300769 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
770
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800771 if (mode == t->mode)
772 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700773
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800774 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700775
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800776 if (check_mode(t, cmd) == EINVAL) {
777 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300778 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300779 ops->standby(&t->fe);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800780 return EINVAL;
781 }
782 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700783}
784
785#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800786 tuner_dbg("switching to v4l2\n"); \
787 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700788
789static inline int check_v4l2(struct tuner *t)
790{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300791 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
792 TV, v4l1 for radio), until that is fixed this code is disabled.
793 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
794 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700795 return 0;
796}
797
798static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799{
800 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300801 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300802 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
Hans Verkuilf9195de2006-01-11 19:01:01 -0200804 if (tuner_debug>1)
Hans Verkuil1cba97d72007-09-14 05:13:54 -0300805 v4l_i2c_print_ioctl(client,cmd);
Michael Krufky5e453dc2006-01-09 15:32:31 -0200806
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700807 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700809 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300810 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 -0700811 ((struct tuner_setup *)arg)->type,
812 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300813 ((struct tuner_setup *)arg)->mode_mask,
814 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700815
816 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700817 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200819 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
820 == EINVAL)
821 return 0;
822 if (t->radio_freq)
823 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700825 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200826 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
827 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300828 t->mode = T_STANDBY;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300829 if (ops && ops->standby)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300830 ops->standby(&t->fe);
Hans Verkuil27487d42006-01-15 15:04:52 -0200831 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300832#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700833 case VIDIOCSAUDIO:
834 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
835 return 0;
836 if (check_v4l2(t) == EINVAL)
837 return 0;
838
839 /* Should be implemented, since bttv calls it */
840 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700841 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700843 {
844 static const v4l2_std_id map[] = {
845 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
846 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
847 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
848 [4 /* bttv */ ] = V4L2_STD_PAL_M,
849 [5 /* bttv */ ] = V4L2_STD_PAL_N,
850 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
851 };
852 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700854 if (check_v4l2(t) == EINVAL)
855 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700856
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700857 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
858 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700860 if (vc->norm < ARRAY_SIZE(map))
861 t->std = map[vc->norm];
862 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200863 if (t->tv_freq)
864 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700865 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700866 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700867 case VIDIOCSFREQ:
868 {
869 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700870
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700871 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
872 return 0;
873 if (check_v4l2(t) == EINVAL)
874 return 0;
875
876 set_freq(client, *v);
877 return 0;
878 }
879 case VIDIOCGTUNER:
880 {
881 struct video_tuner *vt = arg;
882
883 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
884 return 0;
885 if (check_v4l2(t) == EINVAL)
886 return 0;
887
888 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300889 if (fe_tuner_ops->get_status) {
890 u32 tuner_status;
891
892 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300893 if (tuner_status & TUNER_STATUS_STEREO)
894 vt->flags |= VIDEO_TUNER_STEREO_ON;
895 else
896 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300897 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300898 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -0300899 if (ops->is_stereo(&t->fe))
Michael Krufkye18f9442007-08-21 01:25:48 -0300900 vt->flags |=
901 VIDEO_TUNER_STEREO_ON;
902 else
903 vt->flags &=
904 ~VIDEO_TUNER_STEREO_ON;
905 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700906 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300907 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300908 vt->signal = ops->has_signal(&t->fe);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300909
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700910 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
911
912 vt->rangelow = radio_range[0] * 16000;
913 vt->rangehigh = radio_range[1] * 16000;
914
915 } else {
916 vt->rangelow = tv_range[0] * 16;
917 vt->rangehigh = tv_range[1] * 16;
918 }
919
920 return 0;
921 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700923 {
924 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700926 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
927 return 0;
928 if (check_v4l2(t) == EINVAL)
929 return 0;
930
Michael Krufkye18f9442007-08-21 01:25:48 -0300931 if (V4L2_TUNER_RADIO == t->mode) {
932 if (fe_tuner_ops->get_status) {
933 u32 tuner_status;
934
935 fe_tuner_ops->get_status(&t->fe, &tuner_status);
936 va->mode = (tuner_status & TUNER_STATUS_STEREO)
937 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -0300938 } else if (ops && ops->is_stereo)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300939 va->mode = ops->is_stereo(&t->fe)
Michael Krufkye18f9442007-08-21 01:25:48 -0300940 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
941 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700942 return 0;
943 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300944#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300945 case TUNER_SET_CONFIG:
946 {
947 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
948 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300950 if (t->type != cfg->tuner)
951 break;
952
953 if (t->type == TUNER_TDA9887) {
954 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300955 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300956 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300957 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300958
959 if (NULL == fe_tuner_ops->set_config) {
960 tuner_warn("Tuner frontend module has no way to "
961 "set config\n");
962 break;
963 }
964 fe_tuner_ops->set_config(&t->fe, cfg->priv);
965
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300966 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300967 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300968 /* --- v4l ioctls --- */
969 /* take care: bttv does userspace copying, we'll get a
970 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700972 {
973 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700975 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
976 == EINVAL)
977 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700978
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700979 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700981 t->std = *id;
982 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200983 if (t->tv_freq)
984 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700985 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700986 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700987 case VIDIOC_S_FREQUENCY:
988 {
989 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700990
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300991 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
992 == EINVAL)
993 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700994 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200995 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700996
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700997 break;
998 }
999 case VIDIOC_G_FREQUENCY:
1000 {
1001 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -07001002
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001003 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
1004 return 0;
1005 switch_v4l2();
1006 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -03001007 if (fe_tuner_ops->get_frequency) {
1008 u32 abs_freq;
1009
1010 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
1011 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
1012 (abs_freq * 2 + 125/2) / 125 :
1013 (abs_freq + 62500/2) / 62500;
1014 break;
1015 }
Hans Verkuil27487d42006-01-15 15:04:52 -02001016 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
1017 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001018 break;
1019 }
1020 case VIDIOC_G_TUNER:
1021 {
1022 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -07001023
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001024 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
1025 return 0;
1026 switch_v4l2();
1027
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001028 tuner->type = t->mode;
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001029 if (ops && ops->get_afc)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001030 tuner->afc = ops->get_afc(&t->fe);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001031 if (t->mode == V4L2_TUNER_ANALOG_TV)
1032 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001033 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001034 tuner->rangelow = tv_range[0] * 16;
1035 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001036 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001037 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001038
1039 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001040 tuner->rxsubchans =
1041 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001042 if (fe_tuner_ops->get_status) {
1043 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001044
Michael Krufkye18f9442007-08-21 01:25:48 -03001045 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky4e9154b2007-10-21 19:39:50 -03001046 tuner->rxsubchans =
1047 (tuner_status & TUNER_STATUS_STEREO) ?
1048 V4L2_TUNER_SUB_STEREO :
1049 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001050 } else {
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001051 if (ops && ops->is_stereo) {
Michael Krufky4e9154b2007-10-21 19:39:50 -03001052 tuner->rxsubchans =
1053 ops->is_stereo(&t->fe) ?
1054 V4L2_TUNER_SUB_STEREO :
1055 V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001056 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001057 }
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001058 if (ops && ops->has_signal)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001059 tuner->signal = ops->has_signal(&t->fe);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001060 tuner->capability |=
1061 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1062 tuner->audmode = t->audmode;
1063 tuner->rangelow = radio_range[0] * 16000;
1064 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001065 break;
1066 }
1067 case VIDIOC_S_TUNER:
1068 {
1069 struct v4l2_tuner *tuner = arg;
1070
1071 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1072 return 0;
1073
1074 switch_v4l2();
1075
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001076 /* do nothing unless we're a radio tuner */
1077 if (t->mode != V4L2_TUNER_RADIO)
1078 break;
1079 t->audmode = tuner->audmode;
1080 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001081 break;
1082 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001083 case VIDIOC_LOG_STATUS:
Michael Krufkyaf3b0f32007-10-22 18:03:29 -03001084 if (ops && ops->tuner_status)
Michael Krufky4e9154b2007-10-21 19:39:50 -03001085 ops->tuner_status(&t->fe);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001086 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 }
1088
1089 return 0;
1090}
1091
Jean Delvare21b48a72007-03-12 19:20:15 -03001092static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001094 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001096 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 /* FIXME: power down ??? */
1098 return 0;
1099}
1100
Jean Delvare21b48a72007-03-12 19:20:15 -03001101static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001103 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001105 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001106 if (V4L2_TUNER_RADIO == t->mode) {
1107 if (t->radio_freq)
1108 set_freq(c, t->radio_freq);
1109 } else {
1110 if (t->tv_freq)
1111 set_freq(c, t->tv_freq);
1112 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 return 0;
1114}
1115
1116/* ----------------------------------------------------------------------- */
1117
1118static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001119 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001120 .attach_adapter = tuner_probe,
1121 .detach_client = tuner_detach,
1122 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001123 .suspend = tuner_suspend,
1124 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001126 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001127 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001129static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001130 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001131 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132};
1133
1134static int __init tuner_init_module(void)
1135{
1136 return i2c_add_driver(&driver);
1137}
1138
1139static void __exit tuner_cleanup_module(void)
1140{
1141 i2c_del_driver(&driver);
1142}
1143
1144module_init(tuner_init_module);
1145module_exit(tuner_cleanup_module);
1146
1147/*
1148 * Overrides for Emacs so that we follow Linus's tabbing style.
1149 * ---------------------------------------------------------------------------
1150 * Local variables:
1151 * c-basic-offset: 8
1152 * End:
1153 */