blob: 13112732ed2ce1b8c949f0e4b874b9e9d3231b98 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * i2c tv tuner chip device driver
4 * core core, i.e. kernel interfaces, registering and so on
5 */
6
7#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/string.h>
10#include <linux/timer.h>
11#include <linux/delay.h>
12#include <linux/errno.h>
13#include <linux/slab.h>
14#include <linux/poll.h>
15#include <linux/i2c.h>
16#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/init.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030018#include <linux/videodev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <media/tuner.h>
Michael Krufky4adad282007-08-27 21:59:08 -030020#include <media/tuner-types.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020021#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030022#include "tuner-driver.h"
Michael Krufky96c0b7c2007-08-27 21:23:08 -030023#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030024#include "tda8290.h"
Michael Krufky7ab10bf2007-08-27 21:23:40 -030025#include "tea5761.h"
Michael Krufky8d0936e2007-08-27 21:24:27 -030026#include "tea5767.h"
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030027#include "tuner-xc2028.h"
Michael Krufky4adad282007-08-27 21:59:08 -030028#include "tuner-simple.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#define UNSET (-1U)
31
32/* standard i2c insmod options */
33static unsigned short normal_i2c[] = {
Adrian Bunk04d934f2007-10-24 09:06:47 -030034#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030035 0x10,
36#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080037 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070038 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
39 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 I2C_CLIENT_END
41};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043I2C_CLIENT_INSMOD;
44
45/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070046static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070047static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070048static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070049
Linus Torvalds1da177e2005-04-16 15:20:36 -070050/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020051int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070053static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070054static unsigned int radio_range[2] = { 65, 108 };
55
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020056static char pal[] = "--";
57static char secam[] = "--";
58static char ntsc[] = "-";
59
Hans Verkuilf9195de2006-01-11 19:01:01 -020060
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020061module_param(addr, int, 0444);
62module_param(no_autodetect, int, 0444);
63module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020064module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020065module_param_string(pal, pal, sizeof(pal), 0644);
66module_param_string(secam, secam, sizeof(secam), 0644);
67module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070068module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069module_param_array(radio_range, int, NULL, 0644);
70
71MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
72MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
73MODULE_LICENSE("GPL");
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static struct i2c_driver driver;
76static struct i2c_client client_template;
77
78/* ---------------------------------------------------------------------- */
79
Michael Krufkye18f9442007-08-21 01:25:48 -030080static void fe_set_freq(struct tuner *t, unsigned int freq)
81{
82 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
83
84 struct analog_parameters params = {
85 .frequency = freq,
86 .mode = t->mode,
87 .audmode = t->audmode,
88 .std = t->std
89 };
90
91 if (NULL == fe_tuner_ops->set_analog_params) {
92 tuner_warn("Tuner frontend module has no way to set freq\n");
93 return;
94 }
95 fe_tuner_ops->set_analog_params(&t->fe, &params);
96}
97
98static void fe_release(struct tuner *t)
99{
100 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
101
102 if (fe_tuner_ops->release)
103 fe_tuner_ops->release(&t->fe);
104}
105
106static void fe_standby(struct tuner *t)
107{
108 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
109
110 if (fe_tuner_ops->sleep)
111 fe_tuner_ops->sleep(&t->fe);
112}
113
Michael Krufky1f5ef192007-08-31 17:38:02 -0300114static int fe_has_signal(struct tuner *t)
115{
116 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Michael Krufky14196832007-10-14 18:11:53 -0300117 u16 strength = 0;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300118
119 if (fe_tuner_ops->get_rf_strength)
120 fe_tuner_ops->get_rf_strength(&t->fe, &strength);
121
122 return strength;
123}
124
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700125/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126static void set_tv_freq(struct i2c_client *c, unsigned int freq)
127{
128 struct tuner *t = i2c_get_clientdata(c);
129
130 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700131 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 return;
133 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300134 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700135 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 return;
137 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700138 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
139 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
140 freq / 16, freq % 16 * 100 / 16, tv_range[0],
141 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200142 /* V4L2 spec: if the freq is not possible then the closest
143 possible value should be selected */
144 if (freq < tv_range[0] * 16)
145 freq = tv_range[0] * 16;
146 else
147 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300149 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150}
151
152static void set_radio_freq(struct i2c_client *c, unsigned int freq)
153{
154 struct tuner *t = i2c_get_clientdata(c);
155
156 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700157 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 return;
159 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300160 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700161 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 return;
163 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200164 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700165 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
166 freq / 16000, freq % 16000 * 100 / 16000,
167 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200168 /* V4L2 spec: if the freq is not possible then the closest
169 possible value should be selected */
170 if (freq < radio_range[0] * 16000)
171 freq = radio_range[0] * 16000;
172 else
173 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700175
Michael Krufkydb8a6952007-08-21 01:24:42 -0300176 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177}
178
179static void set_freq(struct i2c_client *c, unsigned long freq)
180{
181 struct tuner *t = i2c_get_clientdata(c);
182
183 switch (t->mode) {
184 case V4L2_TUNER_RADIO:
185 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700186 freq / 16000, freq % 16000 * 100 / 16000);
187 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200188 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 break;
190 case V4L2_TUNER_ANALOG_TV:
191 case V4L2_TUNER_DIGITAL_TV:
192 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700193 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200195 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300197 default:
198 tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200}
201
Michael Krufky293197c2007-08-28 17:20:42 -0300202static void tuner_i2c_address_check(struct tuner *t)
203{
204 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
205 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
206 return;
207
208 tuner_warn("====================== WARNING! ======================\n");
209 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
210 tuner_warn("will soon be dropped. This message indicates that your\n");
211 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
212 t->i2c.name, t->i2c.addr);
213 tuner_warn("To ensure continued support for your device, please\n");
214 tuner_warn("send a copy of this message, along with full dmesg\n");
215 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
216 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
217 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
218 t->i2c.adapter->name, t->i2c.addr, t->type,
219 tuners[t->type].name);
220 tuner_warn("====================== WARNING! ======================\n");
221}
222
Michael Krufky910bb3e2007-08-27 21:22:20 -0300223static void attach_tda8290(struct tuner *t)
224{
225 struct tda8290_config cfg = {
226 .lna_cfg = &t->config,
227 .tuner_callback = t->tuner_callback
228 };
229 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
230}
231
Michael Krufky4adad282007-08-27 21:59:08 -0300232static void attach_simple_tuner(struct tuner *t)
233{
234 struct simple_tuner_config cfg = {
235 .type = t->type,
236 .tun = &tuners[t->type]
237 };
238 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
239}
240
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700241static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300242 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300243 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
245 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300246 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700247 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700249 if (type == UNSET || type == TUNER_ABSENT) {
250 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 return;
252 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700254 if (type >= tuner_count) {
255 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
256 return;
257 }
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300260 t->config = new_config;
261 if (tuner_callback != NULL) {
262 tuner_dbg("defining GPIO callback\n");
263 t->tuner_callback = tuner_callback;
264 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300265
266 /* This code detects calls by card attach_inform */
267 if (NULL == t->i2c.dev.driver) {
268 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
269
270 return;
271 }
272
Michael Krufkyb2083192007-05-29 22:54:06 -0300273 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300274 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300275 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300276 else {
277 kfree(t->priv);
278 t->priv = NULL;
279 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 switch (t->type) {
282 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300283 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 break;
285 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300286 {
287 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300289 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700290 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300291 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700292 t->type = TUNER_ABSENT;
293 t->mode_mask = T_UNINITIALIZED;
294 return;
295 }
296 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700297 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300298 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300299 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300300 t->type = TUNER_ABSENT;
301 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300302 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300303 }
304 t->mode_mask = T_RADIO;
305 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700306 case TUNER_PHILIPS_FMD1216ME_MK3:
307 buffer[0] = 0x0b;
308 buffer[1] = 0xdc;
309 buffer[2] = 0x9c;
310 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700311 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700312 mdelay(1);
313 buffer[2] = 0x86;
314 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700315 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300316 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700317 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800318 case TUNER_PHILIPS_TD1316:
319 buffer[0] = 0x0b;
320 buffer[1] = 0xdc;
321 buffer[2] = 0x86;
322 buffer[3] = 0xa4;
323 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300324 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200325 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300326 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300327 {
328 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
329 &c->dev, c->adapter->algo_data,
330 t->tuner_callback);
331 if (rc<0) {
332 t->type = TUNER_ABSENT;
333 t->mode_mask = T_UNINITIALIZED;
334 return;
335 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300336 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300337 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300338 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300339 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300340 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300342 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 break;
344 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700345
Michael Krufkye18f9442007-08-21 01:25:48 -0300346 if (fe_tuner_ops->set_analog_params) {
347 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
348
349 t->ops.set_tv_freq = fe_set_freq;
350 t->ops.set_radio_freq = fe_set_freq;
351 t->ops.standby = fe_standby;
352 t->ops.release = fe_release;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300353 t->ops.has_signal = fe_has_signal;
Michael Krufkye18f9442007-08-21 01:25:48 -0300354 }
355
356 tuner_info("type set to %s\n", t->i2c.name);
357
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700358 if (t->mode_mask == T_UNINITIALIZED)
359 t->mode_mask = new_mode_mask;
360
Hans Verkuil27487d42006-01-15 15:04:52 -0200361 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700362 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100363 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700364 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300365 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366}
367
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700368/*
369 * This function apply tuner config to tuner specified
370 * by tun_setup structure. I addr is unset, then admin status
371 * and tun addr status is more precise then current status,
372 * it's applied. Otherwise status and type are applied only to
373 * tuner with exactly the same addr.
374*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700375
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700376static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700377{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700378 struct tuner *t = i2c_get_clientdata(c);
379
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300380 tuner_dbg("set addr for type %i\n", t->type);
381
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300382 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
383 (t->mode_mask & tun_setup->mode_mask))) ||
384 (tun_setup->addr == c->addr)) {
385 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300386 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700387 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700388}
389
390static inline int check_mode(struct tuner *t, char *cmd)
391{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700392 if ((1 << t->mode & t->mode_mask) == 0) {
393 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700394 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700395
396 switch (t->mode) {
397 case V4L2_TUNER_RADIO:
398 tuner_dbg("Cmd %s accepted for radio\n", cmd);
399 break;
400 case V4L2_TUNER_ANALOG_TV:
401 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
402 break;
403 case V4L2_TUNER_DIGITAL_TV:
404 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
405 break;
406 }
407 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700408}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700409
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700410/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411static int tuner_fixup_std(struct tuner *t)
412{
413 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300415 case '6':
416 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
417 t->std = V4L2_STD_PAL_60;
418 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 case 'b':
420 case 'B':
421 case 'g':
422 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700423 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 t->std = V4L2_STD_PAL_BG;
425 break;
426 case 'i':
427 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700428 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 t->std = V4L2_STD_PAL_I;
430 break;
431 case 'd':
432 case 'D':
433 case 'k':
434 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700435 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 t->std = V4L2_STD_PAL_DK;
437 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700438 case 'M':
439 case 'm':
440 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
441 t->std = V4L2_STD_PAL_M;
442 break;
443 case 'N':
444 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200445 if (pal[1] == 'c' || pal[1] == 'C') {
446 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
447 t->std = V4L2_STD_PAL_Nc;
448 } else {
449 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
450 t->std = V4L2_STD_PAL_N;
451 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700452 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700453 case '-':
454 /* default parameter, do nothing */
455 break;
456 default:
457 tuner_warn ("pal= argument not recognised\n");
458 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 }
460 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700461 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
462 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200463 case 'b':
464 case 'B':
465 case 'g':
466 case 'G':
467 case 'h':
468 case 'H':
469 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
470 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
471 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700472 case 'd':
473 case 'D':
474 case 'k':
475 case 'K':
476 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
477 t->std = V4L2_STD_SECAM_DK;
478 break;
479 case 'l':
480 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800481 if ((secam[1]=='C')||(secam[1]=='c')) {
482 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
483 t->std = V4L2_STD_SECAM_LC;
484 } else {
485 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
486 t->std = V4L2_STD_SECAM_L;
487 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700488 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700489 case '-':
490 /* default parameter, do nothing */
491 break;
492 default:
493 tuner_warn ("secam= argument not recognised\n");
494 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700495 }
496 }
497
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200498 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
499 switch (ntsc[0]) {
500 case 'm':
501 case 'M':
502 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
503 t->std = V4L2_STD_NTSC_M;
504 break;
505 case 'j':
506 case 'J':
507 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
508 t->std = V4L2_STD_NTSC_M_JP;
509 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200510 case 'k':
511 case 'K':
512 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
513 t->std = V4L2_STD_NTSC_M_KR;
514 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200515 case '-':
516 /* default parameter, do nothing */
517 break;
518 default:
519 tuner_info("ntsc= argument not recognised\n");
520 break;
521 }
522 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 return 0;
524}
525
Michael Krufkydb8a6952007-08-21 01:24:42 -0300526static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200527{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200528 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300529 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200530 const char *p;
531
532 switch (t->mode) {
533 case V4L2_TUNER_RADIO: p = "radio"; break;
534 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
535 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
536 default: p = "undefined"; break;
537 }
538 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200539 freq = t->radio_freq / 16000;
540 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200541 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200542 freq = t->tv_freq / 16;
543 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200544 }
545 tuner_info("Tuner mode: %s\n", p);
546 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300547 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200548 if (t->mode != V4L2_TUNER_RADIO)
549 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300550 if (fe_tuner_ops->get_status) {
551 u32 tuner_status;
552
553 fe_tuner_ops->get_status(&t->fe, &tuner_status);
554 if (tuner_status & TUNER_STATUS_LOCKED)
555 tuner_info("Tuner is locked.\n");
556 if (tuner_status & TUNER_STATUS_STEREO)
557 tuner_info("Stereo: yes\n");
558 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300559 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300560 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200561 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300562 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300563 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200564 }
565}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567/* ---------------------------------------------------------------------- */
568
Hans Verkuilba8fc392006-06-25 15:34:39 -0300569/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700570static unsigned default_mode_mask;
571
572/* During client attach, set_type is called by adapter's attach_inform callback.
573 set_type must then be completed by tuner_attach.
574 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
576{
577 struct tuner *t;
578
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700579 client_template.adapter = adap;
580 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Panagiotis Issaris74081872006-01-11 19:40:56 -0200582 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700583 if (NULL == t)
584 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700585 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700587 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700588 t->audmode = V4L2_TUNER_MODE_STEREO;
589 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300590 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700592 if (show_i2c) {
593 unsigned char buffer[16];
594 int i,rc;
595
596 memset(buffer, 0, sizeof(buffer));
597 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800598 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700599 for (i=0;i<rc;i++)
600 printk("%02x ",buffer[i]);
601 printk("\n");
602 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300603 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
604 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
605 return -ENODEV;
606
Markus Rechberger257c6452006-01-23 17:11:11 -0200607 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700608 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800609 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300610 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300611 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300612 t->type = TUNER_TEA5761;
613 t->mode_mask = T_RADIO;
614 t->mode = T_STANDBY;
615 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
616 default_mode_mask &= ~T_RADIO;
617
618 goto register_client;
619 }
620 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800621 case 0x42:
622 case 0x43:
623 case 0x4a:
624 case 0x4b:
625 /* If chip is not tda8290, don't register.
626 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300627 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300628 tuner_dbg("chip at addr %x is a tda8290\n", addr);
629 } else {
630 /* Default is being tda9887 */
631 t->type = TUNER_TDA9887;
632 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
633 t->mode = T_STANDBY;
634 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800635 }
636 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800637 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300638 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700639 t->type = TUNER_TEA5767;
640 t->mode_mask = T_RADIO;
641 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200642 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700643 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700644
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800645 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700646 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800647 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700648 }
649 }
650
651 /* Initializes only the first adapter found */
652 if (default_mode_mask != T_UNINITIALIZED) {
653 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
654 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200655 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
656 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700657 default_mode_mask = T_UNINITIALIZED;
658 }
659
660 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800661register_client:
662 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700663 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300664 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 return 0;
666}
667
668static int tuner_probe(struct i2c_adapter *adap)
669{
670 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700671 normal_i2c[0] = addr;
672 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Michael Krufkya1dec512007-08-24 01:13:07 -0300675 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
676 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
677 * and an RTC at 0x6f which can get corrupted if probed.
678 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300679 if ((adap->id == I2C_HW_B_CX2388x) ||
680 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300681 unsigned int i = 0;
682
683 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
684 i += 2;
685 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
686 ignore[i+0] = adap->nr;
687 ignore[i+1] = 0x6b;
688 ignore[i+2] = adap->nr;
689 ignore[i+3] = 0x6f;
690 ignore[i+4] = I2C_CLIENT_END;
691 } else
692 printk(KERN_WARNING "tuner: "
693 "too many options specified "
694 "in i2c probe ignore list!\n");
695 }
696
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700697 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700698
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 if (adap->class & I2C_CLASS_TV_ANALOG)
700 return i2c_probe(adap, &addr_data, tuner_attach);
701 return 0;
702}
703
704static int tuner_detach(struct i2c_client *client)
705{
706 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700707 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700709 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700710 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700711 tuner_warn
712 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700713 return err;
714 }
715
Michael Krufky7a91a802007-06-06 16:10:39 -0300716 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300717 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300718 else {
719 kfree(t->priv);
720 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 kfree(t);
722 return 0;
723}
724
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700725/*
726 * Switch tuner to other mode. If tuner support both tv and radio,
727 * set another frequency to some value (This is needed for some pal
728 * tuners to avoid locking). Otherwise, just put second tuner in
729 * standby mode.
730 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700732static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
733{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800734 if (mode == t->mode)
735 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700736
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800737 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700738
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800739 if (check_mode(t, cmd) == EINVAL) {
740 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300741 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300742 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800743 return EINVAL;
744 }
745 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700746}
747
748#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800749 tuner_dbg("switching to v4l2\n"); \
750 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700751
752static inline int check_v4l2(struct tuner *t)
753{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300754 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
755 TV, v4l1 for radio), until that is fixed this code is disabled.
756 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
757 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758 return 0;
759}
760
761static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762{
763 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300764 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Hans Verkuilf9195de2006-01-11 19:01:01 -0200766 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200767 v4l_i2c_print_ioctl(&(t->i2c),cmd);
768
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700769 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700771 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300772 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 -0700773 ((struct tuner_setup *)arg)->type,
774 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300775 ((struct tuner_setup *)arg)->mode_mask,
776 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700777
778 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700779 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200781 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
782 == EINVAL)
783 return 0;
784 if (t->radio_freq)
785 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700787 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200788 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
789 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300790 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300791 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300792 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200793 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300794#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700795 case VIDIOCSAUDIO:
796 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
797 return 0;
798 if (check_v4l2(t) == EINVAL)
799 return 0;
800
801 /* Should be implemented, since bttv calls it */
802 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700803 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700805 {
806 static const v4l2_std_id map[] = {
807 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
808 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
809 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
810 [4 /* bttv */ ] = V4L2_STD_PAL_M,
811 [5 /* bttv */ ] = V4L2_STD_PAL_N,
812 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
813 };
814 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700816 if (check_v4l2(t) == EINVAL)
817 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700818
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700819 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
820 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700822 if (vc->norm < ARRAY_SIZE(map))
823 t->std = map[vc->norm];
824 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200825 if (t->tv_freq)
826 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700827 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700828 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700829 case VIDIOCSFREQ:
830 {
831 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700832
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700833 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
834 return 0;
835 if (check_v4l2(t) == EINVAL)
836 return 0;
837
838 set_freq(client, *v);
839 return 0;
840 }
841 case VIDIOCGTUNER:
842 {
843 struct video_tuner *vt = arg;
844
845 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
846 return 0;
847 if (check_v4l2(t) == EINVAL)
848 return 0;
849
850 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300851 if (fe_tuner_ops->get_status) {
852 u32 tuner_status;
853
854 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300855 if (tuner_status & TUNER_STATUS_STEREO)
856 vt->flags |= VIDEO_TUNER_STEREO_ON;
857 else
858 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300859 } else {
860 if (t->ops.is_stereo) {
861 if (t->ops.is_stereo(t))
862 vt->flags |=
863 VIDEO_TUNER_STEREO_ON;
864 else
865 vt->flags &=
866 ~VIDEO_TUNER_STEREO_ON;
867 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700868 }
Michael Krufky1f5ef192007-08-31 17:38:02 -0300869 if (t->ops.has_signal)
870 vt->signal = t->ops.has_signal(t);
871
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700872 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
873
874 vt->rangelow = radio_range[0] * 16000;
875 vt->rangehigh = radio_range[1] * 16000;
876
877 } else {
878 vt->rangelow = tv_range[0] * 16;
879 vt->rangehigh = tv_range[1] * 16;
880 }
881
882 return 0;
883 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700885 {
886 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700888 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
889 return 0;
890 if (check_v4l2(t) == EINVAL)
891 return 0;
892
Michael Krufkye18f9442007-08-21 01:25:48 -0300893 if (V4L2_TUNER_RADIO == t->mode) {
894 if (fe_tuner_ops->get_status) {
895 u32 tuner_status;
896
897 fe_tuner_ops->get_status(&t->fe, &tuner_status);
898 va->mode = (tuner_status & TUNER_STATUS_STEREO)
899 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
900 } else if (t->ops.is_stereo)
901 va->mode = t->ops.is_stereo(t)
902 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
903 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700904 return 0;
905 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300906#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300907 case TUNER_SET_CONFIG:
908 {
909 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
910 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300912 if (t->type != cfg->tuner)
913 break;
914
915 if (t->type == TUNER_TDA9887) {
916 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300917 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300918 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300919 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300920
921 if (NULL == fe_tuner_ops->set_config) {
922 tuner_warn("Tuner frontend module has no way to "
923 "set config\n");
924 break;
925 }
926 fe_tuner_ops->set_config(&t->fe, cfg->priv);
927
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300928 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300929 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300930 /* --- v4l ioctls --- */
931 /* take care: bttv does userspace copying, we'll get a
932 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700934 {
935 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700937 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
938 == EINVAL)
939 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700940
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700941 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700943 t->std = *id;
944 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200945 if (t->tv_freq)
946 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700947 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700948 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700949 case VIDIOC_S_FREQUENCY:
950 {
951 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700952
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300953 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
954 == EINVAL)
955 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700956 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200957 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700958
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700959 break;
960 }
961 case VIDIOC_G_FREQUENCY:
962 {
963 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700964
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700965 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
966 return 0;
967 switch_v4l2();
968 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300969 if (fe_tuner_ops->get_frequency) {
970 u32 abs_freq;
971
972 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
973 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
974 (abs_freq * 2 + 125/2) / 125 :
975 (abs_freq + 62500/2) / 62500;
976 break;
977 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200978 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
979 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700980 break;
981 }
982 case VIDIOC_G_TUNER:
983 {
984 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700985
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700986 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
987 return 0;
988 switch_v4l2();
989
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200990 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300991 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300992 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300993 if (t->mode == V4L2_TUNER_ANALOG_TV)
994 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200995 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700996 tuner->rangelow = tv_range[0] * 16;
997 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200998 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700999 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001000
1001 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001002 tuner->rxsubchans =
1003 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001004 if (fe_tuner_ops->get_status) {
1005 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001006
Michael Krufkye18f9442007-08-21 01:25:48 -03001007 fe_tuner_ops->get_status(&t->fe, &tuner_status);
1008 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
1009 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001010 } else {
1011 if (t->ops.is_stereo) {
1012 tuner->rxsubchans = t->ops.is_stereo(t) ?
1013 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
1014 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001015 }
Michael Krufky1f5ef192007-08-31 17:38:02 -03001016 if (t->ops.has_signal)
1017 tuner->signal = t->ops.has_signal(t);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001018 tuner->capability |=
1019 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1020 tuner->audmode = t->audmode;
1021 tuner->rangelow = radio_range[0] * 16000;
1022 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001023 break;
1024 }
1025 case VIDIOC_S_TUNER:
1026 {
1027 struct v4l2_tuner *tuner = arg;
1028
1029 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1030 return 0;
1031
1032 switch_v4l2();
1033
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001034 /* do nothing unless we're a radio tuner */
1035 if (t->mode != V4L2_TUNER_RADIO)
1036 break;
1037 t->audmode = tuner->audmode;
1038 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001039 break;
1040 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001041 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -03001042 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -03001043 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001044 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 }
1046
1047 return 0;
1048}
1049
Jean Delvare21b48a72007-03-12 19:20:15 -03001050static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001052 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001054 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 /* FIXME: power down ??? */
1056 return 0;
1057}
1058
Jean Delvare21b48a72007-03-12 19:20:15 -03001059static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001061 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001063 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001064 if (V4L2_TUNER_RADIO == t->mode) {
1065 if (t->radio_freq)
1066 set_freq(c, t->radio_freq);
1067 } else {
1068 if (t->tv_freq)
1069 set_freq(c, t->tv_freq);
1070 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 return 0;
1072}
1073
1074/* ----------------------------------------------------------------------- */
1075
1076static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001077 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001078 .attach_adapter = tuner_probe,
1079 .detach_client = tuner_detach,
1080 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001081 .suspend = tuner_suspend,
1082 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001084 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001085 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001087static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001088 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001089 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090};
1091
1092static int __init tuner_init_module(void)
1093{
1094 return i2c_add_driver(&driver);
1095}
1096
1097static void __exit tuner_cleanup_module(void)
1098{
1099 i2c_del_driver(&driver);
1100}
1101
1102module_init(tuner_init_module);
1103module_exit(tuner_cleanup_module);
1104
1105/*
1106 * Overrides for Emacs so that we follow Linus's tabbing style.
1107 * ---------------------------------------------------------------------------
1108 * Local variables:
1109 * c-basic-offset: 8
1110 * End:
1111 */