174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab     TDA10021  - Single Chip Cable Channel Receiver driver module
49a0bf528SMauro Carvalho Chehab 	       used on the Siemens DVB-C cards
59a0bf528SMauro Carvalho Chehab 
69a0bf528SMauro Carvalho Chehab     Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
79a0bf528SMauro Carvalho Chehab     Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
89a0bf528SMauro Carvalho Chehab 		   Support for TDA10021
99a0bf528SMauro Carvalho Chehab 
109a0bf528SMauro Carvalho Chehab */
119a0bf528SMauro Carvalho Chehab 
129a0bf528SMauro Carvalho Chehab #include <linux/delay.h>
139a0bf528SMauro Carvalho Chehab #include <linux/errno.h>
149a0bf528SMauro Carvalho Chehab #include <linux/init.h>
159a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
169a0bf528SMauro Carvalho Chehab #include <linux/module.h>
179a0bf528SMauro Carvalho Chehab #include <linux/string.h>
189a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
199a0bf528SMauro Carvalho Chehab 
20fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
219a0bf528SMauro Carvalho Chehab #include "tda1002x.h"
229a0bf528SMauro Carvalho Chehab 
239a0bf528SMauro Carvalho Chehab 
249a0bf528SMauro Carvalho Chehab struct tda10021_state {
259a0bf528SMauro Carvalho Chehab 	struct i2c_adapter* i2c;
269a0bf528SMauro Carvalho Chehab 	/* configuration settings */
279a0bf528SMauro Carvalho Chehab 	const struct tda1002x_config* config;
289a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
299a0bf528SMauro Carvalho Chehab 
309a0bf528SMauro Carvalho Chehab 	u8 pwm;
319a0bf528SMauro Carvalho Chehab 	u8 reg0;
329a0bf528SMauro Carvalho Chehab };
339a0bf528SMauro Carvalho Chehab 
349a0bf528SMauro Carvalho Chehab 
359a0bf528SMauro Carvalho Chehab #if 0
369a0bf528SMauro Carvalho Chehab #define dprintk(x...) printk(x)
379a0bf528SMauro Carvalho Chehab #else
389a0bf528SMauro Carvalho Chehab #define dprintk(x...)
399a0bf528SMauro Carvalho Chehab #endif
409a0bf528SMauro Carvalho Chehab 
419a0bf528SMauro Carvalho Chehab static int verbose;
429a0bf528SMauro Carvalho Chehab 
439a0bf528SMauro Carvalho Chehab #define XIN 57840000UL
449a0bf528SMauro Carvalho Chehab 
459a0bf528SMauro Carvalho Chehab #define FIN (XIN >> 4)
469a0bf528SMauro Carvalho Chehab 
479a0bf528SMauro Carvalho Chehab static int tda10021_inittab_size = 0x40;
489a0bf528SMauro Carvalho Chehab static u8 tda10021_inittab[0x40]=
499a0bf528SMauro Carvalho Chehab {
509a0bf528SMauro Carvalho Chehab 	0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
519a0bf528SMauro Carvalho Chehab 	0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
529a0bf528SMauro Carvalho Chehab 	0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff,
539a0bf528SMauro Carvalho Chehab 	0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
549a0bf528SMauro Carvalho Chehab 	0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
559a0bf528SMauro Carvalho Chehab 	0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
569a0bf528SMauro Carvalho Chehab 	0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00,
579a0bf528SMauro Carvalho Chehab 	0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
589a0bf528SMauro Carvalho Chehab };
599a0bf528SMauro Carvalho Chehab 
_tda10021_writereg(struct tda10021_state * state,u8 reg,u8 data)609a0bf528SMauro Carvalho Chehab static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
619a0bf528SMauro Carvalho Chehab {
629a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
639a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
649a0bf528SMauro Carvalho Chehab 	int ret;
659a0bf528SMauro Carvalho Chehab 
669a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer (state->i2c, &msg, 1);
679a0bf528SMauro Carvalho Chehab 	if (ret != 1)
684bd69e7bSMauro Carvalho Chehab 		printk("DVB: TDA10021(%d): %s, writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
699a0bf528SMauro Carvalho Chehab 			state->frontend.dvb->num, __func__, reg, data, ret);
709a0bf528SMauro Carvalho Chehab 
719a0bf528SMauro Carvalho Chehab 	msleep(10);
729a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -EREMOTEIO : 0;
739a0bf528SMauro Carvalho Chehab }
749a0bf528SMauro Carvalho Chehab 
tda10021_readreg(struct tda10021_state * state,u8 reg)759a0bf528SMauro Carvalho Chehab static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
769a0bf528SMauro Carvalho Chehab {
779a0bf528SMauro Carvalho Chehab 	u8 b0 [] = { reg };
789a0bf528SMauro Carvalho Chehab 	u8 b1 [] = { 0 };
799a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
809a0bf528SMauro Carvalho Chehab 				  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
819a0bf528SMauro Carvalho Chehab 	int ret;
829a0bf528SMauro Carvalho Chehab 
839a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer (state->i2c, msg, 2);
849a0bf528SMauro Carvalho Chehab 	// Don't print an error message if the id is read.
859a0bf528SMauro Carvalho Chehab 	if (ret != 2 && reg != 0x1a)
869a0bf528SMauro Carvalho Chehab 		printk("DVB: TDA10021: %s: readreg error (ret == %i)\n",
879a0bf528SMauro Carvalho Chehab 				__func__, ret);
889a0bf528SMauro Carvalho Chehab 	return b1[0];
899a0bf528SMauro Carvalho Chehab }
909a0bf528SMauro Carvalho Chehab 
919a0bf528SMauro Carvalho Chehab //get access to tuner
lock_tuner(struct tda10021_state * state)929a0bf528SMauro Carvalho Chehab static int lock_tuner(struct tda10021_state* state)
939a0bf528SMauro Carvalho Chehab {
949a0bf528SMauro Carvalho Chehab 	u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 };
959a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
969a0bf528SMauro Carvalho Chehab 
979a0bf528SMauro Carvalho Chehab 	if(i2c_transfer(state->i2c, &msg, 1) != 1)
989a0bf528SMauro Carvalho Chehab 	{
999a0bf528SMauro Carvalho Chehab 		printk("tda10021: lock tuner fails\n");
1009a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
1019a0bf528SMauro Carvalho Chehab 	}
1029a0bf528SMauro Carvalho Chehab 	return 0;
1039a0bf528SMauro Carvalho Chehab }
1049a0bf528SMauro Carvalho Chehab 
1059a0bf528SMauro Carvalho Chehab //release access from tuner
unlock_tuner(struct tda10021_state * state)1069a0bf528SMauro Carvalho Chehab static int unlock_tuner(struct tda10021_state* state)
1079a0bf528SMauro Carvalho Chehab {
1089a0bf528SMauro Carvalho Chehab 	u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f };
1099a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
1109a0bf528SMauro Carvalho Chehab 
1119a0bf528SMauro Carvalho Chehab 	if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
1129a0bf528SMauro Carvalho Chehab 	{
1139a0bf528SMauro Carvalho Chehab 		printk("tda10021: unlock tuner fails\n");
1149a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
1159a0bf528SMauro Carvalho Chehab 	}
1169a0bf528SMauro Carvalho Chehab 	return 0;
1179a0bf528SMauro Carvalho Chehab }
1189a0bf528SMauro Carvalho Chehab 
tda10021_setup_reg0(struct tda10021_state * state,u8 reg0,enum fe_spectral_inversion inversion)1199a0bf528SMauro Carvalho Chehab static int tda10021_setup_reg0(struct tda10021_state *state, u8 reg0,
1200df289a2SMauro Carvalho Chehab 			       enum fe_spectral_inversion inversion)
1219a0bf528SMauro Carvalho Chehab {
1229a0bf528SMauro Carvalho Chehab 	reg0 |= state->reg0 & 0x63;
1239a0bf528SMauro Carvalho Chehab 
1249a0bf528SMauro Carvalho Chehab 	if ((INVERSION_ON == inversion) ^ (state->config->invert == 0))
1259a0bf528SMauro Carvalho Chehab 		reg0 &= ~0x20;
1269a0bf528SMauro Carvalho Chehab 	else
1279a0bf528SMauro Carvalho Chehab 		reg0 |= 0x20;
1289a0bf528SMauro Carvalho Chehab 
1299a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x00, reg0 & 0xfe);
1309a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x00, reg0 | 0x01);
1319a0bf528SMauro Carvalho Chehab 
1329a0bf528SMauro Carvalho Chehab 	state->reg0 = reg0;
1339a0bf528SMauro Carvalho Chehab 	return 0;
1349a0bf528SMauro Carvalho Chehab }
1359a0bf528SMauro Carvalho Chehab 
tda10021_set_symbolrate(struct tda10021_state * state,u32 symbolrate)1369a0bf528SMauro Carvalho Chehab static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate)
1379a0bf528SMauro Carvalho Chehab {
1389a0bf528SMauro Carvalho Chehab 	s32 BDR;
1399a0bf528SMauro Carvalho Chehab 	s32 BDRI;
1409a0bf528SMauro Carvalho Chehab 	s16 SFIL = 0;
1419a0bf528SMauro Carvalho Chehab 	u16 NDEC = 0;
1429a0bf528SMauro Carvalho Chehab 	u32 tmp, ratio;
1439a0bf528SMauro Carvalho Chehab 
1449a0bf528SMauro Carvalho Chehab 	if (symbolrate > XIN / 2)
1459a0bf528SMauro Carvalho Chehab 		symbolrate = XIN / 2;
1463f83aa6bSMauro Carvalho Chehab 	else if (symbolrate < 500000)
1479a0bf528SMauro Carvalho Chehab 		symbolrate = 500000;
1489a0bf528SMauro Carvalho Chehab 
1493f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN / 16)
1503f83aa6bSMauro Carvalho Chehab 		NDEC = 1;
1513f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN / 32)
1523f83aa6bSMauro Carvalho Chehab 		NDEC = 2;
1533f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN / 64)
1543f83aa6bSMauro Carvalho Chehab 		NDEC = 3;
1559a0bf528SMauro Carvalho Chehab 
1563f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN * 10 / 123)
1573f83aa6bSMauro Carvalho Chehab 		SFIL = 1;
1583f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN * 10 / 160)
1593f83aa6bSMauro Carvalho Chehab 		SFIL = 0;
1603f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN * 10 / 246)
1613f83aa6bSMauro Carvalho Chehab 		SFIL = 1;
1623f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN * 10 / 320)
1633f83aa6bSMauro Carvalho Chehab 		SFIL = 0;
1643f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN * 10 / 492)
1653f83aa6bSMauro Carvalho Chehab 		SFIL = 1;
1663f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN * 10 / 640)
1673f83aa6bSMauro Carvalho Chehab 		SFIL = 0;
1683f83aa6bSMauro Carvalho Chehab 	if (symbolrate < XIN * 10 / 984)
1693f83aa6bSMauro Carvalho Chehab 		SFIL = 1;
1709a0bf528SMauro Carvalho Chehab 
1719a0bf528SMauro Carvalho Chehab 	symbolrate <<= NDEC;
1729a0bf528SMauro Carvalho Chehab 	ratio = (symbolrate << 4) / FIN;
1739a0bf528SMauro Carvalho Chehab 	tmp =  ((symbolrate << 4) % FIN) << 8;
1749a0bf528SMauro Carvalho Chehab 	ratio = (ratio << 8) + tmp / FIN;
1759a0bf528SMauro Carvalho Chehab 	tmp = (tmp % FIN) << 8;
1769a0bf528SMauro Carvalho Chehab 	ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN);
1779a0bf528SMauro Carvalho Chehab 
1789a0bf528SMauro Carvalho Chehab 	BDR = ratio;
1799a0bf528SMauro Carvalho Chehab 	BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
1809a0bf528SMauro Carvalho Chehab 
1819a0bf528SMauro Carvalho Chehab 	if (BDRI > 0xFF)
1829a0bf528SMauro Carvalho Chehab 		BDRI = 0xFF;
1839a0bf528SMauro Carvalho Chehab 
1849a0bf528SMauro Carvalho Chehab 	SFIL = (SFIL << 4) | tda10021_inittab[0x0E];
1859a0bf528SMauro Carvalho Chehab 
1869a0bf528SMauro Carvalho Chehab 	NDEC = (NDEC << 6) | tda10021_inittab[0x03];
1879a0bf528SMauro Carvalho Chehab 
1889a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x03, NDEC);
1899a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x0a, BDR&0xff);
1909a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
1919a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
1929a0bf528SMauro Carvalho Chehab 
1939a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x0d, BDRI);
1949a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x0e, SFIL);
1959a0bf528SMauro Carvalho Chehab 
1969a0bf528SMauro Carvalho Chehab 	return 0;
1979a0bf528SMauro Carvalho Chehab }
1989a0bf528SMauro Carvalho Chehab 
tda10021_init(struct dvb_frontend * fe)1999a0bf528SMauro Carvalho Chehab static int tda10021_init (struct dvb_frontend *fe)
2009a0bf528SMauro Carvalho Chehab {
2019a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
2029a0bf528SMauro Carvalho Chehab 	int i;
2039a0bf528SMauro Carvalho Chehab 
2049a0bf528SMauro Carvalho Chehab 	dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
2059a0bf528SMauro Carvalho Chehab 
2069a0bf528SMauro Carvalho Chehab 	//_tda10021_writereg (fe, 0, 0);
2079a0bf528SMauro Carvalho Chehab 
2089a0bf528SMauro Carvalho Chehab 	for (i=0; i<tda10021_inittab_size; i++)
2099a0bf528SMauro Carvalho Chehab 		_tda10021_writereg (state, i, tda10021_inittab[i]);
2109a0bf528SMauro Carvalho Chehab 
2119a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x34, state->pwm);
2129a0bf528SMauro Carvalho Chehab 
2139a0bf528SMauro Carvalho Chehab 	//Comment by markus
2149a0bf528SMauro Carvalho Chehab 	//0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
2159a0bf528SMauro Carvalho Chehab 	//0x2A[4] == BYPPLL -> Power down mode (default 1)
2169a0bf528SMauro Carvalho Chehab 	//0x2A[5] == LCK -> PLL Lock Flag
2179a0bf528SMauro Carvalho Chehab 	//0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
2189a0bf528SMauro Carvalho Chehab 
2199a0bf528SMauro Carvalho Chehab 	//Activate PLL
2209a0bf528SMauro Carvalho Chehab 	_tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
2219a0bf528SMauro Carvalho Chehab 	return 0;
2229a0bf528SMauro Carvalho Chehab }
2239a0bf528SMauro Carvalho Chehab 
2249a0bf528SMauro Carvalho Chehab struct qam_params {
2259a0bf528SMauro Carvalho Chehab 	u8 conf, agcref, lthr, mseth, aref;
2269a0bf528SMauro Carvalho Chehab };
2279a0bf528SMauro Carvalho Chehab 
tda10021_set_parameters(struct dvb_frontend * fe)2289a0bf528SMauro Carvalho Chehab static int tda10021_set_parameters(struct dvb_frontend *fe)
2299a0bf528SMauro Carvalho Chehab {
2309a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
2319a0bf528SMauro Carvalho Chehab 	u32 delsys  = c->delivery_system;
2329a0bf528SMauro Carvalho Chehab 	unsigned qam = c->modulation;
2339a0bf528SMauro Carvalho Chehab 	bool is_annex_c;
2349a0bf528SMauro Carvalho Chehab 	u32 reg0x3d;
2359a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
2369a0bf528SMauro Carvalho Chehab 	static const struct qam_params qam_params[] = {
2379a0bf528SMauro Carvalho Chehab 		/* Modulation  Conf  AGCref  LTHR  MSETH  AREF */
2389a0bf528SMauro Carvalho Chehab 		[QPSK]	   = { 0x14, 0x78,   0x78, 0x8c,  0x96 },
2399a0bf528SMauro Carvalho Chehab 		[QAM_16]   = { 0x00, 0x8c,   0x87, 0xa2,  0x91 },
2409a0bf528SMauro Carvalho Chehab 		[QAM_32]   = { 0x04, 0x8c,   0x64, 0x74,  0x96 },
2419a0bf528SMauro Carvalho Chehab 		[QAM_64]   = { 0x08, 0x6a,   0x46, 0x43,  0x6a },
2429a0bf528SMauro Carvalho Chehab 		[QAM_128]  = { 0x0c, 0x78,   0x36, 0x34,  0x7e },
2439a0bf528SMauro Carvalho Chehab 		[QAM_256]  = { 0x10, 0x5c,   0x26, 0x23,  0x6b },
2449a0bf528SMauro Carvalho Chehab 	};
2459a0bf528SMauro Carvalho Chehab 
2469a0bf528SMauro Carvalho Chehab 	switch (delsys) {
2479a0bf528SMauro Carvalho Chehab 	case SYS_DVBC_ANNEX_A:
2489a0bf528SMauro Carvalho Chehab 		is_annex_c = false;
2499a0bf528SMauro Carvalho Chehab 		break;
2509a0bf528SMauro Carvalho Chehab 	case SYS_DVBC_ANNEX_C:
2519a0bf528SMauro Carvalho Chehab 		is_annex_c = true;
2529a0bf528SMauro Carvalho Chehab 		break;
2539a0bf528SMauro Carvalho Chehab 	default:
2549a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2559a0bf528SMauro Carvalho Chehab 	}
2569a0bf528SMauro Carvalho Chehab 
2579a0bf528SMauro Carvalho Chehab 	/*
2585a13e40bSMauro Carvalho Chehab 	 * gcc optimizes the code below the same way as it would code:
2599a0bf528SMauro Carvalho Chehab 	 *           "if (qam > 5) return -EINVAL;"
2609a0bf528SMauro Carvalho Chehab 	 * Yet, the code is clearer, as it shows what QAM standards are
2619a0bf528SMauro Carvalho Chehab 	 * supported by the driver, and avoids the usage of magic numbers on
2629a0bf528SMauro Carvalho Chehab 	 * it.
2639a0bf528SMauro Carvalho Chehab 	 */
2649a0bf528SMauro Carvalho Chehab 	switch (qam) {
2659a0bf528SMauro Carvalho Chehab 	case QPSK:
2669a0bf528SMauro Carvalho Chehab 	case QAM_16:
2679a0bf528SMauro Carvalho Chehab 	case QAM_32:
2689a0bf528SMauro Carvalho Chehab 	case QAM_64:
2699a0bf528SMauro Carvalho Chehab 	case QAM_128:
2709a0bf528SMauro Carvalho Chehab 	case QAM_256:
2719a0bf528SMauro Carvalho Chehab 		break;
2729a0bf528SMauro Carvalho Chehab 	default:
2739a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2749a0bf528SMauro Carvalho Chehab 	}
2759a0bf528SMauro Carvalho Chehab 
2769a0bf528SMauro Carvalho Chehab 	if (c->inversion != INVERSION_ON && c->inversion != INVERSION_OFF)
2779a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2789a0bf528SMauro Carvalho Chehab 
2799a0bf528SMauro Carvalho Chehab 	/*printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->symbol_rate);*/
2809a0bf528SMauro Carvalho Chehab 
2819a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
2829a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
2839a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
2849a0bf528SMauro Carvalho Chehab 	}
2859a0bf528SMauro Carvalho Chehab 
2869a0bf528SMauro Carvalho Chehab 	tda10021_set_symbolrate(state, c->symbol_rate);
2879a0bf528SMauro Carvalho Chehab 	_tda10021_writereg(state, 0x34, state->pwm);
2889a0bf528SMauro Carvalho Chehab 
2899a0bf528SMauro Carvalho Chehab 	_tda10021_writereg(state, 0x01, qam_params[qam].agcref);
2909a0bf528SMauro Carvalho Chehab 	_tda10021_writereg(state, 0x05, qam_params[qam].lthr);
2919a0bf528SMauro Carvalho Chehab 	_tda10021_writereg(state, 0x08, qam_params[qam].mseth);
2929a0bf528SMauro Carvalho Chehab 	_tda10021_writereg(state, 0x09, qam_params[qam].aref);
2939a0bf528SMauro Carvalho Chehab 
2949a0bf528SMauro Carvalho Chehab 	/*
2959a0bf528SMauro Carvalho Chehab 	 * Bit 0 == 0 means roll-off = 0.15 (Annex A)
2969a0bf528SMauro Carvalho Chehab 	 *	 == 1 means roll-off = 0.13 (Annex C)
2979a0bf528SMauro Carvalho Chehab 	 */
2989a0bf528SMauro Carvalho Chehab 	reg0x3d = tda10021_readreg (state, 0x3d);
2999a0bf528SMauro Carvalho Chehab 	if (is_annex_c)
3009a0bf528SMauro Carvalho Chehab 		_tda10021_writereg (state, 0x3d, 0x01 | reg0x3d);
3019a0bf528SMauro Carvalho Chehab 	else
3029a0bf528SMauro Carvalho Chehab 		_tda10021_writereg (state, 0x3d, 0xfe & reg0x3d);
3039a0bf528SMauro Carvalho Chehab 	tda10021_setup_reg0(state, qam_params[qam].conf, c->inversion);
3049a0bf528SMauro Carvalho Chehab 
3059a0bf528SMauro Carvalho Chehab 	return 0;
3069a0bf528SMauro Carvalho Chehab }
3079a0bf528SMauro Carvalho Chehab 
tda10021_read_status(struct dvb_frontend * fe,enum fe_status * status)3080df289a2SMauro Carvalho Chehab static int tda10021_read_status(struct dvb_frontend *fe,
3090df289a2SMauro Carvalho Chehab 				enum fe_status *status)
3109a0bf528SMauro Carvalho Chehab {
3119a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
3129a0bf528SMauro Carvalho Chehab 	int sync;
3139a0bf528SMauro Carvalho Chehab 
3149a0bf528SMauro Carvalho Chehab 	*status = 0;
3159a0bf528SMauro Carvalho Chehab 	//0x11[0] == EQALGO -> Equalizer algorithms state
3169a0bf528SMauro Carvalho Chehab 	//0x11[1] == CARLOCK -> Carrier locked
3179a0bf528SMauro Carvalho Chehab 	//0x11[2] == FSYNC -> Frame synchronisation
3189a0bf528SMauro Carvalho Chehab 	//0x11[3] == FEL -> Front End locked
3199a0bf528SMauro Carvalho Chehab 	//0x11[6] == NODVB -> DVB Mode Information
3209a0bf528SMauro Carvalho Chehab 	sync = tda10021_readreg (state, 0x11);
3219a0bf528SMauro Carvalho Chehab 
3229a0bf528SMauro Carvalho Chehab 	if (sync & 2)
3239a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
3249a0bf528SMauro Carvalho Chehab 
3259a0bf528SMauro Carvalho Chehab 	if (sync & 4)
3269a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC|FE_HAS_VITERBI;
3279a0bf528SMauro Carvalho Chehab 
3289a0bf528SMauro Carvalho Chehab 	if (sync & 8)
3299a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
3309a0bf528SMauro Carvalho Chehab 
3319a0bf528SMauro Carvalho Chehab 	return 0;
3329a0bf528SMauro Carvalho Chehab }
3339a0bf528SMauro Carvalho Chehab 
tda10021_read_ber(struct dvb_frontend * fe,u32 * ber)3349a0bf528SMauro Carvalho Chehab static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber)
3359a0bf528SMauro Carvalho Chehab {
3369a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
3379a0bf528SMauro Carvalho Chehab 
3389a0bf528SMauro Carvalho Chehab 	u32 _ber = tda10021_readreg(state, 0x14) |
3399a0bf528SMauro Carvalho Chehab 		(tda10021_readreg(state, 0x15) << 8) |
3409a0bf528SMauro Carvalho Chehab 		((tda10021_readreg(state, 0x16) & 0x0f) << 16);
3419a0bf528SMauro Carvalho Chehab 	_tda10021_writereg(state, 0x10, (tda10021_readreg(state, 0x10) & ~0xc0)
3429a0bf528SMauro Carvalho Chehab 					| (tda10021_inittab[0x10] & 0xc0));
3439a0bf528SMauro Carvalho Chehab 	*ber = 10 * _ber;
3449a0bf528SMauro Carvalho Chehab 
3459a0bf528SMauro Carvalho Chehab 	return 0;
3469a0bf528SMauro Carvalho Chehab }
3479a0bf528SMauro Carvalho Chehab 
tda10021_read_signal_strength(struct dvb_frontend * fe,u16 * strength)3489a0bf528SMauro Carvalho Chehab static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength)
3499a0bf528SMauro Carvalho Chehab {
3509a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
3519a0bf528SMauro Carvalho Chehab 
3529a0bf528SMauro Carvalho Chehab 	u8 config = tda10021_readreg(state, 0x02);
3539a0bf528SMauro Carvalho Chehab 	u8 gain = tda10021_readreg(state, 0x17);
3549a0bf528SMauro Carvalho Chehab 	if (config & 0x02)
3559a0bf528SMauro Carvalho Chehab 		/* the agc value is inverted */
3569a0bf528SMauro Carvalho Chehab 		gain = ~gain;
3579a0bf528SMauro Carvalho Chehab 	*strength = (gain << 8) | gain;
3589a0bf528SMauro Carvalho Chehab 
3599a0bf528SMauro Carvalho Chehab 	return 0;
3609a0bf528SMauro Carvalho Chehab }
3619a0bf528SMauro Carvalho Chehab 
tda10021_read_snr(struct dvb_frontend * fe,u16 * snr)3629a0bf528SMauro Carvalho Chehab static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr)
3639a0bf528SMauro Carvalho Chehab {
3649a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
3659a0bf528SMauro Carvalho Chehab 
3669a0bf528SMauro Carvalho Chehab 	u8 quality = ~tda10021_readreg(state, 0x18);
3679a0bf528SMauro Carvalho Chehab 	*snr = (quality << 8) | quality;
3689a0bf528SMauro Carvalho Chehab 
3699a0bf528SMauro Carvalho Chehab 	return 0;
3709a0bf528SMauro Carvalho Chehab }
3719a0bf528SMauro Carvalho Chehab 
tda10021_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)3729a0bf528SMauro Carvalho Chehab static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
3739a0bf528SMauro Carvalho Chehab {
3749a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
3759a0bf528SMauro Carvalho Chehab 
3769a0bf528SMauro Carvalho Chehab 	*ucblocks = tda10021_readreg (state, 0x13) & 0x7f;
3779a0bf528SMauro Carvalho Chehab 	if (*ucblocks == 0x7f)
3789a0bf528SMauro Carvalho Chehab 		*ucblocks = 0xffffffff;
3799a0bf528SMauro Carvalho Chehab 
3809a0bf528SMauro Carvalho Chehab 	/* reset uncorrected block counter */
3819a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
3829a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
3839a0bf528SMauro Carvalho Chehab 
3849a0bf528SMauro Carvalho Chehab 	return 0;
3859a0bf528SMauro Carvalho Chehab }
3869a0bf528SMauro Carvalho Chehab 
tda10021_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * p)3877e3e68bcSMauro Carvalho Chehab static int tda10021_get_frontend(struct dvb_frontend *fe,
3887e3e68bcSMauro Carvalho Chehab 				 struct dtv_frontend_properties *p)
3899a0bf528SMauro Carvalho Chehab {
3909a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
3919a0bf528SMauro Carvalho Chehab 	int sync;
3929a0bf528SMauro Carvalho Chehab 	s8 afc = 0;
3939a0bf528SMauro Carvalho Chehab 
3949a0bf528SMauro Carvalho Chehab 	sync = tda10021_readreg(state, 0x11);
3959a0bf528SMauro Carvalho Chehab 	afc = tda10021_readreg(state, 0x19);
3969a0bf528SMauro Carvalho Chehab 	if (verbose) {
3979a0bf528SMauro Carvalho Chehab 		/* AFC only valid when carrier has been recovered */
3989a0bf528SMauro Carvalho Chehab 		printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" :
3999a0bf528SMauro Carvalho Chehab 				  "DVB: TDA10021(%d): [AFC (%d) %dHz]\n",
4009a0bf528SMauro Carvalho Chehab 			state->frontend.dvb->num, afc,
4019a0bf528SMauro Carvalho Chehab 		       -((s32)p->symbol_rate * afc) >> 10);
4029a0bf528SMauro Carvalho Chehab 	}
4039a0bf528SMauro Carvalho Chehab 
4049a0bf528SMauro Carvalho Chehab 	p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF;
4059a0bf528SMauro Carvalho Chehab 	p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
4069a0bf528SMauro Carvalho Chehab 
4079a0bf528SMauro Carvalho Chehab 	p->fec_inner = FEC_NONE;
4089a0bf528SMauro Carvalho Chehab 	p->frequency = ((p->frequency + 31250) / 62500) * 62500;
4099a0bf528SMauro Carvalho Chehab 
4109a0bf528SMauro Carvalho Chehab 	if (sync & 2)
4119a0bf528SMauro Carvalho Chehab 		p->frequency -= ((s32)p->symbol_rate * afc) >> 10;
4129a0bf528SMauro Carvalho Chehab 
4139a0bf528SMauro Carvalho Chehab 	return 0;
4149a0bf528SMauro Carvalho Chehab }
4159a0bf528SMauro Carvalho Chehab 
tda10021_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)4169a0bf528SMauro Carvalho Chehab static int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
4179a0bf528SMauro Carvalho Chehab {
4189a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
4199a0bf528SMauro Carvalho Chehab 
4209a0bf528SMauro Carvalho Chehab 	if (enable) {
4219a0bf528SMauro Carvalho Chehab 		lock_tuner(state);
4229a0bf528SMauro Carvalho Chehab 	} else {
4239a0bf528SMauro Carvalho Chehab 		unlock_tuner(state);
4249a0bf528SMauro Carvalho Chehab 	}
4259a0bf528SMauro Carvalho Chehab 	return 0;
4269a0bf528SMauro Carvalho Chehab }
4279a0bf528SMauro Carvalho Chehab 
tda10021_sleep(struct dvb_frontend * fe)4289a0bf528SMauro Carvalho Chehab static int tda10021_sleep(struct dvb_frontend* fe)
4299a0bf528SMauro Carvalho Chehab {
4309a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
4319a0bf528SMauro Carvalho Chehab 
4329a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x1b, 0x02);  /* pdown ADC */
4339a0bf528SMauro Carvalho Chehab 	_tda10021_writereg (state, 0x00, 0x80);  /* standby */
4349a0bf528SMauro Carvalho Chehab 
4359a0bf528SMauro Carvalho Chehab 	return 0;
4369a0bf528SMauro Carvalho Chehab }
4379a0bf528SMauro Carvalho Chehab 
tda10021_release(struct dvb_frontend * fe)4389a0bf528SMauro Carvalho Chehab static void tda10021_release(struct dvb_frontend* fe)
4399a0bf528SMauro Carvalho Chehab {
4409a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = fe->demodulator_priv;
4419a0bf528SMauro Carvalho Chehab 	kfree(state);
4429a0bf528SMauro Carvalho Chehab }
4439a0bf528SMauro Carvalho Chehab 
444bd336e63SMax Kellermann static const struct dvb_frontend_ops tda10021_ops;
4459a0bf528SMauro Carvalho Chehab 
tda10021_attach(const struct tda1002x_config * config,struct i2c_adapter * i2c,u8 pwm)4469a0bf528SMauro Carvalho Chehab struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
4479a0bf528SMauro Carvalho Chehab 				     struct i2c_adapter* i2c,
4489a0bf528SMauro Carvalho Chehab 				     u8 pwm)
4499a0bf528SMauro Carvalho Chehab {
4509a0bf528SMauro Carvalho Chehab 	struct tda10021_state* state = NULL;
4519a0bf528SMauro Carvalho Chehab 	u8 id;
4529a0bf528SMauro Carvalho Chehab 
4539a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
4549a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL);
4559a0bf528SMauro Carvalho Chehab 	if (state == NULL) goto error;
4569a0bf528SMauro Carvalho Chehab 
4579a0bf528SMauro Carvalho Chehab 	/* setup the state */
4589a0bf528SMauro Carvalho Chehab 	state->config = config;
4599a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
4609a0bf528SMauro Carvalho Chehab 	state->pwm = pwm;
4619a0bf528SMauro Carvalho Chehab 	state->reg0 = tda10021_inittab[0];
4629a0bf528SMauro Carvalho Chehab 
4639a0bf528SMauro Carvalho Chehab 	/* check if the demod is there */
4649a0bf528SMauro Carvalho Chehab 	id = tda10021_readreg(state, 0x1a);
4659a0bf528SMauro Carvalho Chehab 	if ((id & 0xf0) != 0x70) goto error;
4669a0bf528SMauro Carvalho Chehab 
4679a0bf528SMauro Carvalho Chehab 	/* Don't claim TDA10023 */
4689a0bf528SMauro Carvalho Chehab 	if (id == 0x7d)
4699a0bf528SMauro Carvalho Chehab 		goto error;
4709a0bf528SMauro Carvalho Chehab 
4719a0bf528SMauro Carvalho Chehab 	printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n",
4729a0bf528SMauro Carvalho Chehab 	       state->config->demod_address, id);
4739a0bf528SMauro Carvalho Chehab 
4749a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
4759a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
4769a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
4779a0bf528SMauro Carvalho Chehab 	return &state->frontend;
4789a0bf528SMauro Carvalho Chehab 
4799a0bf528SMauro Carvalho Chehab error:
4809a0bf528SMauro Carvalho Chehab 	kfree(state);
4819a0bf528SMauro Carvalho Chehab 	return NULL;
4829a0bf528SMauro Carvalho Chehab }
4839a0bf528SMauro Carvalho Chehab 
484bd336e63SMax Kellermann static const struct dvb_frontend_ops tda10021_ops = {
4859a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C },
4869a0bf528SMauro Carvalho Chehab 	.info = {
4879a0bf528SMauro Carvalho Chehab 		.name = "Philips TDA10021 DVB-C",
488f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz =  47 * MHz,
489f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz = 862 * MHz,
490f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz = 62500,
4919a0bf528SMauro Carvalho Chehab 		.symbol_rate_min = (XIN / 2) / 64,     /* SACLK/64 == (XIN/2)/64 */
4929a0bf528SMauro Carvalho Chehab 		.symbol_rate_max = (XIN / 2) / 4,      /* SACLK/4 */
4939a0bf528SMauro Carvalho Chehab 	#if 0
4949a0bf528SMauro Carvalho Chehab 		.frequency_tolerance = ???,
4959a0bf528SMauro Carvalho Chehab 		.symbol_rate_tolerance = ???,  /* ppm */  /* == 8% (spec p. 5) */
4969a0bf528SMauro Carvalho Chehab 	#endif
4979a0bf528SMauro Carvalho Chehab 		.caps = 0x400 | //FE_CAN_QAM_4
4989a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
4999a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_128 | FE_CAN_QAM_256 |
5009a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_AUTO
5019a0bf528SMauro Carvalho Chehab 	},
5029a0bf528SMauro Carvalho Chehab 
5039a0bf528SMauro Carvalho Chehab 	.release = tda10021_release,
5049a0bf528SMauro Carvalho Chehab 
5059a0bf528SMauro Carvalho Chehab 	.init = tda10021_init,
5069a0bf528SMauro Carvalho Chehab 	.sleep = tda10021_sleep,
5079a0bf528SMauro Carvalho Chehab 	.i2c_gate_ctrl = tda10021_i2c_gate_ctrl,
5089a0bf528SMauro Carvalho Chehab 
5099a0bf528SMauro Carvalho Chehab 	.set_frontend = tda10021_set_parameters,
5109a0bf528SMauro Carvalho Chehab 	.get_frontend = tda10021_get_frontend,
5119a0bf528SMauro Carvalho Chehab 
5129a0bf528SMauro Carvalho Chehab 	.read_status = tda10021_read_status,
5139a0bf528SMauro Carvalho Chehab 	.read_ber = tda10021_read_ber,
5149a0bf528SMauro Carvalho Chehab 	.read_signal_strength = tda10021_read_signal_strength,
5159a0bf528SMauro Carvalho Chehab 	.read_snr = tda10021_read_snr,
5169a0bf528SMauro Carvalho Chehab 	.read_ucblocks = tda10021_read_ucblocks,
5179a0bf528SMauro Carvalho Chehab };
5189a0bf528SMauro Carvalho Chehab 
5199a0bf528SMauro Carvalho Chehab module_param(verbose, int, 0644);
5209a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
5219a0bf528SMauro Carvalho Chehab 
5229a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver");
5239a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz");
5249a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
5259a0bf528SMauro Carvalho Chehab 
526*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tda10021_attach);
527