blob: 6ae8cb205d8eaf9679c19fd02f1e0b7464a77a81 [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 Krufky4adad282007-08-27 21:59:08 -0300223static void attach_simple_tuner(struct tuner *t)
224{
225 struct simple_tuner_config cfg = {
226 .type = t->type,
227 .tun = &tuners[t->type]
228 };
229 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
230}
231
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700232static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300233 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300234 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235{
236 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300237 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700238 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700240 if (type == UNSET || type == TUNER_ABSENT) {
241 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 return;
243 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700245 if (type >= tuner_count) {
246 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
247 return;
248 }
249
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300251 t->config = new_config;
252 if (tuner_callback != NULL) {
253 tuner_dbg("defining GPIO callback\n");
254 t->tuner_callback = tuner_callback;
255 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300256
257 /* This code detects calls by card attach_inform */
258 if (NULL == t->i2c.dev.driver) {
259 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
260
261 return;
262 }
263
Michael Krufkyb2083192007-05-29 22:54:06 -0300264 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300265 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300266 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300267 else {
268 kfree(t->priv);
269 t->priv = NULL;
270 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 switch (t->type) {
273 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300274 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 break;
276 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300277 {
Michael Krufky746d97322007-08-25 19:08:45 -0300278 tda8290_attach(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300280 }
Michael Krufky5bea1cd2007-10-22 09:56:38 -0300281 case TUNER_PHILIPS_TDA8295:
282 {
283 tda8295_attach(t);
284 break;
285 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700286 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300287 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700288 t->type = TUNER_ABSENT;
289 t->mode_mask = T_UNINITIALIZED;
290 return;
291 }
292 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700293 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300294 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300295 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300296 t->type = TUNER_ABSENT;
297 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300298 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300299 }
300 t->mode_mask = T_RADIO;
301 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700302 case TUNER_PHILIPS_FMD1216ME_MK3:
303 buffer[0] = 0x0b;
304 buffer[1] = 0xdc;
305 buffer[2] = 0x9c;
306 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700307 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700308 mdelay(1);
309 buffer[2] = 0x86;
310 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700311 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300312 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700313 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800314 case TUNER_PHILIPS_TD1316:
315 buffer[0] = 0x0b;
316 buffer[1] = 0xdc;
317 buffer[2] = 0x86;
318 buffer[3] = 0xa4;
319 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300320 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200321 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300322 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300323 {
324 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
325 &c->dev, c->adapter->algo_data,
326 t->tuner_callback);
327 if (rc<0) {
328 t->type = TUNER_ABSENT;
329 t->mode_mask = T_UNINITIALIZED;
330 return;
331 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300332 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300333 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300334 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300335 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300336 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300338 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 break;
340 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700341
Michael Krufky746d97322007-08-25 19:08:45 -0300342 if ((fe_tuner_ops->set_analog_params) &&
343 ((NULL == t->ops.set_tv_freq) && (NULL == t->ops.set_radio_freq))) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300344 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
345
346 t->ops.set_tv_freq = fe_set_freq;
347 t->ops.set_radio_freq = fe_set_freq;
348 t->ops.standby = fe_standby;
349 t->ops.release = fe_release;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300350 t->ops.has_signal = fe_has_signal;
Michael Krufkye18f9442007-08-21 01:25:48 -0300351 }
352
353 tuner_info("type set to %s\n", t->i2c.name);
354
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700355 if (t->mode_mask == T_UNINITIALIZED)
356 t->mode_mask = new_mode_mask;
357
Hans Verkuil27487d42006-01-15 15:04:52 -0200358 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700359 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100360 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700361 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300362 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363}
364
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700365/*
366 * This function apply tuner config to tuner specified
367 * by tun_setup structure. I addr is unset, then admin status
368 * and tun addr status is more precise then current status,
369 * it's applied. Otherwise status and type are applied only to
370 * tuner with exactly the same addr.
371*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700372
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700373static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700374{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700375 struct tuner *t = i2c_get_clientdata(c);
376
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300377 tuner_dbg("set addr for type %i\n", t->type);
378
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300379 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
380 (t->mode_mask & tun_setup->mode_mask))) ||
381 (tun_setup->addr == c->addr)) {
382 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300383 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700384 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700385}
386
387static inline int check_mode(struct tuner *t, char *cmd)
388{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700389 if ((1 << t->mode & t->mode_mask) == 0) {
390 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700391 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700392
393 switch (t->mode) {
394 case V4L2_TUNER_RADIO:
395 tuner_dbg("Cmd %s accepted for radio\n", cmd);
396 break;
397 case V4L2_TUNER_ANALOG_TV:
398 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
399 break;
400 case V4L2_TUNER_DIGITAL_TV:
401 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
402 break;
403 }
404 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700405}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700406
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700407/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408static int tuner_fixup_std(struct tuner *t)
409{
410 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300412 case '6':
413 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
414 t->std = V4L2_STD_PAL_60;
415 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 case 'b':
417 case 'B':
418 case 'g':
419 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700420 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 t->std = V4L2_STD_PAL_BG;
422 break;
423 case 'i':
424 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700425 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 t->std = V4L2_STD_PAL_I;
427 break;
428 case 'd':
429 case 'D':
430 case 'k':
431 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700432 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 t->std = V4L2_STD_PAL_DK;
434 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700435 case 'M':
436 case 'm':
437 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
438 t->std = V4L2_STD_PAL_M;
439 break;
440 case 'N':
441 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200442 if (pal[1] == 'c' || pal[1] == 'C') {
443 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
444 t->std = V4L2_STD_PAL_Nc;
445 } else {
446 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
447 t->std = V4L2_STD_PAL_N;
448 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700449 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700450 case '-':
451 /* default parameter, do nothing */
452 break;
453 default:
454 tuner_warn ("pal= argument not recognised\n");
455 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 }
457 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700458 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
459 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200460 case 'b':
461 case 'B':
462 case 'g':
463 case 'G':
464 case 'h':
465 case 'H':
466 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
467 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
468 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700469 case 'd':
470 case 'D':
471 case 'k':
472 case 'K':
473 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
474 t->std = V4L2_STD_SECAM_DK;
475 break;
476 case 'l':
477 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800478 if ((secam[1]=='C')||(secam[1]=='c')) {
479 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
480 t->std = V4L2_STD_SECAM_LC;
481 } else {
482 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
483 t->std = V4L2_STD_SECAM_L;
484 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700485 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700486 case '-':
487 /* default parameter, do nothing */
488 break;
489 default:
490 tuner_warn ("secam= argument not recognised\n");
491 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700492 }
493 }
494
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200495 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
496 switch (ntsc[0]) {
497 case 'm':
498 case 'M':
499 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
500 t->std = V4L2_STD_NTSC_M;
501 break;
502 case 'j':
503 case 'J':
504 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
505 t->std = V4L2_STD_NTSC_M_JP;
506 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200507 case 'k':
508 case 'K':
509 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
510 t->std = V4L2_STD_NTSC_M_KR;
511 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200512 case '-':
513 /* default parameter, do nothing */
514 break;
515 default:
516 tuner_info("ntsc= argument not recognised\n");
517 break;
518 }
519 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 return 0;
521}
522
Michael Krufkydb8a6952007-08-21 01:24:42 -0300523static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200524{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200525 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300526 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200527 const char *p;
528
529 switch (t->mode) {
530 case V4L2_TUNER_RADIO: p = "radio"; break;
531 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
532 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
533 default: p = "undefined"; break;
534 }
535 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200536 freq = t->radio_freq / 16000;
537 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200538 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200539 freq = t->tv_freq / 16;
540 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200541 }
542 tuner_info("Tuner mode: %s\n", p);
543 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300544 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200545 if (t->mode != V4L2_TUNER_RADIO)
546 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300547 if (fe_tuner_ops->get_status) {
548 u32 tuner_status;
549
550 fe_tuner_ops->get_status(&t->fe, &tuner_status);
551 if (tuner_status & TUNER_STATUS_LOCKED)
552 tuner_info("Tuner is locked.\n");
553 if (tuner_status & TUNER_STATUS_STEREO)
554 tuner_info("Stereo: yes\n");
555 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300556 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300557 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200558 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300559 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300560 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200561 }
562}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564/* ---------------------------------------------------------------------- */
565
Hans Verkuilba8fc392006-06-25 15:34:39 -0300566/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700567static unsigned default_mode_mask;
568
569/* During client attach, set_type is called by adapter's attach_inform callback.
570 set_type must then be completed by tuner_attach.
571 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
573{
574 struct tuner *t;
575
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700576 client_template.adapter = adap;
577 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
Panagiotis Issaris74081872006-01-11 19:40:56 -0200579 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700580 if (NULL == t)
581 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700582 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700584 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700585 t->audmode = V4L2_TUNER_MODE_STEREO;
586 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300587 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700589 if (show_i2c) {
590 unsigned char buffer[16];
591 int i,rc;
592
593 memset(buffer, 0, sizeof(buffer));
594 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800595 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700596 for (i=0;i<rc;i++)
597 printk("%02x ",buffer[i]);
598 printk("\n");
599 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300600 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
601 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
602 return -ENODEV;
603
Markus Rechberger257c6452006-01-23 17:11:11 -0200604 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700605 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800606 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300607 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300608 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300609 t->type = TUNER_TEA5761;
610 t->mode_mask = T_RADIO;
611 t->mode = T_STANDBY;
612 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
613 default_mode_mask &= ~T_RADIO;
614
615 goto register_client;
616 }
617 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800618 case 0x42:
619 case 0x43:
620 case 0x4a:
621 case 0x4b:
622 /* If chip is not tda8290, don't register.
623 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300624 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300625 tuner_dbg("chip at addr %x is a tda8290\n", addr);
626 } else {
627 /* Default is being tda9887 */
628 t->type = TUNER_TDA9887;
629 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
630 t->mode = T_STANDBY;
631 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800632 }
633 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800634 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300635 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700636 t->type = TUNER_TEA5767;
637 t->mode_mask = T_RADIO;
638 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200639 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700640 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700641
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800642 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700643 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800644 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700645 }
646 }
647
648 /* Initializes only the first adapter found */
649 if (default_mode_mask != T_UNINITIALIZED) {
650 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
651 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200652 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
653 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700654 default_mode_mask = T_UNINITIALIZED;
655 }
656
657 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800658register_client:
659 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700660 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300661 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 return 0;
663}
664
665static int tuner_probe(struct i2c_adapter *adap)
666{
667 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700668 normal_i2c[0] = addr;
669 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
Michael Krufkya1dec512007-08-24 01:13:07 -0300672 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
673 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
674 * and an RTC at 0x6f which can get corrupted if probed.
675 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300676 if ((adap->id == I2C_HW_B_CX2388x) ||
677 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300678 unsigned int i = 0;
679
680 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
681 i += 2;
682 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
683 ignore[i+0] = adap->nr;
684 ignore[i+1] = 0x6b;
685 ignore[i+2] = adap->nr;
686 ignore[i+3] = 0x6f;
687 ignore[i+4] = I2C_CLIENT_END;
688 } else
689 printk(KERN_WARNING "tuner: "
690 "too many options specified "
691 "in i2c probe ignore list!\n");
692 }
693
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700694 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 if (adap->class & I2C_CLASS_TV_ANALOG)
697 return i2c_probe(adap, &addr_data, tuner_attach);
698 return 0;
699}
700
701static int tuner_detach(struct i2c_client *client)
702{
703 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700704 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700706 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700707 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700708 tuner_warn
709 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700710 return err;
711 }
712
Michael Krufky7a91a802007-06-06 16:10:39 -0300713 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300714 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300715 else {
716 kfree(t->priv);
717 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 kfree(t);
719 return 0;
720}
721
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700722/*
723 * Switch tuner to other mode. If tuner support both tv and radio,
724 * set another frequency to some value (This is needed for some pal
725 * tuners to avoid locking). Otherwise, just put second tuner in
726 * standby mode.
727 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700729static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
730{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800731 if (mode == t->mode)
732 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700733
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800734 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700735
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800736 if (check_mode(t, cmd) == EINVAL) {
737 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300738 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300739 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800740 return EINVAL;
741 }
742 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700743}
744
745#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800746 tuner_dbg("switching to v4l2\n"); \
747 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700748
749static inline int check_v4l2(struct tuner *t)
750{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300751 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
752 TV, v4l1 for radio), until that is fixed this code is disabled.
753 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
754 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700755 return 0;
756}
757
758static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759{
760 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300761 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Hans Verkuilf9195de2006-01-11 19:01:01 -0200763 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200764 v4l_i2c_print_ioctl(&(t->i2c),cmd);
765
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700766 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700768 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300769 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 -0700770 ((struct tuner_setup *)arg)->type,
771 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300772 ((struct tuner_setup *)arg)->mode_mask,
773 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700774
775 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700776 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200778 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
779 == EINVAL)
780 return 0;
781 if (t->radio_freq)
782 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700784 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200785 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
786 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300787 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300788 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300789 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200790 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300791#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700792 case VIDIOCSAUDIO:
793 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
794 return 0;
795 if (check_v4l2(t) == EINVAL)
796 return 0;
797
798 /* Should be implemented, since bttv calls it */
799 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700800 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700802 {
803 static const v4l2_std_id map[] = {
804 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
805 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
806 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
807 [4 /* bttv */ ] = V4L2_STD_PAL_M,
808 [5 /* bttv */ ] = V4L2_STD_PAL_N,
809 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
810 };
811 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700813 if (check_v4l2(t) == EINVAL)
814 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700815
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700816 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
817 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700819 if (vc->norm < ARRAY_SIZE(map))
820 t->std = map[vc->norm];
821 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200822 if (t->tv_freq)
823 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700824 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700825 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700826 case VIDIOCSFREQ:
827 {
828 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700829
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700830 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
831 return 0;
832 if (check_v4l2(t) == EINVAL)
833 return 0;
834
835 set_freq(client, *v);
836 return 0;
837 }
838 case VIDIOCGTUNER:
839 {
840 struct video_tuner *vt = arg;
841
842 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
843 return 0;
844 if (check_v4l2(t) == EINVAL)
845 return 0;
846
847 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300848 if (fe_tuner_ops->get_status) {
849 u32 tuner_status;
850
851 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300852 if (tuner_status & TUNER_STATUS_STEREO)
853 vt->flags |= VIDEO_TUNER_STEREO_ON;
854 else
855 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300856 } else {
857 if (t->ops.is_stereo) {
858 if (t->ops.is_stereo(t))
859 vt->flags |=
860 VIDEO_TUNER_STEREO_ON;
861 else
862 vt->flags &=
863 ~VIDEO_TUNER_STEREO_ON;
864 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700865 }
Michael Krufky1f5ef192007-08-31 17:38:02 -0300866 if (t->ops.has_signal)
867 vt->signal = t->ops.has_signal(t);
868
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700869 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
870
871 vt->rangelow = radio_range[0] * 16000;
872 vt->rangehigh = radio_range[1] * 16000;
873
874 } else {
875 vt->rangelow = tv_range[0] * 16;
876 vt->rangehigh = tv_range[1] * 16;
877 }
878
879 return 0;
880 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700882 {
883 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700885 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
886 return 0;
887 if (check_v4l2(t) == EINVAL)
888 return 0;
889
Michael Krufkye18f9442007-08-21 01:25:48 -0300890 if (V4L2_TUNER_RADIO == t->mode) {
891 if (fe_tuner_ops->get_status) {
892 u32 tuner_status;
893
894 fe_tuner_ops->get_status(&t->fe, &tuner_status);
895 va->mode = (tuner_status & TUNER_STATUS_STEREO)
896 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
897 } else if (t->ops.is_stereo)
898 va->mode = t->ops.is_stereo(t)
899 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
900 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700901 return 0;
902 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300903#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300904 case TUNER_SET_CONFIG:
905 {
906 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
907 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300909 if (t->type != cfg->tuner)
910 break;
911
912 if (t->type == TUNER_TDA9887) {
913 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300914 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300915 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300916 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300917
918 if (NULL == fe_tuner_ops->set_config) {
919 tuner_warn("Tuner frontend module has no way to "
920 "set config\n");
921 break;
922 }
923 fe_tuner_ops->set_config(&t->fe, cfg->priv);
924
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300925 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300926 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300927 /* --- v4l ioctls --- */
928 /* take care: bttv does userspace copying, we'll get a
929 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700931 {
932 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700934 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
935 == EINVAL)
936 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700937
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700938 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700940 t->std = *id;
941 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200942 if (t->tv_freq)
943 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700944 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700945 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700946 case VIDIOC_S_FREQUENCY:
947 {
948 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700949
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300950 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
951 == EINVAL)
952 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700953 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200954 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700955
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700956 break;
957 }
958 case VIDIOC_G_FREQUENCY:
959 {
960 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700961
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700962 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
963 return 0;
964 switch_v4l2();
965 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300966 if (fe_tuner_ops->get_frequency) {
967 u32 abs_freq;
968
969 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
970 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
971 (abs_freq * 2 + 125/2) / 125 :
972 (abs_freq + 62500/2) / 62500;
973 break;
974 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200975 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
976 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700977 break;
978 }
979 case VIDIOC_G_TUNER:
980 {
981 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700982
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700983 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
984 return 0;
985 switch_v4l2();
986
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200987 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300988 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300989 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300990 if (t->mode == V4L2_TUNER_ANALOG_TV)
991 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200992 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700993 tuner->rangelow = tv_range[0] * 16;
994 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200995 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700996 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200997
998 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200999 tuner->rxsubchans =
1000 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001001 if (fe_tuner_ops->get_status) {
1002 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001003
Michael Krufkye18f9442007-08-21 01:25:48 -03001004 fe_tuner_ops->get_status(&t->fe, &tuner_status);
1005 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
1006 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001007 } else {
1008 if (t->ops.is_stereo) {
1009 tuner->rxsubchans = t->ops.is_stereo(t) ?
1010 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
1011 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001012 }
Michael Krufky1f5ef192007-08-31 17:38:02 -03001013 if (t->ops.has_signal)
1014 tuner->signal = t->ops.has_signal(t);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001015 tuner->capability |=
1016 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1017 tuner->audmode = t->audmode;
1018 tuner->rangelow = radio_range[0] * 16000;
1019 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001020 break;
1021 }
1022 case VIDIOC_S_TUNER:
1023 {
1024 struct v4l2_tuner *tuner = arg;
1025
1026 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1027 return 0;
1028
1029 switch_v4l2();
1030
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001031 /* do nothing unless we're a radio tuner */
1032 if (t->mode != V4L2_TUNER_RADIO)
1033 break;
1034 t->audmode = tuner->audmode;
1035 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001036 break;
1037 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001038 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -03001039 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -03001040 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001041 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 }
1043
1044 return 0;
1045}
1046
Jean Delvare21b48a72007-03-12 19:20:15 -03001047static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001049 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001051 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 /* FIXME: power down ??? */
1053 return 0;
1054}
1055
Jean Delvare21b48a72007-03-12 19:20:15 -03001056static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001058 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001060 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001061 if (V4L2_TUNER_RADIO == t->mode) {
1062 if (t->radio_freq)
1063 set_freq(c, t->radio_freq);
1064 } else {
1065 if (t->tv_freq)
1066 set_freq(c, t->tv_freq);
1067 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 return 0;
1069}
1070
1071/* ----------------------------------------------------------------------- */
1072
1073static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001074 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001075 .attach_adapter = tuner_probe,
1076 .detach_client = tuner_detach,
1077 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001078 .suspend = tuner_suspend,
1079 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001081 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001082 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001084static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001085 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001086 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087};
1088
1089static int __init tuner_init_module(void)
1090{
1091 return i2c_add_driver(&driver);
1092}
1093
1094static void __exit tuner_cleanup_module(void)
1095{
1096 i2c_del_driver(&driver);
1097}
1098
1099module_init(tuner_init_module);
1100module_exit(tuner_cleanup_module);
1101
1102/*
1103 * Overrides for Emacs so that we follow Linus's tabbing style.
1104 * ---------------------------------------------------------------------------
1105 * Local variables:
1106 * c-basic-offset: 8
1107 * End:
1108 */