blob: 183bbb9ba6ab873cff4aa066f1e3f525ddb62bc1 [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
23#define UNSET (-1U)
24
25/* standard i2c insmod options */
26static unsigned short normal_i2c[] = {
Adrian Bunk9ee476a2007-06-05 05:22:00 -030027#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030028 0x10,
29#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080030 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070031 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
32 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 I2C_CLIENT_END
34};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070035
Linus Torvalds1da177e2005-04-16 15:20:36 -070036I2C_CLIENT_INSMOD;
37
38/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070039static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070040static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070041static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020044int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070046static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070047static unsigned int radio_range[2] = { 65, 108 };
48
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020049static char pal[] = "--";
50static char secam[] = "--";
51static char ntsc[] = "-";
52
Hans Verkuilf9195de2006-01-11 19:01:01 -020053
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020054module_param(addr, int, 0444);
55module_param(no_autodetect, int, 0444);
56module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020057module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020058module_param_string(pal, pal, sizeof(pal), 0644);
59module_param_string(secam, secam, sizeof(secam), 0644);
60module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070061module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062module_param_array(radio_range, int, NULL, 0644);
63
64MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
65MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
66MODULE_LICENSE("GPL");
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068static struct i2c_driver driver;
69static struct i2c_client client_template;
70
71/* ---------------------------------------------------------------------- */
72
Michael Krufkye18f9442007-08-21 01:25:48 -030073static void fe_set_freq(struct tuner *t, unsigned int freq)
74{
75 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
76
77 struct analog_parameters params = {
78 .frequency = freq,
79 .mode = t->mode,
80 .audmode = t->audmode,
81 .std = t->std
82 };
83
84 if (NULL == fe_tuner_ops->set_analog_params) {
85 tuner_warn("Tuner frontend module has no way to set freq\n");
86 return;
87 }
88 fe_tuner_ops->set_analog_params(&t->fe, &params);
89}
90
91static void fe_release(struct tuner *t)
92{
93 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
94
95 if (fe_tuner_ops->release)
96 fe_tuner_ops->release(&t->fe);
97}
98
99static void fe_standby(struct tuner *t)
100{
101 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
102
103 if (fe_tuner_ops->sleep)
104 fe_tuner_ops->sleep(&t->fe);
105}
106
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700107/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108static void set_tv_freq(struct i2c_client *c, unsigned int freq)
109{
110 struct tuner *t = i2c_get_clientdata(c);
111
112 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700113 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 return;
115 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300116 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700117 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 return;
119 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700120 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
121 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
122 freq / 16, freq % 16 * 100 / 16, tv_range[0],
123 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200124 /* V4L2 spec: if the freq is not possible then the closest
125 possible value should be selected */
126 if (freq < tv_range[0] * 16)
127 freq = tv_range[0] * 16;
128 else
129 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300131 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132}
133
134static void set_radio_freq(struct i2c_client *c, unsigned int freq)
135{
136 struct tuner *t = i2c_get_clientdata(c);
137
138 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700139 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 return;
141 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300142 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700143 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 return;
145 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200146 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700147 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
148 freq / 16000, freq % 16000 * 100 / 16000,
149 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200150 /* V4L2 spec: if the freq is not possible then the closest
151 possible value should be selected */
152 if (freq < radio_range[0] * 16000)
153 freq = radio_range[0] * 16000;
154 else
155 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700157
Michael Krufkydb8a6952007-08-21 01:24:42 -0300158 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159}
160
161static void set_freq(struct i2c_client *c, unsigned long freq)
162{
163 struct tuner *t = i2c_get_clientdata(c);
164
165 switch (t->mode) {
166 case V4L2_TUNER_RADIO:
167 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700168 freq / 16000, freq % 16000 * 100 / 16000);
169 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200170 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 break;
172 case V4L2_TUNER_ANALOG_TV:
173 case V4L2_TUNER_DIGITAL_TV:
174 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700175 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200177 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 break;
179 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
Michael Krufky293197c2007-08-28 17:20:42 -0300182static void tuner_i2c_address_check(struct tuner *t)
183{
184 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
185 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
186 return;
187
188 tuner_warn("====================== WARNING! ======================\n");
189 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
190 tuner_warn("will soon be dropped. This message indicates that your\n");
191 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
192 t->i2c.name, t->i2c.addr);
193 tuner_warn("To ensure continued support for your device, please\n");
194 tuner_warn("send a copy of this message, along with full dmesg\n");
195 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
196 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
197 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
198 t->i2c.adapter->name, t->i2c.addr, t->type,
199 tuners[t->type].name);
200 tuner_warn("====================== WARNING! ======================\n");
201}
202
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700203static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300204 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300205 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
207 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300208 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700209 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700211 if (type == UNSET || type == TUNER_ABSENT) {
212 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 return;
214 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700216 if (type >= tuner_count) {
217 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
218 return;
219 }
220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300222 t->config = new_config;
223 if (tuner_callback != NULL) {
224 tuner_dbg("defining GPIO callback\n");
225 t->tuner_callback = tuner_callback;
226 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300227
228 /* This code detects calls by card attach_inform */
229 if (NULL == t->i2c.dev.driver) {
230 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
231
232 return;
233 }
234
Michael Krufkyb2083192007-05-29 22:54:06 -0300235 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300236 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300237 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300238 else {
239 kfree(t->priv);
240 t->priv = NULL;
241 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300242
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 switch (t->type) {
244 case TUNER_MT2032:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300245 microtune_init(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 break;
247 case TUNER_PHILIPS_TDA8290:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300248 tda8290_init(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700250 case TUNER_TEA5767:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300251 if (tea5767_tuner_init(t) == EINVAL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700252 t->type = TUNER_ABSENT;
253 t->mode_mask = T_UNINITIALIZED;
254 return;
255 }
256 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700257 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300258#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300259 case TUNER_TEA5761:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300260 if (tea5761_tuner_init(t) == EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300261 t->type = TUNER_ABSENT;
262 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300263 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300264 }
265 t->mode_mask = T_RADIO;
266 break;
267#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700268 case TUNER_PHILIPS_FMD1216ME_MK3:
269 buffer[0] = 0x0b;
270 buffer[1] = 0xdc;
271 buffer[2] = 0x9c;
272 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700273 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700274 mdelay(1);
275 buffer[2] = 0x86;
276 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700277 i2c_master_send(c, buffer, 4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300278 default_tuner_init(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700279 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800280 case TUNER_PHILIPS_TD1316:
281 buffer[0] = 0x0b;
282 buffer[1] = 0xdc;
283 buffer[2] = 0x86;
284 buffer[3] = 0xa4;
285 i2c_master_send(c,buffer,4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300286 default_tuner_init(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200287 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300288 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300289 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300290 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 default:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300292 default_tuner_init(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 break;
294 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700295
Michael Krufkye18f9442007-08-21 01:25:48 -0300296 if (fe_tuner_ops->set_analog_params) {
297 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
298
299 t->ops.set_tv_freq = fe_set_freq;
300 t->ops.set_radio_freq = fe_set_freq;
301 t->ops.standby = fe_standby;
302 t->ops.release = fe_release;
303 }
304
305 tuner_info("type set to %s\n", t->i2c.name);
306
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700307 if (t->mode_mask == T_UNINITIALIZED)
308 t->mode_mask = new_mode_mask;
309
Hans Verkuil27487d42006-01-15 15:04:52 -0200310 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700311 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100312 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700313 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300314 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315}
316
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700317/*
318 * This function apply tuner config to tuner specified
319 * by tun_setup structure. I addr is unset, then admin status
320 * and tun addr status is more precise then current status,
321 * it's applied. Otherwise status and type are applied only to
322 * tuner with exactly the same addr.
323*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700324
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700325static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700326{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700327 struct tuner *t = i2c_get_clientdata(c);
328
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300329 tuner_dbg("set addr for type %i\n", t->type);
330
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300331 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
332 (t->mode_mask & tun_setup->mode_mask))) ||
333 (tun_setup->addr == c->addr)) {
334 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300335 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700336 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700337}
338
339static inline int check_mode(struct tuner *t, char *cmd)
340{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700341 if ((1 << t->mode & t->mode_mask) == 0) {
342 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700343 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700344
345 switch (t->mode) {
346 case V4L2_TUNER_RADIO:
347 tuner_dbg("Cmd %s accepted for radio\n", cmd);
348 break;
349 case V4L2_TUNER_ANALOG_TV:
350 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
351 break;
352 case V4L2_TUNER_DIGITAL_TV:
353 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
354 break;
355 }
356 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700357}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700358
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700359/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360static int tuner_fixup_std(struct tuner *t)
361{
362 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300364 case '6':
365 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
366 t->std = V4L2_STD_PAL_60;
367 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 case 'b':
369 case 'B':
370 case 'g':
371 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700372 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 t->std = V4L2_STD_PAL_BG;
374 break;
375 case 'i':
376 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700377 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 t->std = V4L2_STD_PAL_I;
379 break;
380 case 'd':
381 case 'D':
382 case 'k':
383 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700384 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 t->std = V4L2_STD_PAL_DK;
386 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700387 case 'M':
388 case 'm':
389 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
390 t->std = V4L2_STD_PAL_M;
391 break;
392 case 'N':
393 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200394 if (pal[1] == 'c' || pal[1] == 'C') {
395 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
396 t->std = V4L2_STD_PAL_Nc;
397 } else {
398 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
399 t->std = V4L2_STD_PAL_N;
400 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700401 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700402 case '-':
403 /* default parameter, do nothing */
404 break;
405 default:
406 tuner_warn ("pal= argument not recognised\n");
407 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 }
409 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700410 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
411 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200412 case 'b':
413 case 'B':
414 case 'g':
415 case 'G':
416 case 'h':
417 case 'H':
418 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
419 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
420 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700421 case 'd':
422 case 'D':
423 case 'k':
424 case 'K':
425 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
426 t->std = V4L2_STD_SECAM_DK;
427 break;
428 case 'l':
429 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800430 if ((secam[1]=='C')||(secam[1]=='c')) {
431 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
432 t->std = V4L2_STD_SECAM_LC;
433 } else {
434 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
435 t->std = V4L2_STD_SECAM_L;
436 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700437 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700438 case '-':
439 /* default parameter, do nothing */
440 break;
441 default:
442 tuner_warn ("secam= argument not recognised\n");
443 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700444 }
445 }
446
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200447 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
448 switch (ntsc[0]) {
449 case 'm':
450 case 'M':
451 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
452 t->std = V4L2_STD_NTSC_M;
453 break;
454 case 'j':
455 case 'J':
456 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
457 t->std = V4L2_STD_NTSC_M_JP;
458 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200459 case 'k':
460 case 'K':
461 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
462 t->std = V4L2_STD_NTSC_M_KR;
463 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200464 case '-':
465 /* default parameter, do nothing */
466 break;
467 default:
468 tuner_info("ntsc= argument not recognised\n");
469 break;
470 }
471 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 return 0;
473}
474
Michael Krufkydb8a6952007-08-21 01:24:42 -0300475static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200476{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200477 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300478 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200479 const char *p;
480
481 switch (t->mode) {
482 case V4L2_TUNER_RADIO: p = "radio"; break;
483 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
484 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
485 default: p = "undefined"; break;
486 }
487 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200488 freq = t->radio_freq / 16000;
489 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200490 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200491 freq = t->tv_freq / 16;
492 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200493 }
494 tuner_info("Tuner mode: %s\n", p);
495 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300496 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200497 if (t->mode != V4L2_TUNER_RADIO)
498 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300499 if (fe_tuner_ops->get_status) {
500 u32 tuner_status;
501
502 fe_tuner_ops->get_status(&t->fe, &tuner_status);
503 if (tuner_status & TUNER_STATUS_LOCKED)
504 tuner_info("Tuner is locked.\n");
505 if (tuner_status & TUNER_STATUS_STEREO)
506 tuner_info("Stereo: yes\n");
507 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300508 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300509 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200510 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300511 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300512 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200513 }
514}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200515
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516/* ---------------------------------------------------------------------- */
517
Hans Verkuilba8fc392006-06-25 15:34:39 -0300518/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700519static unsigned default_mode_mask;
520
521/* During client attach, set_type is called by adapter's attach_inform callback.
522 set_type must then be completed by tuner_attach.
523 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
525{
526 struct tuner *t;
527
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700528 client_template.adapter = adap;
529 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
Panagiotis Issaris74081872006-01-11 19:40:56 -0200531 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700532 if (NULL == t)
533 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700534 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700536 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700537 t->audmode = V4L2_TUNER_MODE_STEREO;
538 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300539 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700541 if (show_i2c) {
542 unsigned char buffer[16];
543 int i,rc;
544
545 memset(buffer, 0, sizeof(buffer));
546 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800547 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700548 for (i=0;i<rc;i++)
549 printk("%02x ",buffer[i]);
550 printk("\n");
551 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300552 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
553 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
554 return -ENODEV;
555
Markus Rechberger257c6452006-01-23 17:11:11 -0200556 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700557 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800558 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300559#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300560 case 0x10:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300561 if (tea5761_autodetection(t) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300562 t->type = TUNER_TEA5761;
563 t->mode_mask = T_RADIO;
564 t->mode = T_STANDBY;
565 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
566 default_mode_mask &= ~T_RADIO;
567
568 goto register_client;
569 }
570 break;
571#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800572 case 0x42:
573 case 0x43:
574 case 0x4a:
575 case 0x4b:
576 /* If chip is not tda8290, don't register.
577 since it can be tda9887*/
Michael Krufkydb8a6952007-08-21 01:24:42 -0300578 if (tda8290_probe(t) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300579 tuner_dbg("chip at addr %x is a tda8290\n", addr);
580 } else {
581 /* Default is being tda9887 */
582 t->type = TUNER_TDA9887;
583 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
584 t->mode = T_STANDBY;
585 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800586 }
587 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800588 case 0x60:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300589 if (tea5767_autodetection(t) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700590 t->type = TUNER_TEA5767;
591 t->mode_mask = T_RADIO;
592 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200593 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700594 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700595
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800596 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700597 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800598 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700599 }
600 }
601
602 /* Initializes only the first adapter found */
603 if (default_mode_mask != T_UNINITIALIZED) {
604 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
605 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200606 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
607 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700608 default_mode_mask = T_UNINITIALIZED;
609 }
610
611 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800612register_client:
613 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700614 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300615 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 return 0;
617}
618
619static int tuner_probe(struct i2c_adapter *adap)
620{
621 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700622 normal_i2c[0] = addr;
623 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700626 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700627
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 if (adap->class & I2C_CLASS_TV_ANALOG)
629 return i2c_probe(adap, &addr_data, tuner_attach);
630 return 0;
631}
632
633static int tuner_detach(struct i2c_client *client)
634{
635 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700636 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700638 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700639 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700640 tuner_warn
641 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700642 return err;
643 }
644
Michael Krufky7a91a802007-06-06 16:10:39 -0300645 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300646 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300647 else {
648 kfree(t->priv);
649 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 kfree(t);
651 return 0;
652}
653
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700654/*
655 * Switch tuner to other mode. If tuner support both tv and radio,
656 * set another frequency to some value (This is needed for some pal
657 * tuners to avoid locking). Otherwise, just put second tuner in
658 * standby mode.
659 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700661static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
662{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800663 if (mode == t->mode)
664 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700665
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800666 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700667
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800668 if (check_mode(t, cmd) == EINVAL) {
669 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300670 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300671 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800672 return EINVAL;
673 }
674 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700675}
676
677#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800678 tuner_dbg("switching to v4l2\n"); \
679 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680
681static inline int check_v4l2(struct tuner *t)
682{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300683 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
684 TV, v4l1 for radio), until that is fixed this code is disabled.
685 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
686 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700687 return 0;
688}
689
690static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
692 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300693 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Hans Verkuilf9195de2006-01-11 19:01:01 -0200695 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200696 v4l_i2c_print_ioctl(&(t->i2c),cmd);
697
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700698 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700700 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300701 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 -0700702 ((struct tuner_setup *)arg)->type,
703 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300704 ((struct tuner_setup *)arg)->mode_mask,
705 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700706
707 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700708 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200710 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
711 == EINVAL)
712 return 0;
713 if (t->radio_freq)
714 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700716 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200717 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
718 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300719 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300720 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300721 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200722 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300723#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700724 case VIDIOCSAUDIO:
725 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
726 return 0;
727 if (check_v4l2(t) == EINVAL)
728 return 0;
729
730 /* Should be implemented, since bttv calls it */
731 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700732 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700734 {
735 static const v4l2_std_id map[] = {
736 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
737 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
738 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
739 [4 /* bttv */ ] = V4L2_STD_PAL_M,
740 [5 /* bttv */ ] = V4L2_STD_PAL_N,
741 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
742 };
743 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700745 if (check_v4l2(t) == EINVAL)
746 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700747
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700748 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
749 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700751 if (vc->norm < ARRAY_SIZE(map))
752 t->std = map[vc->norm];
753 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200754 if (t->tv_freq)
755 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700756 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700757 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758 case VIDIOCSFREQ:
759 {
760 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700761
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700762 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
763 return 0;
764 if (check_v4l2(t) == EINVAL)
765 return 0;
766
767 set_freq(client, *v);
768 return 0;
769 }
770 case VIDIOCGTUNER:
771 {
772 struct video_tuner *vt = arg;
773
774 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
775 return 0;
776 if (check_v4l2(t) == EINVAL)
777 return 0;
778
779 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300780 if (fe_tuner_ops->get_status) {
781 u32 tuner_status;
782
783 fe_tuner_ops->get_status(&t->fe, &tuner_status);
784 if (tuner_status & TUNER_STATUS_STEREO)
785 vt->flags |= VIDEO_TUNER_STEREO_ON;
786 else
787 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
788 vt->signal = tuner_status & TUNER_STATUS_LOCKED
789 ? 65535 : 0;
790 } else {
791 if (t->ops.is_stereo) {
792 if (t->ops.is_stereo(t))
793 vt->flags |=
794 VIDEO_TUNER_STEREO_ON;
795 else
796 vt->flags &=
797 ~VIDEO_TUNER_STEREO_ON;
798 }
799 if (t->ops.has_signal)
800 vt->signal = t->ops.has_signal(t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700801 }
802 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
803
804 vt->rangelow = radio_range[0] * 16000;
805 vt->rangehigh = radio_range[1] * 16000;
806
807 } else {
808 vt->rangelow = tv_range[0] * 16;
809 vt->rangehigh = tv_range[1] * 16;
810 }
811
812 return 0;
813 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700815 {
816 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700818 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
819 return 0;
820 if (check_v4l2(t) == EINVAL)
821 return 0;
822
Michael Krufkye18f9442007-08-21 01:25:48 -0300823 if (V4L2_TUNER_RADIO == t->mode) {
824 if (fe_tuner_ops->get_status) {
825 u32 tuner_status;
826
827 fe_tuner_ops->get_status(&t->fe, &tuner_status);
828 va->mode = (tuner_status & TUNER_STATUS_STEREO)
829 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
830 } else if (t->ops.is_stereo)
831 va->mode = t->ops.is_stereo(t)
832 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
833 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700834 return 0;
835 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300836#endif
837 case TDA9887_SET_CONFIG:
838 if (t->type == TUNER_TDA9887) {
839 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300841 t->tda9887_config = *i;
842 set_freq(client, t->tv_freq);
843 }
844 break;
845 /* --- v4l ioctls --- */
846 /* take care: bttv does userspace copying, we'll get a
847 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700849 {
850 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700852 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
853 == EINVAL)
854 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700855
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700856 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700858 t->std = *id;
859 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200860 if (t->tv_freq)
861 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700862 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700863 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700864 case VIDIOC_S_FREQUENCY:
865 {
866 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700867
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300868 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
869 == EINVAL)
870 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700871 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200872 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700873
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700874 break;
875 }
876 case VIDIOC_G_FREQUENCY:
877 {
878 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700879
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700880 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
881 return 0;
882 switch_v4l2();
883 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300884 if (fe_tuner_ops->get_frequency) {
885 u32 abs_freq;
886
887 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
888 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
889 (abs_freq * 2 + 125/2) / 125 :
890 (abs_freq + 62500/2) / 62500;
891 break;
892 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200893 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
894 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700895 break;
896 }
897 case VIDIOC_G_TUNER:
898 {
899 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700900
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700901 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
902 return 0;
903 switch_v4l2();
904
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200905 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300906 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300907 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300908 if (t->mode == V4L2_TUNER_ANALOG_TV)
909 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200910 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700911 tuner->rangelow = tv_range[0] * 16;
912 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200913 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700914 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200915
916 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200917 tuner->rxsubchans =
918 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300919 if (fe_tuner_ops->get_status) {
920 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200921
Michael Krufkye18f9442007-08-21 01:25:48 -0300922 fe_tuner_ops->get_status(&t->fe, &tuner_status);
923 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
924 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
925 tuner->signal = tuner_status & TUNER_STATUS_LOCKED ? 65535 : 0;
926 } else {
927 if (t->ops.is_stereo) {
928 tuner->rxsubchans = t->ops.is_stereo(t) ?
929 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
930 }
931 if (t->ops.has_signal)
932 tuner->signal = t->ops.has_signal(t);
933 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200934 tuner->capability |=
935 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
936 tuner->audmode = t->audmode;
937 tuner->rangelow = radio_range[0] * 16000;
938 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700939 break;
940 }
941 case VIDIOC_S_TUNER:
942 {
943 struct v4l2_tuner *tuner = arg;
944
945 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
946 return 0;
947
948 switch_v4l2();
949
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200950 /* do nothing unless we're a radio tuner */
951 if (t->mode != V4L2_TUNER_RADIO)
952 break;
953 t->audmode = tuner->audmode;
954 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700955 break;
956 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200957 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300958 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300959 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200960 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 }
962
963 return 0;
964}
965
Jean Delvare21b48a72007-03-12 19:20:15 -0300966static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700968 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700970 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 /* FIXME: power down ??? */
972 return 0;
973}
974
Jean Delvare21b48a72007-03-12 19:20:15 -0300975static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700977 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700979 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200980 if (V4L2_TUNER_RADIO == t->mode) {
981 if (t->radio_freq)
982 set_freq(c, t->radio_freq);
983 } else {
984 if (t->tv_freq)
985 set_freq(c, t->tv_freq);
986 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 return 0;
988}
989
990/* ----------------------------------------------------------------------- */
991
992static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700993 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700994 .attach_adapter = tuner_probe,
995 .detach_client = tuner_detach,
996 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300997 .suspend = tuner_suspend,
998 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001000 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001001 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001003static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001004 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001005 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006};
1007
1008static int __init tuner_init_module(void)
1009{
1010 return i2c_add_driver(&driver);
1011}
1012
1013static void __exit tuner_cleanup_module(void)
1014{
1015 i2c_del_driver(&driver);
1016}
1017
1018module_init(tuner_init_module);
1019module_exit(tuner_cleanup_module);
1020
1021/*
1022 * Overrides for Emacs so that we follow Linus's tabbing style.
1023 * ---------------------------------------------------------------------------
1024 * Local variables:
1025 * c-basic-offset: 8
1026 * End:
1027 */