18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    TDA10021  - Single Chip Cable Channel Receiver driver module
48c2ecf20Sopenharmony_ci	       used on the Siemens DVB-C cards
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
78c2ecf20Sopenharmony_ci    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
88c2ecf20Sopenharmony_ci		   Support for TDA10021
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci*/
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
218c2ecf20Sopenharmony_ci#include "tda1002x.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct tda10021_state {
258c2ecf20Sopenharmony_ci	struct i2c_adapter* i2c;
268c2ecf20Sopenharmony_ci	/* configuration settings */
278c2ecf20Sopenharmony_ci	const struct tda1002x_config* config;
288c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	u8 pwm;
318c2ecf20Sopenharmony_ci	u8 reg0;
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#if 0
368c2ecf20Sopenharmony_ci#define dprintk(x...) printk(x)
378c2ecf20Sopenharmony_ci#else
388c2ecf20Sopenharmony_ci#define dprintk(x...)
398c2ecf20Sopenharmony_ci#endif
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int verbose;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define XIN 57840000UL
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define FIN (XIN >> 4)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int tda10021_inittab_size = 0x40;
488c2ecf20Sopenharmony_cistatic u8 tda10021_inittab[0x40]=
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
518c2ecf20Sopenharmony_ci	0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
528c2ecf20Sopenharmony_ci	0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff,
538c2ecf20Sopenharmony_ci	0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
548c2ecf20Sopenharmony_ci	0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
558c2ecf20Sopenharmony_ci	0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
568c2ecf20Sopenharmony_ci	0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00,
578c2ecf20Sopenharmony_ci	0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	u8 buf[] = { reg, data };
638c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
648c2ecf20Sopenharmony_ci	int ret;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	ret = i2c_transfer (state->i2c, &msg, 1);
678c2ecf20Sopenharmony_ci	if (ret != 1)
688c2ecf20Sopenharmony_ci		printk("DVB: TDA10021(%d): %s, writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
698c2ecf20Sopenharmony_ci			state->frontend.dvb->num, __func__, reg, data, ret);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	msleep(10);
728c2ecf20Sopenharmony_ci	return (ret != 1) ? -EREMOTEIO : 0;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	u8 b0 [] = { reg };
788c2ecf20Sopenharmony_ci	u8 b1 [] = { 0 };
798c2ecf20Sopenharmony_ci	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
808c2ecf20Sopenharmony_ci				  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
818c2ecf20Sopenharmony_ci	int ret;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	ret = i2c_transfer (state->i2c, msg, 2);
848c2ecf20Sopenharmony_ci	// Don't print an error message if the id is read.
858c2ecf20Sopenharmony_ci	if (ret != 2 && reg != 0x1a)
868c2ecf20Sopenharmony_ci		printk("DVB: TDA10021: %s: readreg error (ret == %i)\n",
878c2ecf20Sopenharmony_ci				__func__, ret);
888c2ecf20Sopenharmony_ci	return b1[0];
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci//get access to tuner
928c2ecf20Sopenharmony_cistatic int lock_tuner(struct tda10021_state* state)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 };
958c2ecf20Sopenharmony_ci	struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if(i2c_transfer(state->i2c, &msg, 1) != 1)
988c2ecf20Sopenharmony_ci	{
998c2ecf20Sopenharmony_ci		printk("tda10021: lock tuner fails\n");
1008c2ecf20Sopenharmony_ci		return -EREMOTEIO;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci//release access from tuner
1068c2ecf20Sopenharmony_cistatic int unlock_tuner(struct tda10021_state* state)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f };
1098c2ecf20Sopenharmony_ci	struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
1128c2ecf20Sopenharmony_ci	{
1138c2ecf20Sopenharmony_ci		printk("tda10021: unlock tuner fails\n");
1148c2ecf20Sopenharmony_ci		return -EREMOTEIO;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci	return 0;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int tda10021_setup_reg0(struct tda10021_state *state, u8 reg0,
1208c2ecf20Sopenharmony_ci			       enum fe_spectral_inversion inversion)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	reg0 |= state->reg0 & 0x63;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if ((INVERSION_ON == inversion) ^ (state->config->invert == 0))
1258c2ecf20Sopenharmony_ci		reg0 &= ~0x20;
1268c2ecf20Sopenharmony_ci	else
1278c2ecf20Sopenharmony_ci		reg0 |= 0x20;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x00, reg0 & 0xfe);
1308c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x00, reg0 | 0x01);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	state->reg0 = reg0;
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	s32 BDR;
1398c2ecf20Sopenharmony_ci	s32 BDRI;
1408c2ecf20Sopenharmony_ci	s16 SFIL = 0;
1418c2ecf20Sopenharmony_ci	u16 NDEC = 0;
1428c2ecf20Sopenharmony_ci	u32 tmp, ratio;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (symbolrate > XIN / 2)
1458c2ecf20Sopenharmony_ci		symbolrate = XIN / 2;
1468c2ecf20Sopenharmony_ci	else if (symbolrate < 500000)
1478c2ecf20Sopenharmony_ci		symbolrate = 500000;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (symbolrate < XIN / 16)
1508c2ecf20Sopenharmony_ci		NDEC = 1;
1518c2ecf20Sopenharmony_ci	if (symbolrate < XIN / 32)
1528c2ecf20Sopenharmony_ci		NDEC = 2;
1538c2ecf20Sopenharmony_ci	if (symbolrate < XIN / 64)
1548c2ecf20Sopenharmony_ci		NDEC = 3;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (symbolrate < XIN * 10 / 123)
1578c2ecf20Sopenharmony_ci		SFIL = 1;
1588c2ecf20Sopenharmony_ci	if (symbolrate < XIN * 10 / 160)
1598c2ecf20Sopenharmony_ci		SFIL = 0;
1608c2ecf20Sopenharmony_ci	if (symbolrate < XIN * 10 / 246)
1618c2ecf20Sopenharmony_ci		SFIL = 1;
1628c2ecf20Sopenharmony_ci	if (symbolrate < XIN * 10 / 320)
1638c2ecf20Sopenharmony_ci		SFIL = 0;
1648c2ecf20Sopenharmony_ci	if (symbolrate < XIN * 10 / 492)
1658c2ecf20Sopenharmony_ci		SFIL = 1;
1668c2ecf20Sopenharmony_ci	if (symbolrate < XIN * 10 / 640)
1678c2ecf20Sopenharmony_ci		SFIL = 0;
1688c2ecf20Sopenharmony_ci	if (symbolrate < XIN * 10 / 984)
1698c2ecf20Sopenharmony_ci		SFIL = 1;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	symbolrate <<= NDEC;
1728c2ecf20Sopenharmony_ci	ratio = (symbolrate << 4) / FIN;
1738c2ecf20Sopenharmony_ci	tmp =  ((symbolrate << 4) % FIN) << 8;
1748c2ecf20Sopenharmony_ci	ratio = (ratio << 8) + tmp / FIN;
1758c2ecf20Sopenharmony_ci	tmp = (tmp % FIN) << 8;
1768c2ecf20Sopenharmony_ci	ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	BDR = ratio;
1798c2ecf20Sopenharmony_ci	BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (BDRI > 0xFF)
1828c2ecf20Sopenharmony_ci		BDRI = 0xFF;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	SFIL = (SFIL << 4) | tda10021_inittab[0x0E];
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	NDEC = (NDEC << 6) | tda10021_inittab[0x03];
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x03, NDEC);
1898c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x0a, BDR&0xff);
1908c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
1918c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x0d, BDRI);
1948c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x0e, SFIL);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return 0;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int tda10021_init (struct dvb_frontend *fe)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
2028c2ecf20Sopenharmony_ci	int i;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	//_tda10021_writereg (fe, 0, 0);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	for (i=0; i<tda10021_inittab_size; i++)
2098c2ecf20Sopenharmony_ci		_tda10021_writereg (state, i, tda10021_inittab[i]);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x34, state->pwm);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	//Comment by markus
2148c2ecf20Sopenharmony_ci	//0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
2158c2ecf20Sopenharmony_ci	//0x2A[4] == BYPPLL -> Power down mode (default 1)
2168c2ecf20Sopenharmony_ci	//0x2A[5] == LCK -> PLL Lock Flag
2178c2ecf20Sopenharmony_ci	//0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	//Activate PLL
2208c2ecf20Sopenharmony_ci	_tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistruct qam_params {
2258c2ecf20Sopenharmony_ci	u8 conf, agcref, lthr, mseth, aref;
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int tda10021_set_parameters(struct dvb_frontend *fe)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
2318c2ecf20Sopenharmony_ci	u32 delsys  = c->delivery_system;
2328c2ecf20Sopenharmony_ci	unsigned qam = c->modulation;
2338c2ecf20Sopenharmony_ci	bool is_annex_c;
2348c2ecf20Sopenharmony_ci	u32 reg0x3d;
2358c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
2368c2ecf20Sopenharmony_ci	static const struct qam_params qam_params[] = {
2378c2ecf20Sopenharmony_ci		/* Modulation  Conf  AGCref  LTHR  MSETH  AREF */
2388c2ecf20Sopenharmony_ci		[QPSK]	   = { 0x14, 0x78,   0x78, 0x8c,  0x96 },
2398c2ecf20Sopenharmony_ci		[QAM_16]   = { 0x00, 0x8c,   0x87, 0xa2,  0x91 },
2408c2ecf20Sopenharmony_ci		[QAM_32]   = { 0x04, 0x8c,   0x64, 0x74,  0x96 },
2418c2ecf20Sopenharmony_ci		[QAM_64]   = { 0x08, 0x6a,   0x46, 0x43,  0x6a },
2428c2ecf20Sopenharmony_ci		[QAM_128]  = { 0x0c, 0x78,   0x36, 0x34,  0x7e },
2438c2ecf20Sopenharmony_ci		[QAM_256]  = { 0x10, 0x5c,   0x26, 0x23,  0x6b },
2448c2ecf20Sopenharmony_ci	};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	switch (delsys) {
2478c2ecf20Sopenharmony_ci	case SYS_DVBC_ANNEX_A:
2488c2ecf20Sopenharmony_ci		is_annex_c = false;
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci	case SYS_DVBC_ANNEX_C:
2518c2ecf20Sopenharmony_ci		is_annex_c = true;
2528c2ecf20Sopenharmony_ci		break;
2538c2ecf20Sopenharmony_ci	default:
2548c2ecf20Sopenharmony_ci		return -EINVAL;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/*
2588c2ecf20Sopenharmony_ci	 * gcc optimizes the code below the same way as it would code:
2598c2ecf20Sopenharmony_ci	 *           "if (qam > 5) return -EINVAL;"
2608c2ecf20Sopenharmony_ci	 * Yet, the code is clearer, as it shows what QAM standards are
2618c2ecf20Sopenharmony_ci	 * supported by the driver, and avoids the usage of magic numbers on
2628c2ecf20Sopenharmony_ci	 * it.
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci	switch (qam) {
2658c2ecf20Sopenharmony_ci	case QPSK:
2668c2ecf20Sopenharmony_ci	case QAM_16:
2678c2ecf20Sopenharmony_ci	case QAM_32:
2688c2ecf20Sopenharmony_ci	case QAM_64:
2698c2ecf20Sopenharmony_ci	case QAM_128:
2708c2ecf20Sopenharmony_ci	case QAM_256:
2718c2ecf20Sopenharmony_ci		break;
2728c2ecf20Sopenharmony_ci	default:
2738c2ecf20Sopenharmony_ci		return -EINVAL;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (c->inversion != INVERSION_ON && c->inversion != INVERSION_OFF)
2778c2ecf20Sopenharmony_ci		return -EINVAL;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/*printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->symbol_rate);*/
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params) {
2828c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
2838c2ecf20Sopenharmony_ci		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	tda10021_set_symbolrate(state, c->symbol_rate);
2878c2ecf20Sopenharmony_ci	_tda10021_writereg(state, 0x34, state->pwm);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	_tda10021_writereg(state, 0x01, qam_params[qam].agcref);
2908c2ecf20Sopenharmony_ci	_tda10021_writereg(state, 0x05, qam_params[qam].lthr);
2918c2ecf20Sopenharmony_ci	_tda10021_writereg(state, 0x08, qam_params[qam].mseth);
2928c2ecf20Sopenharmony_ci	_tda10021_writereg(state, 0x09, qam_params[qam].aref);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/*
2958c2ecf20Sopenharmony_ci	 * Bit 0 == 0 means roll-off = 0.15 (Annex A)
2968c2ecf20Sopenharmony_ci	 *	 == 1 means roll-off = 0.13 (Annex C)
2978c2ecf20Sopenharmony_ci	 */
2988c2ecf20Sopenharmony_ci	reg0x3d = tda10021_readreg (state, 0x3d);
2998c2ecf20Sopenharmony_ci	if (is_annex_c)
3008c2ecf20Sopenharmony_ci		_tda10021_writereg (state, 0x3d, 0x01 | reg0x3d);
3018c2ecf20Sopenharmony_ci	else
3028c2ecf20Sopenharmony_ci		_tda10021_writereg (state, 0x3d, 0xfe & reg0x3d);
3038c2ecf20Sopenharmony_ci	tda10021_setup_reg0(state, qam_params[qam].conf, c->inversion);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	return 0;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int tda10021_read_status(struct dvb_frontend *fe,
3098c2ecf20Sopenharmony_ci				enum fe_status *status)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
3128c2ecf20Sopenharmony_ci	int sync;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	*status = 0;
3158c2ecf20Sopenharmony_ci	//0x11[0] == EQALGO -> Equalizer algorithms state
3168c2ecf20Sopenharmony_ci	//0x11[1] == CARLOCK -> Carrier locked
3178c2ecf20Sopenharmony_ci	//0x11[2] == FSYNC -> Frame synchronisation
3188c2ecf20Sopenharmony_ci	//0x11[3] == FEL -> Front End locked
3198c2ecf20Sopenharmony_ci	//0x11[6] == NODVB -> DVB Mode Information
3208c2ecf20Sopenharmony_ci	sync = tda10021_readreg (state, 0x11);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (sync & 2)
3238c2ecf20Sopenharmony_ci		*status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (sync & 4)
3268c2ecf20Sopenharmony_ci		*status |= FE_HAS_SYNC|FE_HAS_VITERBI;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (sync & 8)
3298c2ecf20Sopenharmony_ci		*status |= FE_HAS_LOCK;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return 0;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int tda10021_read_ber(struct dvb_frontend* fe, u32* ber)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	u32 _ber = tda10021_readreg(state, 0x14) |
3398c2ecf20Sopenharmony_ci		(tda10021_readreg(state, 0x15) << 8) |
3408c2ecf20Sopenharmony_ci		((tda10021_readreg(state, 0x16) & 0x0f) << 16);
3418c2ecf20Sopenharmony_ci	_tda10021_writereg(state, 0x10, (tda10021_readreg(state, 0x10) & ~0xc0)
3428c2ecf20Sopenharmony_ci					| (tda10021_inittab[0x10] & 0xc0));
3438c2ecf20Sopenharmony_ci	*ber = 10 * _ber;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	return 0;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	u8 config = tda10021_readreg(state, 0x02);
3538c2ecf20Sopenharmony_ci	u8 gain = tda10021_readreg(state, 0x17);
3548c2ecf20Sopenharmony_ci	if (config & 0x02)
3558c2ecf20Sopenharmony_ci		/* the agc value is inverted */
3568c2ecf20Sopenharmony_ci		gain = ~gain;
3578c2ecf20Sopenharmony_ci	*strength = (gain << 8) | gain;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return 0;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic int tda10021_read_snr(struct dvb_frontend* fe, u16* snr)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	u8 quality = ~tda10021_readreg(state, 0x18);
3678c2ecf20Sopenharmony_ci	*snr = (quality << 8) | quality;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return 0;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	*ucblocks = tda10021_readreg (state, 0x13) & 0x7f;
3778c2ecf20Sopenharmony_ci	if (*ucblocks == 0x7f)
3788c2ecf20Sopenharmony_ci		*ucblocks = 0xffffffff;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* reset uncorrected block counter */
3818c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
3828c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int tda10021_get_frontend(struct dvb_frontend *fe,
3888c2ecf20Sopenharmony_ci				 struct dtv_frontend_properties *p)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
3918c2ecf20Sopenharmony_ci	int sync;
3928c2ecf20Sopenharmony_ci	s8 afc = 0;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	sync = tda10021_readreg(state, 0x11);
3958c2ecf20Sopenharmony_ci	afc = tda10021_readreg(state, 0x19);
3968c2ecf20Sopenharmony_ci	if (verbose) {
3978c2ecf20Sopenharmony_ci		/* AFC only valid when carrier has been recovered */
3988c2ecf20Sopenharmony_ci		printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" :
3998c2ecf20Sopenharmony_ci				  "DVB: TDA10021(%d): [AFC (%d) %dHz]\n",
4008c2ecf20Sopenharmony_ci			state->frontend.dvb->num, afc,
4018c2ecf20Sopenharmony_ci		       -((s32)p->symbol_rate * afc) >> 10);
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF;
4058c2ecf20Sopenharmony_ci	p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	p->fec_inner = FEC_NONE;
4088c2ecf20Sopenharmony_ci	p->frequency = ((p->frequency + 31250) / 62500) * 62500;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (sync & 2)
4118c2ecf20Sopenharmony_ci		p->frequency -= ((s32)p->symbol_rate * afc) >> 10;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (enable) {
4218c2ecf20Sopenharmony_ci		lock_tuner(state);
4228c2ecf20Sopenharmony_ci	} else {
4238c2ecf20Sopenharmony_ci		unlock_tuner(state);
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci	return 0;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic int tda10021_sleep(struct dvb_frontend* fe)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x1b, 0x02);  /* pdown ADC */
4338c2ecf20Sopenharmony_ci	_tda10021_writereg (state, 0x00, 0x80);  /* standby */
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	return 0;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void tda10021_release(struct dvb_frontend* fe)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct tda10021_state* state = fe->demodulator_priv;
4418c2ecf20Sopenharmony_ci	kfree(state);
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops tda10021_ops;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistruct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
4478c2ecf20Sopenharmony_ci				     struct i2c_adapter* i2c,
4488c2ecf20Sopenharmony_ci				     u8 pwm)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct tda10021_state* state = NULL;
4518c2ecf20Sopenharmony_ci	u8 id;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* allocate memory for the internal state */
4548c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL);
4558c2ecf20Sopenharmony_ci	if (state == NULL) goto error;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* setup the state */
4588c2ecf20Sopenharmony_ci	state->config = config;
4598c2ecf20Sopenharmony_ci	state->i2c = i2c;
4608c2ecf20Sopenharmony_ci	state->pwm = pwm;
4618c2ecf20Sopenharmony_ci	state->reg0 = tda10021_inittab[0];
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* check if the demod is there */
4648c2ecf20Sopenharmony_ci	id = tda10021_readreg(state, 0x1a);
4658c2ecf20Sopenharmony_ci	if ((id & 0xf0) != 0x70) goto error;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* Don't claim TDA10023 */
4688c2ecf20Sopenharmony_ci	if (id == 0x7d)
4698c2ecf20Sopenharmony_ci		goto error;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n",
4728c2ecf20Sopenharmony_ci	       state->config->demod_address, id);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* create dvb_frontend */
4758c2ecf20Sopenharmony_ci	memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
4768c2ecf20Sopenharmony_ci	state->frontend.demodulator_priv = state;
4778c2ecf20Sopenharmony_ci	return &state->frontend;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cierror:
4808c2ecf20Sopenharmony_ci	kfree(state);
4818c2ecf20Sopenharmony_ci	return NULL;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops tda10021_ops = {
4858c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C },
4868c2ecf20Sopenharmony_ci	.info = {
4878c2ecf20Sopenharmony_ci		.name = "Philips TDA10021 DVB-C",
4888c2ecf20Sopenharmony_ci		.frequency_min_hz =  47 * MHz,
4898c2ecf20Sopenharmony_ci		.frequency_max_hz = 862 * MHz,
4908c2ecf20Sopenharmony_ci		.frequency_stepsize_hz = 62500,
4918c2ecf20Sopenharmony_ci		.symbol_rate_min = (XIN / 2) / 64,     /* SACLK/64 == (XIN/2)/64 */
4928c2ecf20Sopenharmony_ci		.symbol_rate_max = (XIN / 2) / 4,      /* SACLK/4 */
4938c2ecf20Sopenharmony_ci	#if 0
4948c2ecf20Sopenharmony_ci		.frequency_tolerance = ???,
4958c2ecf20Sopenharmony_ci		.symbol_rate_tolerance = ???,  /* ppm */  /* == 8% (spec p. 5) */
4968c2ecf20Sopenharmony_ci	#endif
4978c2ecf20Sopenharmony_ci		.caps = 0x400 | //FE_CAN_QAM_4
4988c2ecf20Sopenharmony_ci			FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
4998c2ecf20Sopenharmony_ci			FE_CAN_QAM_128 | FE_CAN_QAM_256 |
5008c2ecf20Sopenharmony_ci			FE_CAN_FEC_AUTO
5018c2ecf20Sopenharmony_ci	},
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	.release = tda10021_release,
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	.init = tda10021_init,
5068c2ecf20Sopenharmony_ci	.sleep = tda10021_sleep,
5078c2ecf20Sopenharmony_ci	.i2c_gate_ctrl = tda10021_i2c_gate_ctrl,
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	.set_frontend = tda10021_set_parameters,
5108c2ecf20Sopenharmony_ci	.get_frontend = tda10021_get_frontend,
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	.read_status = tda10021_read_status,
5138c2ecf20Sopenharmony_ci	.read_ber = tda10021_read_ber,
5148c2ecf20Sopenharmony_ci	.read_signal_strength = tda10021_read_signal_strength,
5158c2ecf20Sopenharmony_ci	.read_snr = tda10021_read_snr,
5168c2ecf20Sopenharmony_ci	.read_ucblocks = tda10021_read_ucblocks,
5178c2ecf20Sopenharmony_ci};
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cimodule_param(verbose, int, 0644);
5208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver");
5238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz");
5248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tda10021_attach);
527