blob: 9903b3f2530078e02ae2ed33e0ced6df8248099c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * i2c tv tuner chip device driver
4 * core core, i.e. kernel interfaces, registering and so on
5 */
6
7#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/string.h>
10#include <linux/timer.h>
11#include <linux/delay.h>
12#include <linux/errno.h>
13#include <linux/slab.h>
14#include <linux/poll.h>
15#include <linux/i2c.h>
16#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/init.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030018#include <linux/videodev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <media/tuner.h>
Michael Krufky4adad282007-08-27 21:59:08 -030020#include <media/tuner-types.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020021#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030022#include "tuner-driver.h"
Michael Krufky96c0b7c2007-08-27 21:23:08 -030023#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030024#include "tda8290.h"
Michael Krufky7ab10bf2007-08-27 21:23:40 -030025#include "tea5761.h"
Michael Krufky8d0936e2007-08-27 21:24:27 -030026#include "tea5767.h"
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030027#include "tuner-xc2028.h"
Michael Krufky4adad282007-08-27 21:59:08 -030028#include "tuner-simple.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#define UNSET (-1U)
31
32/* standard i2c insmod options */
33static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030034#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030035 0x10,
36#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080037 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070038 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
39 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 I2C_CLIENT_END
41};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043I2C_CLIENT_INSMOD;
44
45/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070046static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070047static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070048static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070049
Linus Torvalds1da177e2005-04-16 15:20:36 -070050/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020051int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070053static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070054static unsigned int radio_range[2] = { 65, 108 };
55
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020056static char pal[] = "--";
57static char secam[] = "--";
58static char ntsc[] = "-";
59
Hans Verkuilf9195de2006-01-11 19:01:01 -020060
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020061module_param(addr, int, 0444);
62module_param(no_autodetect, int, 0444);
63module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020064module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020065module_param_string(pal, pal, sizeof(pal), 0644);
66module_param_string(secam, secam, sizeof(secam), 0644);
67module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070068module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069module_param_array(radio_range, int, NULL, 0644);
70
71MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
72MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
73MODULE_LICENSE("GPL");
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static struct i2c_driver driver;
76static struct i2c_client client_template;
77
78/* ---------------------------------------------------------------------- */
79
Michael Krufkye18f9442007-08-21 01:25:48 -030080static void fe_set_freq(struct tuner *t, unsigned int freq)
81{
82 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
83
84 struct analog_parameters params = {
85 .frequency = freq,
86 .mode = t->mode,
87 .audmode = t->audmode,
88 .std = t->std
89 };
90
91 if (NULL == fe_tuner_ops->set_analog_params) {
92 tuner_warn("Tuner frontend module has no way to set freq\n");
93 return;
94 }
95 fe_tuner_ops->set_analog_params(&t->fe, &params);
96}
97
98static void fe_release(struct tuner *t)
99{
100 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
101
102 if (fe_tuner_ops->release)
103 fe_tuner_ops->release(&t->fe);
Michael Krufkye2be32a2007-10-21 14:35:21 -0300104
105 t->fe.ops.analog_demod_ops = NULL;
Michael Krufkye18f9442007-08-21 01:25:48 -0300106}
107
108static void fe_standby(struct tuner *t)
109{
110 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
111
112 if (fe_tuner_ops->sleep)
113 fe_tuner_ops->sleep(&t->fe);
114}
115
Michael Krufky1f5ef192007-08-31 17:38:02 -0300116static int fe_has_signal(struct tuner *t)
117{
118 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky14196832007-10-14 18:11:53 -0300119 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300120
121 if (fe_tuner_ops->get_rf_strength)
122 fe_tuner_ops->get_rf_strength(&t->fe, &strength);
123
124 return strength;
125}
126
Michael Krufky1dde7a42007-10-21 13:40:56 -0300127static void tuner_status(struct tuner *t);
128
129static struct analog_tuner_ops tuner_core_ops = {
130 .set_tv_freq = fe_set_freq,
131 .set_radio_freq = fe_set_freq,
132 .standby = fe_standby,
133 .release = fe_release,
134 .has_signal = fe_has_signal,
135 .tuner_status = tuner_status
136};
137
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700138/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139static void set_tv_freq(struct i2c_client *c, unsigned int freq)
140{
141 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300142 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700145 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 return;
147 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300148 if ((NULL == ops) || (NULL == ops->set_tv_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700149 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 return;
151 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700152 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
153 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
154 freq / 16, freq % 16 * 100 / 16, tv_range[0],
155 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200156 /* V4L2 spec: if the freq is not possible then the closest
157 possible value should be selected */
158 if (freq < tv_range[0] * 16)
159 freq = tv_range[0] * 16;
160 else
161 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300163 ops->set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164}
165
166static void set_radio_freq(struct i2c_client *c, unsigned int freq)
167{
168 struct tuner *t = i2c_get_clientdata(c);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300169 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
171 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700172 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 return;
174 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300175 if ((NULL == ops) || (NULL == ops->set_radio_freq)) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700176 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 return;
178 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200179 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700180 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
181 freq / 16000, freq % 16000 * 100 / 16000,
182 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200183 /* V4L2 spec: if the freq is not possible then the closest
184 possible value should be selected */
185 if (freq < radio_range[0] * 16000)
186 freq = radio_range[0] * 16000;
187 else
188 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700190
Michael Krufky1dde7a42007-10-21 13:40:56 -0300191 ops->set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192}
193
194static void set_freq(struct i2c_client *c, unsigned long freq)
195{
196 struct tuner *t = i2c_get_clientdata(c);
197
198 switch (t->mode) {
199 case V4L2_TUNER_RADIO:
200 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700201 freq / 16000, freq % 16000 * 100 / 16000);
202 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200203 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 break;
205 case V4L2_TUNER_ANALOG_TV:
206 case V4L2_TUNER_DIGITAL_TV:
207 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700208 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200210 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300212 default:
213 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215}
216
Michael Krufky293197c2007-08-28 17:20:42 -0300217static void tuner_i2c_address_check(struct tuner *t)
218{
219 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
220 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
221 return;
222
223 tuner_warn("====================== WARNING! ======================\n");
224 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
225 tuner_warn("will soon be dropped. This message indicates that your\n");
226 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
227 t->i2c.name, t->i2c.addr);
228 tuner_warn("To ensure continued support for your device, please\n");
229 tuner_warn("send a copy of this message, along with full dmesg\n");
230 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
231 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
232 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
233 t->i2c.adapter->name, t->i2c.addr, t->type,
234 tuners[t->type].name);
235 tuner_warn("====================== WARNING! ======================\n");
236}
237
Michael Krufky4adad282007-08-27 21:59:08 -0300238static void attach_simple_tuner(struct tuner *t)
239{
240 struct simple_tuner_config cfg = {
241 .type = t->type,
242 .tun = &tuners[t->type]
243 };
244 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
245}
246
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700247static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300248 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300249 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
251 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300252 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300253 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700254 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700256 if (type == UNSET || type == TUNER_ABSENT) {
257 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 return;
259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700261 if (type >= tuner_count) {
262 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
263 return;
264 }
265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300267 t->config = new_config;
268 if (tuner_callback != NULL) {
269 tuner_dbg("defining GPIO callback\n");
270 t->tuner_callback = tuner_callback;
271 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300272
273 /* This code detects calls by card attach_inform */
274 if (NULL == t->i2c.dev.driver) {
275 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
276
277 return;
278 }
279
Michael Krufkyb2083192007-05-29 22:54:06 -0300280 /* discard private data, in case set_type() was previously called */
Michael Krufky1dde7a42007-10-21 13:40:56 -0300281 if ((ops) && (ops->release))
282 ops->release(t);
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300283
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 switch (t->type) {
285 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300286 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 break;
288 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300289 {
Michael Krufky746d97322007-08-25 19:08:45 -0300290 tda8290_attach(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300292 }
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300293 case TUNER_PHILIPS_TDA8295:
294 {
295 tda8295_attach(t);
296 break;
297 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700298 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300299 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700300 t->type = TUNER_ABSENT;
301 t->mode_mask = T_UNINITIALIZED;
302 return;
303 }
304 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700305 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300306 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300307 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300308 t->type = TUNER_ABSENT;
309 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300310 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300311 }
312 t->mode_mask = T_RADIO;
313 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700314 case TUNER_PHILIPS_FMD1216ME_MK3:
315 buffer[0] = 0x0b;
316 buffer[1] = 0xdc;
317 buffer[2] = 0x9c;
318 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700319 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700320 mdelay(1);
321 buffer[2] = 0x86;
322 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700323 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300324 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700325 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800326 case TUNER_PHILIPS_TD1316:
327 buffer[0] = 0x0b;
328 buffer[1] = 0xdc;
329 buffer[2] = 0x86;
330 buffer[3] = 0xa4;
331 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300332 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200333 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300334 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300335 {
336 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
337 &c->dev, c->adapter->algo_data,
338 t->tuner_callback);
339 if (rc<0) {
340 t->type = TUNER_ABSENT;
341 t->mode_mask = T_UNINITIALIZED;
342 return;
343 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300344 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300345 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300346 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300347 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300348 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300350 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 break;
352 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700353
Michael Krufkye2be32a2007-10-21 14:35:21 -0300354 ops = t->fe.ops.analog_demod_ops;
355
Michael Krufky1dde7a42007-10-21 13:40:56 -0300356 if (((NULL == ops) ||
357 ((NULL == ops->set_tv_freq) && (NULL == ops->set_radio_freq))) &&
358 (fe_tuner_ops->set_analog_params)) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300359 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
360
Michael Krufky1dde7a42007-10-21 13:40:56 -0300361 t->fe.ops.analog_demod_ops = &tuner_core_ops;
Michael Krufkye18f9442007-08-21 01:25:48 -0300362 }
363
364 tuner_info("type set to %s\n", t->i2c.name);
365
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700366 if (t->mode_mask == T_UNINITIALIZED)
367 t->mode_mask = new_mode_mask;
368
Hans Verkuil27487d42006-01-15 15:04:52 -0200369 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700370 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100371 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700372 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300373 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374}
375
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700376/*
377 * This function apply tuner config to tuner specified
378 * by tun_setup structure. I addr is unset, then admin status
379 * and tun addr status is more precise then current status,
380 * it's applied. Otherwise status and type are applied only to
381 * tuner with exactly the same addr.
382*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700383
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700384static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700385{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700386 struct tuner *t = i2c_get_clientdata(c);
387
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300388 tuner_dbg("set addr for type %i\n", t->type);
389
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300390 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
391 (t->mode_mask & tun_setup->mode_mask))) ||
392 (tun_setup->addr == c->addr)) {
393 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300394 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700395 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700396}
397
398static inline int check_mode(struct tuner *t, char *cmd)
399{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700400 if ((1 << t->mode & t->mode_mask) == 0) {
401 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700402 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700403
404 switch (t->mode) {
405 case V4L2_TUNER_RADIO:
406 tuner_dbg("Cmd %s accepted for radio\n", cmd);
407 break;
408 case V4L2_TUNER_ANALOG_TV:
409 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
410 break;
411 case V4L2_TUNER_DIGITAL_TV:
412 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
413 break;
414 }
415 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700416}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700417
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700418/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419static int tuner_fixup_std(struct tuner *t)
420{
421 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300423 case '6':
424 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
425 t->std = V4L2_STD_PAL_60;
426 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 case 'b':
428 case 'B':
429 case 'g':
430 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700431 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 t->std = V4L2_STD_PAL_BG;
433 break;
434 case 'i':
435 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700436 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 t->std = V4L2_STD_PAL_I;
438 break;
439 case 'd':
440 case 'D':
441 case 'k':
442 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700443 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 t->std = V4L2_STD_PAL_DK;
445 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700446 case 'M':
447 case 'm':
448 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
449 t->std = V4L2_STD_PAL_M;
450 break;
451 case 'N':
452 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200453 if (pal[1] == 'c' || pal[1] == 'C') {
454 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
455 t->std = V4L2_STD_PAL_Nc;
456 } else {
457 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
458 t->std = V4L2_STD_PAL_N;
459 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700460 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700461 case '-':
462 /* default parameter, do nothing */
463 break;
464 default:
465 tuner_warn ("pal= argument not recognised\n");
466 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 }
468 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700469 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
470 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200471 case 'b':
472 case 'B':
473 case 'g':
474 case 'G':
475 case 'h':
476 case 'H':
477 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
478 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
479 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700480 case 'd':
481 case 'D':
482 case 'k':
483 case 'K':
484 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
485 t->std = V4L2_STD_SECAM_DK;
486 break;
487 case 'l':
488 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800489 if ((secam[1]=='C')||(secam[1]=='c')) {
490 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
491 t->std = V4L2_STD_SECAM_LC;
492 } else {
493 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
494 t->std = V4L2_STD_SECAM_L;
495 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700496 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700497 case '-':
498 /* default parameter, do nothing */
499 break;
500 default:
501 tuner_warn ("secam= argument not recognised\n");
502 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700503 }
504 }
505
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200506 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
507 switch (ntsc[0]) {
508 case 'm':
509 case 'M':
510 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
511 t->std = V4L2_STD_NTSC_M;
512 break;
513 case 'j':
514 case 'J':
515 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
516 t->std = V4L2_STD_NTSC_M_JP;
517 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200518 case 'k':
519 case 'K':
520 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
521 t->std = V4L2_STD_NTSC_M_KR;
522 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200523 case '-':
524 /* default parameter, do nothing */
525 break;
526 default:
527 tuner_info("ntsc= argument not recognised\n");
528 break;
529 }
530 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 return 0;
532}
533
Michael Krufkydb8a6952007-08-21 01:24:42 -0300534static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200535{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200536 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300537 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300538 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200539 const char *p;
540
541 switch (t->mode) {
542 case V4L2_TUNER_RADIO: p = "radio"; break;
543 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
544 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
545 default: p = "undefined"; break;
546 }
547 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200548 freq = t->radio_freq / 16000;
549 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200550 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200551 freq = t->tv_freq / 16;
552 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200553 }
554 tuner_info("Tuner mode: %s\n", p);
555 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300556 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200557 if (t->mode != V4L2_TUNER_RADIO)
558 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300559 if (fe_tuner_ops->get_status) {
560 u32 tuner_status;
561
562 fe_tuner_ops->get_status(&t->fe, &tuner_status);
563 if (tuner_status & TUNER_STATUS_LOCKED)
564 tuner_info("Tuner is locked.\n");
565 if (tuner_status & TUNER_STATUS_STEREO)
566 tuner_info("Stereo: yes\n");
567 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300568 if ((ops) && (ops->has_signal)) {
569 tuner_info("Signal strength: %d\n", ops->has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200570 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300571 if ((ops) && (ops->is_stereo)) {
572 tuner_info("Stereo: %s\n", ops->is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200573 }
574}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200575
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576/* ---------------------------------------------------------------------- */
577
Hans Verkuilba8fc392006-06-25 15:34:39 -0300578/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700579static unsigned default_mode_mask;
580
581/* During client attach, set_type is called by adapter's attach_inform callback.
582 set_type must then be completed by tuner_attach.
583 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
585{
586 struct tuner *t;
587
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700588 client_template.adapter = adap;
589 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Panagiotis Issaris74081872006-01-11 19:40:56 -0200591 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700592 if (NULL == t)
593 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700594 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700596 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700597 t->audmode = V4L2_TUNER_MODE_STEREO;
598 t->mode_mask = T_UNINITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700600 if (show_i2c) {
601 unsigned char buffer[16];
602 int i,rc;
603
604 memset(buffer, 0, sizeof(buffer));
605 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800606 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700607 for (i=0;i<rc;i++)
608 printk("%02x ",buffer[i]);
609 printk("\n");
610 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300611 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
612 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
613 return -ENODEV;
614
Markus Rechberger257c6452006-01-23 17:11:11 -0200615 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700616 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800617 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300618 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300619 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300620 t->type = TUNER_TEA5761;
621 t->mode_mask = T_RADIO;
622 t->mode = T_STANDBY;
623 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
624 default_mode_mask &= ~T_RADIO;
625
626 goto register_client;
627 }
628 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800629 case 0x42:
630 case 0x43:
631 case 0x4a:
632 case 0x4b:
633 /* If chip is not tda8290, don't register.
634 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300635 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300636 tuner_dbg("chip at addr %x is a tda8290\n", addr);
637 } else {
638 /* Default is being tda9887 */
639 t->type = TUNER_TDA9887;
640 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
641 t->mode = T_STANDBY;
642 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800643 }
644 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800645 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300646 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700647 t->type = TUNER_TEA5767;
648 t->mode_mask = T_RADIO;
649 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200650 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700651 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700652
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800653 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700654 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800655 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700656 }
657 }
658
659 /* Initializes only the first adapter found */
660 if (default_mode_mask != T_UNINITIALIZED) {
661 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
662 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200663 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
664 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700665 default_mode_mask = T_UNINITIALIZED;
666 }
667
668 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800669register_client:
670 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700671 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300672 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 return 0;
674}
675
676static int tuner_probe(struct i2c_adapter *adap)
677{
678 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700679 normal_i2c[0] = addr;
680 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
Michael Krufkya1dec512007-08-24 01:13:07 -0300683 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
684 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
685 * and an RTC at 0x6f which can get corrupted if probed.
686 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300687 if ((adap->id == I2C_HW_B_CX2388x) ||
688 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300689 unsigned int i = 0;
690
691 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
692 i += 2;
693 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
694 ignore[i+0] = adap->nr;
695 ignore[i+1] = 0x6b;
696 ignore[i+2] = adap->nr;
697 ignore[i+3] = 0x6f;
698 ignore[i+4] = I2C_CLIENT_END;
699 } else
700 printk(KERN_WARNING "tuner: "
701 "too many options specified "
702 "in i2c probe ignore list!\n");
703 }
704
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700705 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700706
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 if (adap->class & I2C_CLASS_TV_ANALOG)
708 return i2c_probe(adap, &addr_data, tuner_attach);
709 return 0;
710}
711
712static int tuner_detach(struct i2c_client *client)
713{
714 struct tuner *t = i2c_get_clientdata(client);
Michael Krufky1dde7a42007-10-21 13:40:56 -0300715 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700716 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700718 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700719 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700720 tuner_warn
721 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700722 return err;
723 }
724
Michael Krufky1dde7a42007-10-21 13:40:56 -0300725 if ((ops) && (ops->release))
726 ops->release(t);
Michael Krufky16f29162007-10-21 15:22:25 -0300727
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 kfree(t);
729 return 0;
730}
731
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700732/*
733 * Switch tuner to other mode. If tuner support both tv and radio,
734 * set another frequency to some value (This is needed for some pal
735 * tuners to avoid locking). Otherwise, just put second tuner in
736 * standby mode.
737 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700739static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
740{
Michael Krufky1dde7a42007-10-21 13:40:56 -0300741 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
742
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800743 if (mode == t->mode)
744 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700745
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800746 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700747
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800748 if (check_mode(t, cmd) == EINVAL) {
749 t->mode = T_STANDBY;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300750 if ((ops) && (ops->standby))
751 ops->standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800752 return EINVAL;
753 }
754 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700755}
756
757#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800758 tuner_dbg("switching to v4l2\n"); \
759 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760
761static inline int check_v4l2(struct tuner *t)
762{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300763 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
764 TV, v4l1 for radio), until that is fixed this code is disabled.
765 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
766 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700767 return 0;
768}
769
770static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771{
772 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300773 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300774 struct analog_tuner_ops *ops = t->fe.ops.analog_demod_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775
Hans Verkuilf9195de2006-01-11 19:01:01 -0200776 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200777 v4l_i2c_print_ioctl(&(t->i2c),cmd);
778
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700779 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700781 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300782 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 -0700783 ((struct tuner_setup *)arg)->type,
784 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300785 ((struct tuner_setup *)arg)->mode_mask,
786 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700787
788 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700789 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200791 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
792 == EINVAL)
793 return 0;
794 if (t->radio_freq)
795 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700797 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200798 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
799 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300800 t->mode = T_STANDBY;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300801 if ((ops) && (ops->standby))
802 ops->standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200803 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300804#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700805 case VIDIOCSAUDIO:
806 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
807 return 0;
808 if (check_v4l2(t) == EINVAL)
809 return 0;
810
811 /* Should be implemented, since bttv calls it */
812 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700813 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700815 {
816 static const v4l2_std_id map[] = {
817 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
818 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
819 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
820 [4 /* bttv */ ] = V4L2_STD_PAL_M,
821 [5 /* bttv */ ] = V4L2_STD_PAL_N,
822 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
823 };
824 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700826 if (check_v4l2(t) == EINVAL)
827 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700828
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700829 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
830 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700832 if (vc->norm < ARRAY_SIZE(map))
833 t->std = map[vc->norm];
834 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200835 if (t->tv_freq)
836 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700837 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700838 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700839 case VIDIOCSFREQ:
840 {
841 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700842
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700843 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
844 return 0;
845 if (check_v4l2(t) == EINVAL)
846 return 0;
847
848 set_freq(client, *v);
849 return 0;
850 }
851 case VIDIOCGTUNER:
852 {
853 struct video_tuner *vt = arg;
854
855 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
856 return 0;
857 if (check_v4l2(t) == EINVAL)
858 return 0;
859
860 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300861 if (fe_tuner_ops->get_status) {
862 u32 tuner_status;
863
864 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300865 if (tuner_status & TUNER_STATUS_STEREO)
866 vt->flags |= VIDEO_TUNER_STEREO_ON;
867 else
868 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300869 } else {
Michael Krufky1dde7a42007-10-21 13:40:56 -0300870 if ((ops) && (ops->is_stereo)) {
871 if (ops->is_stereo(t))
Michael Krufkye18f9442007-08-21 01:25:48 -0300872 vt->flags |=
873 VIDEO_TUNER_STEREO_ON;
874 else
875 vt->flags &=
876 ~VIDEO_TUNER_STEREO_ON;
877 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700878 }
Michael Krufky1dde7a42007-10-21 13:40:56 -0300879 if ((ops) && (ops->has_signal))
880 vt->signal = ops->has_signal(t);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300881
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700882 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
883
884 vt->rangelow = radio_range[0] * 16000;
885 vt->rangehigh = radio_range[1] * 16000;
886
887 } else {
888 vt->rangelow = tv_range[0] * 16;
889 vt->rangehigh = tv_range[1] * 16;
890 }
891
892 return 0;
893 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700895 {
896 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700898 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
899 return 0;
900 if (check_v4l2(t) == EINVAL)
901 return 0;
902
Michael Krufkye18f9442007-08-21 01:25:48 -0300903 if (V4L2_TUNER_RADIO == t->mode) {
904 if (fe_tuner_ops->get_status) {
905 u32 tuner_status;
906
907 fe_tuner_ops->get_status(&t->fe, &tuner_status);
908 va->mode = (tuner_status & TUNER_STATUS_STEREO)
909 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
Michael Krufky1dde7a42007-10-21 13:40:56 -0300910 } else if ((ops) && (ops->is_stereo))
911 va->mode = ops->is_stereo(t)
Michael Krufkye18f9442007-08-21 01:25:48 -0300912 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
913 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700914 return 0;
915 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300916#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300917 case TUNER_SET_CONFIG:
918 {
919 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
920 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300922 if (t->type != cfg->tuner)
923 break;
924
925 if (t->type == TUNER_TDA9887) {
926 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300927 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300928 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300929 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300930
931 if (NULL == fe_tuner_ops->set_config) {
932 tuner_warn("Tuner frontend module has no way to "
933 "set config\n");
934 break;
935 }
936 fe_tuner_ops->set_config(&t->fe, cfg->priv);
937
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300938 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300939 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300940 /* --- v4l ioctls --- */
941 /* take care: bttv does userspace copying, we'll get a
942 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700944 {
945 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700947 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
948 == EINVAL)
949 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700950
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700951 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700953 t->std = *id;
954 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200955 if (t->tv_freq)
956 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700957 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700958 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700959 case VIDIOC_S_FREQUENCY:
960 {
961 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700962
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300963 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
964 == EINVAL)
965 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700966 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200967 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700968
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700969 break;
970 }
971 case VIDIOC_G_FREQUENCY:
972 {
973 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700974
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700975 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
976 return 0;
977 switch_v4l2();
978 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300979 if (fe_tuner_ops->get_frequency) {
980 u32 abs_freq;
981
982 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
983 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
984 (abs_freq * 2 + 125/2) / 125 :
985 (abs_freq + 62500/2) / 62500;
986 break;
987 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200988 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
989 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700990 break;
991 }
992 case VIDIOC_G_TUNER:
993 {
994 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700995
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700996 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
997 return 0;
998 switch_v4l2();
999
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001000 tuner->type = t->mode;
Michael Krufky1dde7a42007-10-21 13:40:56 -03001001 if ((ops) && (ops->get_afc))
1002 tuner->afc = ops->get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -03001003 if (t->mode == V4L2_TUNER_ANALOG_TV)
1004 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001005 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001006 tuner->rangelow = tv_range[0] * 16;
1007 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001008 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001009 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001010
1011 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001012 tuner->rxsubchans =
1013 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001014 if (fe_tuner_ops->get_status) {
1015 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001016
Michael Krufkye18f9442007-08-21 01:25:48 -03001017 fe_tuner_ops->get_status(&t->fe, &tuner_status);
1018 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
1019 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001020 } else {
Michael Krufky1dde7a42007-10-21 13:40:56 -03001021 if ((ops) && (ops->is_stereo)) {
1022 tuner->rxsubchans = ops->is_stereo(t) ?
Michael Krufkye18f9442007-08-21 01:25:48 -03001023 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
1024 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001025 }
Michael Krufky1dde7a42007-10-21 13:40:56 -03001026 if ((ops) && (ops->has_signal))
1027 tuner->signal = ops->has_signal(t);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001028 tuner->capability |=
1029 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1030 tuner->audmode = t->audmode;
1031 tuner->rangelow = radio_range[0] * 16000;
1032 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001033 break;
1034 }
1035 case VIDIOC_S_TUNER:
1036 {
1037 struct v4l2_tuner *tuner = arg;
1038
1039 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1040 return 0;
1041
1042 switch_v4l2();
1043
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001044 /* do nothing unless we're a radio tuner */
1045 if (t->mode != V4L2_TUNER_RADIO)
1046 break;
1047 t->audmode = tuner->audmode;
1048 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001049 break;
1050 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001051 case VIDIOC_LOG_STATUS:
Michael Krufky1dde7a42007-10-21 13:40:56 -03001052 if ((ops) && (ops->tuner_status))
1053 ops->tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001054 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 }
1056
1057 return 0;
1058}
1059
Jean Delvare21b48a72007-03-12 19:20:15 -03001060static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001062 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001064 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 /* FIXME: power down ??? */
1066 return 0;
1067}
1068
Jean Delvare21b48a72007-03-12 19:20:15 -03001069static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001071 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001073 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001074 if (V4L2_TUNER_RADIO == t->mode) {
1075 if (t->radio_freq)
1076 set_freq(c, t->radio_freq);
1077 } else {
1078 if (t->tv_freq)
1079 set_freq(c, t->tv_freq);
1080 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 return 0;
1082}
1083
1084/* ----------------------------------------------------------------------- */
1085
1086static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001087 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001088 .attach_adapter = tuner_probe,
1089 .detach_client = tuner_detach,
1090 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001091 .suspend = tuner_suspend,
1092 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001094 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001095 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001097static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001098 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001099 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100};
1101
1102static int __init tuner_init_module(void)
1103{
1104 return i2c_add_driver(&driver);
1105}
1106
1107static void __exit tuner_cleanup_module(void)
1108{
1109 i2c_del_driver(&driver);
1110}
1111
1112module_init(tuner_init_module);
1113module_exit(tuner_cleanup_module);
1114
1115/*
1116 * Overrides for Emacs so that we follow Linus's tabbing style.
1117 * ---------------------------------------------------------------------------
1118 * Local variables:
1119 * c-basic-offset: 8
1120 * End:
1121 */