tuner-core.c 33.2 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
 *
 * i2c tv tuner chip device driver
 * core core, i.e. kernel interfaces, registering and so on
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/init.h>
18
#include <linux/videodev.h>
Linus Torvalds's avatar
Linus Torvalds committed
19
#include <media/tuner.h>
20
#include <media/tuner-types.h>
21
#include <media/v4l2-common.h>
22
#include <media/v4l2-i2c-drv-legacy.h>
23
#include "mt20xx.h"
24
#include "tda8290.h"
25
#include "tea5761.h"
26
#include "tea5767.h"
27
#include "tuner-xc2028.h"
28
#include "tuner-simple.h"
29
#include "tda9887.h"
30
#include "xc5000.h"
Linus Torvalds's avatar
Linus Torvalds committed
31
32
33

#define UNSET (-1U)

34
#define PREFIX t->i2c->driver->driver.name
35

36
/** This macro allows us to probe dynamically, avoiding static links */
37
#ifdef CONFIG_MEDIA_ATTACH
38
39
40
41
42
#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
	int __r = -EINVAL; \
	typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
	if (__a) { \
		__r = (int) __a(ARGS); \
43
		symbol_put(FUNCTION); \
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
	} else { \
		printk(KERN_ERR "TUNER: Unable to find " \
				"symbol "#FUNCTION"()\n"); \
	} \
	__r; \
})

static void tuner_detach(struct dvb_frontend *fe)
{
	if (fe->ops.tuner_ops.release) {
		fe->ops.tuner_ops.release(fe);
		symbol_put_addr(fe->ops.tuner_ops.release);
	}
	if (fe->ops.analog_ops.release) {
		fe->ops.analog_ops.release(fe);
		symbol_put_addr(fe->ops.analog_ops.release);
	}
}
#else
#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
	FUNCTION(ARGS); \
})

static void tuner_detach(struct dvb_frontend *fe)
{
	if (fe->ops.tuner_ops.release)
		fe->ops.tuner_ops.release(fe);
	if (fe->ops.analog_ops.release)
		fe->ops.analog_ops.release(fe);
}
#endif

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
struct tuner {
	/* device */
	struct dvb_frontend fe;
	struct i2c_client   *i2c;
	struct list_head    list;
	unsigned int        using_v4l2:1;

	/* keep track of the current settings */
	v4l2_std_id         std;
	unsigned int        tv_freq;
	unsigned int        radio_freq;
	unsigned int        audmode;

	unsigned int        mode;
	unsigned int        mode_mask; /* Combination of allowable modes */

	unsigned int        type; /* chip type id */
	unsigned int        config;
	int (*tuner_callback) (void *dev, int command, int arg);
};

Linus Torvalds's avatar
Linus Torvalds committed
97
98
/* standard i2c insmod options */
static unsigned short normal_i2c[] = {
99
#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
100
101
	0x10,
#endif
102
	0x42, 0x43, 0x4a, 0x4b,			/* tda8290 */
103
104
	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
Linus Torvalds's avatar
Linus Torvalds committed
105
106
	I2C_CLIENT_END
};
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
107

Linus Torvalds's avatar
Linus Torvalds committed
108
109
110
I2C_CLIENT_INSMOD;

/* insmod options used at init time => read/only */
111
112
113
static unsigned int addr;
static unsigned int no_autodetect;
static unsigned int show_i2c;
114

Linus Torvalds's avatar
Linus Torvalds committed
115
/* insmod options used at runtime => read/write */
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
static int tuner_debug;

#define tuner_warn(fmt, arg...) do {			\
	printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
	       i2c_adapter_id(t->i2c->adapter),		\
	       t->i2c->addr, ##arg);			\
	 } while (0)

#define tuner_info(fmt, arg...) do {			\
	printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX,	\
	       i2c_adapter_id(t->i2c->adapter),		\
	       t->i2c->addr, ##arg);			\
	 } while (0)

#define tuner_err(fmt, arg...) do {			\
	printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX,	\
	       i2c_adapter_id(t->i2c->adapter),		\
	       t->i2c->addr, ##arg);			\
	 } while (0)

#define tuner_dbg(fmt, arg...) do {				\
	if (tuner_debug)					\
		printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX,	\
		       i2c_adapter_id(t->i2c->adapter),		\
		       t->i2c->addr, ##arg);			\
	 } while (0)

/* ------------------------------------------------------------------------ */
Linus Torvalds's avatar
Linus Torvalds committed
144

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
145
static unsigned int tv_range[2] = { 44, 958 };
Linus Torvalds's avatar
Linus Torvalds committed
146
147
static unsigned int radio_range[2] = { 65, 108 };

148
149
150
151
static char pal[] = "--";
static char secam[] = "--";
static char ntsc[] = "-";

152

153
154
155
module_param(addr, int, 0444);
module_param(no_autodetect, int, 0444);
module_param(show_i2c, int, 0444);
156
module_param_named(debug,tuner_debug, int, 0644);
157
158
159
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
160
module_param_array(tv_range, int, NULL, 0644);
Linus Torvalds's avatar
Linus Torvalds committed
161
162
163
164
165
166
167
168
module_param_array(radio_range, int, NULL, 0644);

MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
MODULE_LICENSE("GPL");

/* ---------------------------------------------------------------------- */

169
170
static void fe_set_params(struct dvb_frontend *fe,
			  struct analog_parameters *params)
171
{
172
173
	struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
	struct tuner *t = fe->analog_demod_priv;
174
175
176
177
178

	if (NULL == fe_tuner_ops->set_analog_params) {
		tuner_warn("Tuner frontend module has no way to set freq\n");
		return;
	}
179
	fe_tuner_ops->set_analog_params(fe, params);
180
181
}

182
static void fe_standby(struct dvb_frontend *fe)
183
{
184
	struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
185
186

	if (fe_tuner_ops->sleep)
187
		fe_tuner_ops->sleep(fe);
188
189
}

190
static int fe_has_signal(struct dvb_frontend *fe)
191
{
192
	u16 strength = 0;
193

194
195
	if (fe->ops.tuner_ops.get_rf_strength)
		fe->ops.tuner_ops.get_rf_strength(fe, &strength);
196
197
198
199

	return strength;
}

200
201
202
203
204
205
206
207
208
209
210
211
212
static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
{
	struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
	struct tuner *t = fe->analog_demod_priv;

	if (fe_tuner_ops->set_config)
		return fe_tuner_ops->set_config(fe, priv_cfg);

	tuner_warn("Tuner frontend module has no way to set config\n");

	return 0;
}

213
static void tuner_status(struct dvb_frontend *fe);
214

215
static struct analog_demod_ops tuner_core_ops = {
216
	.set_params     = fe_set_params,
217
218
	.standby        = fe_standby,
	.has_signal     = fe_has_signal,
219
	.set_config     = fe_set_config,
220
221
222
	.tuner_status   = tuner_status
};

223
/* Set tuner frequency,  freq in Units of 62.5kHz = 1/16MHz */
Linus Torvalds's avatar
Linus Torvalds committed
224
225
226
static void set_tv_freq(struct i2c_client *c, unsigned int freq)
{
	struct tuner *t = i2c_get_clientdata(c);
227
	struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
Linus Torvalds's avatar
Linus Torvalds committed
228

229
230
231
232
233
234
	struct analog_parameters params = {
		.mode      = t->mode,
		.audmode   = t->audmode,
		.std       = t->std
	};

Linus Torvalds's avatar
Linus Torvalds committed
235
	if (t->type == UNSET) {
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
236
		tuner_warn ("tuner type not set\n");
Linus Torvalds's avatar
Linus Torvalds committed
237
238
		return;
	}
239
	if (NULL == analog_ops->set_params) {
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
240
		tuner_warn ("Tuner has no way to set tv freq\n");
Linus Torvalds's avatar
Linus Torvalds committed
241
242
		return;
	}
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
243
244
245
246
	if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
		tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n",
			   freq / 16, freq % 16 * 100 / 16, tv_range[0],
			   tv_range[1]);
247
248
249
250
251
252
		/* V4L2 spec: if the freq is not possible then the closest
		   possible value should be selected */
		if (freq < tv_range[0] * 16)
			freq = tv_range[0] * 16;
		else
			freq = tv_range[1] * 16;
Linus Torvalds's avatar
Linus Torvalds committed
253
	}
254
255
	params.frequency = freq;

256
	analog_ops->set_params(&t->fe, &params);
Linus Torvalds's avatar
Linus Torvalds committed
257
258
259
260
261
}

static void set_radio_freq(struct i2c_client *c, unsigned int freq)
{
	struct tuner *t = i2c_get_clientdata(c);
262
	struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
Linus Torvalds's avatar
Linus Torvalds committed
263

264
265
266
267
268
269
	struct analog_parameters params = {
		.mode      = t->mode,
		.audmode   = t->audmode,
		.std       = t->std
	};

Linus Torvalds's avatar
Linus Torvalds committed
270
	if (t->type == UNSET) {
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
271
		tuner_warn ("tuner type not set\n");
Linus Torvalds's avatar
Linus Torvalds committed
272
273
		return;
	}
274
	if (NULL == analog_ops->set_params) {
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
275
		tuner_warn ("tuner has no way to set radio frequency\n");
Linus Torvalds's avatar
Linus Torvalds committed
276
277
		return;
	}
278
	if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
279
280
281
		tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n",
			   freq / 16000, freq % 16000 * 100 / 16000,
			   radio_range[0], radio_range[1]);
282
283
284
285
286
287
		/* V4L2 spec: if the freq is not possible then the closest
		   possible value should be selected */
		if (freq < radio_range[0] * 16000)
			freq = radio_range[0] * 16000;
		else
			freq = radio_range[1] * 16000;
Linus Torvalds's avatar
Linus Torvalds committed
288
	}
289
	params.frequency = freq;
290

291
	analog_ops->set_params(&t->fe, &params);
Linus Torvalds's avatar
Linus Torvalds committed
292
293
294
295
296
297
298
299
300
}

static void set_freq(struct i2c_client *c, unsigned long freq)
{
	struct tuner *t = i2c_get_clientdata(c);

	switch (t->mode) {
	case V4L2_TUNER_RADIO:
		tuner_dbg("radio freq set to %lu.%02lu\n",
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
301
302
			  freq / 16000, freq % 16000 * 100 / 16000);
		set_radio_freq(c, freq);
303
		t->radio_freq = freq;
Linus Torvalds's avatar
Linus Torvalds committed
304
305
306
307
		break;
	case V4L2_TUNER_ANALOG_TV:
	case V4L2_TUNER_DIGITAL_TV:
		tuner_dbg("tv freq set to %lu.%02lu\n",
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
308
			  freq / 16, freq % 16 * 100 / 16);
Linus Torvalds's avatar
Linus Torvalds committed
309
		set_tv_freq(c, freq);
310
		t->tv_freq = freq;
Linus Torvalds's avatar
Linus Torvalds committed
311
		break;
312
313
	default:
		tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
Linus Torvalds's avatar
Linus Torvalds committed
314
315
316
	}
}

317
318
319
static void tuner_i2c_address_check(struct tuner *t)
{
	if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
320
	    ((t->i2c->addr < 0x64) || (t->i2c->addr > 0x6f)))
321
322
		return;

323
324
325
326
327
328
	/* We already know that the XC5000 can only be located at
	 * i2c address 0x61, 0x62, 0x63 or 0x64 */
	if ((t->type == TUNER_XC5000) &&
	    ((t->i2c->addr <= 0x64)) && (t->i2c->addr >= 0x61))
		return;

329
330
331
332
	tuner_warn("====================== WARNING! ======================\n");
	tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
	tuner_warn("will soon be dropped. This message indicates that your\n");
	tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
333
		   t->i2c->name, t->i2c->addr);
334
335
336
337
338
	tuner_warn("To ensure continued support for your device, please\n");
	tuner_warn("send a copy of this message, along with full dmesg\n");
	tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
	tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
	tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
339
		   t->i2c->adapter->name, t->i2c->addr, t->type, t->i2c->name);
340
341
342
	tuner_warn("====================== WARNING! ======================\n");
}

343
344
static struct xc5000_config xc5000_cfg;

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
345
static void set_type(struct i2c_client *c, unsigned int type,
346
		     unsigned int new_mode_mask, unsigned int new_config,
347
		     int (*tuner_callback) (void *dev, int command,int arg))
Linus Torvalds's avatar
Linus Torvalds committed
348
349
{
	struct tuner *t = i2c_get_clientdata(c);
350
	struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
351
	struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
352
	unsigned char buffer[4];
Linus Torvalds's avatar
Linus Torvalds committed
353

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
354
355
	if (type == UNSET || type == TUNER_ABSENT) {
		tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
Linus Torvalds's avatar
Linus Torvalds committed
356
		return;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
357
358
	}

359
360
361
362
363
364
365
	t->type = type;
	t->config = new_config;
	if (tuner_callback != NULL) {
		tuner_dbg("defining GPIO callback\n");
		t->tuner_callback = tuner_callback;
	}

366
	if (t->mode == T_UNINITIALIZED) {
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
367
368
		tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);

Linus Torvalds's avatar
Linus Torvalds committed
369
370
		return;
	}
371

372
	/* discard private data, in case set_type() was previously called */
373
374
	tuner_detach(&t->fe);
	t->fe.analog_demod_priv = NULL;
375

Linus Torvalds's avatar
Linus Torvalds committed
376
377
	switch (t->type) {
	case TUNER_MT2032:
378
379
380
		if (!dvb_attach(microtune_attach,
			   &t->fe, t->i2c->adapter, t->i2c->addr))
			goto attach_failed;
Linus Torvalds's avatar
Linus Torvalds committed
381
382
		break;
	case TUNER_PHILIPS_TDA8290:
383
	{
384
385
386
387
388
389
390
		struct tda829x_config cfg = {
			.lna_cfg        = t->config,
			.tuner_callback = t->tuner_callback,
		};
		if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter,
				t->i2c->addr, &cfg))
			goto attach_failed;
391
392
		break;
	}
393
	case TUNER_TEA5767:
394
395
		if (!dvb_attach(tea5767_attach, &t->fe,
				t->i2c->adapter, t->i2c->addr))
396
			goto attach_failed;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
397
		t->mode_mask = T_RADIO;
398
		break;
399
	case TUNER_TEA5761:
400
401
		if (!dvb_attach(tea5761_attach, &t->fe,
				t->i2c->adapter, t->i2c->addr))
402
			goto attach_failed;
403
404
		t->mode_mask = T_RADIO;
		break;
405
406
407
408
409
	case TUNER_PHILIPS_FMD1216ME_MK3:
		buffer[0] = 0x0b;
		buffer[1] = 0xdc;
		buffer[2] = 0x9c;
		buffer[3] = 0x60;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
410
		i2c_master_send(c, buffer, 4);
411
412
413
		mdelay(1);
		buffer[2] = 0x86;
		buffer[3] = 0x54;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
414
		i2c_master_send(c, buffer, 4);
415
416
		if (!dvb_attach(simple_tuner_attach, &t->fe,
				t->i2c->adapter, t->i2c->addr, t->type))
417
			goto attach_failed;
418
		break;
419
420
421
422
423
	case TUNER_PHILIPS_TD1316:
		buffer[0] = 0x0b;
		buffer[1] = 0xdc;
		buffer[2] = 0x86;
		buffer[3] = 0xa4;
424
425
426
		i2c_master_send(c, buffer, 4);
		if (!dvb_attach(simple_tuner_attach, &t->fe,
				t->i2c->adapter, t->i2c->addr, t->type))
427
			goto attach_failed;
428
		break;
429
430
	case TUNER_XC2028:
	{
431
432
433
434
435
		struct xc2028_config cfg = {
			.i2c_adap  = t->i2c->adapter,
			.i2c_addr  = t->i2c->addr,
			.callback  = t->tuner_callback,
		};
436
		if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
437
			goto attach_failed;
438
439
		break;
	}
440
	case TUNER_TDA9887:
441
442
443
		if (!dvb_attach(tda9887_attach,
			   &t->fe, t->i2c->adapter, t->i2c->addr))
			goto attach_failed;
444
		break;
445
	case TUNER_XC5000:
446
447
448
	{
		struct dvb_tuner_ops *xc_tuner_ops;

449
450
		xc5000_cfg.i2c_address	  = t->i2c->addr;
		xc5000_cfg.if_khz	  = 5380;
451
		xc5000_cfg.priv           = c->adapter->algo_data;
452
		xc5000_cfg.tuner_callback = t->tuner_callback;
453
454
		if (!dvb_attach(xc5000_attach,
				&t->fe, t->i2c->adapter, &xc5000_cfg))
455
456
			goto attach_failed;

457
		xc_tuner_ops = &t->fe.ops.tuner_ops;
458
		if (xc_tuner_ops->init)
459
460
			xc_tuner_ops->init(&t->fe);
		break;
461
	}
Linus Torvalds's avatar
Linus Torvalds committed
462
	default:
463
464
		if (!dvb_attach(simple_tuner_attach, &t->fe,
				t->i2c->adapter, t->i2c->addr, t->type))
465
466
			goto attach_failed;

Linus Torvalds's avatar
Linus Torvalds committed
467
468
		break;
	}
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
469

470
	if ((NULL == analog_ops->set_params) &&
471
	    (fe_tuner_ops->set_analog_params)) {
472

473
474
		strlcpy(t->i2c->name, fe_tuner_ops->info.name,
			sizeof(t->i2c->name));
475

476
		t->fe.analog_demod_priv = t;
477
478
		memcpy(analog_ops, &tuner_core_ops,
		       sizeof(struct analog_demod_ops));
479

480
	} else {
481
		strlcpy(t->i2c->name, analog_ops->info.name,
482
			sizeof(t->i2c->name));
483
484
	}

485
	tuner_dbg("type set to %s\n", t->i2c->name);
486

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
487
488
489
	if (t->mode_mask == T_UNINITIALIZED)
		t->mode_mask = new_mode_mask;

490
491
492
493
494
495
496
497
498
	/* xc2028/3028 and xc5000 requires a firmware to be set-up later
	   trying to set a frequency here will just fail
	   FIXME: better to move set_freq to the tuner code. This is needed
	   on analog tuners for PLL to properly work
	 */
	if (t->type != TUNER_XC2028 && t->type != TUNER_XC5000)
		set_freq(c, (V4L2_TUNER_RADIO == t->mode) ?
			    t->radio_freq : t->tv_freq);

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
499
	tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
500
		  c->adapter->name, c->driver->driver.name, c->addr << 1, type,
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
501
		  t->mode_mask);
502
	tuner_i2c_address_check(t);
503
504
505
506
507
508
509
510
	return;

attach_failed:
	tuner_dbg("Tuner attach for type = %d failed.\n", t->type);
	t->type = TUNER_ABSENT;
	t->mode_mask = T_UNINITIALIZED;

	return;
Linus Torvalds's avatar
Linus Torvalds committed
511
512
}

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
513
514
515
516
517
518
519
520
521
522
523
524
/*
 * This function apply tuner config to tuner specified
 * by tun_setup structure. I addr is unset, then admin status
 * and tun addr status is more precise then current status,
 * it's applied. Otherwise status and type are applied only to
 * tuner with exactly the same addr.
*/

static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
{
	struct tuner *t = i2c_get_clientdata(c);

525
526
527
528
	if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
		(t->mode_mask & tun_setup->mode_mask))) ||
		(tun_setup->addr == c->addr)) {
			set_type(c, tun_setup->type, tun_setup->mode_mask,
529
				 tun_setup->config, tun_setup->tuner_callback);
530
531
532
533
534
	} else
		tuner_dbg("set addr discarded for type %i, mask %x. "
			  "Asked to change tuner at addr 0x%02x, with mask %x\n",
			  t->type, t->mode_mask,
			  tun_setup->addr, tun_setup->mode_mask);
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
535
}
536

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
537
static inline int check_mode(struct tuner *t, char *cmd)
538
{
539
540
541
542
543
544
545
546
547
548
549
550
551
552
	if ((1 << t->mode & t->mode_mask) == 0) {
		return EINVAL;
	}

	switch (t->mode) {
	case V4L2_TUNER_RADIO:
		tuner_dbg("Cmd %s accepted for radio\n", cmd);
		break;
	case V4L2_TUNER_ANALOG_TV:
		tuner_dbg("Cmd %s accepted for analog TV\n", cmd);
		break;
	case V4L2_TUNER_DIGITAL_TV:
		tuner_dbg("Cmd %s accepted for digital TV\n", cmd);
		break;
553
	}
554
	return 0;
555
556
}

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
557
/* get more precise norm info from insmod option */
Linus Torvalds's avatar
Linus Torvalds committed
558
559
560
561
static int tuner_fixup_std(struct tuner *t)
{
	if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
		switch (pal[0]) {
562
563
564
565
		case '6':
			tuner_dbg ("insmod fixup: PAL => PAL-60\n");
			t->std = V4L2_STD_PAL_60;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
566
567
568
569
		case 'b':
		case 'B':
		case 'g':
		case 'G':
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
570
			tuner_dbg ("insmod fixup: PAL => PAL-BG\n");
Linus Torvalds's avatar
Linus Torvalds committed
571
572
573
574
			t->std = V4L2_STD_PAL_BG;
			break;
		case 'i':
		case 'I':
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
575
			tuner_dbg ("insmod fixup: PAL => PAL-I\n");
Linus Torvalds's avatar
Linus Torvalds committed
576
577
578
579
580
581
			t->std = V4L2_STD_PAL_I;
			break;
		case 'd':
		case 'D':
		case 'k':
		case 'K':
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
582
			tuner_dbg ("insmod fixup: PAL => PAL-DK\n");
Linus Torvalds's avatar
Linus Torvalds committed
583
584
			t->std = V4L2_STD_PAL_DK;
			break;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
585
586
587
588
589
590
591
		case 'M':
		case 'm':
			tuner_dbg ("insmod fixup: PAL => PAL-M\n");
			t->std = V4L2_STD_PAL_M;
			break;
		case 'N':
		case 'n':
592
593
594
595
596
597
598
			if (pal[1] == 'c' || pal[1] == 'C') {
				tuner_dbg("insmod fixup: PAL => PAL-Nc\n");
				t->std = V4L2_STD_PAL_Nc;
			} else {
				tuner_dbg ("insmod fixup: PAL => PAL-N\n");
				t->std = V4L2_STD_PAL_N;
			}
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
599
			break;
600
601
602
603
604
605
		case '-':
			/* default parameter, do nothing */
			break;
		default:
			tuner_warn ("pal= argument not recognised\n");
			break;
Linus Torvalds's avatar
Linus Torvalds committed
606
607
		}
	}
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
608
609
	if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
		switch (secam[0]) {
610
611
612
613
614
615
616
617
618
		case 'b':
		case 'B':
		case 'g':
		case 'G':
		case 'h':
		case 'H':
			tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n");
			t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
			break;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
619
620
621
622
623
624
625
626
627
		case 'd':
		case 'D':
		case 'k':
		case 'K':
			tuner_dbg ("insmod fixup: SECAM => SECAM-DK\n");
			t->std = V4L2_STD_SECAM_DK;
			break;
		case 'l':
		case 'L':
628
629
630
631
632
633
634
			if ((secam[1]=='C')||(secam[1]=='c')) {
				tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
				t->std = V4L2_STD_SECAM_LC;
			} else {
				tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
				t->std = V4L2_STD_SECAM_L;
			}
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
635
			break;
636
637
638
639
640
641
		case '-':
			/* default parameter, do nothing */
			break;
		default:
			tuner_warn ("secam= argument not recognised\n");
			break;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
642
643
644
		}
	}

645
646
647
648
649
650
651
652
653
654
655
656
	if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
		switch (ntsc[0]) {
		case 'm':
		case 'M':
			tuner_dbg("insmod fixup: NTSC => NTSC-M\n");
			t->std = V4L2_STD_NTSC_M;
			break;
		case 'j':
		case 'J':
			tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n");
			t->std = V4L2_STD_NTSC_M_JP;
			break;
657
658
659
660
661
		case 'k':
		case 'K':
			tuner_dbg("insmod fixup: NTSC => NTSC_M_KR\n");
			t->std = V4L2_STD_NTSC_M_KR;
			break;
662
663
664
665
666
667
668
669
		case '-':
			/* default parameter, do nothing */
			break;
		default:
			tuner_info("ntsc= argument not recognised\n");
			break;
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
670
671
672
	return 0;
}

673
static void tuner_status(struct dvb_frontend *fe)
674
{
675
	struct tuner *t = fe->analog_demod_priv;
676
	unsigned long freq, freq_fraction;
677
678
	struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
	struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
679
680
681
682
683
684
685
686
687
	const char *p;

	switch (t->mode) {
		case V4L2_TUNER_RADIO: 	    p = "radio"; break;
		case V4L2_TUNER_ANALOG_TV:  p = "analog TV"; break;
		case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break;
		default: p = "undefined"; break;
	}
	if (t->mode == V4L2_TUNER_RADIO) {
688
689
		freq = t->radio_freq / 16000;
		freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
690
	} else {
691
692
		freq = t->tv_freq / 16;
		freq_fraction = (t->tv_freq % 16) * 100 / 16;
693
694
695
	}
	tuner_info("Tuner mode:      %s\n", p);
	tuner_info("Frequency:       %lu.%02lu MHz\n", freq, freq_fraction);
696
	tuner_info("Standard:        0x%08lx\n", (unsigned long)t->std);
697
698
	if (t->mode != V4L2_TUNER_RADIO)
	       return;
699
700
701
702
703
704
705
706
707
	if (fe_tuner_ops->get_status) {
		u32 tuner_status;

		fe_tuner_ops->get_status(&t->fe, &tuner_status);
		if (tuner_status & TUNER_STATUS_LOCKED)
			tuner_info("Tuner is locked.\n");
		if (tuner_status & TUNER_STATUS_STEREO)
			tuner_info("Stereo:          yes\n");
	}
708
709
710
711
712
713
	if (analog_ops->has_signal)
		tuner_info("Signal strength: %d\n",
			   analog_ops->has_signal(fe));
	if (analog_ops->is_stereo)
		tuner_info("Stereo:          %s\n",
			   analog_ops->is_stereo(fe) ? "yes" : "no");
714
}
715

Linus Torvalds's avatar
Linus Torvalds committed
716
717
/* ---------------------------------------------------------------------- */

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
718
719
720
721
722
723
724
725
726
/*
 * Switch tuner to other mode. If tuner support both tv and radio,
 * set another frequency to some value (This is needed for some pal
 * tuners to avoid locking). Otherwise, just put second tuner in
 * standby mode.
 */

static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
{
727
	struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
728

729
730
731
732
733
734
735
	if (mode == t->mode)
		return 0;

	t->mode = mode;

	if (check_mode(t, cmd) == EINVAL) {
		t->mode = T_STANDBY;
736
737
		if (analog_ops->standby)
			analog_ops->standby(&t->fe);
738
739
740
		return EINVAL;
	}
	return 0;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
741
742
743
}

#define switch_v4l2()	if (!t->using_v4l2) \
744
745
			    tuner_dbg("switching to v4l2\n"); \
			t->using_v4l2 = 1;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
746
747
748

static inline int check_v4l2(struct tuner *t)
{
749
750
751
752
	/* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for
	   TV, v4l1 for radio), until that is fixed this code is disabled.
	   Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2)
	   first. */
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
753
754
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
755

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
756
static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
Linus Torvalds's avatar
Linus Torvalds committed
757
758
{
	struct tuner *t = i2c_get_clientdata(client);
759
	struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
760
	struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
Linus Torvalds's avatar
Linus Torvalds committed
761

762
	if (tuner_debug > 1) {
763
		v4l_i2c_print_ioctl(client,cmd);
764
765
		printk("\n");
	}
766

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
767
	switch (cmd) {
Linus Torvalds's avatar
Linus Torvalds committed
768
	/* --- configuration --- */
769
	case TUNER_SET_TYPE_ADDR:
770
		tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
771
772
				((struct tuner_setup *)arg)->type,
				((struct tuner_setup *)arg)->addr,
773
774
				((struct tuner_setup *)arg)->mode_mask,
				((struct tuner_setup *)arg)->config);
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
775
776

		set_addr(client, (struct tuner_setup *)arg);
777
		break;
Linus Torvalds's avatar
Linus Torvalds committed
778
	case AUDC_SET_RADIO:
779
780
781
782
783
		if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
				== EINVAL)
			return 0;
		if (t->radio_freq)
			set_freq(client, t->radio_freq);
Linus Torvalds's avatar
Linus Torvalds committed
784
		break;
785
	case TUNER_SET_STANDBY:
786
787
		if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
			return 0;
788
		t->mode = T_STANDBY;
789
790
		if (analog_ops->standby)
			analog_ops->standby(&t->fe);
791
		break;
792
#ifdef CONFIG_VIDEO_ALLOW_V4L1
793
794
795
796
797
798
799
800
	case VIDIOCSAUDIO:
		if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
			return 0;
		if (check_v4l2(t) == EINVAL)
			return 0;

		/* Should be implemented, since bttv calls it */
		tuner_dbg("VIDIOCSAUDIO not implemented.\n");
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
801
		break;
Linus Torvalds's avatar
Linus Torvalds committed
802
	case VIDIOCSCHAN:
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
		{
			static const v4l2_std_id map[] = {
				[VIDEO_MODE_PAL] = V4L2_STD_PAL,
				[VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
				[VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
				[4 /* bttv */ ] = V4L2_STD_PAL_M,
				[5 /* bttv */ ] = V4L2_STD_PAL_N,
				[6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
			};
			struct video_channel *vc = arg;

			if (check_v4l2(t) == EINVAL)
				return 0;

			if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
				return 0;

			if (vc->norm < ARRAY_SIZE(map))
				t->std = map[vc->norm];
			tuner_fixup_std(t);
823
824
			if (t->tv_freq)
				set_tv_freq(client, t->tv_freq);
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
825
826
			return 0;
		}
Linus Torvalds's avatar
Linus Torvalds committed
827
	case VIDIOCSFREQ:
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
828
829
		{
			unsigned long *v = arg;
Linus Torvalds's avatar
Linus Torvalds committed
830

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
831
832
833
834
835
836
837
838
			if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
				return 0;
			if (check_v4l2(t) == EINVAL)
				return 0;

			set_freq(client, *v);
			return 0;
		}
Linus Torvalds's avatar
Linus Torvalds committed
839
	case VIDIOCGTUNER:
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
840
841
842
843
844
845
846
847
848
		{
			struct video_tuner *vt = arg;

			if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
				return 0;
			if (check_v4l2(t) == EINVAL)
				return 0;

			if (V4L2_TUNER_RADIO == t->mode) {
849
850
851
852
				if (fe_tuner_ops->get_status) {
					u32 tuner_status;

					fe_tuner_ops->get_status(&t->fe, &tuner_status);
853
854
855
856
					if (tuner_status & TUNER_STATUS_STEREO)
						vt->flags |= VIDEO_TUNER_STEREO_ON;
					else
						vt->flags &= ~VIDEO_TUNER_STEREO_ON;
857
				} else {
858
859
					if (analog_ops->is_stereo) {
						if (analog_ops->is_stereo(&t->fe))
860
861
862
863
864
865
							vt->flags |=
								VIDEO_TUNER_STEREO_ON;
						else
							vt->flags &=
								~VIDEO_TUNER_STEREO_ON;
					}
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
866
				}
867
868
869
				if (analog_ops->has_signal)
					vt->signal =
						analog_ops->has_signal(&t->fe);
870

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
871
				vt->flags |= VIDEO_TUNER_LOW;	/* Allow freqs at 62.5 Hz */
872

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
873
874
				vt->rangelow = radio_range[0] * 16000;
				vt->rangehigh = radio_range[1] * 16000;
875

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
876
877
878
879
			} else {
				vt->rangelow = tv_range[0] * 16;
				vt->rangehigh = tv_range[1] * 16;
			}
880

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
881
882
			return 0;
		}
Linus Torvalds's avatar
Linus Torvalds committed
883
	case VIDIOCGAUDIO:
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
884
885
886
887
888
889
890
891
		{
			struct video_audio *va = arg;

			if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
				return 0;
			if (check_v4l2(t) == EINVAL)
				return 0;

892
893
894
895
896
897
898
			if (V4L2_TUNER_RADIO == t->mode) {
				if (fe_tuner_ops->get_status) {
					u32 tuner_status;

					fe_tuner_ops->get_status(&t->fe, &tuner_status);
					va->mode = (tuner_status & TUNER_STATUS_STEREO)
					    ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
899
900
				} else if (analog_ops->is_stereo)
					va->mode = analog_ops->is_stereo(&t->fe)
901
902
					    ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
			}
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
903
904
			return 0;
		}
905
#endif
906
907
908
909
910
911
	case TUNER_SET_CONFIG:
	{
		struct v4l2_priv_tun_config *cfg = arg;

		if (t->type != cfg->tuner)
			break;
Linus Torvalds's avatar
Linus Torvalds committed
912

913
		if (analog_ops->set_config) {
914
			analog_ops->set_config(&t->fe, cfg->priv);
915
916
917
			break;
		}

918
		tuner_dbg("Tuner frontend module has no way to set config\n");
919
		break;
920
	}
921
922
923
	/* --- v4l ioctls --- */
	/* take care: bttv does userspace copying, we'll get a
	   kernel pointer here... */
Linus Torvalds's avatar
Linus Torvalds committed
924
	case VIDIOC_S_STD:
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
925
926
		{
			v4l2_std_id *id = arg;
Linus Torvalds's avatar
Linus Torvalds committed
927

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
928
929
930
			if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
					== EINVAL)
				return 0;
931

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
932
933
934
935
			switch_v4l2();

			t->std = *id;
			tuner_fixup_std(t);
936
937
			if (t->tv_freq)
				set_freq(client, t->tv_freq);
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
938
939
			break;
		}
Linus Torvalds's avatar
Linus Torvalds committed
940
	case VIDIOC_S_FREQUENCY:
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
941
942
943
		{
			struct v4l2_frequency *f = arg;

944
945
946
			if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
					== EINVAL)
				return 0;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
947
			switch_v4l2();
948
			set_freq(client,f->frequency);
949

Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
950
951
952
953
954
955
956
957
958
959
			break;
		}
	case VIDIOC_G_FREQUENCY:
		{
			struct v4l2_frequency *f = arg;

			if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
				return 0;
			switch_v4l2();
			f->type = t->mode;
960
961
962
963
964
965
966
967
968
			if (fe_tuner_ops->get_frequency) {
				u32 abs_freq;

				fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
				f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
					(abs_freq * 2 + 125/2) / 125 :
					(abs_freq + 62500/2) / 62500;
				break;
			}
969
970
			f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
				t->radio_freq : t->tv_freq;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
971
972
			break;
		}
Linus Torvalds's avatar
Linus Torvalds committed
973
	case VIDIOC_G_TUNER:
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
974
975
976
977
978
979
980
		{
			struct v4l2_tuner *tuner = arg;

			if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
				return 0;
			switch_v4l2();

981
			tuner->type = t->mode;
982
983
			if (analog_ops->get_afc)
				tuner->afc = analog_ops->get_afc(&t->fe);
984
985
			if (t->mode == V4L2_TUNER_ANALOG_TV)
				tuner->capability |= V4L2_TUNER_CAP_NORM;
986
			if (t->mode != V4L2_TUNER_RADIO) {
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
987
988
				tuner->rangelow = tv_range[0] * 16;
				tuner->rangehigh = tv_range[1] * 16;
989
990
991
992
993
994
				break;
			}

			/* radio mode */
			tuner->rxsubchans =
				V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
995
996
997
998
			if (fe_tuner_ops->get_status) {
				u32 tuner_status;

				fe_tuner_ops->get_status(&t->fe, &tuner_status);
999
1000
1001
1002
				tuner->rxsubchans =
					(tuner_status & TUNER_STATUS_STEREO) ?
					V4L2_TUNER_SUB_STEREO :
					V4L2_TUNER_SUB_MONO;
1003
			} else {
1004
				if (analog_ops->is_stereo) {
1005
					tuner->rxsubchans =
1006
						analog_ops->is_stereo(&t->fe) ?
1007
1008
						V4L2_TUNER_SUB_STEREO :
						V4L2_TUNER_SUB_MONO;
1009
				}
1010
			}
1011
1012
			if (analog_ops->has_signal)
				tuner->signal = analog_ops->has_signal(&t->fe);
1013
1014
1015
1016
1017
			tuner->capability |=
			    V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
			tuner->audmode = t->audmode;
			tuner->rangelow = radio_range[0] * 16000;
			tuner->rangehigh = radio_range[1] * 16000;
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
			break;
		}
	case VIDIOC_S_TUNER:
		{
			struct v4l2_tuner *tuner = arg;

			if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
				return 0;

			switch_v4l2();

1029
1030
1031
1032
1033
			/* do nothing unless we're a radio tuner */
			if (t->mode != V4L2_TUNER_RADIO)
				break;
			t->audmode = tuner->audmode;
			set_radio_freq(client, t->radio_freq);
Mauro Carvalho Chehab's avatar
Mauro Carvalho Chehab committed
1034
			break;
1035
		}
1036
	case VIDIOC_LOG_STATUS:
1037
1038
		if (analog_ops->tuner_status)
			analog_ops->tuner_status(&t->fe);
1039
		break;
Linus Torvalds's avatar
Linus Torvalds committed
1040
1041
1042
1043
1044
	}

	return 0;
}

1045
static int tuner_suspend(struct i2c_client *c, pm_message_t state)
Linus Torvalds's avatar
Linus Torvalds committed
1046
{
1047
	struct tuner *t = i2c_get_clientdata(c);
Linus Torvalds's avatar
Linus Torvalds committed
1048

1049
	tuner_dbg("suspend\n");
Linus Torvalds's avatar
Linus Torvalds committed
1050
1051
1052
1053
	/* FIXME: power down ??? */
	return 0;
}

1054
static int tuner_resume(struct i2c_client *c)
Linus Torvalds's avatar
Linus Torvalds committed
1055
{
1056
	struct tuner *t = i2c_get_clientdata(c);
Linus Torvalds's avatar
Linus Torvalds committed
1057

1058
	tuner_dbg("resume\n");
1059
1060
1061
1062
1063
1064
1065
	if (V4L2_TUNER_RADIO == t->mode) {
		if (t->radio_freq)
			set_freq(c, t->radio_freq);
	} else {
		if (t->tv_freq)
			set_freq(c, t->tv_freq);
	}
Linus Torvalds's avatar
Linus Torvalds committed
1066
1067
1068
	return 0;
}

1069
1070
/* ---------------------------------------------------------------------- */

1071
static LIST_HEAD(tuner_list);
1072
1073

/* Search for existing radio and/or TV tuners on the given I2C adapter.
1074
   Note that when this function is called from tuner_probe you can be
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094