tuner-simple.c 17.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 * i2c tv tuner chip device driver
 * controls all those simple 4-control-bytes style tuners.
4 5
 *
 * This "tuner-simple" module was split apart from the original "tuner" module.
L
Linus Torvalds 已提交
6 7 8 9 10
 */
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <media/tuner.h>
11
#include <media/v4l2-common.h>
12
#include <media/tuner-types.h>
13 14 15 16 17 18 19 20
#include "tuner-i2c.h"
#include "tuner-simple.h"

static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");

#define PREFIX "tuner-simple "
L
Linus Torvalds 已提交
21

22
static int offset = 0;
23
module_param(offset, int, 0664);
24 25
MODULE_PARM_DESC(offset,"Allows to specify an offset for tuner");

L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/* ---------------------------------------------------------------------- */

/* tv standard selection for Temic 4046 FM5
   this value takes the low bits of control byte 2
   from datasheet Rev.01, Feb.00
     standard     BG      I       L       L2      D
     picture IF   38.9    38.9    38.9    33.95   38.9
     sound 1      33.4    32.9    32.4    40.45   32.4
     sound 2      33.16
     NICAM        33.05   32.348  33.05           33.05
 */
#define TEMIC_SET_PAL_I         0x05
#define TEMIC_SET_PAL_DK        0x09
#define TEMIC_SET_PAL_L         0x0a // SECAM ?
#define TEMIC_SET_PAL_L2        0x0b // change IF !
#define TEMIC_SET_PAL_BG        0x0c

/* tv tuner system standard selection for Philips FQ1216ME
   this value takes the low bits of control byte 2
   from datasheet "1999 Nov 16" (supersedes "1999 Mar 23")
     standard 		BG	DK	I	L	L`
     picture carrier	38.90	38.90	38.90	38.90	33.95
     colour		34.47	34.47	34.47	34.47	38.38
     sound 1		33.40	32.40	32.90	32.40	40.45
     sound 2		33.16	-	-	-	-
     NICAM		33.05	33.05	32.35	33.05	39.80
 */
#define PHILIPS_SET_PAL_I	0x01 /* Bit 2 always zero !*/
#define PHILIPS_SET_PAL_BGDK	0x09
#define PHILIPS_SET_PAL_L2	0x0a
#define PHILIPS_SET_PAL_L	0x0b

/* system switching for Philips FI1216MF MK2
   from datasheet "1996 Jul 09",
    standard         BG     L      L'
    picture carrier  38.90  38.90  33.95
    colour	     34.47  34.37  38.38
    sound 1          33.40  32.40  40.45
    sound 2          33.16  -      -
    NICAM            33.05  33.05  39.80
 */
67 68 69
#define PHILIPS_MF_SET_STD_BG	0x01 /* Bit 2 must be zero, Bit 3 is system output */
#define PHILIPS_MF_SET_STD_L	0x03 /* Used on Secam France */
#define PHILIPS_MF_SET_STD_LC	0x02 /* Used on SECAM L' */
L
Linus Torvalds 已提交
70

M
Mauro Carvalho Chehab 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
/* Control byte */

#define TUNER_RATIO_MASK        0x06 /* Bit cb1:cb2 */
#define TUNER_RATIO_SELECT_50   0x00
#define TUNER_RATIO_SELECT_32   0x02
#define TUNER_RATIO_SELECT_166  0x04
#define TUNER_RATIO_SELECT_62   0x06

#define TUNER_CHARGE_PUMP       0x40  /* Bit cb6 */

/* Status byte */

#define TUNER_POR	  0x80
#define TUNER_FL          0x40
#define TUNER_MODE        0x38
#define TUNER_AFC         0x07
#define TUNER_SIGNAL      0x07
#define TUNER_STEREO      0x10

#define TUNER_PLL_LOCKED   0x40
#define TUNER_STEREO_MK3   0x04
L
Linus Torvalds 已提交
92

93 94
struct tuner_simple_priv {
	u16 last_div;
95
	struct tuner_i2c_props i2c_props;
96 97 98 99 100

	unsigned int type;
	struct tunertype *tun;

	u32 frequency;
101 102
};

L
Linus Torvalds 已提交
103 104
/* ---------------------------------------------------------------------- */

105
static int tuner_read_status(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
106
{
107
	struct tuner_simple_priv *priv = fe->tuner_priv;
L
Linus Torvalds 已提交
108 109
	unsigned char byte;

110
	if (1 != tuner_i2c_xfer_recv(&priv->i2c_props,&byte,1))
L
Linus Torvalds 已提交
111
		return 0;
112

L
Linus Torvalds 已提交
113 114 115
	return byte;
}

116
static inline int tuner_signal(const int status)
L
Linus Torvalds 已提交
117
{
118
	return (status & TUNER_SIGNAL) << 13;
L
Linus Torvalds 已提交
119 120
}

121
static inline int tuner_stereo(const int type, const int status)
L
Linus Torvalds 已提交
122
{
123
	switch (type) {
124
		case TUNER_PHILIPS_FM1216ME_MK3:
125
		case TUNER_PHILIPS_FM1236_MK3:
126
		case TUNER_PHILIPS_FM1256_IH3:
127
		case TUNER_LG_NTSC_TAPE:
128
			return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
129
		default:
130
			return status & TUNER_STEREO;
131
	}
132
}
133

134 135 136 137 138 139 140 141
static inline int tuner_islocked(const int status)
{
	return (status & TUNER_FL);
}

static inline int tuner_afcstatus(const int status)
{
	return (status & TUNER_AFC) - 2;
L
Linus Torvalds 已提交
142 143 144
}


145 146 147
static int simple_get_status(struct dvb_frontend *fe, u32 *status)
{
	struct tuner_simple_priv *priv = fe->tuner_priv;
148
	int tuner_status = tuner_read_status(fe);
149 150 151

	*status = 0;

152
	if (tuner_islocked(tuner_status))
153
		*status = TUNER_STATUS_LOCKED;
154
	if (tuner_stereo(priv->type, tuner_status))
155 156
		*status |= TUNER_STATUS_STEREO;

157 158 159 160 161 162 163 164 165 166 167 168 169
	tuner_dbg("AFC Status: %d\n", tuner_afcstatus(tuner_status));

	return 0;
}

static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
{
	struct tuner_simple_priv *priv = fe->tuner_priv;
	int signal = tuner_signal(tuner_read_status(fe));

	*strength = signal;

	tuner_dbg("Signal strength: %d\n", signal);
170 171 172 173

	return 0;
}

L
Linus Torvalds 已提交
174 175
/* ---------------------------------------------------------------------- */

176 177
static int simple_set_tv_freq(struct dvb_frontend *fe,
			      struct analog_parameters *params)
L
Linus Torvalds 已提交
178
{
179
	struct tuner_simple_priv *priv = fe->tuner_priv;
180
	u8 config, cb, tuneraddr;
L
Linus Torvalds 已提交
181 182
	u16 div;
	struct tunertype *tun;
183
	u8 buffer[4];
184
	int rc, IFPCoff, i, j;
185
	enum param_type desired_type;
186
	struct tuner_params *t_params;
L
Linus Torvalds 已提交
187

188
	tun = priv->tun;
189

190 191 192 193 194 195 196 197 198 199 200 201
	/* IFPCoff = Video Intermediate Frequency - Vif:
		940  =16*58.75  NTSC/J (Japan)
		732  =16*45.75  M/N STD
		704  =16*44     ATSC (at DVB code)
		632  =16*39.50  I U.K.
		622.4=16*38.90  B/G D/K I, L STD
		592  =16*37.00  D China
		590  =16.36.875 B Australia
		543.2=16*33.95  L' STD
		171.2=16*10.70  FM Radio (at set_radio_freq)
	*/

202
	if (params->std == V4L2_STD_NTSC_M_JP) {
203 204
		IFPCoff      = 940;
		desired_type = TUNER_PARAM_TYPE_NTSC;
205 206
	} else if ((params->std & V4L2_STD_MN) &&
		  !(params->std & ~V4L2_STD_MN)) {
207 208
		IFPCoff      = 732;
		desired_type = TUNER_PARAM_TYPE_NTSC;
209
	} else if (params->std == V4L2_STD_SECAM_LC) {
210 211
		IFPCoff      = 543;
		desired_type = TUNER_PARAM_TYPE_SECAM;
212
	} else {
213 214
		IFPCoff      = 623;
		desired_type = TUNER_PARAM_TYPE_PAL;
215 216
	}

217 218 219 220 221
	for (j = 0; j < tun->count-1; j++) {
		if (desired_type != tun->params[j].type)
			continue;
		break;
	}
222
	/* use default tuner_t_params if desired_type not available */
223
	if (desired_type != tun->params[j].type) {
224 225
		tuner_dbg("IFPCoff = %d: tuner_t_params undefined for tuner %d\n",
			  IFPCoff, priv->type);
226
		j = 0;
227
	}
228
	t_params = &tun->params[j];
229

230 231
	for (i = 0; i < t_params->count; i++) {
		if (params->frequency > t_params->ranges[i].limit)
232 233
			continue;
		break;
L
Linus Torvalds 已提交
234
	}
235
	if (i == t_params->count) {
236
		tuner_dbg("TV frequency out of range (%d > %d)",
237 238
				params->frequency, t_params->ranges[i - 1].limit);
		params->frequency = t_params->ranges[--i].limit;
239
	}
240 241
	config = t_params->ranges[i].config;
	cb     = t_params->ranges[i].cb;
242 243 244
	/*  i == 0 -> VHF_LO
	 *  i == 1 -> VHF_HI
	 *  i == 2 -> UHF     */
245
	tuner_dbg("tv: param %d, range %d\n",j,i);
L
Linus Torvalds 已提交
246

247
	div=params->frequency + IFPCoff + offset;
248 249

	tuner_dbg("Freq= %d.%02d MHz, V_IF=%d.%02d MHz, Offset=%d.%02d MHz, div=%0d\n",
250
					params->frequency / 16, params->frequency % 16 * 100 / 16,
251 252 253 254
					IFPCoff / 16, IFPCoff % 16 * 100 / 16,
					offset / 16, offset % 16 * 100 / 16,
					div);

L
Linus Torvalds 已提交
255
	/* tv norm specific stuff for multi-norm tuners */
256
	switch (priv->type) {
L
Linus Torvalds 已提交
257 258 259 260
	case TUNER_PHILIPS_SECAM: // FI1216MF
		/* 0x01 -> ??? no change ??? */
		/* 0x02 -> PAL BDGHI / SECAM L */
		/* 0x04 -> ??? PAL others / SECAM others ??? */
261
		cb &= ~0x03;
262
		if (params->std & V4L2_STD_SECAM_L) //also valid for V4L2_STD_SECAM
263
			cb |= PHILIPS_MF_SET_STD_L;
264
		else if (params->std & V4L2_STD_SECAM_LC)
265
			cb |= PHILIPS_MF_SET_STD_LC;
266
		else /* V4L2_STD_B|V4L2_STD_GH */
267
			cb |= PHILIPS_MF_SET_STD_BG;
L
Linus Torvalds 已提交
268 269 270
		break;

	case TUNER_TEMIC_4046FM5:
271
		cb &= ~0x0f;
L
Linus Torvalds 已提交
272

273
		if (params->std & V4L2_STD_PAL_BG) {
274
			cb |= TEMIC_SET_PAL_BG;
L
Linus Torvalds 已提交
275

276
		} else if (params->std & V4L2_STD_PAL_I) {
277
			cb |= TEMIC_SET_PAL_I;
L
Linus Torvalds 已提交
278

279
		} else if (params->std & V4L2_STD_PAL_DK) {
280
			cb |= TEMIC_SET_PAL_DK;
L
Linus Torvalds 已提交
281

282
		} else if (params->std & V4L2_STD_SECAM_L) {
283
			cb |= TEMIC_SET_PAL_L;
L
Linus Torvalds 已提交
284 285 286 287 288

		}
		break;

	case TUNER_PHILIPS_FQ1216ME:
289
		cb &= ~0x0f;
L
Linus Torvalds 已提交
290

291
		if (params->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) {
292
			cb |= PHILIPS_SET_PAL_BGDK;
L
Linus Torvalds 已提交
293

294
		} else if (params->std & V4L2_STD_PAL_I) {
295
			cb |= PHILIPS_SET_PAL_I;
L
Linus Torvalds 已提交
296

297
		} else if (params->std & V4L2_STD_SECAM_L) {
298
			cb |= PHILIPS_SET_PAL_L;
L
Linus Torvalds 已提交
299 300 301 302 303 304 305 306 307

		}
		break;

	case TUNER_PHILIPS_ATSC:
		/* 0x00 -> ATSC antenna input 1 */
		/* 0x01 -> ATSC antenna input 2 */
		/* 0x02 -> NTSC antenna input 1 */
		/* 0x03 -> NTSC antenna input 2 */
308
		cb &= ~0x03;
309
		if (!(params->std & V4L2_STD_ATSC))
310
			cb |= 2;
L
Linus Torvalds 已提交
311 312 313 314 315
		/* FIXME: input */
		break;

	case TUNER_MICROTUNE_4042FI5:
		/* Set the charge pump for fast tuning */
316
		config |= TUNER_CHARGE_PUMP;
L
Linus Torvalds 已提交
317
		break;
318 319 320 321 322 323

	case TUNER_PHILIPS_TUV1236D:
		/* 0x40 -> ATSC antenna input 1 */
		/* 0x48 -> ATSC antenna input 2 */
		/* 0x00 -> NTSC antenna input 1 */
		/* 0x08 -> NTSC antenna input 2 */
324 325 326 327
		buffer[0] = 0x14;
		buffer[1] = 0x00;
		buffer[2] = 0x17;
		buffer[3] = 0x00;
328
		cb &= ~0x40;
329
		if (params->std & V4L2_STD_ATSC) {
330
			cb |= 0x40;
331 332 333
			buffer[1] = 0x04;
		}
		/* set to the correct mode (analog or digital) */
334 335 336
		tuneraddr = priv->i2c_props.addr;
		priv->i2c_props.addr = 0x0a;
		if (2 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,&buffer[0],2)))
337
			tuner_warn("i2c i/o error: rc == %d (should be 2)\n",rc);
338
		if (2 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,&buffer[2],2)))
339
			tuner_warn("i2c i/o error: rc == %d (should be 2)\n",rc);
340
		priv->i2c_props.addr = tuneraddr;
341 342
		/* FIXME: input */
		break;
L
Linus Torvalds 已提交
343 344
	}

345
	if (t_params->cb_first_if_lower_freq && div < priv->last_div) {
346
		buffer[0] = config;
347
		buffer[1] = cb;
L
Linus Torvalds 已提交
348 349 350 351 352
		buffer[2] = (div>>8) & 0x7f;
		buffer[3] = div      & 0xff;
	} else {
		buffer[0] = (div>>8) & 0x7f;
		buffer[1] = div      & 0xff;
353
		buffer[2] = config;
354
		buffer[3] = cb;
L
Linus Torvalds 已提交
355
	}
356
	priv->last_div = div;
357
	if (t_params->has_tda9887) {
358
		int config = 0;
359 360
		int is_secam_l = (params->std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) &&
			!(params->std & ~(V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC));
361

362 363
		if (params->std == V4L2_STD_SECAM_LC) {
			if (t_params->port1_active ^ t_params->port1_invert_for_secam_lc)
364
				config |= TDA9887_PORT1_ACTIVE;
365
			if (t_params->port2_active ^ t_params->port2_invert_for_secam_lc)
366 367 368
				config |= TDA9887_PORT2_ACTIVE;
		}
		else {
369
			if (t_params->port1_active)
370
				config |= TDA9887_PORT1_ACTIVE;
371
			if (t_params->port2_active)
372 373
				config |= TDA9887_PORT2_ACTIVE;
		}
374
		if (t_params->intercarrier_mode)
375 376
			config |= TDA9887_INTERCARRIER;
		if (is_secam_l) {
377 378 379 380 381 382
			if (i == 0 && t_params->default_top_secam_low)
				config |= TDA9887_TOP(t_params->default_top_secam_low);
			else if (i == 1 && t_params->default_top_secam_mid)
				config |= TDA9887_TOP(t_params->default_top_secam_mid);
			else if (t_params->default_top_secam_high)
				config |= TDA9887_TOP(t_params->default_top_secam_high);
383 384
		}
		else {
385 386 387 388 389 390
			if (i == 0 && t_params->default_top_low)
				config |= TDA9887_TOP(t_params->default_top_low);
			else if (i == 1 && t_params->default_top_mid)
				config |= TDA9887_TOP(t_params->default_top_mid);
			else if (t_params->default_top_high)
				config |= TDA9887_TOP(t_params->default_top_high);
391
		}
392
		if (t_params->default_pll_gating_18)
393
			config |= TDA9887_GATING_18;
394
		i2c_clients_command(priv->i2c_props.adap, TDA9887_SET_CONFIG, &config);
395
	}
L
Linus Torvalds 已提交
396 397 398
	tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
		  buffer[0],buffer[1],buffer[2],buffer[3]);

399
	if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,4)))
L
Linus Torvalds 已提交
400 401
		tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc);

402
	switch (priv->type) {
403 404 405 406 407 408 409 410
	case TUNER_LG_TDVS_H06XF:
		/* Set the Auxiliary Byte. */
		buffer[0] = buffer[2];
		buffer[0] &= ~0x20;
		buffer[0] |= 0x18;
		buffer[1] = 0x20;
		tuner_dbg("tv 0x%02x 0x%02x\n",buffer[0],buffer[1]);

411
		if (2 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,2)))
412 413 414 415
			tuner_warn("i2c i/o error: rc == %d (should be 2)\n",rc);
		break;
	case TUNER_MICROTUNE_4042FI5:
	{
L
Linus Torvalds 已提交
416 417 418 419 420 421 422
		// FIXME - this may also work for other tuners
		unsigned long timeout = jiffies + msecs_to_jiffies(1);
		u8 status_byte = 0;

		/* Wait until the PLL locks */
		for (;;) {
			if (time_after(jiffies,timeout))
423
				return 0;
424
			if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,&status_byte,1))) {
L
Linus Torvalds 已提交
425 426 427
				tuner_warn("i2c i/o read error: rc == %d (should be 1)\n",rc);
				break;
			}
M
Mauro Carvalho Chehab 已提交
428
			if (status_byte & TUNER_PLL_LOCKED)
L
Linus Torvalds 已提交
429 430 431 432 433
				break;
			udelay(10);
		}

		/* Set the charge pump for optimized phase noise figure */
434
		config &= ~TUNER_CHARGE_PUMP;
L
Linus Torvalds 已提交
435 436
		buffer[0] = (div>>8) & 0x7f;
		buffer[1] = div      & 0xff;
437
		buffer[2] = config;
438
		buffer[3] = cb;
L
Linus Torvalds 已提交
439
		tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
440
			  buffer[0],buffer[1],buffer[2],buffer[3]);
L
Linus Torvalds 已提交
441

442
		if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,4)))
L
Linus Torvalds 已提交
443
			tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc);
444 445
		break;
	}
L
Linus Torvalds 已提交
446
	}
447
	return 0;
L
Linus Torvalds 已提交
448 449
}

450 451
static int simple_set_radio_freq(struct dvb_frontend *fe,
				 struct analog_parameters *params)
L
Linus Torvalds 已提交
452 453
{
	struct tunertype *tun;
454
	struct tuner_simple_priv *priv = fe->tuner_priv;
455 456
	u8 buffer[4];
	u16 div;
457
	int rc, j;
458 459
	struct tuner_params *t_params;
	unsigned int freq = params->frequency;
L
Linus Torvalds 已提交
460

461
	tun = priv->tun;
462

463 464 465
	for (j = tun->count-1; j > 0; j--)
		if (tun->params[j].type == TUNER_PARAM_TYPE_RADIO)
			break;
466 467
	/* default t_params (j=0) will be used if desired type wasn't found */
	t_params = &tun->params[j];
468 469

	/* Select Radio 1st IF used */
470
	switch (t_params->radio_if) {
471 472
	case 0: /* 10.7 MHz */
		freq += (unsigned int)(10.7*16000);
473
		break;
474 475 476 477 478 479 480
	case 1: /* 33.3 MHz */
		freq += (unsigned int)(33.3*16000);
		break;
	case 2: /* 41.3 MHz */
		freq += (unsigned int)(41.3*16000);
		break;
	default:
481 482
		tuner_warn("Unsupported radio_if value %d\n", t_params->radio_if);
		return 0;
483
	}
L
Linus Torvalds 已提交
484

485
	/* Bandswitch byte */
486
	switch (priv->type) {
487
	case TUNER_TENA_9533_DI:
488
	case TUNER_YMEC_TVF_5533MF:
489 490
		tuner_dbg("This tuner doesn't have FM. Most cards have a TEA5767 for FM\n");
		return 0;
L
Linus Torvalds 已提交
491 492
	case TUNER_PHILIPS_FM1216ME_MK3:
	case TUNER_PHILIPS_FM1236_MK3:
493
	case TUNER_PHILIPS_FMD1216ME_MK3:
494
	case TUNER_LG_NTSC_TAPE:
495
	case TUNER_PHILIPS_FM1256_IH3:
L
Linus Torvalds 已提交
496 497
		buffer[3] = 0x19;
		break;
498 499 500
	case TUNER_TNF_5335MF:
		buffer[3] = 0x11;
		break;
L
Linus Torvalds 已提交
501 502 503
	case TUNER_LG_PAL_FM:
		buffer[3] = 0xa5;
		break;
504 505
	case TUNER_THOMSON_DTT761X:
		buffer[3] = 0x39;
506
		break;
507
	case TUNER_MICROTUNE_4049FM5:
L
Linus Torvalds 已提交
508 509 510 511
	default:
		buffer[3] = 0xa4;
		break;
	}
512

513
	buffer[2] = (t_params->ranges[0].config & ~TUNER_RATIO_MASK) |
514 515 516 517 518 519 520
		    TUNER_RATIO_SELECT_50; /* 50 kHz step */

	/* Convert from 1/16 kHz V4L steps to 1/20 MHz (=50 kHz) PLL steps
	   freq * (1 Mhz / 16000 V4L steps) * (20 PLL steps / 1 MHz) =
	   freq * (1/800) */
	div = (freq + 400) / 800;

521
	if (t_params->cb_first_if_lower_freq && div < priv->last_div) {
522 523 524 525 526 527 528 529
		buffer[0] = buffer[2];
		buffer[1] = buffer[3];
		buffer[2] = (div>>8) & 0x7f;
		buffer[3] = div      & 0xff;
	} else {
		buffer[0] = (div>>8) & 0x7f;
		buffer[1] = div      & 0xff;
	}
L
Linus Torvalds 已提交
530 531 532

	tuner_dbg("radio 0x%02x 0x%02x 0x%02x 0x%02x\n",
	       buffer[0],buffer[1],buffer[2],buffer[3]);
533
	priv->last_div = div;
L
Linus Torvalds 已提交
534

535
	if (t_params->has_tda9887) {
536
		int config = 0;
537
		if (t_params->port1_active && !t_params->port1_fm_high_sensitivity)
538
			config |= TDA9887_PORT1_ACTIVE;
539
		if (t_params->port2_active && !t_params->port2_fm_high_sensitivity)
540
			config |= TDA9887_PORT2_ACTIVE;
541
		if (t_params->intercarrier_mode)
542
			config |= TDA9887_INTERCARRIER;
543
/*		if (t_params->port1_set_for_fm_mono)
544
			config &= ~TDA9887_PORT1_ACTIVE;*/
545
		if (t_params->fm_gain_normal)
546
			config |= TDA9887_GAIN_NORMAL;
547
		if (t_params->radio_if == 2)
548
			config |= TDA9887_RIF_41_3;
549
		i2c_clients_command(priv->i2c_props.adap, TDA9887_SET_CONFIG, &config);
550
	}
551
	if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,4)))
L
Linus Torvalds 已提交
552
		tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc);
553 554

	return 0;
L
Linus Torvalds 已提交
555 556
}

557 558
static int simple_set_params(struct dvb_frontend *fe,
			     struct analog_parameters *params)
559
{
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
	struct tuner_simple_priv *priv = fe->tuner_priv;
	int ret = -EINVAL;

	switch (params->mode) {
	case V4L2_TUNER_RADIO:
		ret = simple_set_radio_freq(fe, params);
		priv->frequency = params->frequency * 125 / 2;
		break;
	case V4L2_TUNER_ANALOG_TV:
	case V4L2_TUNER_DIGITAL_TV:
		ret = simple_set_tv_freq(fe, params);
		priv->frequency = params->frequency * 62500;
		break;
	}

	return ret;
}


static int simple_release(struct dvb_frontend *fe)
{
	kfree(fe->tuner_priv);
	fe->tuner_priv = NULL;

	return 0;
}

static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
	struct tuner_simple_priv *priv = fe->tuner_priv;
	*frequency = priv->frequency;
	return 0;
592 593
}

594 595
static struct dvb_tuner_ops simple_tuner_ops = {
	.set_analog_params = simple_set_params,
596 597 598 599
	.release           = simple_release,
	.get_frequency     = simple_get_frequency,
	.get_status        = simple_get_status,
	.get_rf_strength   = simple_get_rf_strength,
600 601
};

602 603 604 605
struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
					 struct i2c_adapter *i2c_adap,
					 u8 i2c_addr,
					 struct simple_tuner_config *cfg)
L
Linus Torvalds 已提交
606
{
607 608 609 610
	struct tuner_simple_priv *priv = NULL;

	priv = kzalloc(sizeof(struct tuner_simple_priv), GFP_KERNEL);
	if (priv == NULL)
611 612
		return NULL;
	fe->tuner_priv = priv;
L
Linus Torvalds 已提交
613

614 615 616 617
	priv->i2c_props.addr = i2c_addr;
	priv->i2c_props.adap = i2c_adap;
	priv->type = cfg->type;
	priv->tun  = cfg->tun;
618

619
	memcpy(&fe->ops.tuner_ops, &simple_tuner_ops, sizeof(struct dvb_tuner_ops));
L
Linus Torvalds 已提交
620

621
	tuner_info("type set to %d (%s)\n", cfg->type, cfg->tun->name);
622

623 624 625
	strlcpy(fe->ops.tuner_ops.info.name, cfg->tun->name, sizeof(fe->ops.tuner_ops.info.name));

	return fe;
L
Linus Torvalds 已提交
626 627
}

628 629 630 631 632 633 634

EXPORT_SYMBOL_GPL(simple_tuner_attach);

MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver");
MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
MODULE_LICENSE("GPL");

L
Linus Torvalds 已提交
635 636 637 638 639 640 641
/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-basic-offset: 8
 * End:
 */