blob: 9598a3da85f4ebfd0ed3edaab77eff172884df4f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * i2c tv tuner chip device driver
4 * core core, i.e. kernel interfaces, registering and so on
5 */
6
7#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/string.h>
10#include <linux/timer.h>
11#include <linux/delay.h>
12#include <linux/errno.h>
13#include <linux/slab.h>
14#include <linux/poll.h>
15#include <linux/i2c.h>
16#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/init.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030018#include <linux/videodev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <media/tuner.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020020#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030021#include "tuner-driver.h"
Michael Krufky96c0b7c2007-08-27 21:23:08 -030022#include "mt20xx.h"
Michael Krufky910bb3e2007-08-27 21:22:20 -030023#include "tda8290.h"
Michael Krufky7ab10bf2007-08-27 21:23:40 -030024#include "tea5761.h"
Michael Krufky8d0936e2007-08-27 21:24:27 -030025#include "tea5767.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27#define UNSET (-1U)
28
29/* standard i2c insmod options */
30static unsigned short normal_i2c[] = {
Adrian Bunk9ee476a2007-06-05 05:22:00 -030031#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030032 0x10,
33#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080034 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070035 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
36 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 I2C_CLIENT_END
38};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070039
Linus Torvalds1da177e2005-04-16 15:20:36 -070040I2C_CLIENT_INSMOD;
41
42/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070043static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070044static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070045static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070046
Linus Torvalds1da177e2005-04-16 15:20:36 -070047/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020048int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070050static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070051static unsigned int radio_range[2] = { 65, 108 };
52
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020053static char pal[] = "--";
54static char secam[] = "--";
55static char ntsc[] = "-";
56
Hans Verkuilf9195de2006-01-11 19:01:01 -020057
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020058module_param(addr, int, 0444);
59module_param(no_autodetect, int, 0444);
60module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020061module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020062module_param_string(pal, pal, sizeof(pal), 0644);
63module_param_string(secam, secam, sizeof(secam), 0644);
64module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070065module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070066module_param_array(radio_range, int, NULL, 0644);
67
68MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
69MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
70MODULE_LICENSE("GPL");
71
Linus Torvalds1da177e2005-04-16 15:20:36 -070072static struct i2c_driver driver;
73static struct i2c_client client_template;
74
75/* ---------------------------------------------------------------------- */
76
Michael Krufkye18f9442007-08-21 01:25:48 -030077static void fe_set_freq(struct tuner *t, unsigned int freq)
78{
79 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
80
81 struct analog_parameters params = {
82 .frequency = freq,
83 .mode = t->mode,
84 .audmode = t->audmode,
85 .std = t->std
86 };
87
88 if (NULL == fe_tuner_ops->set_analog_params) {
89 tuner_warn("Tuner frontend module has no way to set freq\n");
90 return;
91 }
92 fe_tuner_ops->set_analog_params(&t->fe, &params);
93}
94
95static void fe_release(struct tuner *t)
96{
97 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
98
99 if (fe_tuner_ops->release)
100 fe_tuner_ops->release(&t->fe);
101}
102
103static void fe_standby(struct tuner *t)
104{
105 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
106
107 if (fe_tuner_ops->sleep)
108 fe_tuner_ops->sleep(&t->fe);
109}
110
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700111/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112static void set_tv_freq(struct i2c_client *c, unsigned int freq)
113{
114 struct tuner *t = i2c_get_clientdata(c);
115
116 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700117 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 return;
119 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300120 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700121 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 return;
123 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700124 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
125 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
126 freq / 16, freq % 16 * 100 / 16, tv_range[0],
127 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200128 /* V4L2 spec: if the freq is not possible then the closest
129 possible value should be selected */
130 if (freq < tv_range[0] * 16)
131 freq = tv_range[0] * 16;
132 else
133 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 }
Michael Krufkydb8a6952007-08-21 01:24:42 -0300135 t->ops.set_tv_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
138static void set_radio_freq(struct i2c_client *c, unsigned int freq)
139{
140 struct tuner *t = i2c_get_clientdata(c);
141
142 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700143 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 return;
145 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300146 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700147 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 return;
149 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200150 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700151 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
152 freq / 16000, freq % 16000 * 100 / 16000,
153 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200154 /* V4L2 spec: if the freq is not possible then the closest
155 possible value should be selected */
156 if (freq < radio_range[0] * 16000)
157 freq = radio_range[0] * 16000;
158 else
159 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700161
Michael Krufkydb8a6952007-08-21 01:24:42 -0300162 t->ops.set_radio_freq(t, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163}
164
165static void set_freq(struct i2c_client *c, unsigned long freq)
166{
167 struct tuner *t = i2c_get_clientdata(c);
168
169 switch (t->mode) {
170 case V4L2_TUNER_RADIO:
171 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700172 freq / 16000, freq % 16000 * 100 / 16000);
173 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200174 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 break;
176 case V4L2_TUNER_ANALOG_TV:
177 case V4L2_TUNER_DIGITAL_TV:
178 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700179 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200181 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 break;
183 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
185
Michael Krufky293197c2007-08-28 17:20:42 -0300186static void tuner_i2c_address_check(struct tuner *t)
187{
188 if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
189 ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
190 return;
191
192 tuner_warn("====================== WARNING! ======================\n");
193 tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
194 tuner_warn("will soon be dropped. This message indicates that your\n");
195 tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
196 t->i2c.name, t->i2c.addr);
197 tuner_warn("To ensure continued support for your device, please\n");
198 tuner_warn("send a copy of this message, along with full dmesg\n");
199 tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
200 tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
201 tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
202 t->i2c.adapter->name, t->i2c.addr, t->type,
203 tuners[t->type].name);
204 tuner_warn("====================== WARNING! ======================\n");
205}
206
Michael Krufky910bb3e2007-08-27 21:22:20 -0300207static void attach_tda8290(struct tuner *t)
208{
209 struct tda8290_config cfg = {
210 .lna_cfg = &t->config,
211 .tuner_callback = t->tuner_callback
212 };
213 tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
214}
215
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700216static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300217 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300218 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219{
220 struct tuner *t = i2c_get_clientdata(c);
Michael Krufkye18f9442007-08-21 01:25:48 -0300221 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700222 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700224 if (type == UNSET || type == TUNER_ABSENT) {
225 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 return;
227 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700229 if (type >= tuner_count) {
230 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
231 return;
232 }
233
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300235 t->config = new_config;
236 if (tuner_callback != NULL) {
237 tuner_dbg("defining GPIO callback\n");
238 t->tuner_callback = tuner_callback;
239 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300240
241 /* This code detects calls by card attach_inform */
242 if (NULL == t->i2c.dev.driver) {
243 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
244
245 return;
246 }
247
Michael Krufkyb2083192007-05-29 22:54:06 -0300248 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300249 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300250 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300251 else {
252 kfree(t->priv);
253 t->priv = NULL;
254 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 switch (t->type) {
257 case TUNER_MT2032:
Michael Krufky96c0b7c2007-08-27 21:23:08 -0300258 microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 break;
260 case TUNER_PHILIPS_TDA8290:
Michael Krufky910bb3e2007-08-27 21:22:20 -0300261 {
262 attach_tda8290(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 break;
Michael Krufky910bb3e2007-08-27 21:22:20 -0300264 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700265 case TUNER_TEA5767:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300266 if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700267 t->type = TUNER_ABSENT;
268 t->mode_mask = T_UNINITIALIZED;
269 return;
270 }
271 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700272 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300273#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300274 case TUNER_TEA5761:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300275 if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300276 t->type = TUNER_ABSENT;
277 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300278 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300279 }
280 t->mode_mask = T_RADIO;
281 break;
282#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700283 case TUNER_PHILIPS_FMD1216ME_MK3:
284 buffer[0] = 0x0b;
285 buffer[1] = 0xdc;
286 buffer[2] = 0x9c;
287 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700288 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700289 mdelay(1);
290 buffer[2] = 0x86;
291 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700292 i2c_master_send(c, buffer, 4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300293 default_tuner_init(t);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700294 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800295 case TUNER_PHILIPS_TD1316:
296 buffer[0] = 0x0b;
297 buffer[1] = 0xdc;
298 buffer[2] = 0x86;
299 buffer[3] = 0xa4;
300 i2c_master_send(c,buffer,4);
Michael Krufkydb8a6952007-08-21 01:24:42 -0300301 default_tuner_init(t);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200302 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300303 case TUNER_TDA9887:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300304 tda9887_tuner_init(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300305 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 default:
Michael Krufkydb8a6952007-08-21 01:24:42 -0300307 default_tuner_init(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 break;
309 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700310
Michael Krufkye18f9442007-08-21 01:25:48 -0300311 if (fe_tuner_ops->set_analog_params) {
312 strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
313
314 t->ops.set_tv_freq = fe_set_freq;
315 t->ops.set_radio_freq = fe_set_freq;
316 t->ops.standby = fe_standby;
317 t->ops.release = fe_release;
318 }
319
320 tuner_info("type set to %s\n", t->i2c.name);
321
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700322 if (t->mode_mask == T_UNINITIALIZED)
323 t->mode_mask = new_mode_mask;
324
Hans Verkuil27487d42006-01-15 15:04:52 -0200325 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700326 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100327 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700328 t->mode_mask);
Michael Krufky293197c2007-08-28 17:20:42 -0300329 tuner_i2c_address_check(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330}
331
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700332/*
333 * This function apply tuner config to tuner specified
334 * by tun_setup structure. I addr is unset, then admin status
335 * and tun addr status is more precise then current status,
336 * it's applied. Otherwise status and type are applied only to
337 * tuner with exactly the same addr.
338*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700339
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700340static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700341{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700342 struct tuner *t = i2c_get_clientdata(c);
343
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300344 tuner_dbg("set addr for type %i\n", t->type);
345
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300346 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
347 (t->mode_mask & tun_setup->mode_mask))) ||
348 (tun_setup->addr == c->addr)) {
349 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300350 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700351 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700352}
353
354static inline int check_mode(struct tuner *t, char *cmd)
355{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700356 if ((1 << t->mode & t->mode_mask) == 0) {
357 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700358 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700359
360 switch (t->mode) {
361 case V4L2_TUNER_RADIO:
362 tuner_dbg("Cmd %s accepted for radio\n", cmd);
363 break;
364 case V4L2_TUNER_ANALOG_TV:
365 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
366 break;
367 case V4L2_TUNER_DIGITAL_TV:
368 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
369 break;
370 }
371 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700372}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700373
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700374/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375static int tuner_fixup_std(struct tuner *t)
376{
377 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300379 case '6':
380 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
381 t->std = V4L2_STD_PAL_60;
382 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 case 'b':
384 case 'B':
385 case 'g':
386 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700387 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 t->std = V4L2_STD_PAL_BG;
389 break;
390 case 'i':
391 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700392 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 t->std = V4L2_STD_PAL_I;
394 break;
395 case 'd':
396 case 'D':
397 case 'k':
398 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700399 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 t->std = V4L2_STD_PAL_DK;
401 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700402 case 'M':
403 case 'm':
404 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
405 t->std = V4L2_STD_PAL_M;
406 break;
407 case 'N':
408 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200409 if (pal[1] == 'c' || pal[1] == 'C') {
410 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
411 t->std = V4L2_STD_PAL_Nc;
412 } else {
413 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
414 t->std = V4L2_STD_PAL_N;
415 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700416 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700417 case '-':
418 /* default parameter, do nothing */
419 break;
420 default:
421 tuner_warn ("pal= argument not recognised\n");
422 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 }
424 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700425 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
426 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200427 case 'b':
428 case 'B':
429 case 'g':
430 case 'G':
431 case 'h':
432 case 'H':
433 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
434 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
435 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700436 case 'd':
437 case 'D':
438 case 'k':
439 case 'K':
440 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
441 t->std = V4L2_STD_SECAM_DK;
442 break;
443 case 'l':
444 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800445 if ((secam[1]=='C')||(secam[1]=='c')) {
446 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
447 t->std = V4L2_STD_SECAM_LC;
448 } else {
449 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
450 t->std = V4L2_STD_SECAM_L;
451 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700452 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700453 case '-':
454 /* default parameter, do nothing */
455 break;
456 default:
457 tuner_warn ("secam= argument not recognised\n");
458 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700459 }
460 }
461
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200462 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
463 switch (ntsc[0]) {
464 case 'm':
465 case 'M':
466 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
467 t->std = V4L2_STD_NTSC_M;
468 break;
469 case 'j':
470 case 'J':
471 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
472 t->std = V4L2_STD_NTSC_M_JP;
473 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200474 case 'k':
475 case 'K':
476 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
477 t->std = V4L2_STD_NTSC_M_KR;
478 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200479 case '-':
480 /* default parameter, do nothing */
481 break;
482 default:
483 tuner_info("ntsc= argument not recognised\n");
484 break;
485 }
486 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 return 0;
488}
489
Michael Krufkydb8a6952007-08-21 01:24:42 -0300490static void tuner_status(struct tuner *t)
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200491{
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200492 unsigned long freq, freq_fraction;
Michael Krufkye18f9442007-08-21 01:25:48 -0300493 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200494 const char *p;
495
496 switch (t->mode) {
497 case V4L2_TUNER_RADIO: p = "radio"; break;
498 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
499 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
500 default: p = "undefined"; break;
501 }
502 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200503 freq = t->radio_freq / 16000;
504 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200505 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200506 freq = t->tv_freq / 16;
507 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200508 }
509 tuner_info("Tuner mode: %s\n", p);
510 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300511 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200512 if (t->mode != V4L2_TUNER_RADIO)
513 return;
Michael Krufkye18f9442007-08-21 01:25:48 -0300514 if (fe_tuner_ops->get_status) {
515 u32 tuner_status;
516
517 fe_tuner_ops->get_status(&t->fe, &tuner_status);
518 if (tuner_status & TUNER_STATUS_LOCKED)
519 tuner_info("Tuner is locked.\n");
520 if (tuner_status & TUNER_STATUS_STEREO)
521 tuner_info("Stereo: yes\n");
522 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300523 if (t->ops.has_signal) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300524 tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200525 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300526 if (t->ops.is_stereo) {
Michael Krufkydb8a6952007-08-21 01:24:42 -0300527 tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200528 }
529}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200530
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531/* ---------------------------------------------------------------------- */
532
Hans Verkuilba8fc392006-06-25 15:34:39 -0300533/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700534static unsigned default_mode_mask;
535
536/* During client attach, set_type is called by adapter's attach_inform callback.
537 set_type must then be completed by tuner_attach.
538 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
540{
541 struct tuner *t;
542
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700543 client_template.adapter = adap;
544 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
Panagiotis Issaris74081872006-01-11 19:40:56 -0200546 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700547 if (NULL == t)
548 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700549 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700551 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700552 t->audmode = V4L2_TUNER_MODE_STEREO;
553 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300554 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700556 if (show_i2c) {
557 unsigned char buffer[16];
558 int i,rc;
559
560 memset(buffer, 0, sizeof(buffer));
561 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800562 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700563 for (i=0;i<rc;i++)
564 printk("%02x ",buffer[i]);
565 printk("\n");
566 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300567 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
568 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
569 return -ENODEV;
570
Markus Rechberger257c6452006-01-23 17:11:11 -0200571 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700572 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800573 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300574#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300575 case 0x10:
Michael Krufky7ab10bf2007-08-27 21:23:40 -0300576 if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300577 t->type = TUNER_TEA5761;
578 t->mode_mask = T_RADIO;
579 t->mode = T_STANDBY;
580 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
581 default_mode_mask &= ~T_RADIO;
582
583 goto register_client;
584 }
585 break;
586#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800587 case 0x42:
588 case 0x43:
589 case 0x4a:
590 case 0x4b:
591 /* If chip is not tda8290, don't register.
592 since it can be tda9887*/
Michael Krufky910bb3e2007-08-27 21:22:20 -0300593 if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300594 tuner_dbg("chip at addr %x is a tda8290\n", addr);
595 } else {
596 /* Default is being tda9887 */
597 t->type = TUNER_TDA9887;
598 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
599 t->mode = T_STANDBY;
600 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800601 }
602 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800603 case 0x60:
Michael Krufky8d0936e2007-08-27 21:24:27 -0300604 if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700605 t->type = TUNER_TEA5767;
606 t->mode_mask = T_RADIO;
607 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200608 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700609 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700610
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800611 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700612 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800613 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700614 }
615 }
616
617 /* Initializes only the first adapter found */
618 if (default_mode_mask != T_UNINITIALIZED) {
619 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
620 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200621 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
622 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700623 default_mode_mask = T_UNINITIALIZED;
624 }
625
626 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800627register_client:
628 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700629 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300630 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 return 0;
632}
633
634static int tuner_probe(struct i2c_adapter *adap)
635{
636 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700637 normal_i2c[0] = addr;
638 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700641 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700642
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 if (adap->class & I2C_CLASS_TV_ANALOG)
644 return i2c_probe(adap, &addr_data, tuner_attach);
645 return 0;
646}
647
648static int tuner_detach(struct i2c_client *client)
649{
650 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700651 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700653 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700654 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700655 tuner_warn
656 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700657 return err;
658 }
659
Michael Krufky7a91a802007-06-06 16:10:39 -0300660 if (t->ops.release)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300661 t->ops.release(t);
Michael Krufky052c50d2007-06-04 16:00:45 -0300662 else {
663 kfree(t->priv);
664 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 kfree(t);
666 return 0;
667}
668
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700669/*
670 * Switch tuner to other mode. If tuner support both tv and radio,
671 * set another frequency to some value (This is needed for some pal
672 * tuners to avoid locking). Otherwise, just put second tuner in
673 * standby mode.
674 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700676static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
677{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800678 if (mode == t->mode)
679 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800681 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700682
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800683 if (check_mode(t, cmd) == EINVAL) {
684 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300685 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300686 t->ops.standby(t);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800687 return EINVAL;
688 }
689 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700690}
691
692#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800693 tuner_dbg("switching to v4l2\n"); \
694 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700695
696static inline int check_v4l2(struct tuner *t)
697{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300698 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
699 TV, v4l1 for radio), until that is fixed this code is disabled.
700 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
701 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700702 return 0;
703}
704
705static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706{
707 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkye18f9442007-08-21 01:25:48 -0300708 struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
Hans Verkuilf9195de2006-01-11 19:01:01 -0200710 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200711 v4l_i2c_print_ioctl(&(t->i2c),cmd);
712
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700713 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700715 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300716 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 -0700717 ((struct tuner_setup *)arg)->type,
718 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300719 ((struct tuner_setup *)arg)->mode_mask,
720 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700721
722 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700723 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200725 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
726 == EINVAL)
727 return 0;
728 if (t->radio_freq)
729 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700731 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200732 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
733 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300734 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300735 if (t->ops.standby)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300736 t->ops.standby(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200737 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300738#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700739 case VIDIOCSAUDIO:
740 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
741 return 0;
742 if (check_v4l2(t) == EINVAL)
743 return 0;
744
745 /* Should be implemented, since bttv calls it */
746 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700747 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700749 {
750 static const v4l2_std_id map[] = {
751 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
752 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
753 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
754 [4 /* bttv */ ] = V4L2_STD_PAL_M,
755 [5 /* bttv */ ] = V4L2_STD_PAL_N,
756 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
757 };
758 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760 if (check_v4l2(t) == EINVAL)
761 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700762
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700763 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
764 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700766 if (vc->norm < ARRAY_SIZE(map))
767 t->std = map[vc->norm];
768 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200769 if (t->tv_freq)
770 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700771 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700772 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700773 case VIDIOCSFREQ:
774 {
775 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700776
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700777 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
778 return 0;
779 if (check_v4l2(t) == EINVAL)
780 return 0;
781
782 set_freq(client, *v);
783 return 0;
784 }
785 case VIDIOCGTUNER:
786 {
787 struct video_tuner *vt = arg;
788
789 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
790 return 0;
791 if (check_v4l2(t) == EINVAL)
792 return 0;
793
794 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufkye18f9442007-08-21 01:25:48 -0300795 if (fe_tuner_ops->get_status) {
796 u32 tuner_status;
797
798 fe_tuner_ops->get_status(&t->fe, &tuner_status);
799 if (tuner_status & TUNER_STATUS_STEREO)
800 vt->flags |= VIDEO_TUNER_STEREO_ON;
801 else
802 vt->flags &= ~VIDEO_TUNER_STEREO_ON;
803 vt->signal = tuner_status & TUNER_STATUS_LOCKED
804 ? 65535 : 0;
805 } else {
806 if (t->ops.is_stereo) {
807 if (t->ops.is_stereo(t))
808 vt->flags |=
809 VIDEO_TUNER_STEREO_ON;
810 else
811 vt->flags &=
812 ~VIDEO_TUNER_STEREO_ON;
813 }
814 if (t->ops.has_signal)
815 vt->signal = t->ops.has_signal(t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700816 }
817 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
818
819 vt->rangelow = radio_range[0] * 16000;
820 vt->rangehigh = radio_range[1] * 16000;
821
822 } else {
823 vt->rangelow = tv_range[0] * 16;
824 vt->rangehigh = tv_range[1] * 16;
825 }
826
827 return 0;
828 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700830 {
831 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700833 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
834 return 0;
835 if (check_v4l2(t) == EINVAL)
836 return 0;
837
Michael Krufkye18f9442007-08-21 01:25:48 -0300838 if (V4L2_TUNER_RADIO == t->mode) {
839 if (fe_tuner_ops->get_status) {
840 u32 tuner_status;
841
842 fe_tuner_ops->get_status(&t->fe, &tuner_status);
843 va->mode = (tuner_status & TUNER_STATUS_STEREO)
844 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
845 } else if (t->ops.is_stereo)
846 va->mode = t->ops.is_stereo(t)
847 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
848 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700849 return 0;
850 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300851#endif
852 case TDA9887_SET_CONFIG:
853 if (t->type == TUNER_TDA9887) {
854 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300856 t->tda9887_config = *i;
857 set_freq(client, t->tv_freq);
858 }
859 break;
860 /* --- v4l ioctls --- */
861 /* take care: bttv does userspace copying, we'll get a
862 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700864 {
865 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700867 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
868 == EINVAL)
869 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700870
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700871 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700873 t->std = *id;
874 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200875 if (t->tv_freq)
876 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700877 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700878 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700879 case VIDIOC_S_FREQUENCY:
880 {
881 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700882
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300883 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
884 == EINVAL)
885 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700886 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200887 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700888
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700889 break;
890 }
891 case VIDIOC_G_FREQUENCY:
892 {
893 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700894
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700895 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
896 return 0;
897 switch_v4l2();
898 f->type = t->mode;
Michael Krufkye18f9442007-08-21 01:25:48 -0300899 if (fe_tuner_ops->get_frequency) {
900 u32 abs_freq;
901
902 fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
903 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
904 (abs_freq * 2 + 125/2) / 125 :
905 (abs_freq + 62500/2) / 62500;
906 break;
907 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200908 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
909 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700910 break;
911 }
912 case VIDIOC_G_TUNER:
913 {
914 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700915
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700916 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
917 return 0;
918 switch_v4l2();
919
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200920 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300921 if (t->ops.get_afc)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300922 tuner->afc=t->ops.get_afc(t);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300923 if (t->mode == V4L2_TUNER_ANALOG_TV)
924 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200925 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700926 tuner->rangelow = tv_range[0] * 16;
927 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200928 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700929 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200930
931 /* radio mode */
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200932 tuner->rxsubchans =
933 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufkye18f9442007-08-21 01:25:48 -0300934 if (fe_tuner_ops->get_status) {
935 u32 tuner_status;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200936
Michael Krufkye18f9442007-08-21 01:25:48 -0300937 fe_tuner_ops->get_status(&t->fe, &tuner_status);
938 tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
939 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
940 tuner->signal = tuner_status & TUNER_STATUS_LOCKED ? 65535 : 0;
941 } else {
942 if (t->ops.is_stereo) {
943 tuner->rxsubchans = t->ops.is_stereo(t) ?
944 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
945 }
946 if (t->ops.has_signal)
947 tuner->signal = t->ops.has_signal(t);
948 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200949 tuner->capability |=
950 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
951 tuner->audmode = t->audmode;
952 tuner->rangelow = radio_range[0] * 16000;
953 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700954 break;
955 }
956 case VIDIOC_S_TUNER:
957 {
958 struct v4l2_tuner *tuner = arg;
959
960 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
961 return 0;
962
963 switch_v4l2();
964
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200965 /* do nothing unless we're a radio tuner */
966 if (t->mode != V4L2_TUNER_RADIO)
967 break;
968 t->audmode = tuner->audmode;
969 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700970 break;
971 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200972 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300973 if (t->ops.tuner_status)
Michael Krufkydb8a6952007-08-21 01:24:42 -0300974 t->ops.tuner_status(t);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200975 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 }
977
978 return 0;
979}
980
Jean Delvare21b48a72007-03-12 19:20:15 -0300981static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700983 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700985 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 /* FIXME: power down ??? */
987 return 0;
988}
989
Jean Delvare21b48a72007-03-12 19:20:15 -0300990static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700992 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700994 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200995 if (V4L2_TUNER_RADIO == t->mode) {
996 if (t->radio_freq)
997 set_freq(c, t->radio_freq);
998 } else {
999 if (t->tv_freq)
1000 set_freq(c, t->tv_freq);
1001 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 return 0;
1003}
1004
1005/* ----------------------------------------------------------------------- */
1006
1007static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001008 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001009 .attach_adapter = tuner_probe,
1010 .detach_client = tuner_detach,
1011 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -03001012 .suspend = tuner_suspend,
1013 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001015 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -02001016 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001018static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +02001019 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -07001020 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021};
1022
1023static int __init tuner_init_module(void)
1024{
1025 return i2c_add_driver(&driver);
1026}
1027
1028static void __exit tuner_cleanup_module(void)
1029{
1030 i2c_del_driver(&driver);
1031}
1032
1033module_init(tuner_init_module);
1034module_exit(tuner_cleanup_module);
1035
1036/*
1037 * Overrides for Emacs so that we follow Linus's tabbing style.
1038 * ---------------------------------------------------------------------------
1039 * Local variables:
1040 * c-basic-offset: 8
1041 * End:
1042 */