18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Conexant 22702 DVB OFDM demodulator driver 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci based on: 68c2ecf20Sopenharmony_ci Alps TDMB7 DVB OFDM demodulator driver 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci Copyright (C) 2001-2002 Convergence Integrated Media GmbH 98c2ecf20Sopenharmony_ci Holger Waechtler <holger@convergence.de> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci Copyright (C) 2004 Steven Toth <stoth@linuxtv.org> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci*/ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 238c2ecf20Sopenharmony_ci#include "cx22702.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct cx22702_state { 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci /* configuration settings */ 308c2ecf20Sopenharmony_ci const struct cx22702_config *config; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* previous uncorrected block counter */ 358c2ecf20Sopenharmony_ci u8 prevUCBlocks; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int debug; 398c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Enable verbose debug messages"); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define dprintk if (debug) printk 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Register values to initialise the demod */ 458c2ecf20Sopenharmony_cistatic const u8 init_tab[] = { 468c2ecf20Sopenharmony_ci 0x00, 0x00, /* Stop acquisition */ 478c2ecf20Sopenharmony_ci 0x0B, 0x06, 488c2ecf20Sopenharmony_ci 0x09, 0x01, 498c2ecf20Sopenharmony_ci 0x0D, 0x41, 508c2ecf20Sopenharmony_ci 0x16, 0x32, 518c2ecf20Sopenharmony_ci 0x20, 0x0A, 528c2ecf20Sopenharmony_ci 0x21, 0x17, 538c2ecf20Sopenharmony_ci 0x24, 0x3e, 548c2ecf20Sopenharmony_ci 0x26, 0xff, 558c2ecf20Sopenharmony_ci 0x27, 0x10, 568c2ecf20Sopenharmony_ci 0x28, 0x00, 578c2ecf20Sopenharmony_ci 0x29, 0x00, 588c2ecf20Sopenharmony_ci 0x2a, 0x10, 598c2ecf20Sopenharmony_ci 0x2b, 0x00, 608c2ecf20Sopenharmony_ci 0x2c, 0x10, 618c2ecf20Sopenharmony_ci 0x2d, 0x00, 628c2ecf20Sopenharmony_ci 0x48, 0xd4, 638c2ecf20Sopenharmony_ci 0x49, 0x56, 648c2ecf20Sopenharmony_ci 0x6b, 0x1e, 658c2ecf20Sopenharmony_ci 0xc8, 0x02, 668c2ecf20Sopenharmony_ci 0xf9, 0x00, 678c2ecf20Sopenharmony_ci 0xfa, 0x00, 688c2ecf20Sopenharmony_ci 0xfb, 0x00, 698c2ecf20Sopenharmony_ci 0xfc, 0x00, 708c2ecf20Sopenharmony_ci 0xfd, 0x00, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int cx22702_writereg(struct cx22702_state *state, u8 reg, u8 data) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int ret; 768c2ecf20Sopenharmony_ci u8 buf[] = { reg, data }; 778c2ecf20Sopenharmony_ci struct i2c_msg msg = { 788c2ecf20Sopenharmony_ci .addr = state->config->demod_address, .flags = 0, 798c2ecf20Sopenharmony_ci .buf = buf, .len = 2 }; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, &msg, 1); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (unlikely(ret != 1)) { 848c2ecf20Sopenharmony_ci printk(KERN_ERR 858c2ecf20Sopenharmony_ci "%s: error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", 868c2ecf20Sopenharmony_ci __func__, reg, data, ret); 878c2ecf20Sopenharmony_ci return -1; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic u8 cx22702_readreg(struct cx22702_state *state, u8 reg) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci int ret; 968c2ecf20Sopenharmony_ci u8 data; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 998c2ecf20Sopenharmony_ci { .addr = state->config->demod_address, .flags = 0, 1008c2ecf20Sopenharmony_ci .buf = ®, .len = 1 }, 1018c2ecf20Sopenharmony_ci { .addr = state->config->demod_address, .flags = I2C_M_RD, 1028c2ecf20Sopenharmony_ci .buf = &data, .len = 1 } }; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, msg, 2); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (unlikely(ret != 2)) { 1078c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: error (reg == 0x%02x, ret == %i)\n", 1088c2ecf20Sopenharmony_ci __func__, reg, ret); 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return data; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int cx22702_set_inversion(struct cx22702_state *state, int inversion) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci u8 val; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci val = cx22702_readreg(state, 0x0C); 1208c2ecf20Sopenharmony_ci switch (inversion) { 1218c2ecf20Sopenharmony_ci case INVERSION_AUTO: 1228c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1238c2ecf20Sopenharmony_ci case INVERSION_ON: 1248c2ecf20Sopenharmony_ci val |= 0x01; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci case INVERSION_OFF: 1278c2ecf20Sopenharmony_ci val &= 0xfe; 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci default: 1308c2ecf20Sopenharmony_ci return -EINVAL; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci return cx22702_writereg(state, 0x0C, val); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* Retrieve the demod settings */ 1368c2ecf20Sopenharmony_cistatic int cx22702_get_tps(struct cx22702_state *state, 1378c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u8 val; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Make sure the TPS regs are valid */ 1428c2ecf20Sopenharmony_ci if (!(cx22702_readreg(state, 0x0A) & 0x20)) 1438c2ecf20Sopenharmony_ci return -EAGAIN; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci val = cx22702_readreg(state, 0x01); 1468c2ecf20Sopenharmony_ci switch ((val & 0x18) >> 3) { 1478c2ecf20Sopenharmony_ci case 0: 1488c2ecf20Sopenharmony_ci p->modulation = QPSK; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case 1: 1518c2ecf20Sopenharmony_ci p->modulation = QAM_16; 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case 2: 1548c2ecf20Sopenharmony_ci p->modulation = QAM_64; 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci switch (val & 0x07) { 1588c2ecf20Sopenharmony_ci case 0: 1598c2ecf20Sopenharmony_ci p->hierarchy = HIERARCHY_NONE; 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci case 1: 1628c2ecf20Sopenharmony_ci p->hierarchy = HIERARCHY_1; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci case 2: 1658c2ecf20Sopenharmony_ci p->hierarchy = HIERARCHY_2; 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci case 3: 1688c2ecf20Sopenharmony_ci p->hierarchy = HIERARCHY_4; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci val = cx22702_readreg(state, 0x02); 1748c2ecf20Sopenharmony_ci switch ((val & 0x38) >> 3) { 1758c2ecf20Sopenharmony_ci case 0: 1768c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_1_2; 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci case 1: 1798c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_2_3; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci case 2: 1828c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_3_4; 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci case 3: 1858c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_5_6; 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci case 4: 1888c2ecf20Sopenharmony_ci p->code_rate_HP = FEC_7_8; 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci switch (val & 0x07) { 1928c2ecf20Sopenharmony_ci case 0: 1938c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_1_2; 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci case 1: 1968c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_2_3; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci case 2: 1998c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_3_4; 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case 3: 2028c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_5_6; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case 4: 2058c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_7_8; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci val = cx22702_readreg(state, 0x03); 2108c2ecf20Sopenharmony_ci switch ((val & 0x0c) >> 2) { 2118c2ecf20Sopenharmony_ci case 0: 2128c2ecf20Sopenharmony_ci p->guard_interval = GUARD_INTERVAL_1_32; 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci case 1: 2158c2ecf20Sopenharmony_ci p->guard_interval = GUARD_INTERVAL_1_16; 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci case 2: 2188c2ecf20Sopenharmony_ci p->guard_interval = GUARD_INTERVAL_1_8; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case 3: 2218c2ecf20Sopenharmony_ci p->guard_interval = GUARD_INTERVAL_1_4; 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci switch (val & 0x03) { 2258c2ecf20Sopenharmony_ci case 0: 2268c2ecf20Sopenharmony_ci p->transmission_mode = TRANSMISSION_MODE_2K; 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci case 1: 2298c2ecf20Sopenharmony_ci p->transmission_mode = TRANSMISSION_MODE_8K; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int cx22702_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 2398c2ecf20Sopenharmony_ci u8 val; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci dprintk("%s(%d)\n", __func__, enable); 2428c2ecf20Sopenharmony_ci val = cx22702_readreg(state, 0x0D); 2438c2ecf20Sopenharmony_ci if (enable) 2448c2ecf20Sopenharmony_ci val &= 0xfe; 2458c2ecf20Sopenharmony_ci else 2468c2ecf20Sopenharmony_ci val |= 0x01; 2478c2ecf20Sopenharmony_ci return cx22702_writereg(state, 0x0D, val); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ 2518c2ecf20Sopenharmony_cistatic int cx22702_set_tps(struct dvb_frontend *fe) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 2548c2ecf20Sopenharmony_ci u8 val; 2558c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 2588c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 2598c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2608c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* set inversion */ 2648c2ecf20Sopenharmony_ci cx22702_set_inversion(state, p->inversion); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* set bandwidth */ 2678c2ecf20Sopenharmony_ci val = cx22702_readreg(state, 0x0C) & 0xcf; 2688c2ecf20Sopenharmony_ci switch (p->bandwidth_hz) { 2698c2ecf20Sopenharmony_ci case 6000000: 2708c2ecf20Sopenharmony_ci val |= 0x20; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci case 7000000: 2738c2ecf20Sopenharmony_ci val |= 0x10; 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case 8000000: 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci default: 2788c2ecf20Sopenharmony_ci dprintk("%s: invalid bandwidth\n", __func__); 2798c2ecf20Sopenharmony_ci return -EINVAL; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x0C, val); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci p->code_rate_LP = FEC_AUTO; /* temp hack as manual not working */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* use auto configuration? */ 2868c2ecf20Sopenharmony_ci if ((p->hierarchy == HIERARCHY_AUTO) || 2878c2ecf20Sopenharmony_ci (p->modulation == QAM_AUTO) || 2888c2ecf20Sopenharmony_ci (p->code_rate_HP == FEC_AUTO) || 2898c2ecf20Sopenharmony_ci (p->code_rate_LP == FEC_AUTO) || 2908c2ecf20Sopenharmony_ci (p->guard_interval == GUARD_INTERVAL_AUTO) || 2918c2ecf20Sopenharmony_ci (p->transmission_mode == TRANSMISSION_MODE_AUTO)) { 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* TPS Source - use hardware driven values */ 2948c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x06, 0x10); 2958c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x07, 0x9); 2968c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x08, 0xC1); 2978c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) 2988c2ecf20Sopenharmony_ci & 0xfc); 2998c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x0C, 3008c2ecf20Sopenharmony_ci (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40); 3018c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x00, 0x01); /* Begin acquisition */ 3028c2ecf20Sopenharmony_ci dprintk("%s: Autodetecting\n", __func__); 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* manually programmed values */ 3078c2ecf20Sopenharmony_ci switch (p->modulation) { /* mask 0x18 */ 3088c2ecf20Sopenharmony_ci case QPSK: 3098c2ecf20Sopenharmony_ci val = 0x00; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci case QAM_16: 3128c2ecf20Sopenharmony_ci val = 0x08; 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci case QAM_64: 3158c2ecf20Sopenharmony_ci val = 0x10; 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci default: 3188c2ecf20Sopenharmony_ci dprintk("%s: invalid modulation\n", __func__); 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci switch (p->hierarchy) { /* mask 0x07 */ 3228c2ecf20Sopenharmony_ci case HIERARCHY_NONE: 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci case HIERARCHY_1: 3258c2ecf20Sopenharmony_ci val |= 0x01; 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci case HIERARCHY_2: 3288c2ecf20Sopenharmony_ci val |= 0x02; 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci case HIERARCHY_4: 3318c2ecf20Sopenharmony_ci val |= 0x03; 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci default: 3348c2ecf20Sopenharmony_ci dprintk("%s: invalid hierarchy\n", __func__); 3358c2ecf20Sopenharmony_ci return -EINVAL; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x06, val); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci switch (p->code_rate_HP) { /* mask 0x38 */ 3408c2ecf20Sopenharmony_ci case FEC_NONE: 3418c2ecf20Sopenharmony_ci case FEC_1_2: 3428c2ecf20Sopenharmony_ci val = 0x00; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci case FEC_2_3: 3458c2ecf20Sopenharmony_ci val = 0x08; 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci case FEC_3_4: 3488c2ecf20Sopenharmony_ci val = 0x10; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci case FEC_5_6: 3518c2ecf20Sopenharmony_ci val = 0x18; 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci case FEC_7_8: 3548c2ecf20Sopenharmony_ci val = 0x20; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci default: 3578c2ecf20Sopenharmony_ci dprintk("%s: invalid code_rate_HP\n", __func__); 3588c2ecf20Sopenharmony_ci return -EINVAL; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci switch (p->code_rate_LP) { /* mask 0x07 */ 3618c2ecf20Sopenharmony_ci case FEC_NONE: 3628c2ecf20Sopenharmony_ci case FEC_1_2: 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case FEC_2_3: 3658c2ecf20Sopenharmony_ci val |= 0x01; 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci case FEC_3_4: 3688c2ecf20Sopenharmony_ci val |= 0x02; 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci case FEC_5_6: 3718c2ecf20Sopenharmony_ci val |= 0x03; 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci case FEC_7_8: 3748c2ecf20Sopenharmony_ci val |= 0x04; 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci default: 3778c2ecf20Sopenharmony_ci dprintk("%s: invalid code_rate_LP\n", __func__); 3788c2ecf20Sopenharmony_ci return -EINVAL; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x07, val); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci switch (p->guard_interval) { /* mask 0x0c */ 3838c2ecf20Sopenharmony_ci case GUARD_INTERVAL_1_32: 3848c2ecf20Sopenharmony_ci val = 0x00; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case GUARD_INTERVAL_1_16: 3878c2ecf20Sopenharmony_ci val = 0x04; 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci case GUARD_INTERVAL_1_8: 3908c2ecf20Sopenharmony_ci val = 0x08; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case GUARD_INTERVAL_1_4: 3938c2ecf20Sopenharmony_ci val = 0x0c; 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci default: 3968c2ecf20Sopenharmony_ci dprintk("%s: invalid guard_interval\n", __func__); 3978c2ecf20Sopenharmony_ci return -EINVAL; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci switch (p->transmission_mode) { /* mask 0x03 */ 4008c2ecf20Sopenharmony_ci case TRANSMISSION_MODE_2K: 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case TRANSMISSION_MODE_8K: 4038c2ecf20Sopenharmony_ci val |= 0x1; 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci default: 4068c2ecf20Sopenharmony_ci dprintk("%s: invalid transmission_mode\n", __func__); 4078c2ecf20Sopenharmony_ci return -EINVAL; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x08, val); 4108c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x0B, 4118c2ecf20Sopenharmony_ci (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02); 4128c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x0C, 4138c2ecf20Sopenharmony_ci (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Begin channel acquisition */ 4168c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x00, 0x01); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* Reset the demod hardware and reset all of the configuration registers 4228c2ecf20Sopenharmony_ci to a default state. */ 4238c2ecf20Sopenharmony_cistatic int cx22702_init(struct dvb_frontend *fe) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci int i; 4268c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci cx22702_writereg(state, 0x00, 0x02); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci msleep(10); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(init_tab); i += 2) 4338c2ecf20Sopenharmony_ci cx22702_writereg(state, init_tab[i], init_tab[i + 1]); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci cx22702_writereg(state, 0xf8, (state->config->output_mode << 1) 4368c2ecf20Sopenharmony_ci & 0x02); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci cx22702_i2c_gate_ctrl(fe, 0); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int cx22702_read_status(struct dvb_frontend *fe, enum fe_status *status) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 4468c2ecf20Sopenharmony_ci u8 reg0A; 4478c2ecf20Sopenharmony_ci u8 reg23; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci *status = 0; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci reg0A = cx22702_readreg(state, 0x0A); 4528c2ecf20Sopenharmony_ci reg23 = cx22702_readreg(state, 0x23); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci dprintk("%s: status demod=0x%02x agc=0x%02x\n" 4558c2ecf20Sopenharmony_ci , __func__, reg0A, reg23); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (reg0A & 0x10) { 4588c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK; 4598c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 4608c2ecf20Sopenharmony_ci *status |= FE_HAS_SYNC; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (reg0A & 0x20) 4648c2ecf20Sopenharmony_ci *status |= FE_HAS_CARRIER; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (reg23 < 0xf0) 4678c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int cx22702_read_ber(struct dvb_frontend *fe, u32 *ber) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (cx22702_readreg(state, 0xE4) & 0x02) { 4778c2ecf20Sopenharmony_ci /* Realtime statistics */ 4788c2ecf20Sopenharmony_ci *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 4798c2ecf20Sopenharmony_ci | (cx22702_readreg(state, 0xDF) & 0x7F); 4808c2ecf20Sopenharmony_ci } else { 4818c2ecf20Sopenharmony_ci /* Averagtine statistics */ 4828c2ecf20Sopenharmony_ci *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 4838c2ecf20Sopenharmony_ci | cx22702_readreg(state, 0xDF); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int cx22702_read_signal_strength(struct dvb_frontend *fe, 4908c2ecf20Sopenharmony_ci u16 *signal_strength) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 4938c2ecf20Sopenharmony_ci u8 reg23; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* 4968c2ecf20Sopenharmony_ci * Experience suggests that the strength signal register works as 4978c2ecf20Sopenharmony_ci * follows: 4988c2ecf20Sopenharmony_ci * - In the absence of signal, value is 0xff. 4998c2ecf20Sopenharmony_ci * - In the presence of a weak signal, bit 7 is set, not sure what 5008c2ecf20Sopenharmony_ci * the lower 7 bits mean. 5018c2ecf20Sopenharmony_ci * - In the presence of a strong signal, the register holds a 7-bit 5028c2ecf20Sopenharmony_ci * value (bit 7 is cleared), with greater values standing for 5038c2ecf20Sopenharmony_ci * weaker signals. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci reg23 = cx22702_readreg(state, 0x23); 5068c2ecf20Sopenharmony_ci if (reg23 & 0x80) { 5078c2ecf20Sopenharmony_ci *signal_strength = 0; 5088c2ecf20Sopenharmony_ci } else { 5098c2ecf20Sopenharmony_ci reg23 = ~reg23 & 0x7f; 5108c2ecf20Sopenharmony_ci /* Scale to 16 bit */ 5118c2ecf20Sopenharmony_ci *signal_strength = (reg23 << 9) | (reg23 << 2) | (reg23 >> 5); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int cx22702_read_snr(struct dvb_frontend *fe, u16 *snr) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci u16 rs_ber; 5228c2ecf20Sopenharmony_ci if (cx22702_readreg(state, 0xE4) & 0x02) { 5238c2ecf20Sopenharmony_ci /* Realtime statistics */ 5248c2ecf20Sopenharmony_ci rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 5258c2ecf20Sopenharmony_ci | (cx22702_readreg(state, 0xDF) & 0x7F); 5268c2ecf20Sopenharmony_ci } else { 5278c2ecf20Sopenharmony_ci /* Averagine statistics */ 5288c2ecf20Sopenharmony_ci rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 8 5298c2ecf20Sopenharmony_ci | cx22702_readreg(state, 0xDF); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci *snr = ~rs_ber; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int cx22702_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci u8 _ucblocks; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* RS Uncorrectable Packet Count then reset */ 5438c2ecf20Sopenharmony_ci _ucblocks = cx22702_readreg(state, 0xE3); 5448c2ecf20Sopenharmony_ci if (state->prevUCBlocks < _ucblocks) 5458c2ecf20Sopenharmony_ci *ucblocks = (_ucblocks - state->prevUCBlocks); 5468c2ecf20Sopenharmony_ci else 5478c2ecf20Sopenharmony_ci *ucblocks = state->prevUCBlocks - _ucblocks; 5488c2ecf20Sopenharmony_ci state->prevUCBlocks = _ucblocks; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int cx22702_get_frontend(struct dvb_frontend *fe, 5548c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci u8 reg0C = cx22702_readreg(state, 0x0C); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci c->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF; 5618c2ecf20Sopenharmony_ci return cx22702_get_tps(state, c); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int cx22702_get_tune_settings(struct dvb_frontend *fe, 5658c2ecf20Sopenharmony_ci struct dvb_frontend_tune_settings *tune) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci tune->min_delay_ms = 1000; 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic void cx22702_release(struct dvb_frontend *fe) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct cx22702_state *state = fe->demodulator_priv; 5748c2ecf20Sopenharmony_ci kfree(state); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx22702_ops; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistruct dvb_frontend *cx22702_attach(const struct cx22702_config *config, 5808c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct cx22702_state *state = NULL; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 5858c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL); 5868c2ecf20Sopenharmony_ci if (state == NULL) 5878c2ecf20Sopenharmony_ci goto error; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* setup the state */ 5908c2ecf20Sopenharmony_ci state->config = config; 5918c2ecf20Sopenharmony_ci state->i2c = i2c; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* check if the demod is there */ 5948c2ecf20Sopenharmony_ci if (cx22702_readreg(state, 0x1f) != 0x3) 5958c2ecf20Sopenharmony_ci goto error; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* create dvb_frontend */ 5988c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &cx22702_ops, 5998c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 6008c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 6018c2ecf20Sopenharmony_ci return &state->frontend; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cierror: 6048c2ecf20Sopenharmony_ci kfree(state); 6058c2ecf20Sopenharmony_ci return NULL; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cx22702_attach); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx22702_ops = { 6108c2ecf20Sopenharmony_ci .delsys = { SYS_DVBT }, 6118c2ecf20Sopenharmony_ci .info = { 6128c2ecf20Sopenharmony_ci .name = "Conexant CX22702 DVB-T", 6138c2ecf20Sopenharmony_ci .frequency_min_hz = 177 * MHz, 6148c2ecf20Sopenharmony_ci .frequency_max_hz = 858 * MHz, 6158c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 166666, 6168c2ecf20Sopenharmony_ci .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 6178c2ecf20Sopenharmony_ci FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 6188c2ecf20Sopenharmony_ci FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | 6198c2ecf20Sopenharmony_ci FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | 6208c2ecf20Sopenharmony_ci FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER 6218c2ecf20Sopenharmony_ci }, 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci .release = cx22702_release, 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci .init = cx22702_init, 6268c2ecf20Sopenharmony_ci .i2c_gate_ctrl = cx22702_i2c_gate_ctrl, 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci .set_frontend = cx22702_set_tps, 6298c2ecf20Sopenharmony_ci .get_frontend = cx22702_get_frontend, 6308c2ecf20Sopenharmony_ci .get_tune_settings = cx22702_get_tune_settings, 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci .read_status = cx22702_read_status, 6338c2ecf20Sopenharmony_ci .read_ber = cx22702_read_ber, 6348c2ecf20Sopenharmony_ci .read_signal_strength = cx22702_read_signal_strength, 6358c2ecf20Sopenharmony_ci .read_snr = cx22702_read_snr, 6368c2ecf20Sopenharmony_ci .read_ucblocks = cx22702_read_ucblocks, 6378c2ecf20Sopenharmony_ci}; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver"); 6408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steven Toth"); 6418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 642