18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci driver for LSI L64781 COFDM demodulator 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH 68c2ecf20Sopenharmony_ci Marko Kohtala <marko.kohtala@luukku.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci*/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 178c2ecf20Sopenharmony_ci#include "l64781.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct l64781_state { 218c2ecf20Sopenharmony_ci struct i2c_adapter* i2c; 228c2ecf20Sopenharmony_ci const struct l64781_config* config; 238c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* private demodulator data */ 268c2ecf20Sopenharmony_ci unsigned int first:1; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define dprintk(args...) \ 308c2ecf20Sopenharmony_ci do { \ 318c2ecf20Sopenharmony_ci if (debug) printk(KERN_DEBUG "l64781: " args); \ 328c2ecf20Sopenharmony_ci } while (0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int debug; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int l64781_writereg (struct l64781_state* state, u8 reg, u8 data) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int ret; 438c2ecf20Sopenharmony_ci u8 buf [] = { reg, data }; 448c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) 478c2ecf20Sopenharmony_ci dprintk ("%s: write_reg error (reg == %02x) = %02x!\n", 488c2ecf20Sopenharmony_ci __func__, reg, ret); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return (ret != 1) ? -1 : 0; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int l64781_readreg (struct l64781_state* state, u8 reg) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int ret; 568c2ecf20Sopenharmony_ci u8 b0 [] = { reg }; 578c2ecf20Sopenharmony_ci u8 b1 [] = { 0 }; 588c2ecf20Sopenharmony_ci struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, 598c2ecf20Sopenharmony_ci { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, msg, 2); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (ret != 2) return ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return b1[0]; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void apply_tps (struct l64781_state* state) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci l64781_writereg (state, 0x2a, 0x00); 718c2ecf20Sopenharmony_ci l64781_writereg (state, 0x2a, 0x01); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* This here is a little bit questionable because it enables 748c2ecf20Sopenharmony_ci the automatic update of TPS registers. I think we'd need to 758c2ecf20Sopenharmony_ci handle the IRQ from FE to update some other registers as 768c2ecf20Sopenharmony_ci well, or at least implement some magic to tuning to correct 778c2ecf20Sopenharmony_ci to the TPS received from transmission. */ 788c2ecf20Sopenharmony_ci l64781_writereg (state, 0x2a, 0x02); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void reset_afc (struct l64781_state* state) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for 858c2ecf20Sopenharmony_ci timing offset */ 868c2ecf20Sopenharmony_ci l64781_writereg (state, 0x07, 0x9e); /* stall AFC */ 878c2ecf20Sopenharmony_ci l64781_writereg (state, 0x08, 0); /* AFC INIT FREQ */ 888c2ecf20Sopenharmony_ci l64781_writereg (state, 0x09, 0); 898c2ecf20Sopenharmony_ci l64781_writereg (state, 0x0a, 0); 908c2ecf20Sopenharmony_ci l64781_writereg (state, 0x07, 0x8e); 918c2ecf20Sopenharmony_ci l64781_writereg (state, 0x0e, 0); /* AGC gain to zero in beginning */ 928c2ecf20Sopenharmony_ci l64781_writereg (state, 0x11, 0x80); /* stall TIM */ 938c2ecf20Sopenharmony_ci l64781_writereg (state, 0x10, 0); /* TIM_OFFSET_LSB */ 948c2ecf20Sopenharmony_ci l64781_writereg (state, 0x12, 0); 958c2ecf20Sopenharmony_ci l64781_writereg (state, 0x13, 0); 968c2ecf20Sopenharmony_ci l64781_writereg (state, 0x11, 0x00); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int reset_and_configure (struct l64781_state* state) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci u8 buf [] = { 0x06 }; 1028c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 }; 1038c2ecf20Sopenharmony_ci // NOTE: this is correct in writing to address 0x00 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int apply_frontend_param(struct dvb_frontend *fe) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 1118c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 1128c2ecf20Sopenharmony_ci /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */ 1138c2ecf20Sopenharmony_ci static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 }; 1148c2ecf20Sopenharmony_ci /* QPSK, QAM_16, QAM_64 */ 1158c2ecf20Sopenharmony_ci static const u8 qam_tab [] = { 2, 4, 0, 6 }; 1168c2ecf20Sopenharmony_ci static const u8 guard_tab [] = { 1, 2, 4, 8 }; 1178c2ecf20Sopenharmony_ci /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */ 1188c2ecf20Sopenharmony_ci static const u32 ppm = 8000; 1198c2ecf20Sopenharmony_ci u32 ddfs_offset_fixed; 1208c2ecf20Sopenharmony_ci/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */ 1218c2ecf20Sopenharmony_ci/* bw_tab[p->bandWidth]<<10)/15625; */ 1228c2ecf20Sopenharmony_ci u32 init_freq; 1238c2ecf20Sopenharmony_ci u32 spi_bias; 1248c2ecf20Sopenharmony_ci u8 val0x04; 1258c2ecf20Sopenharmony_ci u8 val0x05; 1268c2ecf20Sopenharmony_ci u8 val0x06; 1278c2ecf20Sopenharmony_ci int bw; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci switch (p->bandwidth_hz) { 1308c2ecf20Sopenharmony_ci case 8000000: 1318c2ecf20Sopenharmony_ci bw = 8; 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci case 7000000: 1348c2ecf20Sopenharmony_ci bw = 7; 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case 6000000: 1378c2ecf20Sopenharmony_ci bw = 6; 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci default: 1408c2ecf20Sopenharmony_ci return -EINVAL; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 1448c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 1458c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (p->inversion != INVERSION_ON && 1498c2ecf20Sopenharmony_ci p->inversion != INVERSION_OFF) 1508c2ecf20Sopenharmony_ci return -EINVAL; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 && 1538c2ecf20Sopenharmony_ci p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 && 1548c2ecf20Sopenharmony_ci p->code_rate_HP != FEC_7_8) 1558c2ecf20Sopenharmony_ci return -EINVAL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (p->hierarchy != HIERARCHY_NONE && 1588c2ecf20Sopenharmony_ci (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 && 1598c2ecf20Sopenharmony_ci p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 && 1608c2ecf20Sopenharmony_ci p->code_rate_LP != FEC_7_8)) 1618c2ecf20Sopenharmony_ci return -EINVAL; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (p->modulation != QPSK && p->modulation != QAM_16 && 1648c2ecf20Sopenharmony_ci p->modulation != QAM_64) 1658c2ecf20Sopenharmony_ci return -EINVAL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (p->transmission_mode != TRANSMISSION_MODE_2K && 1688c2ecf20Sopenharmony_ci p->transmission_mode != TRANSMISSION_MODE_8K) 1698c2ecf20Sopenharmony_ci return -EINVAL; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if ((int)p->guard_interval < GUARD_INTERVAL_1_32 || 1728c2ecf20Sopenharmony_ci p->guard_interval > GUARD_INTERVAL_1_4) 1738c2ecf20Sopenharmony_ci return -EINVAL; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if ((int)p->hierarchy < HIERARCHY_NONE || 1768c2ecf20Sopenharmony_ci p->hierarchy > HIERARCHY_4) 1778c2ecf20Sopenharmony_ci return -EINVAL; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ddfs_offset_fixed = 0x4000-(ppm<<16)/bw/1000000; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* This works up to 20000 ppm, it overflows if too large ppm! */ 1828c2ecf20Sopenharmony_ci init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) / 1838c2ecf20Sopenharmony_ci bw & 0xFFFFFF); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* SPI bias calculation is slightly modified to fit in 32bit */ 1868c2ecf20Sopenharmony_ci /* will work for high ppm only... */ 1878c2ecf20Sopenharmony_ci spi_bias = 378 * (1 << 10); 1888c2ecf20Sopenharmony_ci spi_bias *= 16; 1898c2ecf20Sopenharmony_ci spi_bias *= bw; 1908c2ecf20Sopenharmony_ci spi_bias *= qam_tab[p->modulation]; 1918c2ecf20Sopenharmony_ci spi_bias /= p->code_rate_HP + 1; 1928c2ecf20Sopenharmony_ci spi_bias /= (guard_tab[p->guard_interval] + 32); 1938c2ecf20Sopenharmony_ci spi_bias *= 1000; 1948c2ecf20Sopenharmony_ci spi_bias /= 1000 + ppm/1000; 1958c2ecf20Sopenharmony_ci spi_bias *= p->code_rate_HP; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci val0x04 = (p->transmission_mode << 2) | p->guard_interval; 1988c2ecf20Sopenharmony_ci val0x05 = fec_tab[p->code_rate_HP]; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (p->hierarchy != HIERARCHY_NONE) 2018c2ecf20Sopenharmony_ci val0x05 |= (p->code_rate_LP - FEC_1_2) << 3; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci val0x06 = (p->hierarchy << 2) | p->modulation; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci l64781_writereg (state, 0x04, val0x04); 2068c2ecf20Sopenharmony_ci l64781_writereg (state, 0x05, val0x05); 2078c2ecf20Sopenharmony_ci l64781_writereg (state, 0x06, val0x06); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci reset_afc (state); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */ 2128c2ecf20Sopenharmony_ci l64781_writereg (state, 0x15, 2138c2ecf20Sopenharmony_ci p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3); 2148c2ecf20Sopenharmony_ci l64781_writereg (state, 0x16, init_freq & 0xff); 2158c2ecf20Sopenharmony_ci l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff); 2168c2ecf20Sopenharmony_ci l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci l64781_writereg (state, 0x1b, spi_bias & 0xff); 2198c2ecf20Sopenharmony_ci l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff); 2208c2ecf20Sopenharmony_ci l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) | 2218c2ecf20Sopenharmony_ci (p->inversion == INVERSION_ON ? 0x80 : 0x00)); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff); 2248c2ecf20Sopenharmony_ci l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci l64781_readreg (state, 0x00); /* clear interrupt registers... */ 2278c2ecf20Sopenharmony_ci l64781_readreg (state, 0x01); /* dto. */ 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci apply_tps (state); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int get_frontend(struct dvb_frontend *fe, 2358c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 2388c2ecf20Sopenharmony_ci int tmp; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci tmp = l64781_readreg(state, 0x04); 2428c2ecf20Sopenharmony_ci switch(tmp & 3) { 2438c2ecf20Sopenharmony_ci case 0: 2448c2ecf20Sopenharmony_ci p->guard_interval = GUARD_INTERVAL_1_32; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case 1: 2478c2ecf20Sopenharmony_ci p->guard_interval = GUARD_INTERVAL_1_16; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case 2: 2508c2ecf20Sopenharmony_ci p->guard_interval = GUARD_INTERVAL_1_8; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case 3: 2538c2ecf20Sopenharmony_ci p->guard_interval = GUARD_INTERVAL_1_4; 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci switch((tmp >> 2) & 3) { 2578c2ecf20Sopenharmony_ci case 0: 2588c2ecf20Sopenharmony_ci p->transmission_mode = TRANSMISSION_MODE_2K; 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci case 1: 2618c2ecf20Sopenharmony_ci p->transmission_mode = TRANSMISSION_MODE_8K; 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci default: 2648c2ecf20Sopenharmony_ci printk(KERN_WARNING "Unexpected value for transmission_mode\n"); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci tmp = l64781_readreg(state, 0x05); 2688c2ecf20Sopenharmony_ci switch(tmp & 7) { 2698c2ecf20Sopenharmony_ci case 0: 2708c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_1_2; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci case 1: 2738c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_2_3; 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case 2: 2768c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_3_4; 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci case 3: 2798c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_5_6; 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci case 4: 2828c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_7_8; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci default: 2858c2ecf20Sopenharmony_ci printk("Unexpected value for code_rate_HP\n"); 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci switch((tmp >> 3) & 7) { 2888c2ecf20Sopenharmony_ci case 0: 2898c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_1_2; 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci case 1: 2928c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_2_3; 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci case 2: 2958c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_3_4; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case 3: 2988c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_5_6; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci case 4: 3018c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_7_8; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci default: 3048c2ecf20Sopenharmony_ci printk("Unexpected value for code_rate_LP\n"); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci tmp = l64781_readreg(state, 0x06); 3088c2ecf20Sopenharmony_ci switch(tmp & 3) { 3098c2ecf20Sopenharmony_ci case 0: 3108c2ecf20Sopenharmony_ci p->modulation = QPSK; 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci case 1: 3138c2ecf20Sopenharmony_ci p->modulation = QAM_16; 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci case 2: 3168c2ecf20Sopenharmony_ci p->modulation = QAM_64; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci default: 3198c2ecf20Sopenharmony_ci printk(KERN_WARNING "Unexpected value for modulation\n"); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci switch((tmp >> 2) & 7) { 3228c2ecf20Sopenharmony_ci case 0: 3238c2ecf20Sopenharmony_ci p->hierarchy = HIERARCHY_NONE; 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci case 1: 3268c2ecf20Sopenharmony_ci p->hierarchy = HIERARCHY_1; 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci case 2: 3298c2ecf20Sopenharmony_ci p->hierarchy = HIERARCHY_2; 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci case 3: 3328c2ecf20Sopenharmony_ci p->hierarchy = HIERARCHY_4; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci default: 3358c2ecf20Sopenharmony_ci printk("Unexpected value for hierarchy\n"); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci tmp = l64781_readreg (state, 0x1d); 3408c2ecf20Sopenharmony_ci p->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci tmp = (int) (l64781_readreg (state, 0x08) | 3438c2ecf20Sopenharmony_ci (l64781_readreg (state, 0x09) << 8) | 3448c2ecf20Sopenharmony_ci (l64781_readreg (state, 0x0a) << 16)); 3458c2ecf20Sopenharmony_ci p->frequency += tmp; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int l64781_read_status(struct dvb_frontend *fe, enum fe_status *status) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 3538c2ecf20Sopenharmony_ci int sync = l64781_readreg (state, 0x32); 3548c2ecf20Sopenharmony_ci int gain = l64781_readreg (state, 0x0e); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci l64781_readreg (state, 0x00); /* clear interrupt registers... */ 3578c2ecf20Sopenharmony_ci l64781_readreg (state, 0x01); /* dto. */ 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci *status = 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (gain > 5) 3628c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (sync & 0x02) /* VCXO locked, this criteria should be ok */ 3658c2ecf20Sopenharmony_ci *status |= FE_HAS_CARRIER; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (sync & 0x20) 3688c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (sync & 0x40) 3718c2ecf20Sopenharmony_ci *status |= FE_HAS_SYNC; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (sync == 0x7f) 3748c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int l64781_read_ber(struct dvb_frontend* fe, u32* ber) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* XXX FIXME: set up counting period (reg 0x26...0x28) 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci *ber = l64781_readreg (state, 0x39) 3868c2ecf20Sopenharmony_ci | (l64781_readreg (state, 0x3a) << 8); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci u8 gain = l64781_readreg (state, 0x0e); 3968c2ecf20Sopenharmony_ci *signal_strength = (gain << 8) | gain; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int l64781_read_snr(struct dvb_frontend* fe, u16* snr) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci u8 avg_quality = 0xff - l64781_readreg (state, 0x33); 4068c2ecf20Sopenharmony_ci *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/ 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci *ucblocks = l64781_readreg (state, 0x37) 4168c2ecf20Sopenharmony_ci | (l64781_readreg (state, 0x38) << 8); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int l64781_sleep(struct dvb_frontend* fe) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Power down */ 4268c2ecf20Sopenharmony_ci return l64781_writereg (state, 0x3e, 0x5a); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int l64781_init(struct dvb_frontend* fe) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci reset_and_configure (state); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Power up */ 4368c2ecf20Sopenharmony_ci l64781_writereg (state, 0x3e, 0xa5); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Reset hard */ 4398c2ecf20Sopenharmony_ci l64781_writereg (state, 0x2a, 0x04); 4408c2ecf20Sopenharmony_ci l64781_writereg (state, 0x2a, 0x00); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Set tuner specific things */ 4438c2ecf20Sopenharmony_ci /* AFC_POL, set also in reset_afc */ 4448c2ecf20Sopenharmony_ci l64781_writereg (state, 0x07, 0x8e); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Use internal ADC */ 4478c2ecf20Sopenharmony_ci l64781_writereg (state, 0x0b, 0x81); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* AGC loop gain, and polarity is positive */ 4508c2ecf20Sopenharmony_ci l64781_writereg (state, 0x0c, 0x84); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* Internal ADC outputs two's complement */ 4538c2ecf20Sopenharmony_ci l64781_writereg (state, 0x0d, 0x8c); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* With ppm=8000, it seems the DTR_SENSITIVITY will result in 4568c2ecf20Sopenharmony_ci value of 2 with all possible bandwidths and guard 4578c2ecf20Sopenharmony_ci intervals, which is the initial value anyway. */ 4588c2ecf20Sopenharmony_ci /*l64781_writereg (state, 0x19, 0x92);*/ 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Everything is two's complement, soft bit and CSI_OUT too */ 4618c2ecf20Sopenharmony_ci l64781_writereg (state, 0x1e, 0x09); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* delay a bit after first init attempt */ 4648c2ecf20Sopenharmony_ci if (state->first) { 4658c2ecf20Sopenharmony_ci state->first = 0; 4668c2ecf20Sopenharmony_ci msleep(200); 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int l64781_get_tune_settings(struct dvb_frontend* fe, 4738c2ecf20Sopenharmony_ci struct dvb_frontend_tune_settings* fesettings) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci fesettings->min_delay_ms = 4000; 4768c2ecf20Sopenharmony_ci fesettings->step_size = 0; 4778c2ecf20Sopenharmony_ci fesettings->max_drift = 0; 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void l64781_release(struct dvb_frontend* fe) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct l64781_state* state = fe->demodulator_priv; 4848c2ecf20Sopenharmony_ci kfree(state); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops l64781_ops; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistruct dvb_frontend* l64781_attach(const struct l64781_config* config, 4908c2ecf20Sopenharmony_ci struct i2c_adapter* i2c) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct l64781_state* state = NULL; 4938c2ecf20Sopenharmony_ci int reg0x3e = -1; 4948c2ecf20Sopenharmony_ci u8 b0 [] = { 0x1a }; 4958c2ecf20Sopenharmony_ci u8 b1 [] = { 0x00 }; 4968c2ecf20Sopenharmony_ci struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 }, 4978c2ecf20Sopenharmony_ci { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 5008c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL); 5018c2ecf20Sopenharmony_ci if (state == NULL) goto error; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* setup the state */ 5048c2ecf20Sopenharmony_ci state->config = config; 5058c2ecf20Sopenharmony_ci state->i2c = i2c; 5068c2ecf20Sopenharmony_ci state->first = 1; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* 5098c2ecf20Sopenharmony_ci * the L64781 won't show up before we send the reset_and_configure() 5108c2ecf20Sopenharmony_ci * broadcast. If nothing responds there is no L64781 on the bus... 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci if (reset_and_configure(state) < 0) { 5138c2ecf20Sopenharmony_ci dprintk("No response to reset and configure broadcast...\n"); 5148c2ecf20Sopenharmony_ci goto error; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* The chip always responds to reads */ 5188c2ecf20Sopenharmony_ci if (i2c_transfer(state->i2c, msg, 2) != 2) { 5198c2ecf20Sopenharmony_ci dprintk("No response to read on I2C bus\n"); 5208c2ecf20Sopenharmony_ci goto error; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* Save current register contents for bailout */ 5248c2ecf20Sopenharmony_ci reg0x3e = l64781_readreg(state, 0x3e); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Reading the POWER_DOWN register always returns 0 */ 5278c2ecf20Sopenharmony_ci if (reg0x3e != 0) { 5288c2ecf20Sopenharmony_ci dprintk("Device doesn't look like L64781\n"); 5298c2ecf20Sopenharmony_ci goto error; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* Turn the chip off */ 5338c2ecf20Sopenharmony_ci l64781_writereg (state, 0x3e, 0x5a); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Responds to all reads with 0 */ 5368c2ecf20Sopenharmony_ci if (l64781_readreg(state, 0x1a) != 0) { 5378c2ecf20Sopenharmony_ci dprintk("Read 1 returned unexpected value\n"); 5388c2ecf20Sopenharmony_ci goto error; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* Turn the chip on */ 5428c2ecf20Sopenharmony_ci l64781_writereg (state, 0x3e, 0xa5); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Responds with register default value */ 5458c2ecf20Sopenharmony_ci if (l64781_readreg(state, 0x1a) != 0xa1) { 5468c2ecf20Sopenharmony_ci dprintk("Read 2 returned unexpected value\n"); 5478c2ecf20Sopenharmony_ci goto error; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* create dvb_frontend */ 5518c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &l64781_ops, sizeof(struct dvb_frontend_ops)); 5528c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 5538c2ecf20Sopenharmony_ci return &state->frontend; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cierror: 5568c2ecf20Sopenharmony_ci if (reg0x3e >= 0) 5578c2ecf20Sopenharmony_ci l64781_writereg (state, 0x3e, reg0x3e); /* restore reg 0x3e */ 5588c2ecf20Sopenharmony_ci kfree(state); 5598c2ecf20Sopenharmony_ci return NULL; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops l64781_ops = { 5638c2ecf20Sopenharmony_ci .delsys = { SYS_DVBT }, 5648c2ecf20Sopenharmony_ci .info = { 5658c2ecf20Sopenharmony_ci .name = "LSI L64781 DVB-T", 5668c2ecf20Sopenharmony_ci /* .frequency_min_hz = ???,*/ 5678c2ecf20Sopenharmony_ci /* .frequency_max_hz = ???,*/ 5688c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 166666, 5698c2ecf20Sopenharmony_ci /* .symbol_rate_tolerance = ???,*/ 5708c2ecf20Sopenharmony_ci .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 5718c2ecf20Sopenharmony_ci FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | 5728c2ecf20Sopenharmony_ci FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | 5738c2ecf20Sopenharmony_ci FE_CAN_MUTE_TS 5748c2ecf20Sopenharmony_ci }, 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci .release = l64781_release, 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci .init = l64781_init, 5798c2ecf20Sopenharmony_ci .sleep = l64781_sleep, 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci .set_frontend = apply_frontend_param, 5828c2ecf20Sopenharmony_ci .get_frontend = get_frontend, 5838c2ecf20Sopenharmony_ci .get_tune_settings = l64781_get_tune_settings, 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci .read_status = l64781_read_status, 5868c2ecf20Sopenharmony_ci .read_ber = l64781_read_ber, 5878c2ecf20Sopenharmony_ci .read_signal_strength = l64781_read_signal_strength, 5888c2ecf20Sopenharmony_ci .read_snr = l64781_read_snr, 5898c2ecf20Sopenharmony_ci .read_ucblocks = l64781_read_ucblocks, 5908c2ecf20Sopenharmony_ci}; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver"); 5938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Holger Waechtler, Marko Kohtala"); 5948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(l64781_attach); 597