blob: ce817a17ccf1f25d7d5a9285522fadc0d74e4cbc [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;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300196 default:
197 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199}
200
Michael Krufky293197c2007-08-28 17:20:42 -0300201static void tuner_i2c_address_check(struct tuner *t)
202{
203 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
204 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
205 return;
206
207 tuner_warn("====================== WARNING! ======================\n");
208 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
209 tuner_warn("will soon be dropped. This message indicates that your\n");
210 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
211 t->i2c.name, t->i2c.addr);
212 tuner_warn("To ensure continued support for your device, please\n");
213 tuner_warn("send a copy of this message, along with full dmesg\n");
214 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
215 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
216 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
217 t->i2c.adapter->name, t->i2c.addr, t->type,
218 tuners[t->type].name);
219 tuner_warn("====================== WARNING! ======================\n");
220}
221
Michael Krufky910bb3e2007-08-27 21:22:20 -0300222static void attach_tda8290(struct tuner *t)
223{
224 struct tda8290_config cfg = {
225 .lna_cfg = &t->config,
226 .tuner_callback = t->tuner_callback
227 };
228 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
229}
230
Michael Krufky4adad282007-08-27 21:59:08 -0300231static void attach_simple_tuner(struct tuner *t)
232{
233 struct simple_tuner_config cfg = {
234 .type = t->type,
235 .tun = &tuners[t->type]
236 };
237 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
238}
239
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700240static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300241 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300242 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243{
244 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300245 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700246 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700248 if (type == UNSET || type == TUNER_ABSENT) {
249 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 return;
251 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700253 if (type >= tuner_count) {
254 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
255 return;
256 }
257
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300259 t->config = new_config;
260 if (tuner_callback != NULL) {
261 tuner_dbg("defining GPIO callback\n");
262 t->tuner_callback = tuner_callback;
263 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300264
265 /* This code detects calls by card attach_inform */
266 if (NULL == t->i2c.dev.driver) {
267 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
268
269 return;
270 }
271
Michael Krufkyb2083192007-05-29 22:54:06 -0300272 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300273 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300274 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300275 else {
276 kfree(t->priv);
277 t->priv = NULL;
278 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300279
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 switch (t->type) {
281 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300282 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 break;
284 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300285 {
286 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300288 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700289 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300290 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700291 t->type = TUNER_ABSENT;
292 t->mode_mask = T_UNINITIALIZED;
293 return;
294 }
295 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700296 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300297 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300298 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300299 t->type = TUNER_ABSENT;
300 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300301 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300302 }
303 t->mode_mask = T_RADIO;
304 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700305 case TUNER_PHILIPS_FMD1216ME_MK3:
306 buffer[0] = 0x0b;
307 buffer[1] = 0xdc;
308 buffer[2] = 0x9c;
309 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700310 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700311 mdelay(1);
312 buffer[2] = 0x86;
313 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700314 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300315 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700316 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800317 case TUNER_PHILIPS_TD1316:
318 buffer[0] = 0x0b;
319 buffer[1] = 0xdc;
320 buffer[2] = 0x86;
321 buffer[3] = 0xa4;
322 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300323 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200324 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300325 case TUNER_XC2028:
326 xc2028_tuner_init(c);
327 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300328 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300329 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300330 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300332 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 break;
334 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700335
Michael Krufkye18f9442007-08-21 01:25:48 -0300336 if (fe_tuner_ops->set_analog_params) {
337 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
338
339 t->ops.set_tv_freq = fe_set_freq;
340 t->ops.set_radio_freq = fe_set_freq;
341 t->ops.standby = fe_standby;
342 t->ops.release = fe_release;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300343 t->ops.has_signal = fe_has_signal;
Michael Krufkye18f9442007-08-21 01:25:48 -0300344 }
345
346 tuner_info("type set to %s\n", t->i2c.name);
347
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700348 if (t->mode_mask == T_UNINITIALIZED)
349 t->mode_mask = new_mode_mask;
350
Hans Verkuil27487d42006-01-15 15:04:52 -0200351 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700352 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100353 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700354 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300355 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356}
357
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700358/*
359 * This function apply tuner config to tuner specified
360 * by tun_setup structure. I addr is unset, then admin status
361 * and tun addr status is more precise then current status,
362 * it's applied. Otherwise status and type are applied only to
363 * tuner with exactly the same addr.
364*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700365
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700366static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700367{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700368 struct tuner *t = i2c_get_clientdata(c);
369
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300370 tuner_dbg("set addr for type %i\n", t->type);
371
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300372 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
373 (t->mode_mask & tun_setup->mode_mask))) ||
374 (tun_setup->addr == c->addr)) {
375 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300376 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700377 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700378}
379
380static inline int check_mode(struct tuner *t, char *cmd)
381{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700382 if ((1 << t->mode & t->mode_mask) == 0) {
383 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700384 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700385
386 switch (t->mode) {
387 case V4L2_TUNER_RADIO:
388 tuner_dbg("Cmd %s accepted for radio\n", cmd);
389 break;
390 case V4L2_TUNER_ANALOG_TV:
391 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
392 break;
393 case V4L2_TUNER_DIGITAL_TV:
394 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
395 break;
396 }
397 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700398}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700399
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700400/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401static int tuner_fixup_std(struct tuner *t)
402{
403 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300405 case '6':
406 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
407 t->std = V4L2_STD_PAL_60;
408 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 case 'b':
410 case 'B':
411 case 'g':
412 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700413 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 t->std = V4L2_STD_PAL_BG;
415 break;
416 case 'i':
417 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700418 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 t->std = V4L2_STD_PAL_I;
420 break;
421 case 'd':
422 case 'D':
423 case 'k':
424 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700425 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 t->std = V4L2_STD_PAL_DK;
427 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700428 case 'M':
429 case 'm':
430 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
431 t->std = V4L2_STD_PAL_M;
432 break;
433 case 'N':
434 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200435 if (pal[1] == 'c' || pal[1] == 'C') {
436 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
437 t->std = V4L2_STD_PAL_Nc;
438 } else {
439 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
440 t->std = V4L2_STD_PAL_N;
441 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700442 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700443 case '-':
444 /* default parameter, do nothing */
445 break;
446 default:
447 tuner_warn ("pal= argument not recognised\n");
448 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
450 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700451 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
452 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200453 case 'b':
454 case 'B':
455 case 'g':
456 case 'G':
457 case 'h':
458 case 'H':
459 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
460 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
461 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700462 case 'd':
463 case 'D':
464 case 'k':
465 case 'K':
466 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
467 t->std = V4L2_STD_SECAM_DK;
468 break;
469 case 'l':
470 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800471 if ((secam[1]=='C')||(secam[1]=='c')) {
472 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
473 t->std = V4L2_STD_SECAM_LC;
474 } else {
475 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
476 t->std = V4L2_STD_SECAM_L;
477 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700478 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700479 case '-':
480 /* default parameter, do nothing */
481 break;
482 default:
483 tuner_warn ("secam= argument not recognised\n");
484 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700485 }
486 }
487
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200488 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
489 switch (ntsc[0]) {
490 case 'm':
491 case 'M':
492 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
493 t->std = V4L2_STD_NTSC_M;
494 break;
495 case 'j':
496 case 'J':
497 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
498 t->std = V4L2_STD_NTSC_M_JP;
499 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200500 case 'k':
501 case 'K':
502 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
503 t->std = V4L2_STD_NTSC_M_KR;
504 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200505 case '-':
506 /* default parameter, do nothing */
507 break;
508 default:
509 tuner_info("ntsc= argument not recognised\n");
510 break;
511 }
512 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 return 0;
514}
515
Michael Krufkydb8a6952007-08-21 01:24:42 -0300516static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200517{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200518 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300519 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200520 const char *p;
521
522 switch (t->mode) {
523 case V4L2_TUNER_RADIO: p = "radio"; break;
524 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
525 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
526 default: p = "undefined"; break;
527 }
528 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200529 freq = t->radio_freq / 16000;
530 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200531 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200532 freq = t->tv_freq / 16;
533 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200534 }
535 tuner_info("Tuner mode: %s\n", p);
536 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300537 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200538 if (t->mode != V4L2_TUNER_RADIO)
539 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300540 if (fe_tuner_ops->get_status) {
541 u32 tuner_status;
542
543 fe_tuner_ops->get_status(&t->fe, &tuner_status);
544 if (tuner_status & TUNER_STATUS_LOCKED)
545 tuner_info("Tuner is locked.\n");
546 if (tuner_status & TUNER_STATUS_STEREO)
547 tuner_info("Stereo: yes\n");
548 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300549 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300550 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200551 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300552 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300553 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200554 }
555}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557/* ---------------------------------------------------------------------- */
558
Hans Verkuilba8fc392006-06-25 15:34:39 -0300559/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700560static unsigned default_mode_mask;
561
562/* During client attach, set_type is called by adapter's attach_inform callback.
563 set_type must then be completed by tuner_attach.
564 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
566{
567 struct tuner *t;
568
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700569 client_template.adapter = adap;
570 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Panagiotis Issaris74081872006-01-11 19:40:56 -0200572 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700573 if (NULL == t)
574 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700575 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700577 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700578 t->audmode = V4L2_TUNER_MODE_STEREO;
579 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300580 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700582 if (show_i2c) {
583 unsigned char buffer[16];
584 int i,rc;
585
586 memset(buffer, 0, sizeof(buffer));
587 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800588 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700589 for (i=0;i<rc;i++)
590 printk("%02x ",buffer[i]);
591 printk("\n");
592 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300593 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
594 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
595 return -ENODEV;
596
Markus Rechberger257c6452006-01-23 17:11:11 -0200597 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700598 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800599 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300600 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300601 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300602 t->type = TUNER_TEA5761;
603 t->mode_mask = T_RADIO;
604 t->mode = T_STANDBY;
605 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
606 default_mode_mask &= ~T_RADIO;
607
608 goto register_client;
609 }
610 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800611 case 0x42:
612 case 0x43:
613 case 0x4a:
614 case 0x4b:
615 /* If chip is not tda8290, don't register.
616 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300617 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300618 tuner_dbg("chip at addr %x is a tda8290\n", addr);
619 } else {
620 /* Default is being tda9887 */
621 t->type = TUNER_TDA9887;
622 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
623 t->mode = T_STANDBY;
624 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800625 }
626 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800627 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300628 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700629 t->type = TUNER_TEA5767;
630 t->mode_mask = T_RADIO;
631 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200632 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700633 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700634
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800635 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700636 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800637 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700638 }
639 }
640
641 /* Initializes only the first adapter found */
642 if (default_mode_mask != T_UNINITIALIZED) {
643 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
644 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200645 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
646 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700647 default_mode_mask = T_UNINITIALIZED;
648 }
649
650 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800651register_client:
652 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700653 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300654 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 return 0;
656}
657
658static int tuner_probe(struct i2c_adapter *adap)
659{
660 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700661 normal_i2c[0] = addr;
662 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Michael Krufkya1dec512007-08-24 01:13:07 -0300665 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
666 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
667 * and an RTC at 0x6f which can get corrupted if probed.
668 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300669 if ((adap->id == I2C_HW_B_CX2388x) ||
670 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300671 unsigned int i = 0;
672
673 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
674 i += 2;
675 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
676 ignore[i+0] = adap->nr;
677 ignore[i+1] = 0x6b;
678 ignore[i+2] = adap->nr;
679 ignore[i+3] = 0x6f;
680 ignore[i+4] = I2C_CLIENT_END;
681 } else
682 printk(KERN_WARNING "tuner: "
683 "too many options specified "
684 "in i2c probe ignore list!\n");
685 }
686
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700687 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700688
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 if (adap->class & I2C_CLASS_TV_ANALOG)
690 return i2c_probe(adap, &addr_data, tuner_attach);
691 return 0;
692}
693
694static int tuner_detach(struct i2c_client *client)
695{
696 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700697 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700699 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700700 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700701 tuner_warn
702 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700703 return err;
704 }
705
Michael Krufky7a91a802007-06-06 16:10:39 -0300706 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300707 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300708 else {
709 kfree(t->priv);
710 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 kfree(t);
712 return 0;
713}
714
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700715/*
716 * Switch tuner to other mode. If tuner support both tv and radio,
717 * set another frequency to some value (This is needed for some pal
718 * tuners to avoid locking). Otherwise, just put second tuner in
719 * standby mode.
720 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700722static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
723{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800724 if (mode == t->mode)
725 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700726
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800727 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700728
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800729 if (check_mode(t, cmd) == EINVAL) {
730 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300731 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300732 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800733 return EINVAL;
734 }
735 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700736}
737
738#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800739 tuner_dbg("switching to v4l2\n"); \
740 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700741
742static inline int check_v4l2(struct tuner *t)
743{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300744 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
745 TV, v4l1 for radio), until that is fixed this code is disabled.
746 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
747 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700748 return 0;
749}
750
751static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752{
753 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300754 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Hans Verkuilf9195de2006-01-11 19:01:01 -0200756 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200757 v4l_i2c_print_ioctl(&(t->i2c),cmd);
758
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700759 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700761 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300762 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 -0700763 ((struct tuner_setup *)arg)->type,
764 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300765 ((struct tuner_setup *)arg)->mode_mask,
766 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700767
768 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700769 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200771 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
772 == EINVAL)
773 return 0;
774 if (t->radio_freq)
775 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700777 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200778 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
779 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300780 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300781 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300782 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200783 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300784#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700785 case VIDIOCSAUDIO:
786 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
787 return 0;
788 if (check_v4l2(t) == EINVAL)
789 return 0;
790
791 /* Should be implemented, since bttv calls it */
792 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700793 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700795 {
796 static const v4l2_std_id map[] = {
797 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
798 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
799 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
800 [4 /* bttv */ ] = V4L2_STD_PAL_M,
801 [5 /* bttv */ ] = V4L2_STD_PAL_N,
802 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
803 };
804 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700806 if (check_v4l2(t) == EINVAL)
807 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700808
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700809 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
810 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700812 if (vc->norm < ARRAY_SIZE(map))
813 t->std = map[vc->norm];
814 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200815 if (t->tv_freq)
816 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700817 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700818 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700819 case VIDIOCSFREQ:
820 {
821 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700822
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700823 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
824 return 0;
825 if (check_v4l2(t) == EINVAL)
826 return 0;
827
828 set_freq(client, *v);
829 return 0;
830 }
831 case VIDIOCGTUNER:
832 {
833 struct video_tuner *vt = arg;
834
835 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
836 return 0;
837 if (check_v4l2(t) == EINVAL)
838 return 0;
839
840 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300841 if (fe_tuner_ops->get_status) {
842 u32 tuner_status;
843
844 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300845 if (tuner_status & TUNER_STATUS_STEREO)
846 vt->flags |= VIDEO_TUNER_STEREO_ON;
847 else
848 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300849 } else {
850 if (t->ops.is_stereo) {
851 if (t->ops.is_stereo(t))
852 vt->flags |=
853 VIDEO_TUNER_STEREO_ON;
854 else
855 vt->flags &=
856 ~VIDEO_TUNER_STEREO_ON;
857 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700858 }
Michael Krufky1f5ef192007-08-31 17:38:02 -0300859 if (t->ops.has_signal)
860 vt->signal = t->ops.has_signal(t);
861
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700862 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
863
864 vt->rangelow = radio_range[0] * 16000;
865 vt->rangehigh = radio_range[1] * 16000;
866
867 } else {
868 vt->rangelow = tv_range[0] * 16;
869 vt->rangehigh = tv_range[1] * 16;
870 }
871
872 return 0;
873 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700875 {
876 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700878 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
879 return 0;
880 if (check_v4l2(t) == EINVAL)
881 return 0;
882
Michael Krufkye18f9442007-08-21 01:25:48 -0300883 if (V4L2_TUNER_RADIO == t->mode) {
884 if (fe_tuner_ops->get_status) {
885 u32 tuner_status;
886
887 fe_tuner_ops->get_status(&t->fe, &tuner_status);
888 va->mode = (tuner_status & TUNER_STATUS_STEREO)
889 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
890 } else if (t->ops.is_stereo)
891 va->mode = t->ops.is_stereo(t)
892 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
893 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700894 return 0;
895 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300896#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300897 case TUNER_SET_CONFIG:
898 {
899 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
900 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300902 if (t->type != cfg->tuner)
903 break;
904
905 if (t->type == TUNER_TDA9887) {
906 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300907 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300908 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300909 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300910
911 if (NULL == fe_tuner_ops->set_config) {
912 tuner_warn("Tuner frontend module has no way to "
913 "set config\n");
914 break;
915 }
916 fe_tuner_ops->set_config(&t->fe, cfg->priv);
917
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300918 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300919 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300920 /* --- v4l ioctls --- */
921 /* take care: bttv does userspace copying, we'll get a
922 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700924 {
925 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700927 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
928 == EINVAL)
929 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700930
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700931 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700933 t->std = *id;
934 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200935 if (t->tv_freq)
936 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700937 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700938 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700939 case VIDIOC_S_FREQUENCY:
940 {
941 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700942
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300943 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
944 == EINVAL)
945 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700946 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200947 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700948
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700949 break;
950 }
951 case VIDIOC_G_FREQUENCY:
952 {
953 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700954
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700955 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
956 return 0;
957 switch_v4l2();
958 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300959 if (fe_tuner_ops->get_frequency) {
960 u32 abs_freq;
961
962 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
963 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
964 (abs_freq * 2 + 125/2) / 125 :
965 (abs_freq + 62500/2) / 62500;
966 break;
967 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200968 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
969 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700970 break;
971 }
972 case VIDIOC_G_TUNER:
973 {
974 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700975
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700976 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
977 return 0;
978 switch_v4l2();
979
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200980 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300981 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300982 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300983 if (t->mode == V4L2_TUNER_ANALOG_TV)
984 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200985 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700986 tuner->rangelow = tv_range[0] * 16;
987 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200988 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700989 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200990
991 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200992 tuner->rxsubchans =
993 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300994 if (fe_tuner_ops->get_status) {
995 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200996
Michael Krufkye18f9442007-08-21 01:25:48 -0300997 fe_tuner_ops->get_status(&t->fe, &tuner_status);
998 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
999 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001000 } else {
1001 if (t->ops.is_stereo) {
1002 tuner->rxsubchans = t->ops.is_stereo(t) ?
1003 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
1004 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001005 }
Michael Krufky1f5ef192007-08-31 17:38:02 -03001006 if (t->ops.has_signal)
1007 tuner->signal = t->ops.has_signal(t);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001008 tuner->capability |=
1009 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1010 tuner->audmode = t->audmode;
1011 tuner->rangelow = radio_range[0] * 16000;
1012 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001013 break;
1014 }
1015 case VIDIOC_S_TUNER:
1016 {
1017 struct v4l2_tuner *tuner = arg;
1018
1019 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1020 return 0;
1021
1022 switch_v4l2();
1023
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001024 /* do nothing unless we're a radio tuner */
1025 if (t->mode != V4L2_TUNER_RADIO)
1026 break;
1027 t->audmode = tuner->audmode;
1028 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001029 break;
1030 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001031 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -03001032 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -03001033 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001034 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 }
1036
1037 return 0;
1038}
1039
Jean Delvare21b48a72007-03-12 19:20:15 -03001040static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001042 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001044 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 /* FIXME: power down ??? */
1046 return 0;
1047}
1048
Jean Delvare21b48a72007-03-12 19:20:15 -03001049static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001051 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001053 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001054 if (V4L2_TUNER_RADIO == t->mode) {
1055 if (t->radio_freq)
1056 set_freq(c, t->radio_freq);
1057 } else {
1058 if (t->tv_freq)
1059 set_freq(c, t->tv_freq);
1060 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 return 0;
1062}
1063
1064/* ----------------------------------------------------------------------- */
1065
1066static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001067 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001068 .attach_adapter = tuner_probe,
1069 .detach_client = tuner_detach,
1070 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001071 .suspend = tuner_suspend,
1072 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001074 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001075 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001077static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001078 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001079 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080};
1081
1082static int __init tuner_init_module(void)
1083{
1084 return i2c_add_driver(&driver);
1085}
1086
1087static void __exit tuner_cleanup_module(void)
1088{
1089 i2c_del_driver(&driver);
1090}
1091
1092module_init(tuner_init_module);
1093module_exit(tuner_cleanup_module);
1094
1095/*
1096 * Overrides for Emacs so that we follow Linus's tabbing style.
1097 * ---------------------------------------------------------------------------
1098 * Local variables:
1099 * c-basic-offset: 8
1100 * End:
1101 */