blob: 6a777604f0708d36570a74fd96c704ec71fa3733 [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 Bunk9ee476a2007-06-05 05:22:00 -030033#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030034 0x10,
35#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080036 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070037 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
38 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 I2C_CLIENT_END
40};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070041
Linus Torvalds1da177e2005-04-16 15:20:36 -070042I2C_CLIENT_INSMOD;
43
44/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070045static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070046static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070047static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070048
Linus Torvalds1da177e2005-04-16 15:20:36 -070049/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020050int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070052static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070053static unsigned int radio_range[2] = { 65, 108 };
54
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020055static char pal[] = "--";
56static char secam[] = "--";
57static char ntsc[] = "-";
58
Hans Verkuilf9195de2006-01-11 19:01:01 -020059
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020060module_param(addr, int, 0444);
61module_param(no_autodetect, int, 0444);
62module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020063module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020064module_param_string(pal, pal, sizeof(pal), 0644);
65module_param_string(secam, secam, sizeof(secam), 0644);
66module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070067module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068module_param_array(radio_range, int, NULL, 0644);
69
70MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
71MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
72MODULE_LICENSE("GPL");
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074static struct i2c_driver driver;
75static struct i2c_client client_template;
76
77/* ---------------------------------------------------------------------- */
78
Michael Krufkye18f9442007-08-21 01:25:48 -030079static void fe_set_freq(struct tuner *t, unsigned int freq)
80{
81 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
82
83 struct analog_parameters params = {
84 .frequency = freq,
85 .mode = t->mode,
86 .audmode = t->audmode,
87 .std = t->std
88 };
89
90 if (NULL == fe_tuner_ops->set_analog_params) {
91 tuner_warn("Tuner frontend module has no way to set freq\n");
92 return;
93 }
94 fe_tuner_ops->set_analog_params(&t->fe, &params);
95}
96
97static void fe_release(struct tuner *t)
98{
99 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
100
101 if (fe_tuner_ops->release)
102 fe_tuner_ops->release(&t->fe);
103}
104
105static void fe_standby(struct tuner *t)
106{
107 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
108
109 if (fe_tuner_ops->sleep)
110 fe_tuner_ops->sleep(&t->fe);
111}
112
Michael Krufky1f5ef192007-08-31 17:38:02 -0300113static int fe_has_signal(struct tuner *t)
114{
115 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky14196832007-10-14 18:11:53 -0300116 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300117
118 if (fe_tuner_ops->get_rf_strength)
119 fe_tuner_ops->get_rf_strength(&t->fe, &strength);
120
121 return strength;
122}
123
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700124/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125static void set_tv_freq(struct i2c_client *c, unsigned int freq)
126{
127 struct tuner *t = i2c_get_clientdata(c);
128
129 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700130 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 return;
132 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300133 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700134 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 return;
136 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700137 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
138 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
139 freq / 16, freq % 16 * 100 / 16, tv_range[0],
140 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200141 /* V4L2 spec: if the freq is not possible then the closest
142 possible value should be selected */
143 if (freq < tv_range[0] * 16)
144 freq = tv_range[0] * 16;
145 else
146 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300148 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149}
150
151static void set_radio_freq(struct i2c_client *c, unsigned int freq)
152{
153 struct tuner *t = i2c_get_clientdata(c);
154
155 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700156 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 return;
158 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300159 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700160 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 return;
162 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200163 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700164 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
165 freq / 16000, freq % 16000 * 100 / 16000,
166 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200167 /* V4L2 spec: if the freq is not possible then the closest
168 possible value should be selected */
169 if (freq < radio_range[0] * 16000)
170 freq = radio_range[0] * 16000;
171 else
172 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700174
Michael Krufkydb8a6952007-08-21 01:24:42 -0300175 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176}
177
178static void set_freq(struct i2c_client *c, unsigned long freq)
179{
180 struct tuner *t = i2c_get_clientdata(c);
181
182 switch (t->mode) {
183 case V4L2_TUNER_RADIO:
184 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700185 freq / 16000, freq % 16000 * 100 / 16000);
186 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200187 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 break;
189 case V4L2_TUNER_ANALOG_TV:
190 case V4L2_TUNER_DIGITAL_TV:
191 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700192 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200194 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 break;
196 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197}
198
Michael Krufky293197c2007-08-28 17:20:42 -0300199static void tuner_i2c_address_check(struct tuner *t)
200{
201 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
202 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
203 return;
204
205 tuner_warn("====================== WARNING! ======================\n");
206 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
207 tuner_warn("will soon be dropped. This message indicates that your\n");
208 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
209 t->i2c.name, t->i2c.addr);
210 tuner_warn("To ensure continued support for your device, please\n");
211 tuner_warn("send a copy of this message, along with full dmesg\n");
212 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
213 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
214 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
215 t->i2c.adapter->name, t->i2c.addr, t->type,
216 tuners[t->type].name);
217 tuner_warn("====================== WARNING! ======================\n");
218}
219
Michael Krufky910bb3e2007-08-27 21:22:20 -0300220static void attach_tda8290(struct tuner *t)
221{
222 struct tda8290_config cfg = {
223 .lna_cfg = &t->config,
224 .tuner_callback = t->tuner_callback
225 };
226 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
227}
228
Michael Krufky4adad282007-08-27 21:59:08 -0300229static void attach_simple_tuner(struct tuner *t)
230{
231 struct simple_tuner_config cfg = {
232 .type = t->type,
233 .tun = &tuners[t->type]
234 };
235 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
236}
237
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700238static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300239 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300240 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241{
242 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300243 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700244 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700246 if (type == UNSET || type == TUNER_ABSENT) {
247 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 return;
249 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700251 if (type >= tuner_count) {
252 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
253 return;
254 }
255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300257 t->config = new_config;
258 if (tuner_callback != NULL) {
259 tuner_dbg("defining GPIO callback\n");
260 t->tuner_callback = tuner_callback;
261 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300262
263 /* This code detects calls by card attach_inform */
264 if (NULL == t->i2c.dev.driver) {
265 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
266
267 return;
268 }
269
Michael Krufkyb2083192007-05-29 22:54:06 -0300270 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300271 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300272 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300273 else {
274 kfree(t->priv);
275 t->priv = NULL;
276 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300277
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 switch (t->type) {
279 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300280 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 break;
282 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300283 {
284 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300286 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700287 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300288 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700289 t->type = TUNER_ABSENT;
290 t->mode_mask = T_UNINITIALIZED;
291 return;
292 }
293 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700294 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300295#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300296 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300297 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300298 t->type = TUNER_ABSENT;
299 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300300 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300301 }
302 t->mode_mask = T_RADIO;
303 break;
304#endif
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 Chehab15396232006-06-23 16:13:56 -0300325 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300326 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300327 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300329 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 break;
331 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700332
Michael Krufkye18f9442007-08-21 01:25:48 -0300333 if (fe_tuner_ops->set_analog_params) {
334 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
335
336 t->ops.set_tv_freq = fe_set_freq;
337 t->ops.set_radio_freq = fe_set_freq;
338 t->ops.standby = fe_standby;
339 t->ops.release = fe_release;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300340 t->ops.has_signal = fe_has_signal;
Michael Krufkye18f9442007-08-21 01:25:48 -0300341 }
342
343 tuner_info("type set to %s\n", t->i2c.name);
344
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700345 if (t->mode_mask == T_UNINITIALIZED)
346 t->mode_mask = new_mode_mask;
347
Hans Verkuil27487d42006-01-15 15:04:52 -0200348 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700349 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100350 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700351 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300352 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353}
354
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700355/*
356 * This function apply tuner config to tuner specified
357 * by tun_setup structure. I addr is unset, then admin status
358 * and tun addr status is more precise then current status,
359 * it's applied. Otherwise status and type are applied only to
360 * tuner with exactly the same addr.
361*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700362
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700363static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700364{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700365 struct tuner *t = i2c_get_clientdata(c);
366
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300367 tuner_dbg("set addr for type %i\n", t->type);
368
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300369 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
370 (t->mode_mask & tun_setup->mode_mask))) ||
371 (tun_setup->addr == c->addr)) {
372 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300373 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700374 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700375}
376
377static inline int check_mode(struct tuner *t, char *cmd)
378{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700379 if ((1 << t->mode & t->mode_mask) == 0) {
380 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700381 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700382
383 switch (t->mode) {
384 case V4L2_TUNER_RADIO:
385 tuner_dbg("Cmd %s accepted for radio\n", cmd);
386 break;
387 case V4L2_TUNER_ANALOG_TV:
388 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
389 break;
390 case V4L2_TUNER_DIGITAL_TV:
391 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
392 break;
393 }
394 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700395}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700396
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700397/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398static int tuner_fixup_std(struct tuner *t)
399{
400 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300402 case '6':
403 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
404 t->std = V4L2_STD_PAL_60;
405 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 case 'b':
407 case 'B':
408 case 'g':
409 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700410 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 t->std = V4L2_STD_PAL_BG;
412 break;
413 case 'i':
414 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700415 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 t->std = V4L2_STD_PAL_I;
417 break;
418 case 'd':
419 case 'D':
420 case 'k':
421 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700422 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 t->std = V4L2_STD_PAL_DK;
424 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700425 case 'M':
426 case 'm':
427 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
428 t->std = V4L2_STD_PAL_M;
429 break;
430 case 'N':
431 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200432 if (pal[1] == 'c' || pal[1] == 'C') {
433 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
434 t->std = V4L2_STD_PAL_Nc;
435 } else {
436 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
437 t->std = V4L2_STD_PAL_N;
438 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700439 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700440 case '-':
441 /* default parameter, do nothing */
442 break;
443 default:
444 tuner_warn ("pal= argument not recognised\n");
445 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 }
447 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700448 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
449 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200450 case 'b':
451 case 'B':
452 case 'g':
453 case 'G':
454 case 'h':
455 case 'H':
456 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
457 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
458 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700459 case 'd':
460 case 'D':
461 case 'k':
462 case 'K':
463 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
464 t->std = V4L2_STD_SECAM_DK;
465 break;
466 case 'l':
467 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800468 if ((secam[1]=='C')||(secam[1]=='c')) {
469 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
470 t->std = V4L2_STD_SECAM_LC;
471 } else {
472 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
473 t->std = V4L2_STD_SECAM_L;
474 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700475 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700476 case '-':
477 /* default parameter, do nothing */
478 break;
479 default:
480 tuner_warn ("secam= argument not recognised\n");
481 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700482 }
483 }
484
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200485 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
486 switch (ntsc[0]) {
487 case 'm':
488 case 'M':
489 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
490 t->std = V4L2_STD_NTSC_M;
491 break;
492 case 'j':
493 case 'J':
494 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
495 t->std = V4L2_STD_NTSC_M_JP;
496 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200497 case 'k':
498 case 'K':
499 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
500 t->std = V4L2_STD_NTSC_M_KR;
501 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200502 case '-':
503 /* default parameter, do nothing */
504 break;
505 default:
506 tuner_info("ntsc= argument not recognised\n");
507 break;
508 }
509 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 return 0;
511}
512
Michael Krufkydb8a6952007-08-21 01:24:42 -0300513static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200514{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200515 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300516 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200517 const char *p;
518
519 switch (t->mode) {
520 case V4L2_TUNER_RADIO: p = "radio"; break;
521 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
522 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
523 default: p = "undefined"; break;
524 }
525 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200526 freq = t->radio_freq / 16000;
527 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200528 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200529 freq = t->tv_freq / 16;
530 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200531 }
532 tuner_info("Tuner mode: %s\n", p);
533 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300534 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200535 if (t->mode != V4L2_TUNER_RADIO)
536 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300537 if (fe_tuner_ops->get_status) {
538 u32 tuner_status;
539
540 fe_tuner_ops->get_status(&t->fe, &tuner_status);
541 if (tuner_status & TUNER_STATUS_LOCKED)
542 tuner_info("Tuner is locked.\n");
543 if (tuner_status & TUNER_STATUS_STEREO)
544 tuner_info("Stereo: yes\n");
545 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300546 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300547 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200548 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300549 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300550 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200551 }
552}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554/* ---------------------------------------------------------------------- */
555
Hans Verkuilba8fc392006-06-25 15:34:39 -0300556/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700557static unsigned default_mode_mask;
558
559/* During client attach, set_type is called by adapter's attach_inform callback.
560 set_type must then be completed by tuner_attach.
561 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
563{
564 struct tuner *t;
565
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700566 client_template.adapter = adap;
567 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Panagiotis Issaris74081872006-01-11 19:40:56 -0200569 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700570 if (NULL == t)
571 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700572 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700574 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700575 t->audmode = V4L2_TUNER_MODE_STEREO;
576 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300577 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700579 if (show_i2c) {
580 unsigned char buffer[16];
581 int i,rc;
582
583 memset(buffer, 0, sizeof(buffer));
584 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800585 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700586 for (i=0;i<rc;i++)
587 printk("%02x ",buffer[i]);
588 printk("\n");
589 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300590 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
591 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
592 return -ENODEV;
593
Markus Rechberger257c6452006-01-23 17:11:11 -0200594 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700595 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800596 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300597#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300598 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300599 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300600 t->type = TUNER_TEA5761;
601 t->mode_mask = T_RADIO;
602 t->mode = T_STANDBY;
603 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
604 default_mode_mask &= ~T_RADIO;
605
606 goto register_client;
607 }
608 break;
609#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800610 case 0x42:
611 case 0x43:
612 case 0x4a:
613 case 0x4b:
614 /* If chip is not tda8290, don't register.
615 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300616 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300617 tuner_dbg("chip at addr %x is a tda8290\n", addr);
618 } else {
619 /* Default is being tda9887 */
620 t->type = TUNER_TDA9887;
621 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
622 t->mode = T_STANDBY;
623 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800624 }
625 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800626 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300627 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700628 t->type = TUNER_TEA5767;
629 t->mode_mask = T_RADIO;
630 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200631 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700632 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700633
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800634 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700635 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800636 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700637 }
638 }
639
640 /* Initializes only the first adapter found */
641 if (default_mode_mask != T_UNINITIALIZED) {
642 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
643 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200644 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
645 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700646 default_mode_mask = T_UNINITIALIZED;
647 }
648
649 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800650register_client:
651 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700652 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300653 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 return 0;
655}
656
657static int tuner_probe(struct i2c_adapter *adap)
658{
659 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700660 normal_i2c[0] = addr;
661 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Michael Krufkya1dec512007-08-24 01:13:07 -0300664 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
665 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
666 * and an RTC at 0x6f which can get corrupted if probed.
667 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300668 if ((adap->id == I2C_HW_B_CX2388x) ||
669 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300670 unsigned int i = 0;
671
672 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
673 i += 2;
674 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
675 ignore[i+0] = adap->nr;
676 ignore[i+1] = 0x6b;
677 ignore[i+2] = adap->nr;
678 ignore[i+3] = 0x6f;
679 ignore[i+4] = I2C_CLIENT_END;
680 } else
681 printk(KERN_WARNING "tuner: "
682 "too many options specified "
683 "in i2c probe ignore list!\n");
684 }
685
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700686 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700687
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 if (adap->class & I2C_CLASS_TV_ANALOG)
689 return i2c_probe(adap, &addr_data, tuner_attach);
690 return 0;
691}
692
693static int tuner_detach(struct i2c_client *client)
694{
695 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700696 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700698 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700699 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700700 tuner_warn
701 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700702 return err;
703 }
704
Michael Krufky7a91a802007-06-06 16:10:39 -0300705 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300706 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300707 else {
708 kfree(t->priv);
709 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 kfree(t);
711 return 0;
712}
713
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700714/*
715 * Switch tuner to other mode. If tuner support both tv and radio,
716 * set another frequency to some value (This is needed for some pal
717 * tuners to avoid locking). Otherwise, just put second tuner in
718 * standby mode.
719 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700721static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
722{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800723 if (mode == t->mode)
724 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700725
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800726 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700727
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800728 if (check_mode(t, cmd) == EINVAL) {
729 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300730 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300731 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800732 return EINVAL;
733 }
734 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700735}
736
737#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800738 tuner_dbg("switching to v4l2\n"); \
739 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700740
741static inline int check_v4l2(struct tuner *t)
742{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300743 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
744 TV, v4l1 for radio), until that is fixed this code is disabled.
745 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
746 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700747 return 0;
748}
749
750static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751{
752 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300753 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Hans Verkuilf9195de2006-01-11 19:01:01 -0200755 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200756 v4l_i2c_print_ioctl(&(t->i2c),cmd);
757
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700760 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300761 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 -0700762 ((struct tuner_setup *)arg)->type,
763 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300764 ((struct tuner_setup *)arg)->mode_mask,
765 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700766
767 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700768 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200770 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
771 == EINVAL)
772 return 0;
773 if (t->radio_freq)
774 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700776 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200777 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
778 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300779 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300780 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300781 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200782 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300783#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700784 case VIDIOCSAUDIO:
785 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
786 return 0;
787 if (check_v4l2(t) == EINVAL)
788 return 0;
789
790 /* Should be implemented, since bttv calls it */
791 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700792 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700794 {
795 static const v4l2_std_id map[] = {
796 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
797 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
798 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
799 [4 /* bttv */ ] = V4L2_STD_PAL_M,
800 [5 /* bttv */ ] = V4L2_STD_PAL_N,
801 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
802 };
803 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700805 if (check_v4l2(t) == EINVAL)
806 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700807
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700808 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
809 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700811 if (vc->norm < ARRAY_SIZE(map))
812 t->std = map[vc->norm];
813 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200814 if (t->tv_freq)
815 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700816 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700817 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700818 case VIDIOCSFREQ:
819 {
820 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700821
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700822 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
823 return 0;
824 if (check_v4l2(t) == EINVAL)
825 return 0;
826
827 set_freq(client, *v);
828 return 0;
829 }
830 case VIDIOCGTUNER:
831 {
832 struct video_tuner *vt = arg;
833
834 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
835 return 0;
836 if (check_v4l2(t) == EINVAL)
837 return 0;
838
839 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300840 if (fe_tuner_ops->get_status) {
841 u32 tuner_status;
842
843 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300844 if (tuner_status & TUNER_STATUS_STEREO)
845 vt->flags |= VIDEO_TUNER_STEREO_ON;
846 else
847 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300848 } else {
849 if (t->ops.is_stereo) {
850 if (t->ops.is_stereo(t))
851 vt->flags |=
852 VIDEO_TUNER_STEREO_ON;
853 else
854 vt->flags &=
855 ~VIDEO_TUNER_STEREO_ON;
856 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700857 }
Michael Krufky1f5ef192007-08-31 17:38:02 -0300858 if (t->ops.has_signal)
859 vt->signal = t->ops.has_signal(t);
860
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700861 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
862
863 vt->rangelow = radio_range[0] * 16000;
864 vt->rangehigh = radio_range[1] * 16000;
865
866 } else {
867 vt->rangelow = tv_range[0] * 16;
868 vt->rangehigh = tv_range[1] * 16;
869 }
870
871 return 0;
872 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700874 {
875 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700877 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
878 return 0;
879 if (check_v4l2(t) == EINVAL)
880 return 0;
881
Michael Krufkye18f9442007-08-21 01:25:48 -0300882 if (V4L2_TUNER_RADIO == t->mode) {
883 if (fe_tuner_ops->get_status) {
884 u32 tuner_status;
885
886 fe_tuner_ops->get_status(&t->fe, &tuner_status);
887 va->mode = (tuner_status & TUNER_STATUS_STEREO)
888 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
889 } else if (t->ops.is_stereo)
890 va->mode = t->ops.is_stereo(t)
891 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
892 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700893 return 0;
894 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300895#endif
896 case TDA9887_SET_CONFIG:
897 if (t->type == TUNER_TDA9887) {
898 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300900 t->tda9887_config = *i;
901 set_freq(client, t->tv_freq);
902 }
903 break;
904 /* --- v4l ioctls --- */
905 /* take care: bttv does userspace copying, we'll get a
906 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700908 {
909 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700911 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
912 == EINVAL)
913 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700914
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700915 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700917 t->std = *id;
918 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200919 if (t->tv_freq)
920 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700921 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700922 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700923 case VIDIOC_S_FREQUENCY:
924 {
925 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700926
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300927 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
928 == EINVAL)
929 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700930 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200931 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700932
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700933 break;
934 }
935 case VIDIOC_G_FREQUENCY:
936 {
937 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700938
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700939 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
940 return 0;
941 switch_v4l2();
942 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300943 if (fe_tuner_ops->get_frequency) {
944 u32 abs_freq;
945
946 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
947 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
948 (abs_freq * 2 + 125/2) / 125 :
949 (abs_freq + 62500/2) / 62500;
950 break;
951 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200952 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
953 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700954 break;
955 }
956 case VIDIOC_G_TUNER:
957 {
958 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700959
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700960 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
961 return 0;
962 switch_v4l2();
963
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200964 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300965 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300966 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300967 if (t->mode == V4L2_TUNER_ANALOG_TV)
968 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200969 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700970 tuner->rangelow = tv_range[0] * 16;
971 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200972 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700973 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200974
975 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200976 tuner->rxsubchans =
977 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300978 if (fe_tuner_ops->get_status) {
979 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200980
Michael Krufkye18f9442007-08-21 01:25:48 -0300981 fe_tuner_ops->get_status(&t->fe, &tuner_status);
982 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
983 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300984 } else {
985 if (t->ops.is_stereo) {
986 tuner->rxsubchans = t->ops.is_stereo(t) ?
987 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
988 }
Michael Krufkye18f9442007-08-21 01:25:48 -0300989 }
Michael Krufky1f5ef192007-08-31 17:38:02 -0300990 if (t->ops.has_signal)
991 tuner->signal = t->ops.has_signal(t);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200992 tuner->capability |=
993 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
994 tuner->audmode = t->audmode;
995 tuner->rangelow = radio_range[0] * 16000;
996 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700997 break;
998 }
999 case VIDIOC_S_TUNER:
1000 {
1001 struct v4l2_tuner *tuner = arg;
1002
1003 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1004 return 0;
1005
1006 switch_v4l2();
1007
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001008 /* do nothing unless we're a radio tuner */
1009 if (t->mode != V4L2_TUNER_RADIO)
1010 break;
1011 t->audmode = tuner->audmode;
1012 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001013 break;
1014 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001015 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -03001016 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -03001017 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001018 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 }
1020
1021 return 0;
1022}
1023
Jean Delvare21b48a72007-03-12 19:20:15 -03001024static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001026 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001028 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 /* FIXME: power down ??? */
1030 return 0;
1031}
1032
Jean Delvare21b48a72007-03-12 19:20:15 -03001033static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001035 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001037 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001038 if (V4L2_TUNER_RADIO == t->mode) {
1039 if (t->radio_freq)
1040 set_freq(c, t->radio_freq);
1041 } else {
1042 if (t->tv_freq)
1043 set_freq(c, t->tv_freq);
1044 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 return 0;
1046}
1047
1048/* ----------------------------------------------------------------------- */
1049
1050static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001051 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001052 .attach_adapter = tuner_probe,
1053 .detach_client = tuner_detach,
1054 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001055 .suspend = tuner_suspend,
1056 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001058 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001059 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001061static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001062 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001063 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064};
1065
1066static int __init tuner_init_module(void)
1067{
1068 return i2c_add_driver(&driver);
1069}
1070
1071static void __exit tuner_cleanup_module(void)
1072{
1073 i2c_del_driver(&driver);
1074}
1075
1076module_init(tuner_init_module);
1077module_exit(tuner_cleanup_module);
1078
1079/*
1080 * Overrides for Emacs so that we follow Linus's tabbing style.
1081 * ---------------------------------------------------------------------------
1082 * Local variables:
1083 * c-basic-offset: 8
1084 * End:
1085 */