blob: 9e99f3636d3dc36a407f46007486b8fe7af252b2 [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"
Michael Krufky4adad282007-08-27 21:59:08 -030027#include "tuner-simple.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#define UNSET (-1U)
30
31/* standard i2c insmod options */
32static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030033#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030034 0x10,
35#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080036 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070037 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
38 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 I2C_CLIENT_END
40};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070041
Linus Torvalds1da177e2005-04-16 15:20:36 -070042I2C_CLIENT_INSMOD;
43
44/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070045static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070046static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070047static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070048
Linus Torvalds1da177e2005-04-16 15:20:36 -070049/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020050int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070052static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070053static unsigned int radio_range[2] = { 65, 108 };
54
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020055static char pal[] = "--";
56static char secam[] = "--";
57static char ntsc[] = "-";
58
Hans Verkuilf9195de2006-01-11 19:01:01 -020059
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020060module_param(addr, int, 0444);
61module_param(no_autodetect, int, 0444);
62module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020063module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020064module_param_string(pal, pal, sizeof(pal), 0644);
65module_param_string(secam, secam, sizeof(secam), 0644);
66module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070067module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068module_param_array(radio_range, int, NULL, 0644);
69
70MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
71MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
72MODULE_LICENSE("GPL");
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074static struct i2c_driver driver;
75static struct i2c_client client_template;
76
77/* ---------------------------------------------------------------------- */
78
Michael Krufkye18f9442007-08-21 01:25:48 -030079static void fe_set_freq(struct tuner *t, unsigned int freq)
80{
81 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
82
83 struct analog_parameters params = {
84 .frequency = freq,
85 .mode = t->mode,
86 .audmode = t->audmode,
87 .std = t->std
88 };
89
90 if (NULL == fe_tuner_ops->set_analog_params) {
91 tuner_warn("Tuner frontend module has no way to set freq\n");
92 return;
93 }
94 fe_tuner_ops->set_analog_params(&t->fe, &params);
95}
96
97static void fe_release(struct tuner *t)
98{
99 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
100
101 if (fe_tuner_ops->release)
102 fe_tuner_ops->release(&t->fe);
103}
104
105static void fe_standby(struct tuner *t)
106{
107 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
108
109 if (fe_tuner_ops->sleep)
110 fe_tuner_ops->sleep(&t->fe);
111}
112
Michael Krufky1f5ef192007-08-31 17:38:02 -0300113static int fe_has_signal(struct tuner *t)
114{
115 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky14196832007-10-14 18:11:53 -0300116 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300117
118 if (fe_tuner_ops->get_rf_strength)
119 fe_tuner_ops->get_rf_strength(&t->fe, &strength);
120
121 return strength;
122}
123
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700124/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125static void set_tv_freq(struct i2c_client *c, unsigned int freq)
126{
127 struct tuner *t = i2c_get_clientdata(c);
128
129 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700130 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 return;
132 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300133 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700134 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 return;
136 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700137 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
138 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
139 freq / 16, freq % 16 * 100 / 16, tv_range[0],
140 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200141 /* V4L2 spec: if the freq is not possible then the closest
142 possible value should be selected */
143 if (freq < tv_range[0] * 16)
144 freq = tv_range[0] * 16;
145 else
146 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300148 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149}
150
151static void set_radio_freq(struct i2c_client *c, unsigned int freq)
152{
153 struct tuner *t = i2c_get_clientdata(c);
154
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 Krufky7a91a802007-06-06 16:10:39 -0300159 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700160 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 return;
162 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200163 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700164 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
165 freq / 16000, freq % 16000 * 100 / 16000,
166 radio_range[0], radio_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 < radio_range[0] * 16000)
170 freq = radio_range[0] * 16000;
171 else
172 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700174
Michael Krufkydb8a6952007-08-21 01:24:42 -0300175 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176}
177
178static void set_freq(struct i2c_client *c, unsigned long freq)
179{
180 struct tuner *t = i2c_get_clientdata(c);
181
182 switch (t->mode) {
183 case V4L2_TUNER_RADIO:
184 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700185 freq / 16000, freq % 16000 * 100 / 16000);
186 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200187 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 break;
189 case V4L2_TUNER_ANALOG_TV:
190 case V4L2_TUNER_DIGITAL_TV:
191 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700192 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200194 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 break;
196 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197}
198
Michael Krufky293197c2007-08-28 17:20:42 -0300199static void tuner_i2c_address_check(struct tuner *t)
200{
201 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
202 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
203 return;
204
205 tuner_warn("====================== WARNING! ======================\n");
206 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
207 tuner_warn("will soon be dropped. This message indicates that your\n");
208 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
209 t->i2c.name, t->i2c.addr);
210 tuner_warn("To ensure continued support for your device, please\n");
211 tuner_warn("send a copy of this message, along with full dmesg\n");
212 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
213 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
214 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
215 t->i2c.adapter->name, t->i2c.addr, t->type,
216 tuners[t->type].name);
217 tuner_warn("====================== WARNING! ======================\n");
218}
219
Michael Krufky910bb3e2007-08-27 21:22:20 -0300220static void attach_tda8290(struct tuner *t)
221{
222 struct tda8290_config cfg = {
223 .lna_cfg = &t->config,
224 .tuner_callback = t->tuner_callback
225 };
226 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
227}
228
Michael Krufky4adad282007-08-27 21:59:08 -0300229static void attach_simple_tuner(struct tuner *t)
230{
231 struct simple_tuner_config cfg = {
232 .type = t->type,
233 .tun = &tuners[t->type]
234 };
235 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
236}
237
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700238static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300239 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300240 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241{
242 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300243 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700244 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700246 if (type == UNSET || type == TUNER_ABSENT) {
247 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 return;
249 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700251 if (type >= tuner_count) {
252 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
253 return;
254 }
255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300257 t->config = new_config;
258 if (tuner_callback != NULL) {
259 tuner_dbg("defining GPIO callback\n");
260 t->tuner_callback = tuner_callback;
261 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300262
263 /* This code detects calls by card attach_inform */
264 if (NULL == t->i2c.dev.driver) {
265 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
266
267 return;
268 }
269
Michael Krufkyb2083192007-05-29 22:54:06 -0300270 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300271 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300272 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300273 else {
274 kfree(t->priv);
275 t->priv = NULL;
276 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300277
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 switch (t->type) {
279 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300280 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 break;
282 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300283 {
284 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300286 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700287 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300288 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700289 t->type = TUNER_ABSENT;
290 t->mode_mask = T_UNINITIALIZED;
291 return;
292 }
293 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700294 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300295 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300296 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300297 t->type = TUNER_ABSENT;
298 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300299 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300300 }
301 t->mode_mask = T_RADIO;
302 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700303 case TUNER_PHILIPS_FMD1216ME_MK3:
304 buffer[0] = 0x0b;
305 buffer[1] = 0xdc;
306 buffer[2] = 0x9c;
307 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700308 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700309 mdelay(1);
310 buffer[2] = 0x86;
311 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700312 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300313 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700314 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800315 case TUNER_PHILIPS_TD1316:
316 buffer[0] = 0x0b;
317 buffer[1] = 0xdc;
318 buffer[2] = 0x86;
319 buffer[3] = 0xa4;
320 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300321 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200322 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300323 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300324 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300325 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300327 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 break;
329 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700330
Michael Krufkye18f9442007-08-21 01:25:48 -0300331 if (fe_tuner_ops->set_analog_params) {
332 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
333
334 t->ops.set_tv_freq = fe_set_freq;
335 t->ops.set_radio_freq = fe_set_freq;
336 t->ops.standby = fe_standby;
337 t->ops.release = fe_release;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300338 t->ops.has_signal = fe_has_signal;
Michael Krufkye18f9442007-08-21 01:25:48 -0300339 }
340
341 tuner_info("type set to %s\n", t->i2c.name);
342
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700343 if (t->mode_mask == T_UNINITIALIZED)
344 t->mode_mask = new_mode_mask;
345
Hans Verkuil27487d42006-01-15 15:04:52 -0200346 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700347 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100348 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700349 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300350 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351}
352
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700353/*
354 * This function apply tuner config to tuner specified
355 * by tun_setup structure. I addr is unset, then admin status
356 * and tun addr status is more precise then current status,
357 * it's applied. Otherwise status and type are applied only to
358 * tuner with exactly the same addr.
359*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700360
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700361static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700362{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700363 struct tuner *t = i2c_get_clientdata(c);
364
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300365 tuner_dbg("set addr for type %i\n", t->type);
366
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300367 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
368 (t->mode_mask & tun_setup->mode_mask))) ||
369 (tun_setup->addr == c->addr)) {
370 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300371 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700372 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700373}
374
375static inline int check_mode(struct tuner *t, char *cmd)
376{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700377 if ((1 << t->mode & t->mode_mask) == 0) {
378 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700379 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700380
381 switch (t->mode) {
382 case V4L2_TUNER_RADIO:
383 tuner_dbg("Cmd %s accepted for radio\n", cmd);
384 break;
385 case V4L2_TUNER_ANALOG_TV:
386 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
387 break;
388 case V4L2_TUNER_DIGITAL_TV:
389 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
390 break;
391 }
392 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700393}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700394
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700395/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396static int tuner_fixup_std(struct tuner *t)
397{
398 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300400 case '6':
401 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
402 t->std = V4L2_STD_PAL_60;
403 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 case 'b':
405 case 'B':
406 case 'g':
407 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700408 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 t->std = V4L2_STD_PAL_BG;
410 break;
411 case 'i':
412 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700413 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 t->std = V4L2_STD_PAL_I;
415 break;
416 case 'd':
417 case 'D':
418 case 'k':
419 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700420 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 t->std = V4L2_STD_PAL_DK;
422 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700423 case 'M':
424 case 'm':
425 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
426 t->std = V4L2_STD_PAL_M;
427 break;
428 case 'N':
429 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200430 if (pal[1] == 'c' || pal[1] == 'C') {
431 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
432 t->std = V4L2_STD_PAL_Nc;
433 } else {
434 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
435 t->std = V4L2_STD_PAL_N;
436 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700437 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700438 case '-':
439 /* default parameter, do nothing */
440 break;
441 default:
442 tuner_warn ("pal= argument not recognised\n");
443 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 }
445 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700446 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
447 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200448 case 'b':
449 case 'B':
450 case 'g':
451 case 'G':
452 case 'h':
453 case 'H':
454 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
455 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
456 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700457 case 'd':
458 case 'D':
459 case 'k':
460 case 'K':
461 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
462 t->std = V4L2_STD_SECAM_DK;
463 break;
464 case 'l':
465 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800466 if ((secam[1]=='C')||(secam[1]=='c')) {
467 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
468 t->std = V4L2_STD_SECAM_LC;
469 } else {
470 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
471 t->std = V4L2_STD_SECAM_L;
472 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700473 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700474 case '-':
475 /* default parameter, do nothing */
476 break;
477 default:
478 tuner_warn ("secam= argument not recognised\n");
479 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700480 }
481 }
482
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200483 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
484 switch (ntsc[0]) {
485 case 'm':
486 case 'M':
487 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
488 t->std = V4L2_STD_NTSC_M;
489 break;
490 case 'j':
491 case 'J':
492 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
493 t->std = V4L2_STD_NTSC_M_JP;
494 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200495 case 'k':
496 case 'K':
497 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
498 t->std = V4L2_STD_NTSC_M_KR;
499 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200500 case '-':
501 /* default parameter, do nothing */
502 break;
503 default:
504 tuner_info("ntsc= argument not recognised\n");
505 break;
506 }
507 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 return 0;
509}
510
Michael Krufkydb8a6952007-08-21 01:24:42 -0300511static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200512{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200513 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300514 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200515 const char *p;
516
517 switch (t->mode) {
518 case V4L2_TUNER_RADIO: p = "radio"; break;
519 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
520 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
521 default: p = "undefined"; break;
522 }
523 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200524 freq = t->radio_freq / 16000;
525 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200526 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200527 freq = t->tv_freq / 16;
528 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200529 }
530 tuner_info("Tuner mode: %s\n", p);
531 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300532 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200533 if (t->mode != V4L2_TUNER_RADIO)
534 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300535 if (fe_tuner_ops->get_status) {
536 u32 tuner_status;
537
538 fe_tuner_ops->get_status(&t->fe, &tuner_status);
539 if (tuner_status & TUNER_STATUS_LOCKED)
540 tuner_info("Tuner is locked.\n");
541 if (tuner_status & TUNER_STATUS_STEREO)
542 tuner_info("Stereo: yes\n");
543 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300544 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300545 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200546 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300547 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300548 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200549 }
550}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200551
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552/* ---------------------------------------------------------------------- */
553
Hans Verkuilba8fc392006-06-25 15:34:39 -0300554/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700555static unsigned default_mode_mask;
556
557/* During client attach, set_type is called by adapter's attach_inform callback.
558 set_type must then be completed by tuner_attach.
559 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
561{
562 struct tuner *t;
563
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700564 client_template.adapter = adap;
565 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Panagiotis Issaris74081872006-01-11 19:40:56 -0200567 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700568 if (NULL == t)
569 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700570 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700572 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700573 t->audmode = V4L2_TUNER_MODE_STEREO;
574 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300575 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700577 if (show_i2c) {
578 unsigned char buffer[16];
579 int i,rc;
580
581 memset(buffer, 0, sizeof(buffer));
582 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800583 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700584 for (i=0;i<rc;i++)
585 printk("%02x ",buffer[i]);
586 printk("\n");
587 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300588 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
589 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
590 return -ENODEV;
591
Markus Rechberger257c6452006-01-23 17:11:11 -0200592 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700593 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800594 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300595 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300596 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300597 t->type = TUNER_TEA5761;
598 t->mode_mask = T_RADIO;
599 t->mode = T_STANDBY;
600 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
601 default_mode_mask &= ~T_RADIO;
602
603 goto register_client;
604 }
605 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800606 case 0x42:
607 case 0x43:
608 case 0x4a:
609 case 0x4b:
610 /* If chip is not tda8290, don't register.
611 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300612 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300613 tuner_dbg("chip at addr %x is a tda8290\n", addr);
614 } else {
615 /* Default is being tda9887 */
616 t->type = TUNER_TDA9887;
617 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
618 t->mode = T_STANDBY;
619 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800620 }
621 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800622 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300623 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700624 t->type = TUNER_TEA5767;
625 t->mode_mask = T_RADIO;
626 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200627 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700628 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700629
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800630 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700631 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800632 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700633 }
634 }
635
636 /* Initializes only the first adapter found */
637 if (default_mode_mask != T_UNINITIALIZED) {
638 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
639 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200640 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
641 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700642 default_mode_mask = T_UNINITIALIZED;
643 }
644
645 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800646register_client:
647 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700648 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300649 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 return 0;
651}
652
653static int tuner_probe(struct i2c_adapter *adap)
654{
655 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700656 normal_i2c[0] = addr;
657 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
Michael Krufkya1dec512007-08-24 01:13:07 -0300660 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
661 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
662 * and an RTC at 0x6f which can get corrupted if probed.
663 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300664 if ((adap->id == I2C_HW_B_CX2388x) ||
665 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300666 unsigned int i = 0;
667
668 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
669 i += 2;
670 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
671 ignore[i+0] = adap->nr;
672 ignore[i+1] = 0x6b;
673 ignore[i+2] = adap->nr;
674 ignore[i+3] = 0x6f;
675 ignore[i+4] = I2C_CLIENT_END;
676 } else
677 printk(KERN_WARNING "tuner: "
678 "too many options specified "
679 "in i2c probe ignore list!\n");
680 }
681
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700682 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700683
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 if (adap->class & I2C_CLASS_TV_ANALOG)
685 return i2c_probe(adap, &addr_data, tuner_attach);
686 return 0;
687}
688
689static int tuner_detach(struct i2c_client *client)
690{
691 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700692 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700694 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700695 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700696 tuner_warn
697 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700698 return err;
699 }
700
Michael Krufky7a91a802007-06-06 16:10:39 -0300701 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300702 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300703 else {
704 kfree(t->priv);
705 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 kfree(t);
707 return 0;
708}
709
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700710/*
711 * Switch tuner to other mode. If tuner support both tv and radio,
712 * set another frequency to some value (This is needed for some pal
713 * tuners to avoid locking). Otherwise, just put second tuner in
714 * standby mode.
715 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700717static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
718{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800719 if (mode == t->mode)
720 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700721
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800722 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700723
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800724 if (check_mode(t, cmd) == EINVAL) {
725 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300726 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300727 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800728 return EINVAL;
729 }
730 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700731}
732
733#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800734 tuner_dbg("switching to v4l2\n"); \
735 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700736
737static inline int check_v4l2(struct tuner *t)
738{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300739 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
740 TV, v4l1 for radio), until that is fixed this code is disabled.
741 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
742 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700743 return 0;
744}
745
746static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747{
748 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300749 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Hans Verkuilf9195de2006-01-11 19:01:01 -0200751 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200752 v4l_i2c_print_ioctl(&(t->i2c),cmd);
753
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700754 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700756 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300757 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 -0700758 ((struct tuner_setup *)arg)->type,
759 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300760 ((struct tuner_setup *)arg)->mode_mask,
761 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700762
763 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700764 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200766 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
767 == EINVAL)
768 return 0;
769 if (t->radio_freq)
770 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700772 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200773 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
774 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300775 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300776 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300777 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200778 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300779#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700780 case VIDIOCSAUDIO:
781 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
782 return 0;
783 if (check_v4l2(t) == EINVAL)
784 return 0;
785
786 /* Should be implemented, since bttv calls it */
787 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700788 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700790 {
791 static const v4l2_std_id map[] = {
792 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
793 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
794 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
795 [4 /* bttv */ ] = V4L2_STD_PAL_M,
796 [5 /* bttv */ ] = V4L2_STD_PAL_N,
797 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
798 };
799 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700801 if (check_v4l2(t) == EINVAL)
802 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700803
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700804 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
805 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700807 if (vc->norm < ARRAY_SIZE(map))
808 t->std = map[vc->norm];
809 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200810 if (t->tv_freq)
811 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700812 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700813 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700814 case VIDIOCSFREQ:
815 {
816 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700817
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700818 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
819 return 0;
820 if (check_v4l2(t) == EINVAL)
821 return 0;
822
823 set_freq(client, *v);
824 return 0;
825 }
826 case VIDIOCGTUNER:
827 {
828 struct video_tuner *vt = arg;
829
830 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
831 return 0;
832 if (check_v4l2(t) == EINVAL)
833 return 0;
834
835 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300836 if (fe_tuner_ops->get_status) {
837 u32 tuner_status;
838
839 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300840 if (tuner_status & TUNER_STATUS_STEREO)
841 vt->flags |= VIDEO_TUNER_STEREO_ON;
842 else
843 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300844 } else {
845 if (t->ops.is_stereo) {
846 if (t->ops.is_stereo(t))
847 vt->flags |=
848 VIDEO_TUNER_STEREO_ON;
849 else
850 vt->flags &=
851 ~VIDEO_TUNER_STEREO_ON;
852 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700853 }
Michael Krufky1f5ef192007-08-31 17:38:02 -0300854 if (t->ops.has_signal)
855 vt->signal = t->ops.has_signal(t);
856
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700857 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
858
859 vt->rangelow = radio_range[0] * 16000;
860 vt->rangehigh = radio_range[1] * 16000;
861
862 } else {
863 vt->rangelow = tv_range[0] * 16;
864 vt->rangehigh = tv_range[1] * 16;
865 }
866
867 return 0;
868 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700870 {
871 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700873 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
874 return 0;
875 if (check_v4l2(t) == EINVAL)
876 return 0;
877
Michael Krufkye18f9442007-08-21 01:25:48 -0300878 if (V4L2_TUNER_RADIO == t->mode) {
879 if (fe_tuner_ops->get_status) {
880 u32 tuner_status;
881
882 fe_tuner_ops->get_status(&t->fe, &tuner_status);
883 va->mode = (tuner_status & TUNER_STATUS_STEREO)
884 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
885 } else if (t->ops.is_stereo)
886 va->mode = t->ops.is_stereo(t)
887 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
888 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700889 return 0;
890 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300891#endif
892 case TDA9887_SET_CONFIG:
893 if (t->type == TUNER_TDA9887) {
894 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300896 t->tda9887_config = *i;
897 set_freq(client, t->tv_freq);
898 }
899 break;
900 /* --- v4l ioctls --- */
901 /* take care: bttv does userspace copying, we'll get a
902 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700904 {
905 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700907 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
908 == EINVAL)
909 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700910
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700911 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700913 t->std = *id;
914 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200915 if (t->tv_freq)
916 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700917 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700918 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700919 case VIDIOC_S_FREQUENCY:
920 {
921 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700922
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300923 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
924 == EINVAL)
925 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700926 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200927 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700928
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700929 break;
930 }
931 case VIDIOC_G_FREQUENCY:
932 {
933 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700934
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700935 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
936 return 0;
937 switch_v4l2();
938 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300939 if (fe_tuner_ops->get_frequency) {
940 u32 abs_freq;
941
942 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
943 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
944 (abs_freq * 2 + 125/2) / 125 :
945 (abs_freq + 62500/2) / 62500;
946 break;
947 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200948 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
949 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700950 break;
951 }
952 case VIDIOC_G_TUNER:
953 {
954 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700955
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700956 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
957 return 0;
958 switch_v4l2();
959
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200960 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300961 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300962 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300963 if (t->mode == V4L2_TUNER_ANALOG_TV)
964 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200965 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700966 tuner->rangelow = tv_range[0] * 16;
967 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200968 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700969 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200970
971 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200972 tuner->rxsubchans =
973 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300974 if (fe_tuner_ops->get_status) {
975 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200976
Michael Krufkye18f9442007-08-21 01:25:48 -0300977 fe_tuner_ops->get_status(&t->fe, &tuner_status);
978 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
979 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300980 } else {
981 if (t->ops.is_stereo) {
982 tuner->rxsubchans = t->ops.is_stereo(t) ?
983 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
984 }
Michael Krufkye18f9442007-08-21 01:25:48 -0300985 }
Michael Krufky1f5ef192007-08-31 17:38:02 -0300986 if (t->ops.has_signal)
987 tuner->signal = t->ops.has_signal(t);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200988 tuner->capability |=
989 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
990 tuner->audmode = t->audmode;
991 tuner->rangelow = radio_range[0] * 16000;
992 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700993 break;
994 }
995 case VIDIOC_S_TUNER:
996 {
997 struct v4l2_tuner *tuner = arg;
998
999 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1000 return 0;
1001
1002 switch_v4l2();
1003
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001004 /* do nothing unless we're a radio tuner */
1005 if (t->mode != V4L2_TUNER_RADIO)
1006 break;
1007 t->audmode = tuner->audmode;
1008 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001009 break;
1010 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001011 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -03001012 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -03001013 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001014 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 }
1016
1017 return 0;
1018}
1019
Jean Delvare21b48a72007-03-12 19:20:15 -03001020static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001022 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001024 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 /* FIXME: power down ??? */
1026 return 0;
1027}
1028
Jean Delvare21b48a72007-03-12 19:20:15 -03001029static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001031 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001033 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001034 if (V4L2_TUNER_RADIO == t->mode) {
1035 if (t->radio_freq)
1036 set_freq(c, t->radio_freq);
1037 } else {
1038 if (t->tv_freq)
1039 set_freq(c, t->tv_freq);
1040 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 return 0;
1042}
1043
1044/* ----------------------------------------------------------------------- */
1045
1046static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001047 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001048 .attach_adapter = tuner_probe,
1049 .detach_client = tuner_detach,
1050 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001051 .suspend = tuner_suspend,
1052 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001054 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001055 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001057static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001058 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001059 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060};
1061
1062static int __init tuner_init_module(void)
1063{
1064 return i2c_add_driver(&driver);
1065}
1066
1067static void __exit tuner_cleanup_module(void)
1068{
1069 i2c_del_driver(&driver);
1070}
1071
1072module_init(tuner_init_module);
1073module_exit(tuner_cleanup_module);
1074
1075/*
1076 * Overrides for Emacs so that we follow Linus's tabbing style.
1077 * ---------------------------------------------------------------------------
1078 * Local variables:
1079 * c-basic-offset: 8
1080 * End:
1081 */