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