blob: 99558c708ae716e6f2489e4d80db8ad53bf7214e [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 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700281 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300282 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700283 t->type = TUNER_ABSENT;
284 t->mode_mask = T_UNINITIALIZED;
285 return;
286 }
287 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700288 break;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300289 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300290 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300291 t->type = TUNER_ABSENT;
292 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300293 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300294 }
295 t->mode_mask = T_RADIO;
296 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700297 case TUNER_PHILIPS_FMD1216ME_MK3:
298 buffer[0] = 0x0b;
299 buffer[1] = 0xdc;
300 buffer[2] = 0x9c;
301 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700302 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700303 mdelay(1);
304 buffer[2] = 0x86;
305 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700306 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300307 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700308 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800309 case TUNER_PHILIPS_TD1316:
310 buffer[0] = 0x0b;
311 buffer[1] = 0xdc;
312 buffer[2] = 0x86;
313 buffer[3] = 0xa4;
314 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300315 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200316 break;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300317 case TUNER_XC2028:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300318 {
319 int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
320 &c->dev, c->adapter->algo_data,
321 t->tuner_callback);
322 if (rc<0) {
323 t->type = TUNER_ABSENT;
324 t->mode_mask = T_UNINITIALIZED;
325 return;
326 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300327 break;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300328 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300329 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300330 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300331 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300333 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 break;
335 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700336
Michael Krufky746d97322007-08-25 19:08:45 -0300337 if ((fe_tuner_ops->set_analog_params) &&
338 ((NULL == t->ops.set_tv_freq) && (NULL == t->ops.set_radio_freq))) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300339 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
340
341 t->ops.set_tv_freq = fe_set_freq;
342 t->ops.set_radio_freq = fe_set_freq;
343 t->ops.standby = fe_standby;
344 t->ops.release = fe_release;
Michael Krufky1f5ef192007-08-31 17:38:02 -0300345 t->ops.has_signal = fe_has_signal;
Michael Krufkye18f9442007-08-21 01:25:48 -0300346 }
347
348 tuner_info("type set to %s\n", t->i2c.name);
349
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700350 if (t->mode_mask == T_UNINITIALIZED)
351 t->mode_mask = new_mode_mask;
352
Hans Verkuil27487d42006-01-15 15:04:52 -0200353 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700354 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100355 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700356 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300357 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358}
359
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700360/*
361 * This function apply tuner config to tuner specified
362 * by tun_setup structure. I addr is unset, then admin status
363 * and tun addr status is more precise then current status,
364 * it's applied. Otherwise status and type are applied only to
365 * tuner with exactly the same addr.
366*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700367
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700368static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700369{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700370 struct tuner *t = i2c_get_clientdata(c);
371
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300372 tuner_dbg("set addr for type %i\n", t->type);
373
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300374 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
375 (t->mode_mask & tun_setup->mode_mask))) ||
376 (tun_setup->addr == c->addr)) {
377 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300378 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700379 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700380}
381
382static inline int check_mode(struct tuner *t, char *cmd)
383{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700384 if ((1 << t->mode & t->mode_mask) == 0) {
385 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700386 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700387
388 switch (t->mode) {
389 case V4L2_TUNER_RADIO:
390 tuner_dbg("Cmd %s accepted for radio\n", cmd);
391 break;
392 case V4L2_TUNER_ANALOG_TV:
393 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
394 break;
395 case V4L2_TUNER_DIGITAL_TV:
396 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
397 break;
398 }
399 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700400}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700401
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700402/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403static int tuner_fixup_std(struct tuner *t)
404{
405 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300407 case '6':
408 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
409 t->std = V4L2_STD_PAL_60;
410 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 case 'b':
412 case 'B':
413 case 'g':
414 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700415 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 t->std = V4L2_STD_PAL_BG;
417 break;
418 case 'i':
419 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700420 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 t->std = V4L2_STD_PAL_I;
422 break;
423 case 'd':
424 case 'D':
425 case 'k':
426 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700427 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 t->std = V4L2_STD_PAL_DK;
429 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700430 case 'M':
431 case 'm':
432 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
433 t->std = V4L2_STD_PAL_M;
434 break;
435 case 'N':
436 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200437 if (pal[1] == 'c' || pal[1] == 'C') {
438 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
439 t->std = V4L2_STD_PAL_Nc;
440 } else {
441 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
442 t->std = V4L2_STD_PAL_N;
443 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700444 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700445 case '-':
446 /* default parameter, do nothing */
447 break;
448 default:
449 tuner_warn ("pal= argument not recognised\n");
450 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 }
452 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700453 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
454 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200455 case 'b':
456 case 'B':
457 case 'g':
458 case 'G':
459 case 'h':
460 case 'H':
461 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
462 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
463 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700464 case 'd':
465 case 'D':
466 case 'k':
467 case 'K':
468 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
469 t->std = V4L2_STD_SECAM_DK;
470 break;
471 case 'l':
472 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800473 if ((secam[1]=='C')||(secam[1]=='c')) {
474 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
475 t->std = V4L2_STD_SECAM_LC;
476 } else {
477 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
478 t->std = V4L2_STD_SECAM_L;
479 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700480 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700481 case '-':
482 /* default parameter, do nothing */
483 break;
484 default:
485 tuner_warn ("secam= argument not recognised\n");
486 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700487 }
488 }
489
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200490 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
491 switch (ntsc[0]) {
492 case 'm':
493 case 'M':
494 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
495 t->std = V4L2_STD_NTSC_M;
496 break;
497 case 'j':
498 case 'J':
499 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
500 t->std = V4L2_STD_NTSC_M_JP;
501 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200502 case 'k':
503 case 'K':
504 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
505 t->std = V4L2_STD_NTSC_M_KR;
506 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200507 case '-':
508 /* default parameter, do nothing */
509 break;
510 default:
511 tuner_info("ntsc= argument not recognised\n");
512 break;
513 }
514 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 return 0;
516}
517
Michael Krufkydb8a6952007-08-21 01:24:42 -0300518static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200519{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200520 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300521 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200522 const char *p;
523
524 switch (t->mode) {
525 case V4L2_TUNER_RADIO: p = "radio"; break;
526 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
527 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
528 default: p = "undefined"; break;
529 }
530 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200531 freq = t->radio_freq / 16000;
532 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200533 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200534 freq = t->tv_freq / 16;
535 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200536 }
537 tuner_info("Tuner mode: %s\n", p);
538 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300539 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200540 if (t->mode != V4L2_TUNER_RADIO)
541 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300542 if (fe_tuner_ops->get_status) {
543 u32 tuner_status;
544
545 fe_tuner_ops->get_status(&t->fe, &tuner_status);
546 if (tuner_status & TUNER_STATUS_LOCKED)
547 tuner_info("Tuner is locked.\n");
548 if (tuner_status & TUNER_STATUS_STEREO)
549 tuner_info("Stereo: yes\n");
550 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300551 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300552 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200553 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300554 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300555 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200556 }
557}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559/* ---------------------------------------------------------------------- */
560
Hans Verkuilba8fc392006-06-25 15:34:39 -0300561/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700562static unsigned default_mode_mask;
563
564/* During client attach, set_type is called by adapter's attach_inform callback.
565 set_type must then be completed by tuner_attach.
566 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
568{
569 struct tuner *t;
570
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700571 client_template.adapter = adap;
572 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
Panagiotis Issaris74081872006-01-11 19:40:56 -0200574 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700575 if (NULL == t)
576 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700577 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700579 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700580 t->audmode = V4L2_TUNER_MODE_STEREO;
581 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300582 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700584 if (show_i2c) {
585 unsigned char buffer[16];
586 int i,rc;
587
588 memset(buffer, 0, sizeof(buffer));
589 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800590 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700591 for (i=0;i<rc;i++)
592 printk("%02x ",buffer[i]);
593 printk("\n");
594 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300595 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
596 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
597 return -ENODEV;
598
Markus Rechberger257c6452006-01-23 17:11:11 -0200599 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700600 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800601 switch (addr) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300602 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300603 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300604 t->type = TUNER_TEA5761;
605 t->mode_mask = T_RADIO;
606 t->mode = T_STANDBY;
607 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
608 default_mode_mask &= ~T_RADIO;
609
610 goto register_client;
611 }
612 break;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800613 case 0x42:
614 case 0x43:
615 case 0x4a:
616 case 0x4b:
617 /* If chip is not tda8290, don't register.
618 since it can be tda9887*/
Michael Krufky746d97322007-08-25 19:08:45 -0300619 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300620 tuner_dbg("chip at addr %x is a tda8290\n", addr);
621 } else {
622 /* Default is being tda9887 */
623 t->type = TUNER_TDA9887;
624 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
625 t->mode = T_STANDBY;
626 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800627 }
628 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800629 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300630 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700631 t->type = TUNER_TEA5767;
632 t->mode_mask = T_RADIO;
633 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200634 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700635 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700636
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800637 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700638 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800639 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700640 }
641 }
642
643 /* Initializes only the first adapter found */
644 if (default_mode_mask != T_UNINITIALIZED) {
645 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
646 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200647 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
648 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700649 default_mode_mask = T_UNINITIALIZED;
650 }
651
652 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800653register_client:
654 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700655 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300656 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 return 0;
658}
659
660static int tuner_probe(struct i2c_adapter *adap)
661{
662 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700663 normal_i2c[0] = addr;
664 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
Michael Krufkya1dec512007-08-24 01:13:07 -0300667 /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
668 * FusionHDTV5 RT Gold has an ir receiver at 0x6b
669 * and an RTC at 0x6f which can get corrupted if probed.
670 */
Michael Krufky9bc37ca2007-09-08 15:17:13 -0300671 if ((adap->id == I2C_HW_B_CX2388x) ||
672 (adap->id == I2C_HW_B_CX23885)) {
Michael Krufkya1dec512007-08-24 01:13:07 -0300673 unsigned int i = 0;
674
675 while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
676 i += 2;
677 if (i + 4 < I2C_CLIENT_MAX_OPTS) {
678 ignore[i+0] = adap->nr;
679 ignore[i+1] = 0x6b;
680 ignore[i+2] = adap->nr;
681 ignore[i+3] = 0x6f;
682 ignore[i+4] = I2C_CLIENT_END;
683 } else
684 printk(KERN_WARNING "tuner: "
685 "too many options specified "
686 "in i2c probe ignore list!\n");
687 }
688
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700689 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700690
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 if (adap->class & I2C_CLASS_TV_ANALOG)
692 return i2c_probe(adap, &addr_data, tuner_attach);
693 return 0;
694}
695
696static int tuner_detach(struct i2c_client *client)
697{
698 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700699 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700701 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700702 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700703 tuner_warn
704 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700705 return err;
706 }
707
Michael Krufky7a91a802007-06-06 16:10:39 -0300708 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300709 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300710 else {
711 kfree(t->priv);
712 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 kfree(t);
714 return 0;
715}
716
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700717/*
718 * Switch tuner to other mode. If tuner support both tv and radio,
719 * set another frequency to some value (This is needed for some pal
720 * tuners to avoid locking). Otherwise, just put second tuner in
721 * standby mode.
722 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700724static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
725{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800726 if (mode == t->mode)
727 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700728
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800729 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700730
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800731 if (check_mode(t, cmd) == EINVAL) {
732 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300733 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300734 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800735 return EINVAL;
736 }
737 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700738}
739
740#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800741 tuner_dbg("switching to v4l2\n"); \
742 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700743
744static inline int check_v4l2(struct tuner *t)
745{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300746 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
747 TV, v4l1 for radio), until that is fixed this code is disabled.
748 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
749 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700750 return 0;
751}
752
753static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754{
755 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300756 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Hans Verkuilf9195de2006-01-11 19:01:01 -0200758 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200759 v4l_i2c_print_ioctl(&(t->i2c),cmd);
760
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700761 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700763 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300764 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 -0700765 ((struct tuner_setup *)arg)->type,
766 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300767 ((struct tuner_setup *)arg)->mode_mask,
768 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700769
770 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700771 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200773 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
774 == EINVAL)
775 return 0;
776 if (t->radio_freq)
777 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700779 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200780 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
781 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300782 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300783 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300784 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200785 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300786#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700787 case VIDIOCSAUDIO:
788 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
789 return 0;
790 if (check_v4l2(t) == EINVAL)
791 return 0;
792
793 /* Should be implemented, since bttv calls it */
794 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700795 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700797 {
798 static const v4l2_std_id map[] = {
799 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
800 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
801 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
802 [4 /* bttv */ ] = V4L2_STD_PAL_M,
803 [5 /* bttv */ ] = V4L2_STD_PAL_N,
804 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
805 };
806 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700808 if (check_v4l2(t) == EINVAL)
809 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700810
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700811 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
812 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700814 if (vc->norm < ARRAY_SIZE(map))
815 t->std = map[vc->norm];
816 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200817 if (t->tv_freq)
818 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700819 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700820 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700821 case VIDIOCSFREQ:
822 {
823 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700824
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700825 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
826 return 0;
827 if (check_v4l2(t) == EINVAL)
828 return 0;
829
830 set_freq(client, *v);
831 return 0;
832 }
833 case VIDIOCGTUNER:
834 {
835 struct video_tuner *vt = arg;
836
837 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
838 return 0;
839 if (check_v4l2(t) == EINVAL)
840 return 0;
841
842 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300843 if (fe_tuner_ops->get_status) {
844 u32 tuner_status;
845
846 fe_tuner_ops->get_status(&t->fe, &tuner_status);
Michael Krufky1f5ef192007-08-31 17:38:02 -0300847 if (tuner_status & TUNER_STATUS_STEREO)
848 vt->flags |= VIDEO_TUNER_STEREO_ON;
849 else
850 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
Michael Krufkye18f9442007-08-21 01:25:48 -0300851 } else {
852 if (t->ops.is_stereo) {
853 if (t->ops.is_stereo(t))
854 vt->flags |=
855 VIDEO_TUNER_STEREO_ON;
856 else
857 vt->flags &=
858 ~VIDEO_TUNER_STEREO_ON;
859 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700860 }
Michael Krufky1f5ef192007-08-31 17:38:02 -0300861 if (t->ops.has_signal)
862 vt->signal = t->ops.has_signal(t);
863
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700864 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
865
866 vt->rangelow = radio_range[0] * 16000;
867 vt->rangehigh = radio_range[1] * 16000;
868
869 } else {
870 vt->rangelow = tv_range[0] * 16;
871 vt->rangehigh = tv_range[1] * 16;
872 }
873
874 return 0;
875 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700877 {
878 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700880 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
881 return 0;
882 if (check_v4l2(t) == EINVAL)
883 return 0;
884
Michael Krufkye18f9442007-08-21 01:25:48 -0300885 if (V4L2_TUNER_RADIO == t->mode) {
886 if (fe_tuner_ops->get_status) {
887 u32 tuner_status;
888
889 fe_tuner_ops->get_status(&t->fe, &tuner_status);
890 va->mode = (tuner_status & TUNER_STATUS_STEREO)
891 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
892 } else if (t->ops.is_stereo)
893 va->mode = t->ops.is_stereo(t)
894 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
895 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700896 return 0;
897 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300898#endif
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300899 case TUNER_SET_CONFIG:
900 {
901 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
902 struct v4l2_priv_tun_config *cfg = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300904 if (t->type != cfg->tuner)
905 break;
906
907 if (t->type == TUNER_TDA9887) {
908 t->tda9887_config = *(unsigned int *)cfg->priv;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300909 set_freq(client, t->tv_freq);
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300910 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300911 }
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300912
913 if (NULL == fe_tuner_ops->set_config) {
914 tuner_warn("Tuner frontend module has no way to "
915 "set config\n");
916 break;
917 }
918 fe_tuner_ops->set_config(&t->fe, cfg->priv);
919
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300920 break;
Mauro Carvalho Chehab7f171122007-10-18 19:56:47 -0300921 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300922 /* --- v4l ioctls --- */
923 /* take care: bttv does userspace copying, we'll get a
924 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700926 {
927 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700929 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
930 == EINVAL)
931 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700932
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700933 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700935 t->std = *id;
936 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200937 if (t->tv_freq)
938 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700939 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700940 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700941 case VIDIOC_S_FREQUENCY:
942 {
943 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700944
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300945 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
946 == EINVAL)
947 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700948 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200949 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700950
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700951 break;
952 }
953 case VIDIOC_G_FREQUENCY:
954 {
955 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700956
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700957 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
958 return 0;
959 switch_v4l2();
960 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300961 if (fe_tuner_ops->get_frequency) {
962 u32 abs_freq;
963
964 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
965 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
966 (abs_freq * 2 + 125/2) / 125 :
967 (abs_freq + 62500/2) / 62500;
968 break;
969 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200970 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
971 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700972 break;
973 }
974 case VIDIOC_G_TUNER:
975 {
976 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700977
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700978 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
979 return 0;
980 switch_v4l2();
981
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200982 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300983 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300984 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300985 if (t->mode == V4L2_TUNER_ANALOG_TV)
986 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200987 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700988 tuner->rangelow = tv_range[0] * 16;
989 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200990 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700991 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200992
993 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200994 tuner->rxsubchans =
995 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300996 if (fe_tuner_ops->get_status) {
997 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200998
Michael Krufkye18f9442007-08-21 01:25:48 -0300999 fe_tuner_ops->get_status(&t->fe, &tuner_status);
1000 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
1001 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
Michael Krufkye18f9442007-08-21 01:25:48 -03001002 } else {
1003 if (t->ops.is_stereo) {
1004 tuner->rxsubchans = t->ops.is_stereo(t) ?
1005 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
1006 }
Michael Krufkye18f9442007-08-21 01:25:48 -03001007 }
Michael Krufky1f5ef192007-08-31 17:38:02 -03001008 if (t->ops.has_signal)
1009 tuner->signal = t->ops.has_signal(t);
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001010 tuner->capability |=
1011 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1012 tuner->audmode = t->audmode;
1013 tuner->rangelow = radio_range[0] * 16000;
1014 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001015 break;
1016 }
1017 case VIDIOC_S_TUNER:
1018 {
1019 struct v4l2_tuner *tuner = arg;
1020
1021 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
1022 return 0;
1023
1024 switch_v4l2();
1025
Hans Verkuil8a4b2752006-01-23 17:11:09 -02001026 /* do nothing unless we're a radio tuner */
1027 if (t->mode != V4L2_TUNER_RADIO)
1028 break;
1029 t->audmode = tuner->audmode;
1030 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001031 break;
1032 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001033 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -03001034 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -03001035 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -02001036 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 }
1038
1039 return 0;
1040}
1041
Jean Delvare21b48a72007-03-12 19:20:15 -03001042static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001044 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001046 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 /* FIXME: power down ??? */
1048 return 0;
1049}
1050
Jean Delvare21b48a72007-03-12 19:20:15 -03001051static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001053 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001055 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001056 if (V4L2_TUNER_RADIO == t->mode) {
1057 if (t->radio_freq)
1058 set_freq(c, t->radio_freq);
1059 } else {
1060 if (t->tv_freq)
1061 set_freq(c, t->tv_freq);
1062 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 return 0;
1064}
1065
1066/* ----------------------------------------------------------------------- */
1067
1068static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001069 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001070 .attach_adapter = tuner_probe,
1071 .detach_client = tuner_detach,
1072 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001073 .suspend = tuner_suspend,
1074 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001076 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001077 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001079static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001080 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001081 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082};
1083
1084static int __init tuner_init_module(void)
1085{
1086 return i2c_add_driver(&driver);
1087}
1088
1089static void __exit tuner_cleanup_module(void)
1090{
1091 i2c_del_driver(&driver);
1092}
1093
1094module_init(tuner_init_module);
1095module_exit(tuner_cleanup_module);
1096
1097/*
1098 * Overrides for Emacs so that we follow Linus's tabbing style.
1099 * ---------------------------------------------------------------------------
1100 * Local variables:
1101 * c-basic-offset: 8
1102 * End:
1103 */