blob: baeae28f28369987a88ffa4d213e29b9c4c96845 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * i2c tv tuner chip device driver
4 * core core, i.e. kernel interfaces, registering and so on
5 */
6
7#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/string.h>
10#include <linux/timer.h>
11#include <linux/delay.h>
12#include <linux/errno.h>
13#include <linux/slab.h>
14#include <linux/poll.h>
15#include <linux/i2c.h>
16#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/init.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030018#include <linux/videodev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <media/tuner.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020020#include <media/v4l2-common.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030021#include "tuner-driver.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
23#define UNSET (-1U)
24
25/* standard i2c insmod options */
26static unsigned short normal_i2c[] = {
Adrian Bunk9ee476a2007-06-05 05:22:00 -030027#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -030028 0x10,
29#endif
Hartmut Hackmannde48eeb2005-11-08 21:37:48 -080030 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -070031 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
32 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 I2C_CLIENT_END
34};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070035
Linus Torvalds1da177e2005-04-16 15:20:36 -070036I2C_CLIENT_INSMOD;
37
38/* insmod options used at init time => read/only */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070039static unsigned int addr = 0;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -070040static unsigned int no_autodetect = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070041static unsigned int show_i2c = 0;
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -070042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043/* insmod options used at runtime => read/write */
Hans Verkuilf9195de2006-01-11 19:01:01 -020044int tuner_debug = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070046static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070047static unsigned int radio_range[2] = { 65, 108 };
48
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020049static char pal[] = "--";
50static char secam[] = "--";
51static char ntsc[] = "-";
52
Hans Verkuilf9195de2006-01-11 19:01:01 -020053
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020054module_param(addr, int, 0444);
55module_param(no_autodetect, int, 0444);
56module_param(show_i2c, int, 0444);
Hans Verkuilf9195de2006-01-11 19:01:01 -020057module_param_named(debug,tuner_debug, int, 0644);
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -020058module_param_string(pal, pal, sizeof(pal), 0644);
59module_param_string(secam, secam, sizeof(secam), 0644);
60module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070061module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062module_param_array(radio_range, int, NULL, 0644);
63
64MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
65MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
66MODULE_LICENSE("GPL");
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068static struct i2c_driver driver;
69static struct i2c_client client_template;
70
71/* ---------------------------------------------------------------------- */
72
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -070073/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds1da177e2005-04-16 15:20:36 -070074static void set_tv_freq(struct i2c_client *c, unsigned int freq)
75{
76 struct tuner *t = i2c_get_clientdata(c);
77
78 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070079 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 return;
81 }
Michael Krufky7a91a802007-06-06 16:10:39 -030082 if (NULL == t->ops.set_tv_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070083 tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 return;
85 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -070086 if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
87 tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
88 freq / 16, freq % 16 * 100 / 16, tv_range[0],
89 tv_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -020090 /* V4L2 spec: if the freq is not possible then the closest
91 possible value should be selected */
92 if (freq < tv_range[0] * 16)
93 freq = tv_range[0] * 16;
94 else
95 freq = tv_range[1] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 }
Michael Krufky7a91a802007-06-06 16:10:39 -030097 t->ops.set_tv_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098}
99
100static void set_radio_freq(struct i2c_client *c, unsigned int freq)
101{
102 struct tuner *t = i2c_get_clientdata(c);
103
104 if (t->type == UNSET) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700105 tuner_warn ("tuner type not set\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 return;
107 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300108 if (NULL == t->ops.set_radio_freq) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700109 tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 return;
111 }
Hans Verkuil27487d42006-01-15 15:04:52 -0200112 if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700113 tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
114 freq / 16000, freq % 16000 * 100 / 16000,
115 radio_range[0], radio_range[1]);
Hans Verkuil27487d42006-01-15 15:04:52 -0200116 /* V4L2 spec: if the freq is not possible then the closest
117 possible value should be selected */
118 if (freq < radio_range[0] * 16000)
119 freq = radio_range[0] * 16000;
120 else
121 freq = radio_range[1] * 16000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 }
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700123
Michael Krufky7a91a802007-06-06 16:10:39 -0300124 t->ops.set_radio_freq(c, freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125}
126
127static void set_freq(struct i2c_client *c, unsigned long freq)
128{
129 struct tuner *t = i2c_get_clientdata(c);
130
131 switch (t->mode) {
132 case V4L2_TUNER_RADIO:
133 tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700134 freq / 16000, freq % 16000 * 100 / 16000);
135 set_radio_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200136 t->radio_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 break;
138 case V4L2_TUNER_ANALOG_TV:
139 case V4L2_TUNER_DIGITAL_TV:
140 tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700141 freq / 16, freq % 16 * 100 / 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 set_tv_freq(c, freq);
Hans Verkuil27487d42006-01-15 15:04:52 -0200143 t->tv_freq = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 break;
145 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
147
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700148static void set_type(struct i2c_client *c, unsigned int type,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300149 unsigned int new_mode_mask, unsigned int new_config,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300150 int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151{
152 struct tuner *t = i2c_get_clientdata(c);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700153 unsigned char buffer[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700155 if (type == UNSET || type == TUNER_ABSENT) {
156 tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 return;
158 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700160 if (type >= tuner_count) {
161 tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count);
162 return;
163 }
164
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 t->type = type;
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300166 t->config = new_config;
167 if (tuner_callback != NULL) {
168 tuner_dbg("defining GPIO callback\n");
169 t->tuner_callback = tuner_callback;
170 }
Hartmut Hackmann80f90fb2007-04-27 12:31:18 -0300171
172 /* This code detects calls by card attach_inform */
173 if (NULL == t->i2c.dev.driver) {
174 tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
175
176 return;
177 }
178
Michael Krufkyb2083192007-05-29 22:54:06 -0300179 /* discard private data, in case set_type() was previously called */
Michael Krufky7a91a802007-06-06 16:10:39 -0300180 if (t->ops.release)
181 t->ops.release(c);
Michael Krufky052c50d2007-06-04 16:00:45 -0300182 else {
183 kfree(t->priv);
184 t->priv = NULL;
185 }
Michael Krufkybe2b85a2007-06-04 14:40:27 -0300186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 switch (t->type) {
188 case TUNER_MT2032:
189 microtune_init(c);
190 break;
191 case TUNER_PHILIPS_TDA8290:
192 tda8290_init(c);
193 break;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700194 case TUNER_TEA5767:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700195 if (tea5767_tuner_init(c) == EINVAL) {
196 t->type = TUNER_ABSENT;
197 t->mode_mask = T_UNINITIALIZED;
198 return;
199 }
200 t->mode_mask = T_RADIO;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700201 break;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300202#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300203 case TUNER_TEA5761:
204 if (tea5761_tuner_init(c) == EINVAL) {
205 t->type = TUNER_ABSENT;
206 t->mode_mask = T_UNINITIALIZED;
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300207 return;
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300208 }
209 t->mode_mask = T_RADIO;
210 break;
211#endif
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700212 case TUNER_PHILIPS_FMD1216ME_MK3:
213 buffer[0] = 0x0b;
214 buffer[1] = 0xdc;
215 buffer[2] = 0x9c;
216 buffer[3] = 0x60;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700217 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700218 mdelay(1);
219 buffer[2] = 0x86;
220 buffer[3] = 0x54;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700221 i2c_master_send(c, buffer, 4);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700222 default_tuner_init(c);
223 break;
Hartmut Hackmann93df3412005-11-08 21:36:31 -0800224 case TUNER_PHILIPS_TD1316:
225 buffer[0] = 0x0b;
226 buffer[1] = 0xdc;
227 buffer[2] = 0x86;
228 buffer[3] = 0xa4;
229 i2c_master_send(c,buffer,4);
230 default_tuner_init(c);
Markus Rechbergerac272ed2006-01-23 17:11:09 -0200231 break;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300232 case TUNER_TDA9887:
233 tda9887_tuner_init(c);
234 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 default:
236 default_tuner_init(c);
237 break;
238 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700239
240 if (t->mode_mask == T_UNINITIALIZED)
241 t->mode_mask = new_mode_mask;
242
Hans Verkuil27487d42006-01-15 15:04:52 -0200243 set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700244 tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
Laurent Riffard604f28e2005-11-26 20:43:39 +0100245 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700246 t->mode_mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247}
248
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700249/*
250 * This function apply tuner config to tuner specified
251 * by tun_setup structure. I addr is unset, then admin status
252 * and tun addr status is more precise then current status,
253 * it's applied. Otherwise status and type are applied only to
254 * tuner with exactly the same addr.
255*/
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700256
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700257static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700258{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700259 struct tuner *t = i2c_get_clientdata(c);
260
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300261 tuner_dbg("set addr for type %i\n", t->type);
262
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300263 if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
264 (t->mode_mask & tun_setup->mode_mask))) ||
265 (tun_setup->addr == c->addr)) {
266 set_type(c, tun_setup->type, tun_setup->mode_mask,
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300267 tun_setup->config, tun_setup->tuner_callback);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700268 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700269}
270
271static inline int check_mode(struct tuner *t, char *cmd)
272{
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700273 if ((1 << t->mode & t->mode_mask) == 0) {
274 return EINVAL;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700275 }
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700276
277 switch (t->mode) {
278 case V4L2_TUNER_RADIO:
279 tuner_dbg("Cmd %s accepted for radio\n", cmd);
280 break;
281 case V4L2_TUNER_ANALOG_TV:
282 tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
283 break;
284 case V4L2_TUNER_DIGITAL_TV:
285 tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
286 break;
287 }
288 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700289}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700290
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700291/* get more precise norm info from insmod option */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292static int tuner_fixup_std(struct tuner *t)
293{
294 if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 switch (pal[0]) {
Hans Verkuile71ced12006-12-11 15:51:43 -0300296 case '6':
297 tuner_dbg ("insmod fixup: PAL => PAL-60\n");
298 t->std = V4L2_STD_PAL_60;
299 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 case 'b':
301 case 'B':
302 case 'g':
303 case 'G':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700304 tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 t->std = V4L2_STD_PAL_BG;
306 break;
307 case 'i':
308 case 'I':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700309 tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 t->std = V4L2_STD_PAL_I;
311 break;
312 case 'd':
313 case 'D':
314 case 'k':
315 case 'K':
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700316 tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 t->std = V4L2_STD_PAL_DK;
318 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700319 case 'M':
320 case 'm':
321 tuner_dbg ("insmod fixup: PAL => PAL-M\n");
322 t->std = V4L2_STD_PAL_M;
323 break;
324 case 'N':
325 case 'n':
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200326 if (pal[1] == 'c' || pal[1] == 'C') {
327 tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
328 t->std = V4L2_STD_PAL_Nc;
329 } else {
330 tuner_dbg ("insmod fixup: PAL => PAL-N\n");
331 t->std = V4L2_STD_PAL_N;
332 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700333 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700334 case '-':
335 /* default parameter, do nothing */
336 break;
337 default:
338 tuner_warn ("pal= argument not recognised\n");
339 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 }
341 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700342 if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
343 switch (secam[0]) {
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200344 case 'b':
345 case 'B':
346 case 'g':
347 case 'G':
348 case 'h':
349 case 'H':
350 tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
351 t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
352 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700353 case 'd':
354 case 'D':
355 case 'k':
356 case 'K':
357 tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
358 t->std = V4L2_STD_SECAM_DK;
359 break;
360 case 'l':
361 case 'L':
Mauro Carvalho Chehab800d3c62005-11-13 16:07:48 -0800362 if ((secam[1]=='C')||(secam[1]=='c')) {
363 tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
364 t->std = V4L2_STD_SECAM_LC;
365 } else {
366 tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
367 t->std = V4L2_STD_SECAM_L;
368 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700369 break;
Mauro Carvalho Chehab21d4df32005-09-09 13:03:59 -0700370 case '-':
371 /* default parameter, do nothing */
372 break;
373 default:
374 tuner_warn ("secam= argument not recognised\n");
375 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700376 }
377 }
378
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200379 if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
380 switch (ntsc[0]) {
381 case 'm':
382 case 'M':
383 tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
384 t->std = V4L2_STD_NTSC_M;
385 break;
386 case 'j':
387 case 'J':
388 tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
389 t->std = V4L2_STD_NTSC_M_JP;
390 break;
Hans Verkuild97a11e2006-02-07 06:48:40 -0200391 case 'k':
392 case 'K':
393 tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
394 t->std = V4L2_STD_NTSC_M_KR;
395 break;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200396 case '-':
397 /* default parameter, do nothing */
398 break;
399 default:
400 tuner_info("ntsc= argument not recognised\n");
401 break;
402 }
403 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 return 0;
405}
406
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200407static void tuner_status(struct i2c_client *client)
408{
409 struct tuner *t = i2c_get_clientdata(client);
410 unsigned long freq, freq_fraction;
411 const char *p;
412
413 switch (t->mode) {
414 case V4L2_TUNER_RADIO: p = "radio"; break;
415 case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break;
416 case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
417 default: p = "undefined"; break;
418 }
419 if (t->mode == V4L2_TUNER_RADIO) {
Hans Verkuil27487d42006-01-15 15:04:52 -0200420 freq = t->radio_freq / 16000;
421 freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200422 } else {
Hans Verkuil27487d42006-01-15 15:04:52 -0200423 freq = t->tv_freq / 16;
424 freq_fraction = (t->tv_freq % 16) * 100 / 16;
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200425 }
426 tuner_info("Tuner mode: %s\n", p);
427 tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
Mauro Carvalho Chehab4ae5c2e2006-03-25 15:53:38 -0300428 tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200429 if (t->mode != V4L2_TUNER_RADIO)
430 return;
Michael Krufky7a91a802007-06-06 16:10:39 -0300431 if (t->ops.has_signal) {
432 tuner_info("Signal strength: %d\n", t->ops.has_signal(client));
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200433 }
Michael Krufky7a91a802007-06-06 16:10:39 -0300434 if (t->ops.is_stereo) {
435 tuner_info("Stereo: %s\n", t->ops.is_stereo(client) ? "yes" : "no");
Mauro Carvalho Chehab7e578192006-01-09 15:25:27 -0200436 }
437}
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200438
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439/* ---------------------------------------------------------------------- */
440
Hans Verkuilba8fc392006-06-25 15:34:39 -0300441/* static vars: used only in tuner_attach and tuner_probe */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700442static unsigned default_mode_mask;
443
444/* During client attach, set_type is called by adapter's attach_inform callback.
445 set_type must then be completed by tuner_attach.
446 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
448{
449 struct tuner *t;
450
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700451 client_template.adapter = adap;
452 client_template.addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Panagiotis Issaris74081872006-01-11 19:40:56 -0200454 t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700455 if (NULL == t)
456 return -ENOMEM;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700457 memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 i2c_set_clientdata(&t->i2c, t);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700459 t->type = UNSET;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700460 t->audmode = V4L2_TUNER_MODE_STEREO;
461 t->mode_mask = T_UNINITIALIZED;
Michael Krufky7a91a802007-06-06 16:10:39 -0300462 t->ops.tuner_status = tuner_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700464 if (show_i2c) {
465 unsigned char buffer[16];
466 int i,rc;
467
468 memset(buffer, 0, sizeof(buffer));
469 rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800470 tuner_info("I2C RECV = ");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700471 for (i=0;i<rc;i++)
472 printk("%02x ",buffer[i]);
473 printk("\n");
474 }
Michael Hunoldc28089a2006-11-28 08:14:44 -0300475 /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
476 if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
477 return -ENODEV;
478
Markus Rechberger257c6452006-01-23 17:11:11 -0200479 /* autodetection code based on the i2c addr */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700480 if (!no_autodetect) {
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800481 switch (addr) {
Adrian Bunk9ee476a2007-06-05 05:22:00 -0300482#ifdef CONFIG_TUNER_TEA5761
Mauro Carvalho Chehab8573a9e2007-04-08 01:09:11 -0300483 case 0x10:
484 if (tea5761_autodetection(&t->i2c) != EINVAL) {
485 t->type = TUNER_TEA5761;
486 t->mode_mask = T_RADIO;
487 t->mode = T_STANDBY;
488 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
489 default_mode_mask &= ~T_RADIO;
490
491 goto register_client;
492 }
493 break;
494#endif
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800495 case 0x42:
496 case 0x43:
497 case 0x4a:
498 case 0x4b:
499 /* If chip is not tda8290, don't register.
500 since it can be tda9887*/
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300501 if (tda8290_probe(&t->i2c) == 0) {
502 tuner_dbg("chip at addr %x is a tda8290\n", addr);
503 } else {
504 /* Default is being tda9887 */
505 t->type = TUNER_TDA9887;
506 t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
507 t->mode = T_STANDBY;
508 goto register_client;
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800509 }
510 break;
Mauro Carvalho Chehab13dd38d2005-11-08 21:37:57 -0800511 case 0x60:
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700512 if (tea5767_autodetection(&t->i2c) != EINVAL) {
513 t->type = TUNER_TEA5767;
514 t->mode_mask = T_RADIO;
515 t->mode = T_STANDBY;
Hans Verkuil27487d42006-01-15 15:04:52 -0200516 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700517 default_mode_mask &= ~T_RADIO;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700518
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800519 goto register_client;
Mauro Carvalho Chehabc5287ba2005-07-15 03:56:28 -0700520 }
Hartmut Hackmann07345f52005-11-08 21:38:09 -0800521 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700522 }
523 }
524
525 /* Initializes only the first adapter found */
526 if (default_mode_mask != T_UNINITIALIZED) {
527 tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
528 t->mode_mask = default_mode_mask;
Hans Verkuil27487d42006-01-15 15:04:52 -0200529 t->tv_freq = 400 * 16; /* Sets freq to VHF High */
530 t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700531 default_mode_mask = T_UNINITIALIZED;
532 }
533
534 /* Should be just before return */
Mauro Carvalho Chehab67678362005-11-08 21:38:02 -0800535register_client:
536 tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700537 i2c_attach_client (&t->i2c);
Hartmut Hackmanncfeb8832007-04-27 12:31:17 -0300538 set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 return 0;
540}
541
542static int tuner_probe(struct i2c_adapter *adap)
543{
544 if (0 != addr) {
Mauro Carvalho Chehabf5bec392005-06-23 22:05:13 -0700545 normal_i2c[0] = addr;
546 normal_i2c[1] = I2C_CLIENT_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700549 default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700550
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 if (adap->class & I2C_CLASS_TV_ANALOG)
552 return i2c_probe(adap, &addr_data, tuner_attach);
553 return 0;
554}
555
556static int tuner_detach(struct i2c_client *client)
557{
558 struct tuner *t = i2c_get_clientdata(client);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700559 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700561 err = i2c_detach_client(&t->i2c);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700562 if (err) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700563 tuner_warn
564 ("Client deregistration failed, client not detached.\n");
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700565 return err;
566 }
567
Michael Krufky7a91a802007-06-06 16:10:39 -0300568 if (t->ops.release)
569 t->ops.release(client);
Michael Krufky052c50d2007-06-04 16:00:45 -0300570 else {
571 kfree(t->priv);
572 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 kfree(t);
574 return 0;
575}
576
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700577/*
578 * Switch tuner to other mode. If tuner support both tv and radio,
579 * set another frequency to some value (This is needed for some pal
580 * tuners to avoid locking). Otherwise, just put second tuner in
581 * standby mode.
582 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700584static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
585{
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800586 if (mode == t->mode)
587 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700588
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800589 t->mode = mode;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700590
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800591 if (check_mode(t, cmd) == EINVAL) {
592 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300593 if (t->ops.standby)
594 t->ops.standby (client);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800595 return EINVAL;
596 }
597 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700598}
599
600#define switch_v4l2() if (!t->using_v4l2) \
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800601 tuner_dbg("switching to v4l2\n"); \
602 t->using_v4l2 = 1;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700603
604static inline int check_v4l2(struct tuner *t)
605{
Hans Verkuil3bbe5a82006-04-01 15:27:52 -0300606 /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
607 TV, v4l1 for radio), until that is fixed this code is disabled.
608 Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
609 first. */
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700610 return 0;
611}
612
613static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614{
615 struct tuner *t = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
Hans Verkuilf9195de2006-01-11 19:01:01 -0200617 if (tuner_debug>1)
Michael Krufky5e453dc2006-01-09 15:32:31 -0200618 v4l_i2c_print_ioctl(&(t->i2c),cmd);
619
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700620 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 /* --- configuration --- */
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700622 case TUNER_SET_TYPE_ADDR:
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300623 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 -0700624 ((struct tuner_setup *)arg)->type,
625 ((struct tuner_setup *)arg)->addr,
Hartmut Hackmannde956c12007-04-27 12:31:12 -0300626 ((struct tuner_setup *)arg)->mode_mask,
627 ((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700628
629 set_addr(client, (struct tuner_setup *)arg);
Mauro Carvalho Chehab391cd722005-06-23 22:02:43 -0700630 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 case AUDC_SET_RADIO:
Hans Verkuil27487d42006-01-15 15:04:52 -0200632 if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
633 == EINVAL)
634 return 0;
635 if (t->radio_freq)
636 set_freq(client, t->radio_freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 break;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700638 case TUNER_SET_STANDBY:
Hans Verkuil27487d42006-01-15 15:04:52 -0200639 if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
640 return 0;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300641 t->mode = T_STANDBY;
Michael Krufky7a91a802007-06-06 16:10:39 -0300642 if (t->ops.standby)
643 t->ops.standby (client);
Hans Verkuil27487d42006-01-15 15:04:52 -0200644 break;
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300645#ifdef CONFIG_VIDEO_V4L1
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700646 case VIDIOCSAUDIO:
647 if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
648 return 0;
649 if (check_v4l2(t) == EINVAL)
650 return 0;
651
652 /* Should be implemented, since bttv calls it */
653 tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700654 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 case VIDIOCSCHAN:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700656 {
657 static const v4l2_std_id map[] = {
658 [VIDEO_MODE_PAL] = V4L2_STD_PAL,
659 [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
660 [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
661 [4 /* bttv */ ] = V4L2_STD_PAL_M,
662 [5 /* bttv */ ] = V4L2_STD_PAL_N,
663 [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
664 };
665 struct video_channel *vc = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700667 if (check_v4l2(t) == EINVAL)
668 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700669
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700670 if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
671 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700673 if (vc->norm < ARRAY_SIZE(map))
674 t->std = map[vc->norm];
675 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200676 if (t->tv_freq)
677 set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700678 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700679 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700680 case VIDIOCSFREQ:
681 {
682 unsigned long *v = arg;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700683
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700684 if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
685 return 0;
686 if (check_v4l2(t) == EINVAL)
687 return 0;
688
689 set_freq(client, *v);
690 return 0;
691 }
692 case VIDIOCGTUNER:
693 {
694 struct video_tuner *vt = arg;
695
696 if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
697 return 0;
698 if (check_v4l2(t) == EINVAL)
699 return 0;
700
701 if (V4L2_TUNER_RADIO == t->mode) {
Michael Krufky7a91a802007-06-06 16:10:39 -0300702 if (t->ops.has_signal)
703 vt->signal = t->ops.has_signal(client);
704 if (t->ops.is_stereo) {
705 if (t->ops.is_stereo(client))
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700706 vt->flags |=
707 VIDEO_TUNER_STEREO_ON;
708 else
709 vt->flags &=
710 ~VIDEO_TUNER_STEREO_ON;
711 }
712 vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
713
714 vt->rangelow = radio_range[0] * 16000;
715 vt->rangehigh = radio_range[1] * 16000;
716
717 } else {
718 vt->rangelow = tv_range[0] * 16;
719 vt->rangehigh = tv_range[1] * 16;
720 }
721
722 return 0;
723 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 case VIDIOCGAUDIO:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700725 {
726 struct video_audio *va = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700728 if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
729 return 0;
730 if (check_v4l2(t) == EINVAL)
731 return 0;
732
Michael Krufky7a91a802007-06-06 16:10:39 -0300733 if (V4L2_TUNER_RADIO == t->mode && t->ops.is_stereo)
734 va->mode = t->ops.is_stereo(client)
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700735 ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
736 return 0;
737 }
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300738#endif
739 case TDA9887_SET_CONFIG:
740 if (t->type == TUNER_TDA9887) {
741 int *i = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
Mauro Carvalho Chehab985bc962006-07-23 06:31:19 -0300743 t->tda9887_config = *i;
744 set_freq(client, t->tv_freq);
745 }
746 break;
747 /* --- v4l ioctls --- */
748 /* take care: bttv does userspace copying, we'll get a
749 kernel pointer here... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 case VIDIOC_S_STD:
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700751 {
752 v4l2_std_id *id = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700754 if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
755 == EINVAL)
756 return 0;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700757
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700758 switch_v4l2();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700760 t->std = *id;
761 tuner_fixup_std(t);
Hans Verkuil27487d42006-01-15 15:04:52 -0200762 if (t->tv_freq)
763 set_freq(client, t->tv_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700764 break;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700765 }
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700766 case VIDIOC_S_FREQUENCY:
767 {
768 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700769
Hans Verkuil4f725cb2006-06-24 09:47:56 -0300770 if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
771 == EINVAL)
772 return 0;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700773 switch_v4l2();
Hans Verkuil27487d42006-01-15 15:04:52 -0200774 set_freq(client,f->frequency);
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700775
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700776 break;
777 }
778 case VIDIOC_G_FREQUENCY:
779 {
780 struct v4l2_frequency *f = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700781
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700782 if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
783 return 0;
784 switch_v4l2();
785 f->type = t->mode;
Hans Verkuil27487d42006-01-15 15:04:52 -0200786 f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
787 t->radio_freq : t->tv_freq;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700788 break;
789 }
790 case VIDIOC_G_TUNER:
791 {
792 struct v4l2_tuner *tuner = arg;
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700793
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700794 if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
795 return 0;
796 switch_v4l2();
797
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200798 tuner->type = t->mode;
Michael Krufky7a91a802007-06-06 16:10:39 -0300799 if (t->ops.get_afc)
800 tuner->afc=t->ops.get_afc(client);
Hans Verkuilab4cecf2006-04-01 16:40:21 -0300801 if (t->mode == V4L2_TUNER_ANALOG_TV)
802 tuner->capability |= V4L2_TUNER_CAP_NORM;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200803 if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700804 tuner->rangelow = tv_range[0] * 16;
805 tuner->rangehigh = tv_range[1] * 16;
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200806 break;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700807 }
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200808
809 /* radio mode */
Michael Krufky7a91a802007-06-06 16:10:39 -0300810 if (t->ops.has_signal)
811 tuner->signal = t->ops.has_signal(client);
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200812
813 tuner->rxsubchans =
814 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
Michael Krufky7a91a802007-06-06 16:10:39 -0300815 if (t->ops.is_stereo) {
816 tuner->rxsubchans = t->ops.is_stereo(client) ?
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200817 V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
818 }
819
820 tuner->capability |=
821 V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
822 tuner->audmode = t->audmode;
823 tuner->rangelow = radio_range[0] * 16000;
824 tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700825 break;
826 }
827 case VIDIOC_S_TUNER:
828 {
829 struct v4l2_tuner *tuner = arg;
830
831 if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
832 return 0;
833
834 switch_v4l2();
835
Hans Verkuil8a4b2752006-01-23 17:11:09 -0200836 /* do nothing unless we're a radio tuner */
837 if (t->mode != V4L2_TUNER_RADIO)
838 break;
839 t->audmode = tuner->audmode;
840 set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700841 break;
842 }
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200843 case VIDIOC_LOG_STATUS:
Michael Krufky7a91a802007-06-06 16:10:39 -0300844 if (t->ops.tuner_status)
845 t->ops.tuner_status(client);
Hans Verkuilcd43c3f2006-01-09 15:25:15 -0200846 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 }
848
849 return 0;
850}
851
Jean Delvare21b48a72007-03-12 19:20:15 -0300852static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700854 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700856 tuner_dbg ("suspend\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 /* FIXME: power down ??? */
858 return 0;
859}
860
Jean Delvare21b48a72007-03-12 19:20:15 -0300861static int tuner_resume(struct i2c_client *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700863 struct tuner *t = i2c_get_clientdata (c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700865 tuner_dbg ("resume\n");
Hans Verkuil27487d42006-01-15 15:04:52 -0200866 if (V4L2_TUNER_RADIO == t->mode) {
867 if (t->radio_freq)
868 set_freq(c, t->radio_freq);
869 } else {
870 if (t->tv_freq)
871 set_freq(c, t->tv_freq);
872 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 return 0;
874}
875
876/* ----------------------------------------------------------------------- */
877
878static struct i2c_driver driver = {
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700879 .id = I2C_DRIVERID_TUNER,
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700880 .attach_adapter = tuner_probe,
881 .detach_client = tuner_detach,
882 .command = tuner_command,
Jean Delvare21b48a72007-03-12 19:20:15 -0300883 .suspend = tuner_suspend,
884 .resume = tuner_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 .driver = {
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200886 .name = "tuner",
Mauro Carvalho Chehabcab462f2006-01-09 15:53:26 -0200887 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888};
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700889static struct i2c_client client_template = {
Jean Delvarefae91e72005-08-15 19:57:04 +0200890 .name = "(tuner unset)",
Mauro Carvalho Chehabf7ce3cc2005-07-12 13:58:55 -0700891 .driver = &driver,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892};
893
894static int __init tuner_init_module(void)
895{
896 return i2c_add_driver(&driver);
897}
898
899static void __exit tuner_cleanup_module(void)
900{
901 i2c_del_driver(&driver);
902}
903
904module_init(tuner_init_module);
905module_exit(tuner_cleanup_module);
906
907/*
908 * Overrides for Emacs so that we follow Linus's tabbing style.
909 * ---------------------------------------------------------------------------
910 * Local variables:
911 * c-basic-offset: 8
912 * End:
913 */