blob: 8ee0be261076d7a51cf68a45a603315727954d34 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * i2c tv tuner chip device driver
4 * core core, i.e. kernel interfaces, registering and so on
5 */
6
7#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/string.h>
10#include <linux/timer.h>
11#include <linux/delay.h>
12#include <linux/errno.h>
13#include <linux/slab.h>
14#include <linux/poll.h>
15#include <linux/i2c.h>
16#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/init.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030018#include <linux/videodev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <media/tuner.h>
Michael Krufky4adad282007-08-27 21:59:08 -030020#include <media/tuner-types.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020021#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030022#include "tuner-driver.h"
Michael Krufky96c0b7c2007-08-27 21:23:08 -030023#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030024#include "tda8290.h"
Michael Krufky7ab10bf2007-08-27 21:23:40 -030025#include "tea5761.h"
Michael Krufky8d0936e2007-08-27 21:24:27 -030026#include "tea5767.h"
Michael Krufky4adad282007-08-27 21:59:08 -030027#include "tuner-simple.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#define UNSET (-1U)
30
31/* standard i2c insmod options */
32static unsigned short normal_i2c[] = {
Adrian Bunk9ee476a2007-06-05 05:22:00 -030033#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030034 0x10,
35#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080036 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070037 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
38 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 I2C_CLIENT_END
40};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070041
Linus Torvalds1da177e2005-04-16 15:20:36 -070042I2C_CLIENT_INSMOD;
43
44/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070045static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070046static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070047static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070048
Linus Torvalds1da177e2005-04-16 15:20:36 -070049/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020050int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070052static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070053static unsigned int radio_range[2] = { 65, 108 };
54
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020055static char pal[] = "--";
56static char secam[] = "--";
57static char ntsc[] = "-";
58
Hans Verkuilf9195de2006-01-11 19:01:01 -020059
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020060module_param(addr, int, 0444);
61module_param(no_autodetect, int, 0444);
62module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020063module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020064module_param_string(pal, pal, sizeof(pal), 0644);
65module_param_string(secam, secam, sizeof(secam), 0644);
66module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070067module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068module_param_array(radio_range, int, NULL, 0644);
69
70MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
71MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
72MODULE_LICENSE("GPL");
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074static struct i2c_driver driver;
75static struct i2c_client client_template;
76
77/* ---------------------------------------------------------------------- */
78
Michael Krufkye18f9442007-08-21 01:25:48 -030079static void fe_set_freq(struct tuner *t, unsigned int freq)
80{
81 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
82
83 struct analog_parameters params = {
84 .frequency = freq,
85 .mode = t->mode,
86 .audmode = t->audmode,
87 .std = t->std
88 };
89
90 if (NULL == fe_tuner_ops->set_analog_params) {
91 tuner_warn("Tuner frontend module has no way to set freq\n");
92 return;
93 }
94 fe_tuner_ops->set_analog_params(&t->fe, &params);
95}
96
97static void fe_release(struct tuner *t)
98{
99 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
100
101 if (fe_tuner_ops->release)
102 fe_tuner_ops->release(&t->fe);
103}
104
105static void fe_standby(struct tuner *t)
106{
107 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
108
109 if (fe_tuner_ops->sleep)
110 fe_tuner_ops->sleep(&t->fe);
111}
112
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700113/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114static void set_tv_freq(struct i2c_client *c, unsigned int freq)
115{
116 struct tuner *t = i2c_get_clientdata(c);
117
118 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700119 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 return;
121 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300122 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700123 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 return;
125 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700126 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
127 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
128 freq / 16, freq % 16 * 100 / 16, tv_range[0],
129 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200130 /* V4L2 spec: if the freq is not possible then the closest
131 possible value should be selected */
132 if (freq < tv_range[0] * 16)
133 freq = tv_range[0] * 16;
134 else
135 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300137 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138}
139
140static void set_radio_freq(struct i2c_client *c, unsigned int freq)
141{
142 struct tuner *t = i2c_get_clientdata(c);
143
144 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700145 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 return;
147 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300148 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700149 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 return;
151 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200152 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700153 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
154 freq / 16000, freq % 16000 * 100 / 16000,
155 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200156 /* V4L2 spec: if the freq is not possible then the closest
157 possible value should be selected */
158 if (freq < radio_range[0] * 16000)
159 freq = radio_range[0] * 16000;
160 else
161 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700163
Michael Krufkydb8a6952007-08-21 01:24:42 -0300164 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165}
166
167static void set_freq(struct i2c_client *c, unsigned long freq)
168{
169 struct tuner *t = i2c_get_clientdata(c);
170
171 switch (t->mode) {
172 case V4L2_TUNER_RADIO:
173 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700174 freq / 16000, freq % 16000 * 100 / 16000);
175 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200176 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 break;
178 case V4L2_TUNER_ANALOG_TV:
179 case V4L2_TUNER_DIGITAL_TV:
180 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700181 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200183 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 break;
185 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186}
187
Michael Krufky293197c2007-08-28 17:20:42 -0300188static void tuner_i2c_address_check(struct tuner *t)
189{
190 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
191 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
192 return;
193
194 tuner_warn("====================== WARNING! ======================\n");
195 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
196 tuner_warn("will soon be dropped. This message indicates that your\n");
197 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
198 t->i2c.name, t->i2c.addr);
199 tuner_warn("To ensure continued support for your device, please\n");
200 tuner_warn("send a copy of this message, along with full dmesg\n");
201 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
202 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
203 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
204 t->i2c.adapter->name, t->i2c.addr, t->type,
205 tuners[t->type].name);
206 tuner_warn("====================== WARNING! ======================\n");
207}
208
Michael Krufky910bb3e2007-08-27 21:22:20 -0300209static void attach_tda8290(struct tuner *t)
210{
211 struct tda8290_config cfg = {
212 .lna_cfg = &t->config,
213 .tuner_callback = t->tuner_callback
214 };
215 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
216}
217
Michael Krufky4adad282007-08-27 21:59:08 -0300218static void attach_simple_tuner(struct tuner *t)
219{
220 struct simple_tuner_config cfg = {
221 .type = t->type,
222 .tun = &tuners[t->type]
223 };
224 simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
225}
226
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700227static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300228 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300229 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
231 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300232 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700233 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700235 if (type == UNSET || type == TUNER_ABSENT) {
236 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 return;
238 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700240 if (type >= tuner_count) {
241 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
242 return;
243 }
244
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300246 t->config = new_config;
247 if (tuner_callback != NULL) {
248 tuner_dbg("defining GPIO callback\n");
249 t->tuner_callback = tuner_callback;
250 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300251
252 /* This code detects calls by card attach_inform */
253 if (NULL == t->i2c.dev.driver) {
254 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
255
256 return;
257 }
258
Michael Krufkyb2083192007-05-29 22:54:06 -0300259 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300260 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300261 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300262 else {
263 kfree(t->priv);
264 t->priv = NULL;
265 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300266
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 switch (t->type) {
268 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300269 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 break;
271 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300272 {
273 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300275 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700276 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300277 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700278 t->type = TUNER_ABSENT;
279 t->mode_mask = T_UNINITIALIZED;
280 return;
281 }
282 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700283 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300284#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300285 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300286 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300287 t->type = TUNER_ABSENT;
288 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300289 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300290 }
291 t->mode_mask = T_RADIO;
292 break;
293#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700294 case TUNER_PHILIPS_FMD1216ME_MK3:
295 buffer[0] = 0x0b;
296 buffer[1] = 0xdc;
297 buffer[2] = 0x9c;
298 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700299 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700300 mdelay(1);
301 buffer[2] = 0x86;
302 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700303 i2c_master_send(c, buffer, 4);
Michael Krufky4adad282007-08-27 21:59:08 -0300304 attach_simple_tuner(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700305 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800306 case TUNER_PHILIPS_TD1316:
307 buffer[0] = 0x0b;
308 buffer[1] = 0xdc;
309 buffer[2] = 0x86;
310 buffer[3] = 0xa4;
311 i2c_master_send(c,buffer,4);
Michael Krufky4adad282007-08-27 21:59:08 -0300312 attach_simple_tuner(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200313 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300314 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300315 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300316 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 default:
Michael Krufky4adad282007-08-27 21:59:08 -0300318 attach_simple_tuner(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 break;
320 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700321
Michael Krufkye18f9442007-08-21 01:25:48 -0300322 if (fe_tuner_ops->set_analog_params) {
323 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
324
325 t->ops.set_tv_freq = fe_set_freq;
326 t->ops.set_radio_freq = fe_set_freq;
327 t->ops.standby = fe_standby;
328 t->ops.release = fe_release;
329 }
330
331 tuner_info("type set to %s\n", t->i2c.name);
332
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700333 if (t->mode_mask == T_UNINITIALIZED)
334 t->mode_mask = new_mode_mask;
335
Hans Verkuil27487d42006-01-15 15:04:52 -0200336 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700337 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100338 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700339 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300340 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341}
342
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700343/*
344 * This function apply tuner config to tuner specified
345 * by tun_setup structure. I addr is unset, then admin status
346 * and tun addr status is more precise then current status,
347 * it's applied. Otherwise status and type are applied only to
348 * tuner with exactly the same addr.
349*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700350
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700351static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700352{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700353 struct tuner *t = i2c_get_clientdata(c);
354
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300355 tuner_dbg("set addr for type %i\n", t->type);
356
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300357 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
358 (t->mode_mask & tun_setup->mode_mask))) ||
359 (tun_setup->addr == c->addr)) {
360 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300361 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700362 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700363}
364
365static inline int check_mode(struct tuner *t, char *cmd)
366{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700367 if ((1 << t->mode & t->mode_mask) == 0) {
368 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700369 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700370
371 switch (t->mode) {
372 case V4L2_TUNER_RADIO:
373 tuner_dbg("Cmd %s accepted for radio\n", cmd);
374 break;
375 case V4L2_TUNER_ANALOG_TV:
376 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
377 break;
378 case V4L2_TUNER_DIGITAL_TV:
379 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
380 break;
381 }
382 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700383}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700384
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700385/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386static int tuner_fixup_std(struct tuner *t)
387{
388 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300390 case '6':
391 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
392 t->std = V4L2_STD_PAL_60;
393 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 case 'b':
395 case 'B':
396 case 'g':
397 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700398 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 t->std = V4L2_STD_PAL_BG;
400 break;
401 case 'i':
402 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700403 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 t->std = V4L2_STD_PAL_I;
405 break;
406 case 'd':
407 case 'D':
408 case 'k':
409 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700410 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 t->std = V4L2_STD_PAL_DK;
412 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700413 case 'M':
414 case 'm':
415 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
416 t->std = V4L2_STD_PAL_M;
417 break;
418 case 'N':
419 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200420 if (pal[1] == 'c' || pal[1] == 'C') {
421 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
422 t->std = V4L2_STD_PAL_Nc;
423 } else {
424 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
425 t->std = V4L2_STD_PAL_N;
426 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700427 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700428 case '-':
429 /* default parameter, do nothing */
430 break;
431 default:
432 tuner_warn ("pal= argument not recognised\n");
433 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 }
435 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700436 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
437 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200438 case 'b':
439 case 'B':
440 case 'g':
441 case 'G':
442 case 'h':
443 case 'H':
444 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
445 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
446 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700447 case 'd':
448 case 'D':
449 case 'k':
450 case 'K':
451 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
452 t->std = V4L2_STD_SECAM_DK;
453 break;
454 case 'l':
455 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800456 if ((secam[1]=='C')||(secam[1]=='c')) {
457 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
458 t->std = V4L2_STD_SECAM_LC;
459 } else {
460 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
461 t->std = V4L2_STD_SECAM_L;
462 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700463 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700464 case '-':
465 /* default parameter, do nothing */
466 break;
467 default:
468 tuner_warn ("secam= argument not recognised\n");
469 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700470 }
471 }
472
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200473 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
474 switch (ntsc[0]) {
475 case 'm':
476 case 'M':
477 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
478 t->std = V4L2_STD_NTSC_M;
479 break;
480 case 'j':
481 case 'J':
482 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
483 t->std = V4L2_STD_NTSC_M_JP;
484 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200485 case 'k':
486 case 'K':
487 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
488 t->std = V4L2_STD_NTSC_M_KR;
489 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200490 case '-':
491 /* default parameter, do nothing */
492 break;
493 default:
494 tuner_info("ntsc= argument not recognised\n");
495 break;
496 }
497 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 return 0;
499}
500
Michael Krufkydb8a6952007-08-21 01:24:42 -0300501static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200502{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200503 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300504 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200505 const char *p;
506
507 switch (t->mode) {
508 case V4L2_TUNER_RADIO: p = "radio"; break;
509 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
510 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
511 default: p = "undefined"; break;
512 }
513 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200514 freq = t->radio_freq / 16000;
515 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200516 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200517 freq = t->tv_freq / 16;
518 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200519 }
520 tuner_info("Tuner mode: %s\n", p);
521 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300522 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200523 if (t->mode != V4L2_TUNER_RADIO)
524 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300525 if (fe_tuner_ops->get_status) {
526 u32 tuner_status;
527
528 fe_tuner_ops->get_status(&t->fe, &tuner_status);
529 if (tuner_status & TUNER_STATUS_LOCKED)
530 tuner_info("Tuner is locked.\n");
531 if (tuner_status & TUNER_STATUS_STEREO)
532 tuner_info("Stereo: yes\n");
533 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300534 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300535 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200536 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300537 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300538 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200539 }
540}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542/* ---------------------------------------------------------------------- */
543
Hans Verkuilba8fc392006-06-25 15:34:39 -0300544/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700545static unsigned default_mode_mask;
546
547/* During client attach, set_type is called by adapter's attach_inform callback.
548 set_type must then be completed by tuner_attach.
549 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
551{
552 struct tuner *t;
553
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700554 client_template.adapter = adap;
555 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Panagiotis Issaris74081872006-01-11 19:40:56 -0200557 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700558 if (NULL == t)
559 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700560 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700562 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700563 t->audmode = V4L2_TUNER_MODE_STEREO;
564 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300565 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700567 if (show_i2c) {
568 unsigned char buffer[16];
569 int i,rc;
570
571 memset(buffer, 0, sizeof(buffer));
572 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800573 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700574 for (i=0;i<rc;i++)
575 printk("%02x ",buffer[i]);
576 printk("\n");
577 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300578 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
579 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
580 return -ENODEV;
581
Markus Rechberger257c6452006-01-23 17:11:11 -0200582 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700583 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800584 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300585#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300586 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300587 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300588 t->type = TUNER_TEA5761;
589 t->mode_mask = T_RADIO;
590 t->mode = T_STANDBY;
591 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
592 default_mode_mask &= ~T_RADIO;
593
594 goto register_client;
595 }
596 break;
597#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800598 case 0x42:
599 case 0x43:
600 case 0x4a:
601 case 0x4b:
602 /* If chip is not tda8290, don't register.
603 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300604 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300605 tuner_dbg("chip at addr %x is a tda8290\n", addr);
606 } else {
607 /* Default is being tda9887 */
608 t->type = TUNER_TDA9887;
609 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
610 t->mode = T_STANDBY;
611 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800612 }
613 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800614 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300615 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700616 t->type = TUNER_TEA5767;
617 t->mode_mask = T_RADIO;
618 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200619 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700620 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700621
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800622 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700623 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800624 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700625 }
626 }
627
628 /* Initializes only the first adapter found */
629 if (default_mode_mask != T_UNINITIALIZED) {
630 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
631 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200632 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
633 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700634 default_mode_mask = T_UNINITIALIZED;
635 }
636
637 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800638register_client:
639 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700640 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300641 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 return 0;
643}
644
645static int tuner_probe(struct i2c_adapter *adap)
646{
647 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700648 normal_i2c[0] = addr;
649 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700652 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700653
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 if (adap->class & I2C_CLASS_TV_ANALOG)
655 return i2c_probe(adap, &addr_data, tuner_attach);
656 return 0;
657}
658
659static int tuner_detach(struct i2c_client *client)
660{
661 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700662 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700664 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700665 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700666 tuner_warn
667 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700668 return err;
669 }
670
Michael Krufky7a91a802007-06-06 16:10:39 -0300671 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300672 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300673 else {
674 kfree(t->priv);
675 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 kfree(t);
677 return 0;
678}
679
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680/*
681 * Switch tuner to other mode. If tuner support both tv and radio,
682 * set another frequency to some value (This is needed for some pal
683 * tuners to avoid locking). Otherwise, just put second tuner in
684 * standby mode.
685 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700687static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
688{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800689 if (mode == t->mode)
690 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700691
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800692 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700693
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800694 if (check_mode(t, cmd) == EINVAL) {
695 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300696 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300697 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800698 return EINVAL;
699 }
700 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700701}
702
703#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800704 tuner_dbg("switching to v4l2\n"); \
705 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700706
707static inline int check_v4l2(struct tuner *t)
708{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300709 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
710 TV, v4l1 for radio), until that is fixed this code is disabled.
711 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
712 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700713 return 0;
714}
715
716static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717{
718 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300719 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
Hans Verkuilf9195de2006-01-11 19:01:01 -0200721 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200722 v4l_i2c_print_ioctl(&(t->i2c),cmd);
723
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700724 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700726 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300727 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 -0700728 ((struct tuner_setup *)arg)->type,
729 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300730 ((struct tuner_setup *)arg)->mode_mask,
731 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700732
733 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700734 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200736 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
737 == EINVAL)
738 return 0;
739 if (t->radio_freq)
740 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700742 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200743 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
744 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300745 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300746 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300747 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200748 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300749#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700750 case VIDIOCSAUDIO:
751 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
752 return 0;
753 if (check_v4l2(t) == EINVAL)
754 return 0;
755
756 /* Should be implemented, since bttv calls it */
757 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700758 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760 {
761 static const v4l2_std_id map[] = {
762 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
763 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
764 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
765 [4 /* bttv */ ] = V4L2_STD_PAL_M,
766 [5 /* bttv */ ] = V4L2_STD_PAL_N,
767 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
768 };
769 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700771 if (check_v4l2(t) == EINVAL)
772 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700773
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700774 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
775 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700777 if (vc->norm < ARRAY_SIZE(map))
778 t->std = map[vc->norm];
779 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200780 if (t->tv_freq)
781 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700782 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700783 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700784 case VIDIOCSFREQ:
785 {
786 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700787
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700788 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
789 return 0;
790 if (check_v4l2(t) == EINVAL)
791 return 0;
792
793 set_freq(client, *v);
794 return 0;
795 }
796 case VIDIOCGTUNER:
797 {
798 struct video_tuner *vt = arg;
799
800 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
801 return 0;
802 if (check_v4l2(t) == EINVAL)
803 return 0;
804
805 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300806 if (fe_tuner_ops->get_status) {
807 u32 tuner_status;
808
809 fe_tuner_ops->get_status(&t->fe, &tuner_status);
810 if (tuner_status & TUNER_STATUS_STEREO)
811 vt->flags |= VIDEO_TUNER_STEREO_ON;
812 else
813 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
814 vt->signal = tuner_status & TUNER_STATUS_LOCKED
815 ? 65535 : 0;
816 } else {
817 if (t->ops.is_stereo) {
818 if (t->ops.is_stereo(t))
819 vt->flags |=
820 VIDEO_TUNER_STEREO_ON;
821 else
822 vt->flags &=
823 ~VIDEO_TUNER_STEREO_ON;
824 }
825 if (t->ops.has_signal)
826 vt->signal = t->ops.has_signal(t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700827 }
828 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
829
830 vt->rangelow = radio_range[0] * 16000;
831 vt->rangehigh = radio_range[1] * 16000;
832
833 } else {
834 vt->rangelow = tv_range[0] * 16;
835 vt->rangehigh = tv_range[1] * 16;
836 }
837
838 return 0;
839 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700841 {
842 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700844 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
845 return 0;
846 if (check_v4l2(t) == EINVAL)
847 return 0;
848
Michael Krufkye18f9442007-08-21 01:25:48 -0300849 if (V4L2_TUNER_RADIO == t->mode) {
850 if (fe_tuner_ops->get_status) {
851 u32 tuner_status;
852
853 fe_tuner_ops->get_status(&t->fe, &tuner_status);
854 va->mode = (tuner_status & TUNER_STATUS_STEREO)
855 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
856 } else if (t->ops.is_stereo)
857 va->mode = t->ops.is_stereo(t)
858 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
859 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700860 return 0;
861 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300862#endif
863 case TDA9887_SET_CONFIG:
864 if (t->type == TUNER_TDA9887) {
865 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300867 t->tda9887_config = *i;
868 set_freq(client, t->tv_freq);
869 }
870 break;
871 /* --- v4l ioctls --- */
872 /* take care: bttv does userspace copying, we'll get a
873 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700875 {
876 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700878 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
879 == EINVAL)
880 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700881
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700882 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700884 t->std = *id;
885 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200886 if (t->tv_freq)
887 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700888 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700889 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700890 case VIDIOC_S_FREQUENCY:
891 {
892 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700893
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300894 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
895 == EINVAL)
896 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700897 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200898 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700899
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700900 break;
901 }
902 case VIDIOC_G_FREQUENCY:
903 {
904 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700905
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700906 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
907 return 0;
908 switch_v4l2();
909 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300910 if (fe_tuner_ops->get_frequency) {
911 u32 abs_freq;
912
913 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
914 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
915 (abs_freq * 2 + 125/2) / 125 :
916 (abs_freq + 62500/2) / 62500;
917 break;
918 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200919 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
920 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700921 break;
922 }
923 case VIDIOC_G_TUNER:
924 {
925 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700926
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700927 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
928 return 0;
929 switch_v4l2();
930
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200931 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300932 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300933 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300934 if (t->mode == V4L2_TUNER_ANALOG_TV)
935 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200936 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700937 tuner->rangelow = tv_range[0] * 16;
938 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200939 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700940 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200941
942 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200943 tuner->rxsubchans =
944 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300945 if (fe_tuner_ops->get_status) {
946 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200947
Michael Krufkye18f9442007-08-21 01:25:48 -0300948 fe_tuner_ops->get_status(&t->fe, &tuner_status);
949 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
950 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
951 tuner->signal = tuner_status & TUNER_STATUS_LOCKED ? 65535 : 0;
952 } else {
953 if (t->ops.is_stereo) {
954 tuner->rxsubchans = t->ops.is_stereo(t) ?
955 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
956 }
957 if (t->ops.has_signal)
958 tuner->signal = t->ops.has_signal(t);
959 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200960 tuner->capability |=
961 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
962 tuner->audmode = t->audmode;
963 tuner->rangelow = radio_range[0] * 16000;
964 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700965 break;
966 }
967 case VIDIOC_S_TUNER:
968 {
969 struct v4l2_tuner *tuner = arg;
970
971 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
972 return 0;
973
974 switch_v4l2();
975
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200976 /* do nothing unless we're a radio tuner */
977 if (t->mode != V4L2_TUNER_RADIO)
978 break;
979 t->audmode = tuner->audmode;
980 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700981 break;
982 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200983 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300984 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300985 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200986 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 }
988
989 return 0;
990}
991
Jean Delvare21b48a72007-03-12 19:20:15 -0300992static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700994 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700996 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 /* FIXME: power down ??? */
998 return 0;
999}
1000
Jean Delvare21b48a72007-03-12 19:20:15 -03001001static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001003 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001005 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -02001006 if (V4L2_TUNER_RADIO == t->mode) {
1007 if (t->radio_freq)
1008 set_freq(c, t->radio_freq);
1009 } else {
1010 if (t->tv_freq)
1011 set_freq(c, t->tv_freq);
1012 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 return 0;
1014}
1015
1016/* ----------------------------------------------------------------------- */
1017
1018static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001019 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001020 .attach_adapter = tuner_probe,
1021 .detach_client = tuner_detach,
1022 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001023 .suspend = tuner_suspend,
1024 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001026 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001027 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001029static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001030 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001031 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032};
1033
1034static int __init tuner_init_module(void)
1035{
1036 return i2c_add_driver(&driver);
1037}
1038
1039static void __exit tuner_cleanup_module(void)
1040{
1041 i2c_del_driver(&driver);
1042}
1043
1044module_init(tuner_init_module);
1045module_exit(tuner_cleanup_module);
1046
1047/*
1048 * Overrides for Emacs so that we follow Linus's tabbing style.
1049 * ---------------------------------------------------------------------------
1050 * Local variables:
1051 * c-basic-offset: 8
1052 * End:
1053 */