blob: f24ec1a18b452fd65c4fb6c5f4d05e0902538906 [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 Krufky5e453dc2006-01-09 15:32:31 -020020#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030021#include "tuner-driver.h"
Michael Krufky96c0b7c2007-08-27 21:23:08 -030022#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030023#include "tda8290.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
25#define UNSET (-1U)
26
27/* standard i2c insmod options */
28static unsigned short normal_i2c[] = {
Adrian Bunk9ee476a2007-06-05 05:22:00 -030029#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030030 0x10,
31#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080032 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070033 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
34 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 I2C_CLIENT_END
36};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070037
Linus Torvalds1da177e2005-04-16 15:20:36 -070038I2C_CLIENT_INSMOD;
39
40/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070041static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070042static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070043static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070044
Linus Torvalds1da177e2005-04-16 15:20:36 -070045/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020046int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070048static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070049static unsigned int radio_range[2] = { 65, 108 };
50
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020051static char pal[] = "--";
52static char secam[] = "--";
53static char ntsc[] = "-";
54
Hans Verkuilf9195de2006-01-11 19:01:01 -020055
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020056module_param(addr, int, 0444);
57module_param(no_autodetect, int, 0444);
58module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020059module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020060module_param_string(pal, pal, sizeof(pal), 0644);
61module_param_string(secam, secam, sizeof(secam), 0644);
62module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070063module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064module_param_array(radio_range, int, NULL, 0644);
65
66MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
67MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
68MODULE_LICENSE("GPL");
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070static struct i2c_driver driver;
71static struct i2c_client client_template;
72
73/* ---------------------------------------------------------------------- */
74
Michael Krufkye18f9442007-08-21 01:25:48 -030075static void fe_set_freq(struct tuner *t, unsigned int freq)
76{
77 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
78
79 struct analog_parameters params = {
80 .frequency = freq,
81 .mode = t->mode,
82 .audmode = t->audmode,
83 .std = t->std
84 };
85
86 if (NULL == fe_tuner_ops->set_analog_params) {
87 tuner_warn("Tuner frontend module has no way to set freq\n");
88 return;
89 }
90 fe_tuner_ops->set_analog_params(&t->fe, &params);
91}
92
93static void fe_release(struct tuner *t)
94{
95 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
96
97 if (fe_tuner_ops->release)
98 fe_tuner_ops->release(&t->fe);
99}
100
101static void fe_standby(struct tuner *t)
102{
103 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
104
105 if (fe_tuner_ops->sleep)
106 fe_tuner_ops->sleep(&t->fe);
107}
108
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700109/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110static void set_tv_freq(struct i2c_client *c, unsigned int freq)
111{
112 struct tuner *t = i2c_get_clientdata(c);
113
114 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700115 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 return;
117 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300118 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700119 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 return;
121 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700122 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
123 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
124 freq / 16, freq % 16 * 100 / 16, tv_range[0],
125 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200126 /* V4L2 spec: if the freq is not possible then the closest
127 possible value should be selected */
128 if (freq < tv_range[0] * 16)
129 freq = tv_range[0] * 16;
130 else
131 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300133 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134}
135
136static void set_radio_freq(struct i2c_client *c, unsigned int freq)
137{
138 struct tuner *t = i2c_get_clientdata(c);
139
140 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700141 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 return;
143 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300144 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700145 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 return;
147 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200148 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700149 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
150 freq / 16000, freq % 16000 * 100 / 16000,
151 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200152 /* V4L2 spec: if the freq is not possible then the closest
153 possible value should be selected */
154 if (freq < radio_range[0] * 16000)
155 freq = radio_range[0] * 16000;
156 else
157 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700159
Michael Krufkydb8a6952007-08-21 01:24:42 -0300160 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161}
162
163static void set_freq(struct i2c_client *c, unsigned long freq)
164{
165 struct tuner *t = i2c_get_clientdata(c);
166
167 switch (t->mode) {
168 case V4L2_TUNER_RADIO:
169 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700170 freq / 16000, freq % 16000 * 100 / 16000);
171 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200172 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 break;
174 case V4L2_TUNER_ANALOG_TV:
175 case V4L2_TUNER_DIGITAL_TV:
176 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700177 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200179 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 break;
181 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182}
183
Michael Krufky293197c2007-08-28 17:20:42 -0300184static void tuner_i2c_address_check(struct tuner *t)
185{
186 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
187 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
188 return;
189
190 tuner_warn("====================== WARNING! ======================\n");
191 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
192 tuner_warn("will soon be dropped. This message indicates that your\n");
193 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
194 t->i2c.name, t->i2c.addr);
195 tuner_warn("To ensure continued support for your device, please\n");
196 tuner_warn("send a copy of this message, along with full dmesg\n");
197 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
198 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
199 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
200 t->i2c.adapter->name, t->i2c.addr, t->type,
201 tuners[t->type].name);
202 tuner_warn("====================== WARNING! ======================\n");
203}
204
Michael Krufky910bb3e2007-08-27 21:22:20 -0300205static void attach_tda8290(struct tuner *t)
206{
207 struct tda8290_config cfg = {
208 .lna_cfg = &t->config,
209 .tuner_callback = t->tuner_callback
210 };
211 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
212}
213
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700214static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300215 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300216 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217{
218 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300219 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700220 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700222 if (type == UNSET || type == TUNER_ABSENT) {
223 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 return;
225 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700227 if (type >= tuner_count) {
228 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
229 return;
230 }
231
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300233 t->config = new_config;
234 if (tuner_callback != NULL) {
235 tuner_dbg("defining GPIO callback\n");
236 t->tuner_callback = tuner_callback;
237 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300238
239 /* This code detects calls by card attach_inform */
240 if (NULL == t->i2c.dev.driver) {
241 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
242
243 return;
244 }
245
Michael Krufkyb2083192007-05-29 22:54:06 -0300246 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300247 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300248 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300249 else {
250 kfree(t->priv);
251 t->priv = NULL;
252 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300253
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 switch (t->type) {
255 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300256 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 break;
258 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300259 {
260 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300262 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700263 case TUNER_TEA5767:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300264 if (tea5767_tuner_init(t) == EINVAL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700265 t->type = TUNER_ABSENT;
266 t->mode_mask = T_UNINITIALIZED;
267 return;
268 }
269 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700270 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300271#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300272 case TUNER_TEA5761:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300273 if (tea5761_tuner_init(t) == EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300274 t->type = TUNER_ABSENT;
275 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300276 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300277 }
278 t->mode_mask = T_RADIO;
279 break;
280#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700281 case TUNER_PHILIPS_FMD1216ME_MK3:
282 buffer[0] = 0x0b;
283 buffer[1] = 0xdc;
284 buffer[2] = 0x9c;
285 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700286 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700287 mdelay(1);
288 buffer[2] = 0x86;
289 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700290 i2c_master_send(c, buffer, 4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300291 default_tuner_init(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700292 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800293 case TUNER_PHILIPS_TD1316:
294 buffer[0] = 0x0b;
295 buffer[1] = 0xdc;
296 buffer[2] = 0x86;
297 buffer[3] = 0xa4;
298 i2c_master_send(c,buffer,4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300299 default_tuner_init(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200300 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300301 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300302 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300303 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 default:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300305 default_tuner_init(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 break;
307 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700308
Michael Krufkye18f9442007-08-21 01:25:48 -0300309 if (fe_tuner_ops->set_analog_params) {
310 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
311
312 t->ops.set_tv_freq = fe_set_freq;
313 t->ops.set_radio_freq = fe_set_freq;
314 t->ops.standby = fe_standby;
315 t->ops.release = fe_release;
316 }
317
318 tuner_info("type set to %s\n", t->i2c.name);
319
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700320 if (t->mode_mask == T_UNINITIALIZED)
321 t->mode_mask = new_mode_mask;
322
Hans Verkuil27487d42006-01-15 15:04:52 -0200323 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700324 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100325 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700326 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300327 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328}
329
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700330/*
331 * This function apply tuner config to tuner specified
332 * by tun_setup structure. I addr is unset, then admin status
333 * and tun addr status is more precise then current status,
334 * it's applied. Otherwise status and type are applied only to
335 * tuner with exactly the same addr.
336*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700337
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700338static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700339{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700340 struct tuner *t = i2c_get_clientdata(c);
341
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300342 tuner_dbg("set addr for type %i\n", t->type);
343
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300344 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
345 (t->mode_mask & tun_setup->mode_mask))) ||
346 (tun_setup->addr == c->addr)) {
347 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300348 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700349 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700350}
351
352static inline int check_mode(struct tuner *t, char *cmd)
353{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700354 if ((1 << t->mode & t->mode_mask) == 0) {
355 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700356 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700357
358 switch (t->mode) {
359 case V4L2_TUNER_RADIO:
360 tuner_dbg("Cmd %s accepted for radio\n", cmd);
361 break;
362 case V4L2_TUNER_ANALOG_TV:
363 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
364 break;
365 case V4L2_TUNER_DIGITAL_TV:
366 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
367 break;
368 }
369 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700370}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700371
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700372/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373static int tuner_fixup_std(struct tuner *t)
374{
375 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300377 case '6':
378 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
379 t->std = V4L2_STD_PAL_60;
380 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 case 'b':
382 case 'B':
383 case 'g':
384 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700385 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 t->std = V4L2_STD_PAL_BG;
387 break;
388 case 'i':
389 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700390 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 t->std = V4L2_STD_PAL_I;
392 break;
393 case 'd':
394 case 'D':
395 case 'k':
396 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700397 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 t->std = V4L2_STD_PAL_DK;
399 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700400 case 'M':
401 case 'm':
402 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
403 t->std = V4L2_STD_PAL_M;
404 break;
405 case 'N':
406 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200407 if (pal[1] == 'c' || pal[1] == 'C') {
408 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
409 t->std = V4L2_STD_PAL_Nc;
410 } else {
411 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
412 t->std = V4L2_STD_PAL_N;
413 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700414 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700415 case '-':
416 /* default parameter, do nothing */
417 break;
418 default:
419 tuner_warn ("pal= argument not recognised\n");
420 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 }
422 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700423 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
424 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200425 case 'b':
426 case 'B':
427 case 'g':
428 case 'G':
429 case 'h':
430 case 'H':
431 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
432 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
433 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700434 case 'd':
435 case 'D':
436 case 'k':
437 case 'K':
438 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
439 t->std = V4L2_STD_SECAM_DK;
440 break;
441 case 'l':
442 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800443 if ((secam[1]=='C')||(secam[1]=='c')) {
444 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
445 t->std = V4L2_STD_SECAM_LC;
446 } else {
447 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
448 t->std = V4L2_STD_SECAM_L;
449 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700450 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700451 case '-':
452 /* default parameter, do nothing */
453 break;
454 default:
455 tuner_warn ("secam= argument not recognised\n");
456 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700457 }
458 }
459
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200460 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
461 switch (ntsc[0]) {
462 case 'm':
463 case 'M':
464 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
465 t->std = V4L2_STD_NTSC_M;
466 break;
467 case 'j':
468 case 'J':
469 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
470 t->std = V4L2_STD_NTSC_M_JP;
471 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200472 case 'k':
473 case 'K':
474 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
475 t->std = V4L2_STD_NTSC_M_KR;
476 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200477 case '-':
478 /* default parameter, do nothing */
479 break;
480 default:
481 tuner_info("ntsc= argument not recognised\n");
482 break;
483 }
484 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 return 0;
486}
487
Michael Krufkydb8a6952007-08-21 01:24:42 -0300488static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200489{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200490 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300491 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200492 const char *p;
493
494 switch (t->mode) {
495 case V4L2_TUNER_RADIO: p = "radio"; break;
496 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
497 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
498 default: p = "undefined"; break;
499 }
500 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200501 freq = t->radio_freq / 16000;
502 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200503 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200504 freq = t->tv_freq / 16;
505 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200506 }
507 tuner_info("Tuner mode: %s\n", p);
508 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300509 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200510 if (t->mode != V4L2_TUNER_RADIO)
511 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300512 if (fe_tuner_ops->get_status) {
513 u32 tuner_status;
514
515 fe_tuner_ops->get_status(&t->fe, &tuner_status);
516 if (tuner_status & TUNER_STATUS_LOCKED)
517 tuner_info("Tuner is locked.\n");
518 if (tuner_status & TUNER_STATUS_STEREO)
519 tuner_info("Stereo: yes\n");
520 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300521 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300522 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200523 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300524 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300525 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200526 }
527}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200528
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529/* ---------------------------------------------------------------------- */
530
Hans Verkuilba8fc392006-06-25 15:34:39 -0300531/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700532static unsigned default_mode_mask;
533
534/* During client attach, set_type is called by adapter's attach_inform callback.
535 set_type must then be completed by tuner_attach.
536 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
538{
539 struct tuner *t;
540
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700541 client_template.adapter = adap;
542 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
Panagiotis Issaris74081872006-01-11 19:40:56 -0200544 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700545 if (NULL == t)
546 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700547 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700549 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700550 t->audmode = V4L2_TUNER_MODE_STEREO;
551 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300552 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700554 if (show_i2c) {
555 unsigned char buffer[16];
556 int i,rc;
557
558 memset(buffer, 0, sizeof(buffer));
559 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800560 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700561 for (i=0;i<rc;i++)
562 printk("%02x ",buffer[i]);
563 printk("\n");
564 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300565 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
566 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
567 return -ENODEV;
568
Markus Rechberger257c6452006-01-23 17:11:11 -0200569 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700570 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800571 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300572#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300573 case 0x10:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300574 if (tea5761_autodetection(t) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300575 t->type = TUNER_TEA5761;
576 t->mode_mask = T_RADIO;
577 t->mode = T_STANDBY;
578 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
579 default_mode_mask &= ~T_RADIO;
580
581 goto register_client;
582 }
583 break;
584#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800585 case 0x42:
586 case 0x43:
587 case 0x4a:
588 case 0x4b:
589 /* If chip is not tda8290, don't register.
590 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300591 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300592 tuner_dbg("chip at addr %x is a tda8290\n", addr);
593 } else {
594 /* Default is being tda9887 */
595 t->type = TUNER_TDA9887;
596 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
597 t->mode = T_STANDBY;
598 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800599 }
600 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800601 case 0x60:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300602 if (tea5767_autodetection(t) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700603 t->type = TUNER_TEA5767;
604 t->mode_mask = T_RADIO;
605 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200606 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700607 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700608
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800609 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700610 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800611 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700612 }
613 }
614
615 /* Initializes only the first adapter found */
616 if (default_mode_mask != T_UNINITIALIZED) {
617 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
618 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200619 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
620 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700621 default_mode_mask = T_UNINITIALIZED;
622 }
623
624 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800625register_client:
626 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700627 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300628 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 return 0;
630}
631
632static int tuner_probe(struct i2c_adapter *adap)
633{
634 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700635 normal_i2c[0] = addr;
636 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700639 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700640
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 if (adap->class & I2C_CLASS_TV_ANALOG)
642 return i2c_probe(adap, &addr_data, tuner_attach);
643 return 0;
644}
645
646static int tuner_detach(struct i2c_client *client)
647{
648 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700649 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700651 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700652 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700653 tuner_warn
654 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700655 return err;
656 }
657
Michael Krufky7a91a802007-06-06 16:10:39 -0300658 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300659 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300660 else {
661 kfree(t->priv);
662 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 kfree(t);
664 return 0;
665}
666
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700667/*
668 * Switch tuner to other mode. If tuner support both tv and radio,
669 * set another frequency to some value (This is needed for some pal
670 * tuners to avoid locking). Otherwise, just put second tuner in
671 * standby mode.
672 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700674static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
675{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800676 if (mode == t->mode)
677 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700678
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800679 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700680
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800681 if (check_mode(t, cmd) == EINVAL) {
682 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300683 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300684 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800685 return EINVAL;
686 }
687 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700688}
689
690#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800691 tuner_dbg("switching to v4l2\n"); \
692 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700693
694static inline int check_v4l2(struct tuner *t)
695{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300696 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
697 TV, v4l1 for radio), until that is fixed this code is disabled.
698 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
699 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700700 return 0;
701}
702
703static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704{
705 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300706 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707
Hans Verkuilf9195de2006-01-11 19:01:01 -0200708 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200709 v4l_i2c_print_ioctl(&(t->i2c),cmd);
710
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700711 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700713 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300714 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 -0700715 ((struct tuner_setup *)arg)->type,
716 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300717 ((struct tuner_setup *)arg)->mode_mask,
718 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700719
720 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700721 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200723 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
724 == EINVAL)
725 return 0;
726 if (t->radio_freq)
727 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700729 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200730 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
731 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300732 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);
Hans Verkuil27487d42006-01-15 15:04:52 -0200735 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300736#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700737 case VIDIOCSAUDIO:
738 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
739 return 0;
740 if (check_v4l2(t) == EINVAL)
741 return 0;
742
743 /* Should be implemented, since bttv calls it */
744 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700745 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700747 {
748 static const v4l2_std_id map[] = {
749 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
750 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
751 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
752 [4 /* bttv */ ] = V4L2_STD_PAL_M,
753 [5 /* bttv */ ] = V4L2_STD_PAL_N,
754 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
755 };
756 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758 if (check_v4l2(t) == EINVAL)
759 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700760
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700761 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
762 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700764 if (vc->norm < ARRAY_SIZE(map))
765 t->std = map[vc->norm];
766 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200767 if (t->tv_freq)
768 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700769 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700770 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700771 case VIDIOCSFREQ:
772 {
773 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700774
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700775 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
776 return 0;
777 if (check_v4l2(t) == EINVAL)
778 return 0;
779
780 set_freq(client, *v);
781 return 0;
782 }
783 case VIDIOCGTUNER:
784 {
785 struct video_tuner *vt = arg;
786
787 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
788 return 0;
789 if (check_v4l2(t) == EINVAL)
790 return 0;
791
792 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300793 if (fe_tuner_ops->get_status) {
794 u32 tuner_status;
795
796 fe_tuner_ops->get_status(&t->fe, &tuner_status);
797 if (tuner_status & TUNER_STATUS_STEREO)
798 vt->flags |= VIDEO_TUNER_STEREO_ON;
799 else
800 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
801 vt->signal = tuner_status & TUNER_STATUS_LOCKED
802 ? 65535 : 0;
803 } else {
804 if (t->ops.is_stereo) {
805 if (t->ops.is_stereo(t))
806 vt->flags |=
807 VIDEO_TUNER_STEREO_ON;
808 else
809 vt->flags &=
810 ~VIDEO_TUNER_STEREO_ON;
811 }
812 if (t->ops.has_signal)
813 vt->signal = t->ops.has_signal(t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700814 }
815 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
816
817 vt->rangelow = radio_range[0] * 16000;
818 vt->rangehigh = radio_range[1] * 16000;
819
820 } else {
821 vt->rangelow = tv_range[0] * 16;
822 vt->rangehigh = tv_range[1] * 16;
823 }
824
825 return 0;
826 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700828 {
829 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700831 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
832 return 0;
833 if (check_v4l2(t) == EINVAL)
834 return 0;
835
Michael Krufkye18f9442007-08-21 01:25:48 -0300836 if (V4L2_TUNER_RADIO == t->mode) {
837 if (fe_tuner_ops->get_status) {
838 u32 tuner_status;
839
840 fe_tuner_ops->get_status(&t->fe, &tuner_status);
841 va->mode = (tuner_status & TUNER_STATUS_STEREO)
842 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
843 } else if (t->ops.is_stereo)
844 va->mode = t->ops.is_stereo(t)
845 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
846 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700847 return 0;
848 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300849#endif
850 case TDA9887_SET_CONFIG:
851 if (t->type == TUNER_TDA9887) {
852 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300854 t->tda9887_config = *i;
855 set_freq(client, t->tv_freq);
856 }
857 break;
858 /* --- v4l ioctls --- */
859 /* take care: bttv does userspace copying, we'll get a
860 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700862 {
863 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700865 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
866 == EINVAL)
867 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700868
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700869 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700871 t->std = *id;
872 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200873 if (t->tv_freq)
874 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700875 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700876 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700877 case VIDIOC_S_FREQUENCY:
878 {
879 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700880
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300881 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
882 == EINVAL)
883 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700884 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200885 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700886
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700887 break;
888 }
889 case VIDIOC_G_FREQUENCY:
890 {
891 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700892
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700893 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
894 return 0;
895 switch_v4l2();
896 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300897 if (fe_tuner_ops->get_frequency) {
898 u32 abs_freq;
899
900 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
901 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
902 (abs_freq * 2 + 125/2) / 125 :
903 (abs_freq + 62500/2) / 62500;
904 break;
905 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200906 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
907 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700908 break;
909 }
910 case VIDIOC_G_TUNER:
911 {
912 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700913
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700914 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
915 return 0;
916 switch_v4l2();
917
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200918 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300919 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300920 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300921 if (t->mode == V4L2_TUNER_ANALOG_TV)
922 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200923 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700924 tuner->rangelow = tv_range[0] * 16;
925 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200926 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700927 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200928
929 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200930 tuner->rxsubchans =
931 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300932 if (fe_tuner_ops->get_status) {
933 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200934
Michael Krufkye18f9442007-08-21 01:25:48 -0300935 fe_tuner_ops->get_status(&t->fe, &tuner_status);
936 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
937 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
938 tuner->signal = tuner_status & TUNER_STATUS_LOCKED ? 65535 : 0;
939 } else {
940 if (t->ops.is_stereo) {
941 tuner->rxsubchans = t->ops.is_stereo(t) ?
942 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
943 }
944 if (t->ops.has_signal)
945 tuner->signal = t->ops.has_signal(t);
946 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200947 tuner->capability |=
948 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
949 tuner->audmode = t->audmode;
950 tuner->rangelow = radio_range[0] * 16000;
951 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700952 break;
953 }
954 case VIDIOC_S_TUNER:
955 {
956 struct v4l2_tuner *tuner = arg;
957
958 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
959 return 0;
960
961 switch_v4l2();
962
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200963 /* do nothing unless we're a radio tuner */
964 if (t->mode != V4L2_TUNER_RADIO)
965 break;
966 t->audmode = tuner->audmode;
967 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700968 break;
969 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200970 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300971 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300972 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200973 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 }
975
976 return 0;
977}
978
Jean Delvare21b48a72007-03-12 19:20:15 -0300979static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700981 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700983 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 /* FIXME: power down ??? */
985 return 0;
986}
987
Jean Delvare21b48a72007-03-12 19:20:15 -0300988static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700990 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700992 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200993 if (V4L2_TUNER_RADIO == t->mode) {
994 if (t->radio_freq)
995 set_freq(c, t->radio_freq);
996 } else {
997 if (t->tv_freq)
998 set_freq(c, t->tv_freq);
999 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 return 0;
1001}
1002
1003/* ----------------------------------------------------------------------- */
1004
1005static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001006 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001007 .attach_adapter = tuner_probe,
1008 .detach_client = tuner_detach,
1009 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001010 .suspend = tuner_suspend,
1011 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001013 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001014 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001016static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001017 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001018 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019};
1020
1021static int __init tuner_init_module(void)
1022{
1023 return i2c_add_driver(&driver);
1024}
1025
1026static void __exit tuner_cleanup_module(void)
1027{
1028 i2c_del_driver(&driver);
1029}
1030
1031module_init(tuner_init_module);
1032module_exit(tuner_cleanup_module);
1033
1034/*
1035 * Overrides for Emacs so that we follow Linus's tabbing style.
1036 * ---------------------------------------------------------------------------
1037 * Local variables:
1038 * c-basic-offset: 8
1039 * End:
1040 */