18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "mxl111sf-demod.h" 98c2ecf20Sopenharmony_ci#include "mxl111sf-reg.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* debug */ 128c2ecf20Sopenharmony_cistatic int mxl111sf_demod_debug; 138c2ecf20Sopenharmony_cimodule_param_named(debug, mxl111sf_demod_debug, int, 0644); 148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define mxl_dbg(fmt, arg...) \ 178c2ecf20Sopenharmony_ci if (mxl111sf_demod_debug) \ 188c2ecf20Sopenharmony_ci mxl_printk(KERN_DEBUG, fmt, ##arg) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct mxl111sf_demod_state { 238c2ecf20Sopenharmony_ci struct mxl111sf_state *mxl_state; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci const struct mxl111sf_demod_config *cfg; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci struct dvb_frontend fe; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state, 338c2ecf20Sopenharmony_ci u8 addr, u8 *data) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci return (state->cfg->read_reg) ? 368c2ecf20Sopenharmony_ci state->cfg->read_reg(state->mxl_state, addr, data) : 378c2ecf20Sopenharmony_ci -EINVAL; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state, 418c2ecf20Sopenharmony_ci u8 addr, u8 data) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci return (state->cfg->write_reg) ? 448c2ecf20Sopenharmony_ci state->cfg->write_reg(state->mxl_state, addr, data) : 458c2ecf20Sopenharmony_ci -EINVAL; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic 498c2ecf20Sopenharmony_ciint mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state, 508c2ecf20Sopenharmony_ci struct mxl111sf_reg_ctrl_info *ctrl_reg_info) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return (state->cfg->program_regs) ? 538c2ecf20Sopenharmony_ci state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : 548c2ecf20Sopenharmony_ci -EINVAL; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 588c2ecf20Sopenharmony_ci/* TPS */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic 618c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state, 628c2ecf20Sopenharmony_ci enum fe_code_rate *code_rate) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci u8 val; 658c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val); 668c2ecf20Sopenharmony_ci /* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */ 678c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 688c2ecf20Sopenharmony_ci goto fail; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci switch (val & V6_CODE_RATE_TPS_MASK) { 718c2ecf20Sopenharmony_ci case 0: 728c2ecf20Sopenharmony_ci *code_rate = FEC_1_2; 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci case 1: 758c2ecf20Sopenharmony_ci *code_rate = FEC_2_3; 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci case 2: 788c2ecf20Sopenharmony_ci *code_rate = FEC_3_4; 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci case 3: 818c2ecf20Sopenharmony_ci *code_rate = FEC_5_6; 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci case 4: 848c2ecf20Sopenharmony_ci *code_rate = FEC_7_8; 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_cifail: 888c2ecf20Sopenharmony_ci return ret; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic 928c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_modulation(struct mxl111sf_demod_state *state, 938c2ecf20Sopenharmony_ci enum fe_modulation *modulation) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci u8 val; 968c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val); 978c2ecf20Sopenharmony_ci /* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */ 988c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 998c2ecf20Sopenharmony_ci goto fail; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) { 1028c2ecf20Sopenharmony_ci case 0: 1038c2ecf20Sopenharmony_ci *modulation = QPSK; 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci case 1: 1068c2ecf20Sopenharmony_ci *modulation = QAM_16; 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci case 2: 1098c2ecf20Sopenharmony_ci *modulation = QAM_64; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_cifail: 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic 1178c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state, 1188c2ecf20Sopenharmony_ci enum fe_transmit_mode *fft_mode) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci u8 val; 1218c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val); 1228c2ecf20Sopenharmony_ci /* FFT Mode, 00:2K, 01:8K, 10:4K */ 1238c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 1248c2ecf20Sopenharmony_ci goto fail; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) { 1278c2ecf20Sopenharmony_ci case 0: 1288c2ecf20Sopenharmony_ci *fft_mode = TRANSMISSION_MODE_2K; 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci case 1: 1318c2ecf20Sopenharmony_ci *fft_mode = TRANSMISSION_MODE_8K; 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci case 2: 1348c2ecf20Sopenharmony_ci *fft_mode = TRANSMISSION_MODE_4K; 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_cifail: 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic 1428c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state, 1438c2ecf20Sopenharmony_ci enum fe_guard_interval *guard) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci u8 val; 1468c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val); 1478c2ecf20Sopenharmony_ci /* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */ 1488c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 1498c2ecf20Sopenharmony_ci goto fail; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci switch ((val & V6_PARAM_GI_MASK) >> 4) { 1528c2ecf20Sopenharmony_ci case 0: 1538c2ecf20Sopenharmony_ci *guard = GUARD_INTERVAL_1_32; 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci case 1: 1568c2ecf20Sopenharmony_ci *guard = GUARD_INTERVAL_1_16; 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci case 2: 1598c2ecf20Sopenharmony_ci *guard = GUARD_INTERVAL_1_8; 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci case 3: 1628c2ecf20Sopenharmony_ci *guard = GUARD_INTERVAL_1_4; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_cifail: 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic 1708c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state, 1718c2ecf20Sopenharmony_ci enum fe_hierarchy *hierarchy) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci u8 val; 1748c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val); 1758c2ecf20Sopenharmony_ci /* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */ 1768c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 1778c2ecf20Sopenharmony_ci goto fail; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) { 1808c2ecf20Sopenharmony_ci case 0: 1818c2ecf20Sopenharmony_ci *hierarchy = HIERARCHY_NONE; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case 1: 1848c2ecf20Sopenharmony_ci *hierarchy = HIERARCHY_1; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case 2: 1878c2ecf20Sopenharmony_ci *hierarchy = HIERARCHY_2; 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci case 3: 1908c2ecf20Sopenharmony_ci *hierarchy = HIERARCHY_4; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_cifail: 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 1988c2ecf20Sopenharmony_ci/* LOCKS */ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic 2018c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state, 2028c2ecf20Sopenharmony_ci int *sync_lock) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci u8 val = 0; 2058c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val); 2068c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 2078c2ecf20Sopenharmony_ci goto fail; 2088c2ecf20Sopenharmony_ci *sync_lock = (val & SYNC_LOCK_MASK) >> 4; 2098c2ecf20Sopenharmony_cifail: 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic 2148c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state, 2158c2ecf20Sopenharmony_ci int *rs_lock) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci u8 val = 0; 2188c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val); 2198c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 2208c2ecf20Sopenharmony_ci goto fail; 2218c2ecf20Sopenharmony_ci *rs_lock = (val & RS_LOCK_DET_MASK) >> 3; 2228c2ecf20Sopenharmony_cifail: 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic 2278c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state, 2288c2ecf20Sopenharmony_ci int *tps_lock) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci u8 val = 0; 2318c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val); 2328c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 2338c2ecf20Sopenharmony_ci goto fail; 2348c2ecf20Sopenharmony_ci *tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6; 2358c2ecf20Sopenharmony_cifail: 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic 2408c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state, 2418c2ecf20Sopenharmony_ci int *fec_lock) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci u8 val = 0; 2448c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val); 2458c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 2468c2ecf20Sopenharmony_ci goto fail; 2478c2ecf20Sopenharmony_ci *fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4; 2488c2ecf20Sopenharmony_cifail: 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci#if 0 2538c2ecf20Sopenharmony_cistatic 2548c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state, 2558c2ecf20Sopenharmony_ci int *cp_lock) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci u8 val = 0; 2588c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val); 2598c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 2608c2ecf20Sopenharmony_ci goto fail; 2618c2ecf20Sopenharmony_ci *cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2; 2628c2ecf20Sopenharmony_cifail: 2638c2ecf20Sopenharmony_ci return ret; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci#endif 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci return mxl111sf_demod_write_reg(state, 0x0e, 0xff); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int mxl111sf_demod_set_frontend(struct dvb_frontend *fe) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = fe->demodulator_priv; 2778c2ecf20Sopenharmony_ci int ret = 0; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci struct mxl111sf_reg_ctrl_info phy_pll_patch[] = { 2808c2ecf20Sopenharmony_ci {0x00, 0xff, 0x01}, /* change page to 1 */ 2818c2ecf20Sopenharmony_ci {0x40, 0xff, 0x05}, 2828c2ecf20Sopenharmony_ci {0x40, 0xff, 0x01}, 2838c2ecf20Sopenharmony_ci {0x41, 0xff, 0xca}, 2848c2ecf20Sopenharmony_ci {0x41, 0xff, 0xc0}, 2858c2ecf20Sopenharmony_ci {0x00, 0xff, 0x00}, /* change page to 0 */ 2868c2ecf20Sopenharmony_ci {0, 0, 0} 2878c2ecf20Sopenharmony_ci }; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mxl_dbg("()"); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 2928c2ecf20Sopenharmony_ci ret = fe->ops.tuner_ops.set_params(fe); 2938c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 2948c2ecf20Sopenharmony_ci goto fail; 2958c2ecf20Sopenharmony_ci msleep(50); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci ret = mxl111sf_demod_program_regs(state, phy_pll_patch); 2988c2ecf20Sopenharmony_ci mxl_fail(ret); 2998c2ecf20Sopenharmony_ci msleep(50); 3008c2ecf20Sopenharmony_ci ret = mxl1x1sf_demod_reset_irq_status(state); 3018c2ecf20Sopenharmony_ci mxl_fail(ret); 3028c2ecf20Sopenharmony_ci msleep(100); 3038c2ecf20Sopenharmony_cifail: 3048c2ecf20Sopenharmony_ci return ret; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci#if 0 3108c2ecf20Sopenharmony_ci/* resets TS Packet error count */ 3118c2ecf20Sopenharmony_ci/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */ 3128c2ecf20Sopenharmony_cistatic 3138c2ecf20Sopenharmony_ciint mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct mxl111sf_reg_ctrl_info reset_per_count[] = { 3168c2ecf20Sopenharmony_ci {0x20, 0x01, 0x01}, 3178c2ecf20Sopenharmony_ci {0x20, 0x01, 0x00}, 3188c2ecf20Sopenharmony_ci {0, 0, 0} 3198c2ecf20Sopenharmony_ci }; 3208c2ecf20Sopenharmony_ci return mxl111sf_demod_program_regs(state, reset_per_count); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci#endif 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/* returns TS Packet error count */ 3258c2ecf20Sopenharmony_ci/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */ 3268c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = fe->demodulator_priv; 3298c2ecf20Sopenharmony_ci u32 fec_per_count, fec_per_scale; 3308c2ecf20Sopenharmony_ci u8 val; 3318c2ecf20Sopenharmony_ci int ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci *ucblocks = 0; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* FEC_PER_COUNT Register */ 3368c2ecf20Sopenharmony_ci ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val); 3378c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 3388c2ecf20Sopenharmony_ci goto fail; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci fec_per_count = val; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* FEC_PER_SCALE Register */ 3438c2ecf20Sopenharmony_ci ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val); 3448c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 3458c2ecf20Sopenharmony_ci goto fail; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci val &= V6_FEC_PER_SCALE_MASK; 3488c2ecf20Sopenharmony_ci val *= 4; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci fec_per_scale = 1 << val; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci fec_per_count *= fec_per_scale; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci *ucblocks = fec_per_count; 3558c2ecf20Sopenharmony_cifail: 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS 3608c2ecf20Sopenharmony_ci/* FIXME: leaving this enabled breaks the build on some architectures, 3618c2ecf20Sopenharmony_ci * and we shouldn't have any floating point math in the kernel, anyway. 3628c2ecf20Sopenharmony_ci * 3638c2ecf20Sopenharmony_ci * These macros need to be re-written, but it's harmless to simply 3648c2ecf20Sopenharmony_ci * return zero for now. */ 3658c2ecf20Sopenharmony_ci#define CALCULATE_BER(avg_errors, count) \ 3668c2ecf20Sopenharmony_ci ((u32)(avg_errors * 4)/(count*64*188*8)) 3678c2ecf20Sopenharmony_ci#define CALCULATE_SNR(data) \ 3688c2ecf20Sopenharmony_ci ((u32)((10 * (u32)data / 64) - 2.5)) 3698c2ecf20Sopenharmony_ci#else 3708c2ecf20Sopenharmony_ci#define CALCULATE_BER(avg_errors, count) 0 3718c2ecf20Sopenharmony_ci#define CALCULATE_SNR(data) 0 3728c2ecf20Sopenharmony_ci#endif 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = fe->demodulator_priv; 3778c2ecf20Sopenharmony_ci u8 val1, val2, val3; 3788c2ecf20Sopenharmony_ci int ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci *ber = 0; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1); 3838c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 3848c2ecf20Sopenharmony_ci goto fail; 3858c2ecf20Sopenharmony_ci ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2); 3868c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 3878c2ecf20Sopenharmony_ci goto fail; 3888c2ecf20Sopenharmony_ci ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3); 3898c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 3908c2ecf20Sopenharmony_ci goto fail; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci *ber = CALCULATE_BER((val1 | (val2 << 8)), val3); 3938c2ecf20Sopenharmony_cifail: 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state, 3988c2ecf20Sopenharmony_ci u16 *snr) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci u8 val1, val2; 4018c2ecf20Sopenharmony_ci int ret; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci *snr = 0; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1); 4068c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 4078c2ecf20Sopenharmony_ci goto fail; 4088c2ecf20Sopenharmony_ci ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2); 4098c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 4108c2ecf20Sopenharmony_ci goto fail; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci *snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8)); 4138c2ecf20Sopenharmony_cifail: 4148c2ecf20Sopenharmony_ci return ret; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = fe->demodulator_priv; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci int ret = mxl111sf_demod_calc_snr(state, snr); 4228c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 4238c2ecf20Sopenharmony_ci goto fail; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci *snr /= 10; /* 0.1 dB */ 4268c2ecf20Sopenharmony_cifail: 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_status(struct dvb_frontend *fe, 4318c2ecf20Sopenharmony_ci enum fe_status *status) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = fe->demodulator_priv; 4348c2ecf20Sopenharmony_ci int ret, locked, cr_lock, sync_lock, fec_lock; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci *status = 0; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked); 4398c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 4408c2ecf20Sopenharmony_ci goto fail; 4418c2ecf20Sopenharmony_ci ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock); 4428c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 4438c2ecf20Sopenharmony_ci goto fail; 4448c2ecf20Sopenharmony_ci ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock); 4458c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 4468c2ecf20Sopenharmony_ci goto fail; 4478c2ecf20Sopenharmony_ci ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock); 4488c2ecf20Sopenharmony_ci if (mxl_fail(ret)) 4498c2ecf20Sopenharmony_ci goto fail; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (locked) 4528c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 4538c2ecf20Sopenharmony_ci if (cr_lock) 4548c2ecf20Sopenharmony_ci *status |= FE_HAS_CARRIER; 4558c2ecf20Sopenharmony_ci if (sync_lock) 4568c2ecf20Sopenharmony_ci *status |= FE_HAS_SYNC; 4578c2ecf20Sopenharmony_ci if (fec_lock) /* false positives? */ 4588c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if ((locked) && (cr_lock) && (sync_lock)) 4618c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK; 4628c2ecf20Sopenharmony_cifail: 4638c2ecf20Sopenharmony_ci return ret; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe, 4678c2ecf20Sopenharmony_ci u16 *signal_strength) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = fe->demodulator_priv; 4708c2ecf20Sopenharmony_ci enum fe_modulation modulation; 4718c2ecf20Sopenharmony_ci int ret; 4728c2ecf20Sopenharmony_ci u16 snr; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci ret = mxl111sf_demod_calc_snr(state, &snr); 4758c2ecf20Sopenharmony_ci if (ret < 0) 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci ret = mxl1x1sf_demod_get_tps_modulation(state, &modulation); 4788c2ecf20Sopenharmony_ci if (ret < 0) 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci switch (modulation) { 4828c2ecf20Sopenharmony_ci case QPSK: 4838c2ecf20Sopenharmony_ci *signal_strength = (snr >= 1300) ? 4848c2ecf20Sopenharmony_ci min(65535, snr * 44) : snr * 38; 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci case QAM_16: 4878c2ecf20Sopenharmony_ci *signal_strength = (snr >= 1500) ? 4888c2ecf20Sopenharmony_ci min(65535, snr * 38) : snr * 33; 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci case QAM_64: 4918c2ecf20Sopenharmony_ci *signal_strength = (snr >= 2000) ? 4928c2ecf20Sopenharmony_ci min(65535, snr * 29) : snr * 25; 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci default: 4958c2ecf20Sopenharmony_ci *signal_strength = 0; 4968c2ecf20Sopenharmony_ci return -EINVAL; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic int mxl111sf_demod_get_frontend(struct dvb_frontend *fe, 5038c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = fe->demodulator_priv; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci mxl_dbg("()"); 5088c2ecf20Sopenharmony_ci#if 0 5098c2ecf20Sopenharmony_ci p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF; 5108c2ecf20Sopenharmony_ci#endif 5118c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.get_bandwidth) 5128c2ecf20Sopenharmony_ci fe->ops.tuner_ops.get_bandwidth(fe, &p->bandwidth_hz); 5138c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.get_frequency) 5148c2ecf20Sopenharmony_ci fe->ops.tuner_ops.get_frequency(fe, &p->frequency); 5158c2ecf20Sopenharmony_ci mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_HP); 5168c2ecf20Sopenharmony_ci mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_LP); 5178c2ecf20Sopenharmony_ci mxl1x1sf_demod_get_tps_modulation(state, &p->modulation); 5188c2ecf20Sopenharmony_ci mxl1x1sf_demod_get_tps_guard_fft_mode(state, 5198c2ecf20Sopenharmony_ci &p->transmission_mode); 5208c2ecf20Sopenharmony_ci mxl1x1sf_demod_get_tps_guard_interval(state, 5218c2ecf20Sopenharmony_ci &p->guard_interval); 5228c2ecf20Sopenharmony_ci mxl1x1sf_demod_get_tps_hierarchy(state, 5238c2ecf20Sopenharmony_ci &p->hierarchy); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic 5298c2ecf20Sopenharmony_ciint mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe, 5308c2ecf20Sopenharmony_ci struct dvb_frontend_tune_settings *tune) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci tune->min_delay_ms = 1000; 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic void mxl111sf_demod_release(struct dvb_frontend *fe) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = fe->demodulator_priv; 5398c2ecf20Sopenharmony_ci mxl_dbg("()"); 5408c2ecf20Sopenharmony_ci kfree(state); 5418c2ecf20Sopenharmony_ci fe->demodulator_priv = NULL; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops mxl111sf_demod_ops = { 5458c2ecf20Sopenharmony_ci .delsys = { SYS_DVBT }, 5468c2ecf20Sopenharmony_ci .info = { 5478c2ecf20Sopenharmony_ci .name = "MaxLinear MxL111SF DVB-T demodulator", 5488c2ecf20Sopenharmony_ci .frequency_min_hz = 177 * MHz, 5498c2ecf20Sopenharmony_ci .frequency_max_hz = 858 * MHz, 5508c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 166666, 5518c2ecf20Sopenharmony_ci .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 5528c2ecf20Sopenharmony_ci FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 5538c2ecf20Sopenharmony_ci FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | 5548c2ecf20Sopenharmony_ci FE_CAN_QAM_AUTO | 5558c2ecf20Sopenharmony_ci FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | 5568c2ecf20Sopenharmony_ci FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER 5578c2ecf20Sopenharmony_ci }, 5588c2ecf20Sopenharmony_ci .release = mxl111sf_demod_release, 5598c2ecf20Sopenharmony_ci#if 0 5608c2ecf20Sopenharmony_ci .init = mxl111sf_init, 5618c2ecf20Sopenharmony_ci .i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl, 5628c2ecf20Sopenharmony_ci#endif 5638c2ecf20Sopenharmony_ci .set_frontend = mxl111sf_demod_set_frontend, 5648c2ecf20Sopenharmony_ci .get_frontend = mxl111sf_demod_get_frontend, 5658c2ecf20Sopenharmony_ci .get_tune_settings = mxl111sf_demod_get_tune_settings, 5668c2ecf20Sopenharmony_ci .read_status = mxl111sf_demod_read_status, 5678c2ecf20Sopenharmony_ci .read_signal_strength = mxl111sf_demod_read_signal_strength, 5688c2ecf20Sopenharmony_ci .read_ber = mxl111sf_demod_read_ber, 5698c2ecf20Sopenharmony_ci .read_snr = mxl111sf_demod_read_snr, 5708c2ecf20Sopenharmony_ci .read_ucblocks = mxl111sf_demod_read_ucblocks, 5718c2ecf20Sopenharmony_ci}; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistruct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, 5748c2ecf20Sopenharmony_ci const struct mxl111sf_demod_config *cfg) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct mxl111sf_demod_state *state = NULL; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci mxl_dbg("()"); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL); 5818c2ecf20Sopenharmony_ci if (state == NULL) 5828c2ecf20Sopenharmony_ci return NULL; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci state->mxl_state = mxl_state; 5858c2ecf20Sopenharmony_ci state->cfg = cfg; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci memcpy(&state->fe.ops, &mxl111sf_demod_ops, 5888c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci state->fe.demodulator_priv = state; 5918c2ecf20Sopenharmony_ci return &state->fe; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mxl111sf_demod_attach); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver"); 5968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); 5978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5988c2ecf20Sopenharmony_ciMODULE_VERSION("0.1"); 599