tda9887.c 17.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/delay.h>
9
#include <linux/videodev.h>
10
#include <media/v4l2-common.h>
L
Linus Torvalds 已提交
11
#include <media/tuner.h>
12
#include "tuner-i2c.h"
13
#include "tda9887.h"
L
Linus Torvalds 已提交
14

15

L
Linus Torvalds 已提交
16 17 18 19 20
/* Chips:
   TDA9885 (PAL, NTSC)
   TDA9886 (PAL, SECAM, NTSC)
   TDA9887 (PAL, SECAM, NTSC, FM Radio)

21
   Used as part of several tuners
L
Linus Torvalds 已提交
22 23
*/

24 25 26 27 28
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");

#define PREFIX "tda9887"
L
Linus Torvalds 已提交
29

30
struct tda9887_priv {
31 32
	struct tuner_i2c_props i2c_props;

33
	unsigned char 	   data[4];
34
	unsigned int       config;
35 36 37
	unsigned int       mode;
	unsigned int       audmode;
	v4l2_std_id        std;
38
};
L
Linus Torvalds 已提交
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

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

#define UNSET       (-1U)

struct tvnorm {
	v4l2_std_id       std;
	char              *name;
	unsigned char     b;
	unsigned char     c;
	unsigned char     e;
};

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

//
// TDA defines
//

//// first reg (b)
#define cVideoTrapBypassOFF     0x00    // bit b0
#define cVideoTrapBypassON      0x01    // bit b0

#define cAutoMuteFmInactive     0x00    // bit b1
#define cAutoMuteFmActive       0x02    // bit b1

#define cIntercarrier           0x00    // bit b2
#define cQSS                    0x04    // bit b2

#define cPositiveAmTV           0x00    // bit b3:4
#define cFmRadio                0x08    // bit b3:4
#define cNegativeFmTV           0x10    // bit b3:4


#define cForcedMuteAudioON      0x20    // bit b5
#define cForcedMuteAudioOFF     0x00    // bit b5

#define cOutputPort1Active      0x00    // bit b6
#define cOutputPort1Inactive    0x40    // bit b6

#define cOutputPort2Active      0x00    // bit b7
#define cOutputPort2Inactive    0x80    // bit b7


//// second reg (c)
#define cDeemphasisOFF          0x00    // bit c5
#define cDeemphasisON           0x20    // bit c5

#define cDeemphasis75           0x00    // bit c6
#define cDeemphasis50           0x40    // bit c6

#define cAudioGain0             0x00    // bit c7
#define cAudioGain6             0x80    // bit c7

93
#define cTopMask                0x1f    // bit c0:4
94
#define cTopDefault		0x10 	// bit c0:4
L
Linus Torvalds 已提交
95 96 97 98 99 100 101 102

//// third reg (e)
#define cAudioIF_4_5             0x00    // bit e0:1
#define cAudioIF_5_5             0x01    // bit e0:1
#define cAudioIF_6_0             0x02    // bit e0:1
#define cAudioIF_6_5             0x03    // bit e0:1


103 104
#define cVideoIFMask		0x1c	// bit e2:4
/* Video IF selection in TV Mode (bit B3=0) */
L
Linus Torvalds 已提交
105 106 107 108 109 110 111 112 113
#define cVideoIF_58_75           0x00    // bit e2:4
#define cVideoIF_45_75           0x04    // bit e2:4
#define cVideoIF_38_90           0x08    // bit e2:4
#define cVideoIF_38_00           0x0C    // bit e2:4
#define cVideoIF_33_90           0x10    // bit e2:4
#define cVideoIF_33_40           0x14    // bit e2:4
#define cRadioIF_45_75           0x18    // bit e2:4
#define cRadioIF_38_90           0x1C    // bit e2:4

114 115 116 117 118 119 120
/* IF1 selection in Radio Mode (bit B3=1) */
#define cRadioIF_33_30		0x00	// bit e2,4 (also 0x10,0x14)
#define cRadioIF_41_30		0x04	// bit e2,4

/* Output of AFC pin in radio mode when bit E7=1 */
#define cRadioAGC_SIF		0x00	// bit e3
#define cRadioAGC_FM		0x08	// bit e3
L
Linus Torvalds 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134

#define cTunerGainNormal         0x00    // bit e5
#define cTunerGainLow            0x20    // bit e5

#define cGating_18               0x00    // bit e6
#define cGating_36               0x40    // bit e6

#define cAgcOutON                0x80    // bit e7
#define cAgcOutOFF               0x00    // bit e7

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

static struct tvnorm tvnorms[] = {
	{
135 136
		.std   = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
		.name  = "PAL-BGHN",
L
Linus Torvalds 已提交
137 138 139
		.b     = ( cNegativeFmTV  |
			   cQSS           ),
		.c     = ( cDeemphasisON  |
140
			   cDeemphasis50  |
141
			   cTopDefault),
142 143
		.e     = ( cGating_36     |
			   cAudioIF_5_5   |
L
Linus Torvalds 已提交
144 145 146 147 148 149 150
			   cVideoIF_38_90 ),
	},{
		.std   = V4L2_STD_PAL_I,
		.name  = "PAL-I",
		.b     = ( cNegativeFmTV  |
			   cQSS           ),
		.c     = ( cDeemphasisON  |
151
			   cDeemphasis50  |
152
			   cTopDefault),
153 154
		.e     = ( cGating_36     |
			   cAudioIF_6_0   |
L
Linus Torvalds 已提交
155 156 157 158 159 160 161
			   cVideoIF_38_90 ),
	},{
		.std   = V4L2_STD_PAL_DK,
		.name  = "PAL-DK",
		.b     = ( cNegativeFmTV  |
			   cQSS           ),
		.c     = ( cDeemphasisON  |
162
			   cDeemphasis50  |
163
			   cTopDefault),
164 165 166
		.e     = ( cGating_36     |
			   cAudioIF_6_5   |
			   cVideoIF_38_90 ),
L
Linus Torvalds 已提交
167
	},{
168 169
		.std   = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
		.name  = "PAL-M/Nc",
L
Linus Torvalds 已提交
170 171 172
		.b     = ( cNegativeFmTV  |
			   cQSS           ),
		.c     = ( cDeemphasisON  |
173
			   cDeemphasis75  |
174
			   cTopDefault),
175 176
		.e     = ( cGating_36     |
			   cAudioIF_4_5   |
L
Linus Torvalds 已提交
177
			   cVideoIF_45_75 ),
178 179 180 181 182
	},{
		.std   = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
		.name  = "SECAM-BGH",
		.b     = ( cPositiveAmTV  |
			   cQSS           ),
183
		.c     = ( cTopDefault),
184 185 186
		.e     = ( cGating_36	  |
			   cAudioIF_5_5   |
			   cVideoIF_38_90 ),
L
Linus Torvalds 已提交
187 188 189 190 191
	},{
		.std   = V4L2_STD_SECAM_L,
		.name  = "SECAM-L",
		.b     = ( cPositiveAmTV  |
			   cQSS           ),
192
		.c     = ( cTopDefault),
N
Nickolay V. Shmyrev 已提交
193
		.e     = ( cGating_36	  |
194
			   cAudioIF_6_5   |
L
Linus Torvalds 已提交
195
			   cVideoIF_38_90 ),
196 197 198 199 200 201
	},{
		.std   = V4L2_STD_SECAM_LC,
		.name  = "SECAM-L'",
		.b     = ( cOutputPort2Inactive |
			   cPositiveAmTV  |
			   cQSS           ),
202
		.c     = ( cTopDefault),
203 204 205
		.e     = ( cGating_36	  |
			   cAudioIF_6_5   |
			   cVideoIF_33_90 ),
L
Linus Torvalds 已提交
206 207 208 209 210 211
	},{
		.std   = V4L2_STD_SECAM_DK,
		.name  = "SECAM-DK",
		.b     = ( cNegativeFmTV  |
			   cQSS           ),
		.c     = ( cDeemphasisON  |
212
			   cDeemphasis50  |
213
			   cTopDefault),
214 215 216
		.e     = ( cGating_36     |
			   cAudioIF_6_5   |
			   cVideoIF_38_90 ),
L
Linus Torvalds 已提交
217
	},{
218
		.std   = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
L
Linus Torvalds 已提交
219 220 221 222
		.name  = "NTSC-M",
		.b     = ( cNegativeFmTV  |
			   cQSS           ),
		.c     = ( cDeemphasisON  |
223
			   cDeemphasis75  |
224
			   cTopDefault),
L
Linus Torvalds 已提交
225 226 227 228 229
		.e     = ( cGating_36     |
			   cAudioIF_4_5   |
			   cVideoIF_45_75 ),
	},{
		.std   = V4L2_STD_NTSC_M_JP,
230
		.name  = "NTSC-M-JP",
L
Linus Torvalds 已提交
231 232 233
		.b     = ( cNegativeFmTV  |
			   cQSS           ),
		.c     = ( cDeemphasisON  |
234
			   cDeemphasis50  |
235
			   cTopDefault),
L
Linus Torvalds 已提交
236 237 238 239 240 241
		.e     = ( cGating_36     |
			   cAudioIF_4_5   |
			   cVideoIF_58_75 ),
	}
};

242 243 244 245 246
static struct tvnorm radio_stereo = {
	.name = "Radio Stereo",
	.b    = ( cFmRadio       |
		  cQSS           ),
	.c    = ( cDeemphasisOFF |
247
		  cAudioGain6    |
248
		  cTopDefault),
249 250
	.e    = ( cTunerGainLow  |
		  cAudioIF_5_5   |
251 252 253 254 255
		  cRadioIF_38_90 ),
};

static struct tvnorm radio_mono = {
	.name = "Radio Mono",
L
Linus Torvalds 已提交
256 257 258
	.b    = ( cFmRadio       |
		  cQSS           ),
	.c    = ( cDeemphasisON  |
259
		  cDeemphasis75  |
260
		  cTopDefault),
261 262
	.e    = ( cTunerGainLow  |
		  cAudioIF_5_5   |
L
Linus Torvalds 已提交
263 264 265 266 267
		  cRadioIF_38_90 ),
};

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

268
static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf)
L
Linus Torvalds 已提交
269
{
270 271
	struct tda9887_priv *priv = fe->analog_demod_priv;

L
Linus Torvalds 已提交
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
	static char *afc[16] = {
		"- 12.5 kHz",
		"- 37.5 kHz",
		"- 62.5 kHz",
		"- 87.5 kHz",
		"-112.5 kHz",
		"-137.5 kHz",
		"-162.5 kHz",
		"-187.5 kHz [min]",
		"+187.5 kHz [max]",
		"+162.5 kHz",
		"+137.5 kHz",
		"+112.5 kHz",
		"+ 87.5 kHz",
		"+ 62.5 kHz",
		"+ 37.5 kHz",
		"+ 12.5 kHz",
	};
290 291 292 293 294 295
	tuner_info("read: 0x%2x\n", buf[0]);
	tuner_info("  after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
	tuner_info("  afc            : %s\n", afc[(buf[0] >> 1) & 0x0f]);
	tuner_info("  fmif level     : %s\n", (buf[0] & 0x20) ? "high" : "low");
	tuner_info("  afc window     : %s\n", (buf[0] & 0x40) ? "in" : "out");
	tuner_info("  vfi level      : %s\n", (buf[0] & 0x80) ? "high" : "low");
L
Linus Torvalds 已提交
296 297
}

298
static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf)
L
Linus Torvalds 已提交
299
{
300 301
	struct tda9887_priv *priv = fe->analog_demod_priv;

L
Linus Torvalds 已提交
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	static char *sound[4] = {
		"AM/TV",
		"FM/radio",
		"FM/TV",
		"FM/radio"
	};
	static char *adjust[32] = {
		"-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
		"-8",  "-7",  "-6",  "-5",  "-4",  "-3",  "-2",  "-1",
		"0",   "+1",  "+2",  "+3",  "+4",  "+5",  "+6",  "+7",
		"+8",  "+9",  "+10", "+11", "+12", "+13", "+14", "+15"
	};
	static char *deemph[4] = {
		"no", "no", "75", "50"
	};
	static char *carrier[4] = {
		"4.5 MHz",
		"5.5 MHz",
		"6.0 MHz",
		"6.5 MHz / AM"
	};
	static char *vif[8] = {
		"58.75 MHz",
		"45.75 MHz",
		"38.9 MHz",
		"38.0 MHz",
		"33.9 MHz",
		"33.4 MHz",
		"45.75 MHz + pin13",
		"38.9 MHz + pin13",
	};
	static char *rif[4] = {
		"44 MHz",
		"52 MHz",
		"52 MHz",
		"44 MHz",
	};

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
	tuner_info("write: byte B 0x%02x\n", buf[1]);
	tuner_info("  B0   video mode      : %s\n",
		   (buf[1] & 0x01) ? "video trap" : "sound trap");
	tuner_info("  B1   auto mute fm    : %s\n",
		   (buf[1] & 0x02) ? "yes" : "no");
	tuner_info("  B2   carrier mode    : %s\n",
		   (buf[1] & 0x04) ? "QSS" : "Intercarrier");
	tuner_info("  B3-4 tv sound/radio  : %s\n",
		   sound[(buf[1] & 0x18) >> 3]);
	tuner_info("  B5   force mute audio: %s\n",
		   (buf[1] & 0x20) ? "yes" : "no");
	tuner_info("  B6   output port 1   : %s\n",
		   (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
	tuner_info("  B7   output port 2   : %s\n",
		   (buf[1] & 0x80) ? "high (inactive)" : "low (active)");

	tuner_info("write: byte C 0x%02x\n", buf[2]);
	tuner_info("  C0-4 top adjustment  : %s dB\n",
		   adjust[buf[2] & 0x1f]);
	tuner_info("  C5-6 de-emphasis     : %s\n",
		   deemph[(buf[2] & 0x60) >> 5]);
	tuner_info("  C7   audio gain      : %s\n",
		   (buf[2] & 0x80) ? "-6" : "0");

	tuner_info("write: byte E 0x%02x\n", buf[3]);
	tuner_info("  E0-1 sound carrier   : %s\n",
		   carrier[(buf[3] & 0x03)]);
	tuner_info("  E6   l pll gating   : %s\n",
		   (buf[3] & 0x40) ? "36" : "13");
L
Linus Torvalds 已提交
369 370 371

	if (buf[1] & 0x08) {
		/* radio */
372 373 374 375 376 377 378
		tuner_info("  E2-4 video if        : %s\n",
			   rif[(buf[3] & 0x0c) >> 2]);
		tuner_info("  E7   vif agc output  : %s\n",
			   (buf[3] & 0x80)
			   ? ((buf[3] & 0x10) ? "fm-agc radio" :
						"sif-agc radio")
			   : "fm radio carrier afc");
L
Linus Torvalds 已提交
379 380
	} else {
		/* video */
381 382 383 384 385 386 387 388 389 390 391
		tuner_info("  E2-4 video if        : %s\n",
			   vif[(buf[3] & 0x1c) >> 2]);
		tuner_info("  E5   tuner gain      : %s\n",
			   (buf[3] & 0x80)
			   ? ((buf[3] & 0x20) ? "external" : "normal")
			   : ((buf[3] & 0x20) ? "minimum"  : "normal"));
		tuner_info("  E7   vif agc output  : %s\n",
			   (buf[3] & 0x80) ? ((buf[3] & 0x20)
				? "pin3 port, pin22 vif agc out"
				: "pin22 port, pin3 vif acg ext in")
				: "pin3+pin22 port");
L
Linus Torvalds 已提交
392
	}
393
	tuner_info("--\n");
L
Linus Torvalds 已提交
394 395 396 397
}

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

398
static int tda9887_set_tvnorm(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
399
{
400
	struct tda9887_priv *priv = fe->analog_demod_priv;
L
Linus Torvalds 已提交
401
	struct tvnorm *norm = NULL;
402
	char *buf = priv->data;
L
Linus Torvalds 已提交
403 404
	int i;

405 406
	if (priv->mode == V4L2_TUNER_RADIO) {
		if (priv->audmode == V4L2_TUNER_MODE_MONO)
407 408
			norm = &radio_mono;
		else
409
			norm = &radio_stereo;
L
Linus Torvalds 已提交
410 411
	} else {
		for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
412
			if (tvnorms[i].std & priv->std) {
L
Linus Torvalds 已提交
413 414 415 416 417 418
				norm = tvnorms+i;
				break;
			}
		}
	}
	if (NULL == norm) {
419
		tuner_dbg("Unsupported tvnorm entry - audio muted\n");
L
Linus Torvalds 已提交
420 421 422
		return -1;
	}

423
	tuner_dbg("configure for: %s\n", norm->name);
L
Linus Torvalds 已提交
424 425 426 427 428 429 430 431 432
	buf[1] = norm->b;
	buf[2] = norm->c;
	buf[3] = norm->e;
	return 0;
}

static unsigned int port1  = UNSET;
static unsigned int port2  = UNSET;
static unsigned int qss    = UNSET;
433 434
static unsigned int adjust = UNSET;

L
Linus Torvalds 已提交
435 436 437 438 439
module_param(port1, int, 0644);
module_param(port2, int, 0644);
module_param(qss, int, 0644);
module_param(adjust, int, 0644);

440
static int tda9887_set_insmod(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
441
{
442 443 444
	struct tda9887_priv *priv = fe->analog_demod_priv;
	char *buf = priv->data;

L
Linus Torvalds 已提交
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
	if (UNSET != port1) {
		if (port1)
			buf[1] |= cOutputPort1Inactive;
		else
			buf[1] &= ~cOutputPort1Inactive;
	}
	if (UNSET != port2) {
		if (port2)
			buf[1] |= cOutputPort2Inactive;
		else
			buf[1] &= ~cOutputPort2Inactive;
	}

	if (UNSET != qss) {
		if (qss)
			buf[1] |= cQSS;
		else
			buf[1] &= ~cQSS;
	}

465 466
	if (adjust >= 0x00 && adjust < 0x20) {
		buf[2] &= ~cTopMask;
L
Linus Torvalds 已提交
467
		buf[2] |= adjust;
468
	}
L
Linus Torvalds 已提交
469 470 471
	return 0;
}

472
static int tda9887_do_config(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
473
{
474 475 476
	struct tda9887_priv *priv = fe->analog_demod_priv;
	char *buf = priv->data;

477
	if (priv->config & TDA9887_PORT1_ACTIVE)
L
Linus Torvalds 已提交
478
		buf[1] &= ~cOutputPort1Inactive;
479
	if (priv->config & TDA9887_PORT1_INACTIVE)
L
Linus Torvalds 已提交
480
		buf[1] |= cOutputPort1Inactive;
481
	if (priv->config & TDA9887_PORT2_ACTIVE)
L
Linus Torvalds 已提交
482
		buf[1] &= ~cOutputPort2Inactive;
483
	if (priv->config & TDA9887_PORT2_INACTIVE)
L
Linus Torvalds 已提交
484 485
		buf[1] |= cOutputPort2Inactive;

486
	if (priv->config & TDA9887_QSS)
L
Linus Torvalds 已提交
487
		buf[1] |= cQSS;
488
	if (priv->config & TDA9887_INTERCARRIER)
L
Linus Torvalds 已提交
489 490
		buf[1] &= ~cQSS;

491
	if (priv->config & TDA9887_AUTOMUTE)
L
Linus Torvalds 已提交
492
		buf[1] |= cAutoMuteFmActive;
493
	if (priv->config & TDA9887_DEEMPHASIS_MASK) {
L
Linus Torvalds 已提交
494
		buf[2] &= ~0x60;
495
		switch (priv->config & TDA9887_DEEMPHASIS_MASK) {
L
Linus Torvalds 已提交
496 497 498 499 500 501 502 503 504 505 506
		case TDA9887_DEEMPHASIS_NONE:
			buf[2] |= cDeemphasisOFF;
			break;
		case TDA9887_DEEMPHASIS_50:
			buf[2] |= cDeemphasisON | cDeemphasis50;
			break;
		case TDA9887_DEEMPHASIS_75:
			buf[2] |= cDeemphasisON | cDeemphasis75;
			break;
		}
	}
507
	if (priv->config & TDA9887_TOP_SET) {
508
		buf[2] &= ~cTopMask;
509
		buf[2] |= (priv->config >> 8) & cTopMask;
510
	}
511
	if ((priv->config & TDA9887_INTERCARRIER_NTSC) &&
512
	    (priv->std & V4L2_STD_NTSC))
513
		buf[1] &= ~cQSS;
514
	if (priv->config & TDA9887_GATING_18)
515
		buf[3] &= ~cGating_36;
516

517
	if (priv->mode == V4L2_TUNER_RADIO) {
518
		if (priv->config & TDA9887_RIF_41_3) {
519 520 521
			buf[3] &= ~cVideoIFMask;
			buf[3] |= cRadioIF_41_30;
		}
522
		if (priv->config & TDA9887_GAIN_NORMAL)
523
			buf[3] &= ~cTunerGainLow;
524 525
	}

L
Linus Torvalds 已提交
526 527 528 529 530
	return 0;
}

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

531
static int tda9887_status(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
532
{
533
	struct tda9887_priv *priv = fe->analog_demod_priv;
L
Linus Torvalds 已提交
534 535 536 537
	unsigned char buf[1];
	int rc;

	memset(buf,0,sizeof(buf));
538
	if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1)))
539
		tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc);
540
	dump_read_message(fe, buf);
L
Linus Torvalds 已提交
541 542 543
	return 0;
}

544
static void tda9887_configure(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
545
{
546
	struct tda9887_priv *priv = fe->analog_demod_priv;
L
Linus Torvalds 已提交
547 548
	int rc;

549
	memset(priv->data,0,sizeof(priv->data));
550
	tda9887_set_tvnorm(fe);
551

552 553 554 555 556 557 558 559 560 561 562 563 564
	/* A note on the port settings:
	   These settings tend to depend on the specifics of the board.
	   By default they are set to inactive (bit value 1) by this driver,
	   overwriting any changes made by the tvnorm. This means that it
	   is the responsibility of the module using the tda9887 to set
	   these values in case of changes in the tvnorm.
	   In many cases port 2 should be made active (0) when selecting
	   SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.

	   For the other standards the tda9887 application note says that
	   the ports should be set to active (0), but, again, that may
	   differ depending on the precise hardware configuration.
	 */
565 566
	priv->data[1] |= cOutputPort1Inactive;
	priv->data[1] |= cOutputPort2Inactive;
567

568
	tda9887_do_config(fe);
569
	tda9887_set_insmod(fe);
L
Linus Torvalds 已提交
570

571
	if (priv->mode == T_STANDBY)
572
		priv->data[1] |= cForcedMuteAudioON;
573

574 575 576
	tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
		  priv->data[1], priv->data[2], priv->data[3]);
	if (debug > 1)
577
		dump_write_message(fe, priv->data);
L
Linus Torvalds 已提交
578

579
	if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4)))
580
		tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc);
L
Linus Torvalds 已提交
581

582
	if (debug > 2) {
L
Linus Torvalds 已提交
583
		msleep_interruptible(1000);
584
		tda9887_status(fe);
L
Linus Torvalds 已提交
585 586 587 588 589
	}
}

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

590
static void tda9887_tuner_status(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
591
{
592
	struct tda9887_priv *priv = fe->analog_demod_priv;
593 594
	tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n",
		   priv->data[1], priv->data[2], priv->data[3]);
L
Linus Torvalds 已提交
595 596
}

597
static int tda9887_get_afc(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
598
{
599
	struct tda9887_priv *priv = fe->analog_demod_priv;
600 601 602 603 604 605 606 607
	static int AFC_BITS_2_kHz[] = {
		-12500,  -37500,  -62500,  -97500,
		-112500, -137500, -162500, -187500,
		187500,  162500,  137500,  112500,
		97500 ,  62500,   37500 ,  12500
	};
	int afc=0;
	__u8 reg = 0;
L
Linus Torvalds 已提交
608

609
	if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,&reg,1))
610
		afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
L
Linus Torvalds 已提交
611

612
	return afc;
L
Linus Torvalds 已提交
613 614
}

615
static void tda9887_standby(struct dvb_frontend *fe)
L
Linus Torvalds 已提交
616
{
617 618 619 620
	struct tda9887_priv *priv = fe->analog_demod_priv;

	priv->mode = T_STANDBY;

621
	tda9887_configure(fe);
L
Linus Torvalds 已提交
622 623
}

624 625
static void tda9887_set_params(struct dvb_frontend *fe,
			       struct analog_parameters *params)
L
Linus Torvalds 已提交
626
{
627 628 629 630 631
	struct tda9887_priv *priv = fe->analog_demod_priv;

	priv->mode    = params->mode;
	priv->audmode = params->audmode;
	priv->std     = params->std;
632
	tda9887_configure(fe);
L
Linus Torvalds 已提交
633 634
}

635 636 637 638 639 640 641 642 643 644
static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg)
{
	struct tda9887_priv *priv = fe->analog_demod_priv;

	priv->config = *(unsigned int *)priv_cfg;
	tda9887_configure(fe);

	return 0;
}

645
static void tda9887_release(struct dvb_frontend *fe)
646
{
647 648
	kfree(fe->analog_demod_priv);
	fe->analog_demod_priv = NULL;
649 650
}

651
static struct analog_tuner_ops tda9887_tuner_ops = {
652 653 654
	.info		= {
		.name	= "TDA9887",
	},
655
	.set_params     = tda9887_set_params,
656 657 658 659
	.standby        = tda9887_standby,
	.tuner_status   = tda9887_tuner_status,
	.get_afc        = tda9887_get_afc,
	.release        = tda9887_release,
660
	.set_config     = tda9887_set_config,
661 662
};

663 664 665
struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
				    struct i2c_adapter *i2c_adap,
				    u8 i2c_addr)
L
Linus Torvalds 已提交
666
{
667
	struct tda9887_priv *priv = NULL;
L
Linus Torvalds 已提交
668

669 670
	priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL);
	if (priv == NULL)
671 672
		return NULL;
	fe->analog_demod_priv = priv;
673

674 675
	priv->i2c_props.addr = i2c_addr;
	priv->i2c_props.adap = i2c_adap;
676
	priv->mode = T_STANDBY;
677

678
	tuner_info("tda988[5/6/7] found\n");
L
Linus Torvalds 已提交
679

680
	fe->ops.analog_demod_ops = &tda9887_tuner_ops;
L
Linus Torvalds 已提交
681

682
	return fe;
L
Linus Torvalds 已提交
683
}
684
EXPORT_SYMBOL_GPL(tda9887_attach);
L
Linus Torvalds 已提交
685

686 687
MODULE_LICENSE("GPL");

L
Linus Torvalds 已提交
688 689 690 691 692 693 694
/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-basic-offset: 8
 * End:
 */