18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    TDA10023  - DVB-C decoder
48c2ecf20Sopenharmony_ci    (as used in Philips CU1216-3 NIM and the Reelbox DVB-C tuner card)
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci    Copyright (C) 2005 Georg Acher, BayCom GmbH (acher at baycom dot de)
78c2ecf20Sopenharmony_ci    Copyright (c) 2006 Hartmut Birr (e9hack at gmail dot com)
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci    Remotely based on tda10021.c
108c2ecf20Sopenharmony_ci    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
118c2ecf20Sopenharmony_ci    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
128c2ecf20Sopenharmony_ci		   Support for TDA10021
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci*/
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/kernel.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/string.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <asm/div64.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
278c2ecf20Sopenharmony_ci#include "tda1002x.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define REG0_INIT_VAL 0x23
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct tda10023_state {
328c2ecf20Sopenharmony_ci	struct i2c_adapter* i2c;
338c2ecf20Sopenharmony_ci	/* configuration settings */
348c2ecf20Sopenharmony_ci	const struct tda10023_config *config;
358c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	u8 pwm;
388c2ecf20Sopenharmony_ci	u8 reg0;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/* clock settings */
418c2ecf20Sopenharmony_ci	u32 xtal;
428c2ecf20Sopenharmony_ci	u8 pll_m;
438c2ecf20Sopenharmony_ci	u8 pll_p;
448c2ecf20Sopenharmony_ci	u8 pll_n;
458c2ecf20Sopenharmony_ci	u32 sysclk;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define dprintk(x...)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int verbose;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic u8 tda10023_readreg (struct tda10023_state* state, u8 reg)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	u8 b0 [] = { reg };
558c2ecf20Sopenharmony_ci	u8 b1 [] = { 0 };
568c2ecf20Sopenharmony_ci	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
578c2ecf20Sopenharmony_ci				  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
588c2ecf20Sopenharmony_ci	int ret;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	ret = i2c_transfer (state->i2c, msg, 2);
618c2ecf20Sopenharmony_ci	if (ret != 2) {
628c2ecf20Sopenharmony_ci		int num = state->frontend.dvb ? state->frontend.dvb->num : -1;
638c2ecf20Sopenharmony_ci		printk(KERN_ERR "DVB: TDA10023(%d): %s: readreg error (reg == 0x%02x, ret == %i)\n",
648c2ecf20Sopenharmony_ci			num, __func__, reg, ret);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci	return b1[0];
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	u8 buf[] = { reg, data };
728c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
738c2ecf20Sopenharmony_ci	int ret;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	ret = i2c_transfer (state->i2c, &msg, 1);
768c2ecf20Sopenharmony_ci	if (ret != 1) {
778c2ecf20Sopenharmony_ci		int num = state->frontend.dvb ? state->frontend.dvb->num : -1;
788c2ecf20Sopenharmony_ci		printk(KERN_ERR "DVB: TDA10023(%d): %s, writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
798c2ecf20Sopenharmony_ci			num, __func__, reg, data, ret);
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci	return (ret != 1) ? -EREMOTEIO : 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int tda10023_writebit (struct tda10023_state* state, u8 reg, u8 mask,u8 data)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	if (mask==0xff)
888c2ecf20Sopenharmony_ci		return tda10023_writereg(state, reg, data);
898c2ecf20Sopenharmony_ci	else {
908c2ecf20Sopenharmony_ci		u8 val;
918c2ecf20Sopenharmony_ci		val=tda10023_readreg(state,reg);
928c2ecf20Sopenharmony_ci		val&=~mask;
938c2ecf20Sopenharmony_ci		val|=(data&mask);
948c2ecf20Sopenharmony_ci		return tda10023_writereg(state, reg, val);
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void tda10023_writetab(struct tda10023_state* state, u8* tab)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	u8 r,m,v;
1018c2ecf20Sopenharmony_ci	while (1) {
1028c2ecf20Sopenharmony_ci		r=*tab++;
1038c2ecf20Sopenharmony_ci		m=*tab++;
1048c2ecf20Sopenharmony_ci		v=*tab++;
1058c2ecf20Sopenharmony_ci		if (r==0xff) {
1068c2ecf20Sopenharmony_ci			if (m==0xff)
1078c2ecf20Sopenharmony_ci				break;
1088c2ecf20Sopenharmony_ci			else
1098c2ecf20Sopenharmony_ci				msleep(m);
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci		else
1128c2ecf20Sopenharmony_ci			tda10023_writebit(state,r,m,v);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci//get access to tuner
1178c2ecf20Sopenharmony_cistatic int lock_tuner(struct tda10023_state* state)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	u8 buf[2] = { 0x0f, 0xc0 };
1208c2ecf20Sopenharmony_ci	struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if(i2c_transfer(state->i2c, &msg, 1) != 1)
1238c2ecf20Sopenharmony_ci	{
1248c2ecf20Sopenharmony_ci		printk("tda10023: lock tuner fails\n");
1258c2ecf20Sopenharmony_ci		return -EREMOTEIO;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci//release access from tuner
1318c2ecf20Sopenharmony_cistatic int unlock_tuner(struct tda10023_state* state)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	u8 buf[2] = { 0x0f, 0x40 };
1348c2ecf20Sopenharmony_ci	struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
1378c2ecf20Sopenharmony_ci	{
1388c2ecf20Sopenharmony_ci		printk("tda10023: unlock tuner fails\n");
1398c2ecf20Sopenharmony_ci		return -EREMOTEIO;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int tda10023_setup_reg0 (struct tda10023_state* state, u8 reg0)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	reg0 |= state->reg0 & 0x63;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x00, reg0 & 0xfe);
1498c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x00, reg0 | 0x01);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	state->reg0 = reg0;
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	s32 BDR;
1588c2ecf20Sopenharmony_ci	s32 BDRI;
1598c2ecf20Sopenharmony_ci	s16 SFIL=0;
1608c2ecf20Sopenharmony_ci	u16 NDEC = 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* avoid floating point operations multiplying syscloc and divider
1638c2ecf20Sopenharmony_ci	   by 10 */
1648c2ecf20Sopenharmony_ci	u32 sysclk_x_10 = state->sysclk * 10;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (sr < (u32)(sysclk_x_10/984)) {
1678c2ecf20Sopenharmony_ci		NDEC=3;
1688c2ecf20Sopenharmony_ci		SFIL=1;
1698c2ecf20Sopenharmony_ci	} else if (sr < (u32)(sysclk_x_10/640)) {
1708c2ecf20Sopenharmony_ci		NDEC=3;
1718c2ecf20Sopenharmony_ci		SFIL=0;
1728c2ecf20Sopenharmony_ci	} else if (sr < (u32)(sysclk_x_10/492)) {
1738c2ecf20Sopenharmony_ci		NDEC=2;
1748c2ecf20Sopenharmony_ci		SFIL=1;
1758c2ecf20Sopenharmony_ci	} else if (sr < (u32)(sysclk_x_10/320)) {
1768c2ecf20Sopenharmony_ci		NDEC=2;
1778c2ecf20Sopenharmony_ci		SFIL=0;
1788c2ecf20Sopenharmony_ci	} else if (sr < (u32)(sysclk_x_10/246)) {
1798c2ecf20Sopenharmony_ci		NDEC=1;
1808c2ecf20Sopenharmony_ci		SFIL=1;
1818c2ecf20Sopenharmony_ci	} else if (sr < (u32)(sysclk_x_10/160)) {
1828c2ecf20Sopenharmony_ci		NDEC=1;
1838c2ecf20Sopenharmony_ci		SFIL=0;
1848c2ecf20Sopenharmony_ci	} else if (sr < (u32)(sysclk_x_10/123)) {
1858c2ecf20Sopenharmony_ci		NDEC=0;
1868c2ecf20Sopenharmony_ci		SFIL=1;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	BDRI = (state->sysclk)*16;
1908c2ecf20Sopenharmony_ci	BDRI>>=NDEC;
1918c2ecf20Sopenharmony_ci	BDRI +=sr/2;
1928c2ecf20Sopenharmony_ci	BDRI /=sr;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (BDRI>255)
1958c2ecf20Sopenharmony_ci		BDRI=255;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	{
1988c2ecf20Sopenharmony_ci		u64 BDRX;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		BDRX=1<<(24+NDEC);
2018c2ecf20Sopenharmony_ci		BDRX*=sr;
2028c2ecf20Sopenharmony_ci		do_div(BDRX, state->sysclk);	/* BDRX/=SYSCLK; */
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		BDR=(s32)BDRX;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	dprintk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",
2078c2ecf20Sopenharmony_ci		sr, BDR, BDRI, NDEC);
2088c2ecf20Sopenharmony_ci	tda10023_writebit (state, 0x03, 0xc0, NDEC<<6);
2098c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x0a, BDR&255);
2108c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x0b, (BDR>>8)&255);
2118c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x0c, (BDR>>16)&31);
2128c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x0d, BDRI);
2138c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x3d, (SFIL<<7));
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int tda10023_init (struct dvb_frontend *fe)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
2208c2ecf20Sopenharmony_ci	u8 tda10023_inittab[] = {
2218c2ecf20Sopenharmony_ci/*        reg  mask val */
2228c2ecf20Sopenharmony_ci/* 000 */ 0x2a, 0xff, 0x02,  /* PLL3, Bypass, Power Down */
2238c2ecf20Sopenharmony_ci/* 003 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
2248c2ecf20Sopenharmony_ci/* 006 */ 0x2a, 0xff, 0x03,  /* PLL3, Bypass, Power Down */
2258c2ecf20Sopenharmony_ci/* 009 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
2268c2ecf20Sopenharmony_ci			   /* PLL1 */
2278c2ecf20Sopenharmony_ci/* 012 */ 0x28, 0xff, (state->pll_m-1),
2288c2ecf20Sopenharmony_ci			   /* PLL2 */
2298c2ecf20Sopenharmony_ci/* 015 */ 0x29, 0xff, ((state->pll_p-1)<<6)|(state->pll_n-1),
2308c2ecf20Sopenharmony_ci			   /* GPR FSAMPLING=1 */
2318c2ecf20Sopenharmony_ci/* 018 */ 0x00, 0xff, REG0_INIT_VAL,
2328c2ecf20Sopenharmony_ci/* 021 */ 0x2a, 0xff, 0x08,  /* PLL3 PSACLK=1 */
2338c2ecf20Sopenharmony_ci/* 024 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
2348c2ecf20Sopenharmony_ci/* 027 */ 0x1f, 0xff, 0x00,  /* RESET */
2358c2ecf20Sopenharmony_ci/* 030 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
2368c2ecf20Sopenharmony_ci/* 033 */ 0xe6, 0x0c, 0x04,  /* RSCFG_IND */
2378c2ecf20Sopenharmony_ci/* 036 */ 0x10, 0xc0, 0x80,  /* DECDVBCFG1 PBER=1 */
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/* 039 */ 0x0e, 0xff, 0x82,  /* GAIN1 */
2408c2ecf20Sopenharmony_ci/* 042 */ 0x03, 0x08, 0x08,  /* CLKCONF DYN=1 */
2418c2ecf20Sopenharmony_ci/* 045 */ 0x2e, 0xbf, 0x30,  /* AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1
2428c2ecf20Sopenharmony_ci				       PPWMTUN=0 PPWMIF=0 */
2438c2ecf20Sopenharmony_ci/* 048 */ 0x01, 0xff, 0x30,  /* AGCREF */
2448c2ecf20Sopenharmony_ci/* 051 */ 0x1e, 0x84, 0x84,  /* CONTROL SACLK_ON=1 */
2458c2ecf20Sopenharmony_ci/* 054 */ 0x1b, 0xff, 0xc8,  /* ADC TWOS=1 */
2468c2ecf20Sopenharmony_ci/* 057 */ 0x3b, 0xff, 0xff,  /* IFMAX */
2478c2ecf20Sopenharmony_ci/* 060 */ 0x3c, 0xff, 0x00,  /* IFMIN */
2488c2ecf20Sopenharmony_ci/* 063 */ 0x34, 0xff, 0x00,  /* PWMREF */
2498c2ecf20Sopenharmony_ci/* 066 */ 0x35, 0xff, 0xff,  /* TUNMAX */
2508c2ecf20Sopenharmony_ci/* 069 */ 0x36, 0xff, 0x00,  /* TUNMIN */
2518c2ecf20Sopenharmony_ci/* 072 */ 0x06, 0xff, 0x7f,  /* EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 */
2528c2ecf20Sopenharmony_ci/* 075 */ 0x1c, 0x30, 0x30,  /* EQCONF2 STEPALGO=SGNALGO=1 */
2538c2ecf20Sopenharmony_ci/* 078 */ 0x37, 0xff, 0xf6,  /* DELTAF_LSB */
2548c2ecf20Sopenharmony_ci/* 081 */ 0x38, 0xff, 0xff,  /* DELTAF_MSB */
2558c2ecf20Sopenharmony_ci/* 084 */ 0x02, 0xff, 0x93,  /* AGCCONF1  IFS=1 KAGCIF=2 KAGCTUN=3 */
2568c2ecf20Sopenharmony_ci/* 087 */ 0x2d, 0xff, 0xf6,  /* SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 */
2578c2ecf20Sopenharmony_ci/* 090 */ 0x04, 0x10, 0x00,  /* SWRAMP=1 */
2588c2ecf20Sopenharmony_ci/* 093 */ 0x12, 0xff, TDA10023_OUTPUT_MODE_PARALLEL_B, /*
2598c2ecf20Sopenharmony_ci				INTP1 POCLKP=1 FEL=1 MFS=0 */
2608c2ecf20Sopenharmony_ci/* 096 */ 0x2b, 0x01, 0xa1,  /* INTS1 */
2618c2ecf20Sopenharmony_ci/* 099 */ 0x20, 0xff, 0x04,  /* INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? */
2628c2ecf20Sopenharmony_ci/* 102 */ 0x2c, 0xff, 0x0d,  /* INTP/S TRIP=0 TRIS=0 */
2638c2ecf20Sopenharmony_ci/* 105 */ 0xc4, 0xff, 0x00,
2648c2ecf20Sopenharmony_ci/* 108 */ 0xc3, 0x30, 0x00,
2658c2ecf20Sopenharmony_ci/* 111 */ 0xb5, 0xff, 0x19,  /* ERAGC_THD */
2668c2ecf20Sopenharmony_ci/* 114 */ 0x00, 0x03, 0x01,  /* GPR, CLBS soft reset */
2678c2ecf20Sopenharmony_ci/* 117 */ 0x00, 0x03, 0x03,  /* GPR, CLBS soft reset */
2688c2ecf20Sopenharmony_ci/* 120 */ 0xff, 0x64, 0x00,  /* Sleep 100ms */
2698c2ecf20Sopenharmony_ci/* 123 */ 0xff, 0xff, 0xff
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_ci	dprintk("DVB: TDA10023(%d): init chip\n", fe->dvb->num);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* override default values if set in config */
2748c2ecf20Sopenharmony_ci	if (state->config->deltaf) {
2758c2ecf20Sopenharmony_ci		tda10023_inittab[80] = (state->config->deltaf & 0xff);
2768c2ecf20Sopenharmony_ci		tda10023_inittab[83] = (state->config->deltaf >> 8);
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (state->config->output_mode)
2808c2ecf20Sopenharmony_ci		tda10023_inittab[95] = state->config->output_mode;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	tda10023_writetab(state, tda10023_inittab);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistruct qam_params {
2888c2ecf20Sopenharmony_ci	u8 qam, lockthr, mseth, aref, agcrefnyq, eragnyq_thd;
2898c2ecf20Sopenharmony_ci};
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int tda10023_set_parameters(struct dvb_frontend *fe)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
2948c2ecf20Sopenharmony_ci	u32 delsys  = c->delivery_system;
2958c2ecf20Sopenharmony_ci	unsigned qam = c->modulation;
2968c2ecf20Sopenharmony_ci	bool is_annex_c;
2978c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
2988c2ecf20Sopenharmony_ci	static const struct qam_params qam_params[] = {
2998c2ecf20Sopenharmony_ci		/* Modulation  QAM    LOCKTHR   MSETH   AREF AGCREFNYQ ERAGCNYQ_THD */
3008c2ecf20Sopenharmony_ci		[QPSK]    = { (5<<2),  0x78,    0x8c,   0x96,   0x78,   0x4c  },
3018c2ecf20Sopenharmony_ci		[QAM_16]  = { (0<<2),  0x87,    0xa2,   0x91,   0x8c,   0x57  },
3028c2ecf20Sopenharmony_ci		[QAM_32]  = { (1<<2),  0x64,    0x74,   0x96,   0x8c,   0x57  },
3038c2ecf20Sopenharmony_ci		[QAM_64]  = { (2<<2),  0x46,    0x43,   0x6a,   0x6a,   0x44  },
3048c2ecf20Sopenharmony_ci		[QAM_128] = { (3<<2),  0x36,    0x34,   0x7e,   0x78,   0x4c  },
3058c2ecf20Sopenharmony_ci		[QAM_256] = { (4<<2),  0x26,    0x23,   0x6c,   0x5c,   0x3c  },
3068c2ecf20Sopenharmony_ci	};
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	switch (delsys) {
3098c2ecf20Sopenharmony_ci	case SYS_DVBC_ANNEX_A:
3108c2ecf20Sopenharmony_ci		is_annex_c = false;
3118c2ecf20Sopenharmony_ci		break;
3128c2ecf20Sopenharmony_ci	case SYS_DVBC_ANNEX_C:
3138c2ecf20Sopenharmony_ci		is_annex_c = true;
3148c2ecf20Sopenharmony_ci		break;
3158c2ecf20Sopenharmony_ci	default:
3168c2ecf20Sopenharmony_ci		return -EINVAL;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/*
3208c2ecf20Sopenharmony_ci	 * gcc optimizes the code below the same way as it would code:
3218c2ecf20Sopenharmony_ci	 *		 "if (qam > 5) return -EINVAL;"
3228c2ecf20Sopenharmony_ci	 * Yet, the code is clearer, as it shows what QAM standards are
3238c2ecf20Sopenharmony_ci	 * supported by the driver, and avoids the usage of magic numbers on
3248c2ecf20Sopenharmony_ci	 * it.
3258c2ecf20Sopenharmony_ci	 */
3268c2ecf20Sopenharmony_ci	switch (qam) {
3278c2ecf20Sopenharmony_ci	case QPSK:
3288c2ecf20Sopenharmony_ci	case QAM_16:
3298c2ecf20Sopenharmony_ci	case QAM_32:
3308c2ecf20Sopenharmony_ci	case QAM_64:
3318c2ecf20Sopenharmony_ci	case QAM_128:
3328c2ecf20Sopenharmony_ci	case QAM_256:
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	default:
3358c2ecf20Sopenharmony_ci		return -EINVAL;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params) {
3398c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
3408c2ecf20Sopenharmony_ci		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	tda10023_set_symbolrate(state, c->symbol_rate);
3448c2ecf20Sopenharmony_ci	tda10023_writereg(state, 0x05, qam_params[qam].lockthr);
3458c2ecf20Sopenharmony_ci	tda10023_writereg(state, 0x08, qam_params[qam].mseth);
3468c2ecf20Sopenharmony_ci	tda10023_writereg(state, 0x09, qam_params[qam].aref);
3478c2ecf20Sopenharmony_ci	tda10023_writereg(state, 0xb4, qam_params[qam].agcrefnyq);
3488c2ecf20Sopenharmony_ci	tda10023_writereg(state, 0xb6, qam_params[qam].eragnyq_thd);
3498c2ecf20Sopenharmony_ci#if 0
3508c2ecf20Sopenharmony_ci	tda10023_writereg(state, 0x04, (c->inversion ? 0x12 : 0x32));
3518c2ecf20Sopenharmony_ci	tda10023_writebit(state, 0x04, 0x60, (c->inversion ? 0 : 0x20));
3528c2ecf20Sopenharmony_ci#endif
3538c2ecf20Sopenharmony_ci	tda10023_writebit(state, 0x04, 0x40, 0x40);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (is_annex_c)
3568c2ecf20Sopenharmony_ci		tda10023_writebit(state, 0x3d, 0xfc, 0x03);
3578c2ecf20Sopenharmony_ci	else
3588c2ecf20Sopenharmony_ci		tda10023_writebit(state, 0x3d, 0xfc, 0x02);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	tda10023_setup_reg0(state, qam_params[qam].qam);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return 0;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int tda10023_read_status(struct dvb_frontend *fe,
3668c2ecf20Sopenharmony_ci				enum fe_status *status)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
3698c2ecf20Sopenharmony_ci	int sync;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	*status = 0;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	//0x11[1] == CARLOCK -> Carrier locked
3748c2ecf20Sopenharmony_ci	//0x11[2] == FSYNC -> Frame synchronisation
3758c2ecf20Sopenharmony_ci	//0x11[3] == FEL -> Front End locked
3768c2ecf20Sopenharmony_ci	//0x11[6] == NODVB -> DVB Mode Information
3778c2ecf20Sopenharmony_ci	sync = tda10023_readreg (state, 0x11);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (sync & 2)
3808c2ecf20Sopenharmony_ci		*status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (sync & 4)
3838c2ecf20Sopenharmony_ci		*status |= FE_HAS_SYNC|FE_HAS_VITERBI;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (sync & 8)
3868c2ecf20Sopenharmony_ci		*status |= FE_HAS_LOCK;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic int tda10023_read_ber(struct dvb_frontend* fe, u32* ber)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
3948c2ecf20Sopenharmony_ci	u8 a,b,c;
3958c2ecf20Sopenharmony_ci	a=tda10023_readreg(state, 0x14);
3968c2ecf20Sopenharmony_ci	b=tda10023_readreg(state, 0x15);
3978c2ecf20Sopenharmony_ci	c=tda10023_readreg(state, 0x16)&0xf;
3988c2ecf20Sopenharmony_ci	tda10023_writebit (state, 0x10, 0xc0, 0x00);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	*ber = a | (b<<8)| (c<<16);
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int tda10023_read_signal_strength(struct dvb_frontend* fe, u16* strength)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
4078c2ecf20Sopenharmony_ci	u8 ifgain=tda10023_readreg(state, 0x2f);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	u16 gain = ((255-tda10023_readreg(state, 0x17))) + (255-ifgain)/16;
4108c2ecf20Sopenharmony_ci	// Max raw value is about 0xb0 -> Normalize to >0xf0 after 0x90
4118c2ecf20Sopenharmony_ci	if (gain>0x90)
4128c2ecf20Sopenharmony_ci		gain=gain+2*(gain-0x90);
4138c2ecf20Sopenharmony_ci	if (gain>255)
4148c2ecf20Sopenharmony_ci		gain=255;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	*strength = (gain<<8)|gain;
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int tda10023_read_snr(struct dvb_frontend* fe, u16* snr)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	u8 quality = ~tda10023_readreg(state, 0x18);
4258c2ecf20Sopenharmony_ci	*snr = (quality << 8) | quality;
4268c2ecf20Sopenharmony_ci	return 0;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic int tda10023_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
4328c2ecf20Sopenharmony_ci	u8 a,b,c,d;
4338c2ecf20Sopenharmony_ci	a= tda10023_readreg (state, 0x74);
4348c2ecf20Sopenharmony_ci	b= tda10023_readreg (state, 0x75);
4358c2ecf20Sopenharmony_ci	c= tda10023_readreg (state, 0x76);
4368c2ecf20Sopenharmony_ci	d= tda10023_readreg (state, 0x77);
4378c2ecf20Sopenharmony_ci	*ucblocks = a | (b<<8)|(c<<16)|(d<<24);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	tda10023_writebit (state, 0x10, 0x20,0x00);
4408c2ecf20Sopenharmony_ci	tda10023_writebit (state, 0x10, 0x20,0x20);
4418c2ecf20Sopenharmony_ci	tda10023_writebit (state, 0x13, 0x01, 0x00);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return 0;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic int tda10023_get_frontend(struct dvb_frontend *fe,
4478c2ecf20Sopenharmony_ci				 struct dtv_frontend_properties *p)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
4508c2ecf20Sopenharmony_ci	int sync,inv;
4518c2ecf20Sopenharmony_ci	s8 afc = 0;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	sync = tda10023_readreg(state, 0x11);
4548c2ecf20Sopenharmony_ci	afc = tda10023_readreg(state, 0x19);
4558c2ecf20Sopenharmony_ci	inv = tda10023_readreg(state, 0x04);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (verbose) {
4588c2ecf20Sopenharmony_ci		/* AFC only valid when carrier has been recovered */
4598c2ecf20Sopenharmony_ci		printk(sync & 2 ? "DVB: TDA10023(%d): AFC (%d) %dHz\n" :
4608c2ecf20Sopenharmony_ci				  "DVB: TDA10023(%d): [AFC (%d) %dHz]\n",
4618c2ecf20Sopenharmony_ci			state->frontend.dvb->num, afc,
4628c2ecf20Sopenharmony_ci		       -((s32)p->symbol_rate * afc) >> 10);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	p->inversion = (inv&0x20?0:1);
4668c2ecf20Sopenharmony_ci	p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	p->fec_inner = FEC_NONE;
4698c2ecf20Sopenharmony_ci	p->frequency = ((p->frequency + 31250) / 62500) * 62500;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (sync & 2)
4728c2ecf20Sopenharmony_ci		p->frequency -= ((s32)p->symbol_rate * afc) >> 10;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	return 0;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic int tda10023_sleep(struct dvb_frontend* fe)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x1b, 0x02);  /* pdown ADC */
4828c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x00, 0x80);  /* standby */
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	return 0;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic int tda10023_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (enable) {
4928c2ecf20Sopenharmony_ci		lock_tuner(state);
4938c2ecf20Sopenharmony_ci	} else {
4948c2ecf20Sopenharmony_ci		unlock_tuner(state);
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci	return 0;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic void tda10023_release(struct dvb_frontend* fe)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct tda10023_state* state = fe->demodulator_priv;
5028c2ecf20Sopenharmony_ci	kfree(state);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops tda10023_ops;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistruct dvb_frontend *tda10023_attach(const struct tda10023_config *config,
5088c2ecf20Sopenharmony_ci				     struct i2c_adapter *i2c,
5098c2ecf20Sopenharmony_ci				     u8 pwm)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	struct tda10023_state* state = NULL;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* allocate memory for the internal state */
5148c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL);
5158c2ecf20Sopenharmony_ci	if (state == NULL) goto error;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* setup the state */
5188c2ecf20Sopenharmony_ci	state->config = config;
5198c2ecf20Sopenharmony_ci	state->i2c = i2c;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* wakeup if in standby */
5228c2ecf20Sopenharmony_ci	tda10023_writereg (state, 0x00, 0x33);
5238c2ecf20Sopenharmony_ci	/* check if the demod is there */
5248c2ecf20Sopenharmony_ci	if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* create dvb_frontend */
5278c2ecf20Sopenharmony_ci	memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
5288c2ecf20Sopenharmony_ci	state->pwm = pwm;
5298c2ecf20Sopenharmony_ci	state->reg0 = REG0_INIT_VAL;
5308c2ecf20Sopenharmony_ci	if (state->config->xtal) {
5318c2ecf20Sopenharmony_ci		state->xtal  = state->config->xtal;
5328c2ecf20Sopenharmony_ci		state->pll_m = state->config->pll_m;
5338c2ecf20Sopenharmony_ci		state->pll_p = state->config->pll_p;
5348c2ecf20Sopenharmony_ci		state->pll_n = state->config->pll_n;
5358c2ecf20Sopenharmony_ci	} else {
5368c2ecf20Sopenharmony_ci		/* set default values if not defined in config */
5378c2ecf20Sopenharmony_ci		state->xtal  = 28920000;
5388c2ecf20Sopenharmony_ci		state->pll_m = 8;
5398c2ecf20Sopenharmony_ci		state->pll_p = 4;
5408c2ecf20Sopenharmony_ci		state->pll_n = 1;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* calc sysclk */
5448c2ecf20Sopenharmony_ci	state->sysclk = (state->xtal * state->pll_m / \
5458c2ecf20Sopenharmony_ci			(state->pll_n * state->pll_p));
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	state->frontend.ops.info.symbol_rate_min = (state->sysclk/2)/64;
5488c2ecf20Sopenharmony_ci	state->frontend.ops.info.symbol_rate_max = (state->sysclk/2)/4;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	dprintk("DVB: TDA10023 %s: xtal:%d pll_m:%d pll_p:%d pll_n:%d\n",
5518c2ecf20Sopenharmony_ci		__func__, state->xtal, state->pll_m, state->pll_p,
5528c2ecf20Sopenharmony_ci		state->pll_n);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	state->frontend.demodulator_priv = state;
5558c2ecf20Sopenharmony_ci	return &state->frontend;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cierror:
5588c2ecf20Sopenharmony_ci	kfree(state);
5598c2ecf20Sopenharmony_ci	return NULL;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops tda10023_ops = {
5638c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C },
5648c2ecf20Sopenharmony_ci	.info = {
5658c2ecf20Sopenharmony_ci		.name = "Philips TDA10023 DVB-C",
5668c2ecf20Sopenharmony_ci		.frequency_min_hz =  47 * MHz,
5678c2ecf20Sopenharmony_ci		.frequency_max_hz = 862 * MHz,
5688c2ecf20Sopenharmony_ci		.frequency_stepsize_hz = 62500,
5698c2ecf20Sopenharmony_ci		.symbol_rate_min = 0,  /* set in tda10023_attach */
5708c2ecf20Sopenharmony_ci		.symbol_rate_max = 0,  /* set in tda10023_attach */
5718c2ecf20Sopenharmony_ci		.caps = 0x400 | //FE_CAN_QAM_4
5728c2ecf20Sopenharmony_ci			FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
5738c2ecf20Sopenharmony_ci			FE_CAN_QAM_128 | FE_CAN_QAM_256 |
5748c2ecf20Sopenharmony_ci			FE_CAN_FEC_AUTO
5758c2ecf20Sopenharmony_ci	},
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	.release = tda10023_release,
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	.init = tda10023_init,
5808c2ecf20Sopenharmony_ci	.sleep = tda10023_sleep,
5818c2ecf20Sopenharmony_ci	.i2c_gate_ctrl = tda10023_i2c_gate_ctrl,
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	.set_frontend = tda10023_set_parameters,
5848c2ecf20Sopenharmony_ci	.get_frontend = tda10023_get_frontend,
5858c2ecf20Sopenharmony_ci	.read_status = tda10023_read_status,
5868c2ecf20Sopenharmony_ci	.read_ber = tda10023_read_ber,
5878c2ecf20Sopenharmony_ci	.read_signal_strength = tda10023_read_signal_strength,
5888c2ecf20Sopenharmony_ci	.read_snr = tda10023_read_snr,
5898c2ecf20Sopenharmony_ci	.read_ucblocks = tda10023_read_ucblocks,
5908c2ecf20Sopenharmony_ci};
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver");
5948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Georg Acher, Hartmut Birr");
5958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tda10023_attach);
598