18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * LGDT3304 support by Jarod Wilson <jarod@redhat.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <asm/div64.h> 118c2ecf20Sopenharmony_ci#include <linux/dvb/frontend.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <media/dvb_math.h> 148c2ecf20Sopenharmony_ci#include "lgdt3305.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int debug; 178c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 188c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define DBG_INFO 1 218c2ecf20Sopenharmony_ci#define DBG_REG 2 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define lg_printk(kern, fmt, arg...) \ 248c2ecf20Sopenharmony_ci printk(kern "%s: " fmt, __func__, ##arg) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define lg_info(fmt, arg...) printk(KERN_INFO "lgdt3305: " fmt, ##arg) 278c2ecf20Sopenharmony_ci#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) 288c2ecf20Sopenharmony_ci#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) 298c2ecf20Sopenharmony_ci#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ 308c2ecf20Sopenharmony_ci lg_printk(KERN_DEBUG, fmt, ##arg) 318c2ecf20Sopenharmony_ci#define lg_reg(fmt, arg...) if (debug & DBG_REG) \ 328c2ecf20Sopenharmony_ci lg_printk(KERN_DEBUG, fmt, ##arg) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define lg_fail(ret) \ 358c2ecf20Sopenharmony_ci({ \ 368c2ecf20Sopenharmony_ci int __ret; \ 378c2ecf20Sopenharmony_ci __ret = (ret < 0); \ 388c2ecf20Sopenharmony_ci if (__ret) \ 398c2ecf20Sopenharmony_ci lg_err("error %d on line %d\n", ret, __LINE__); \ 408c2ecf20Sopenharmony_ci __ret; \ 418c2ecf20Sopenharmony_ci}) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct lgdt3305_state { 448c2ecf20Sopenharmony_ci struct i2c_adapter *i2c_adap; 458c2ecf20Sopenharmony_ci const struct lgdt3305_config *cfg; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci enum fe_modulation current_modulation; 508c2ecf20Sopenharmony_ci u32 current_frequency; 518c2ecf20Sopenharmony_ci u32 snr; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* FIXME: verify & document the LGDT3304 registers */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define LGDT3305_GEN_CTRL_1 0x0000 598c2ecf20Sopenharmony_ci#define LGDT3305_GEN_CTRL_2 0x0001 608c2ecf20Sopenharmony_ci#define LGDT3305_GEN_CTRL_3 0x0002 618c2ecf20Sopenharmony_ci#define LGDT3305_GEN_STATUS 0x0003 628c2ecf20Sopenharmony_ci#define LGDT3305_GEN_CONTROL 0x0007 638c2ecf20Sopenharmony_ci#define LGDT3305_GEN_CTRL_4 0x000a 648c2ecf20Sopenharmony_ci#define LGDT3305_DGTL_AGC_REF_1 0x0012 658c2ecf20Sopenharmony_ci#define LGDT3305_DGTL_AGC_REF_2 0x0013 668c2ecf20Sopenharmony_ci#define LGDT3305_CR_CTR_FREQ_1 0x0106 678c2ecf20Sopenharmony_ci#define LGDT3305_CR_CTR_FREQ_2 0x0107 688c2ecf20Sopenharmony_ci#define LGDT3305_CR_CTR_FREQ_3 0x0108 698c2ecf20Sopenharmony_ci#define LGDT3305_CR_CTR_FREQ_4 0x0109 708c2ecf20Sopenharmony_ci#define LGDT3305_CR_MSE_1 0x011b 718c2ecf20Sopenharmony_ci#define LGDT3305_CR_MSE_2 0x011c 728c2ecf20Sopenharmony_ci#define LGDT3305_CR_LOCK_STATUS 0x011d 738c2ecf20Sopenharmony_ci#define LGDT3305_CR_CTRL_7 0x0126 748c2ecf20Sopenharmony_ci#define LGDT3305_AGC_POWER_REF_1 0x0300 758c2ecf20Sopenharmony_ci#define LGDT3305_AGC_POWER_REF_2 0x0301 768c2ecf20Sopenharmony_ci#define LGDT3305_AGC_DELAY_PT_1 0x0302 778c2ecf20Sopenharmony_ci#define LGDT3305_AGC_DELAY_PT_2 0x0303 788c2ecf20Sopenharmony_ci#define LGDT3305_RFAGC_LOOP_FLTR_BW_1 0x0306 798c2ecf20Sopenharmony_ci#define LGDT3305_RFAGC_LOOP_FLTR_BW_2 0x0307 808c2ecf20Sopenharmony_ci#define LGDT3305_IFBW_1 0x0308 818c2ecf20Sopenharmony_ci#define LGDT3305_IFBW_2 0x0309 828c2ecf20Sopenharmony_ci#define LGDT3305_AGC_CTRL_1 0x030c 838c2ecf20Sopenharmony_ci#define LGDT3305_AGC_CTRL_4 0x0314 848c2ecf20Sopenharmony_ci#define LGDT3305_EQ_MSE_1 0x0413 858c2ecf20Sopenharmony_ci#define LGDT3305_EQ_MSE_2 0x0414 868c2ecf20Sopenharmony_ci#define LGDT3305_EQ_MSE_3 0x0415 878c2ecf20Sopenharmony_ci#define LGDT3305_PT_MSE_1 0x0417 888c2ecf20Sopenharmony_ci#define LGDT3305_PT_MSE_2 0x0418 898c2ecf20Sopenharmony_ci#define LGDT3305_PT_MSE_3 0x0419 908c2ecf20Sopenharmony_ci#define LGDT3305_FEC_BLOCK_CTRL 0x0504 918c2ecf20Sopenharmony_ci#define LGDT3305_FEC_LOCK_STATUS 0x050a 928c2ecf20Sopenharmony_ci#define LGDT3305_FEC_PKT_ERR_1 0x050c 938c2ecf20Sopenharmony_ci#define LGDT3305_FEC_PKT_ERR_2 0x050d 948c2ecf20Sopenharmony_ci#define LGDT3305_TP_CTRL_1 0x050e 958c2ecf20Sopenharmony_ci#define LGDT3305_BERT_PERIOD 0x0801 968c2ecf20Sopenharmony_ci#define LGDT3305_BERT_ERROR_COUNT_1 0x080a 978c2ecf20Sopenharmony_ci#define LGDT3305_BERT_ERROR_COUNT_2 0x080b 988c2ecf20Sopenharmony_ci#define LGDT3305_BERT_ERROR_COUNT_3 0x080c 998c2ecf20Sopenharmony_ci#define LGDT3305_BERT_ERROR_COUNT_4 0x080d 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int lgdt3305_write_reg(struct lgdt3305_state *state, u16 reg, u8 val) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int ret; 1048c2ecf20Sopenharmony_ci u8 buf[] = { reg >> 8, reg & 0xff, val }; 1058c2ecf20Sopenharmony_ci struct i2c_msg msg = { 1068c2ecf20Sopenharmony_ci .addr = state->cfg->i2c_addr, .flags = 0, 1078c2ecf20Sopenharmony_ci .buf = buf, .len = 3, 1088c2ecf20Sopenharmony_ci }; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c_adap, &msg, 1); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (ret != 1) { 1158c2ecf20Sopenharmony_ci lg_err("error (addr %02x %02x <- %02x, err = %i)\n", 1168c2ecf20Sopenharmony_ci msg.buf[0], msg.buf[1], msg.buf[2], ret); 1178c2ecf20Sopenharmony_ci if (ret < 0) 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci return -EREMOTEIO; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int lgdt3305_read_reg(struct lgdt3305_state *state, u16 reg, u8 *val) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci u8 reg_buf[] = { reg >> 8, reg & 0xff }; 1298c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 1308c2ecf20Sopenharmony_ci { .addr = state->cfg->i2c_addr, 1318c2ecf20Sopenharmony_ci .flags = 0, .buf = reg_buf, .len = 2 }, 1328c2ecf20Sopenharmony_ci { .addr = state->cfg->i2c_addr, 1338c2ecf20Sopenharmony_ci .flags = I2C_M_RD, .buf = val, .len = 1 }, 1348c2ecf20Sopenharmony_ci }; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci lg_reg("reg: 0x%04x\n", reg); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c_adap, msg, 2); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (ret != 2) { 1418c2ecf20Sopenharmony_ci lg_err("error (addr %02x reg %04x error (ret == %i)\n", 1428c2ecf20Sopenharmony_ci state->cfg->i2c_addr, reg, ret); 1438c2ecf20Sopenharmony_ci if (ret < 0) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci else 1468c2ecf20Sopenharmony_ci return -EREMOTEIO; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci#define read_reg(state, reg) \ 1528c2ecf20Sopenharmony_ci({ \ 1538c2ecf20Sopenharmony_ci u8 __val; \ 1548c2ecf20Sopenharmony_ci int ret = lgdt3305_read_reg(state, reg, &__val); \ 1558c2ecf20Sopenharmony_ci if (lg_fail(ret)) \ 1568c2ecf20Sopenharmony_ci __val = 0; \ 1578c2ecf20Sopenharmony_ci __val; \ 1588c2ecf20Sopenharmony_ci}) 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int lgdt3305_set_reg_bit(struct lgdt3305_state *state, 1618c2ecf20Sopenharmony_ci u16 reg, int bit, int onoff) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci u8 val; 1648c2ecf20Sopenharmony_ci int ret; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = lgdt3305_read_reg(state, reg, &val); 1698c2ecf20Sopenharmony_ci if (lg_fail(ret)) 1708c2ecf20Sopenharmony_ci goto fail; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci val &= ~(1 << bit); 1738c2ecf20Sopenharmony_ci val |= (onoff & 1) << bit; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, reg, val); 1768c2ecf20Sopenharmony_cifail: 1778c2ecf20Sopenharmony_ci return ret; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistruct lgdt3305_reg { 1818c2ecf20Sopenharmony_ci u16 reg; 1828c2ecf20Sopenharmony_ci u8 val; 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int lgdt3305_write_regs(struct lgdt3305_state *state, 1868c2ecf20Sopenharmony_ci struct lgdt3305_reg *regs, int len) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int i, ret; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci lg_reg("writing %d registers...\n", len); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci for (i = 0; i < len - 1; i++) { 1938c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, regs[i].reg, regs[i].val); 1948c2ecf20Sopenharmony_ci if (lg_fail(ret)) 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int lgdt3305_soft_reset(struct lgdt3305_state *state) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci int ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci lg_dbg("\n"); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 0); 2098c2ecf20Sopenharmony_ci if (lg_fail(ret)) 2108c2ecf20Sopenharmony_ci goto fail; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci msleep(20); 2138c2ecf20Sopenharmony_ci ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 1); 2148c2ecf20Sopenharmony_cifail: 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state, 2198c2ecf20Sopenharmony_ci enum lgdt3305_mpeg_mode mode) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci lg_dbg("(%d)\n", mode); 2228c2ecf20Sopenharmony_ci return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci u8 val; 2288c2ecf20Sopenharmony_ci int ret; 2298c2ecf20Sopenharmony_ci enum lgdt3305_tp_clock_edge edge = state->cfg->tpclk_edge; 2308c2ecf20Sopenharmony_ci enum lgdt3305_tp_clock_mode mode = state->cfg->tpclk_mode; 2318c2ecf20Sopenharmony_ci enum lgdt3305_tp_valid_polarity valid = state->cfg->tpvalid_polarity; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci lg_dbg("edge = %d, valid = %d\n", edge, valid); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = lgdt3305_read_reg(state, LGDT3305_TP_CTRL_1, &val); 2368c2ecf20Sopenharmony_ci if (lg_fail(ret)) 2378c2ecf20Sopenharmony_ci goto fail; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci val &= ~0x09; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (edge) 2428c2ecf20Sopenharmony_ci val |= 0x08; 2438c2ecf20Sopenharmony_ci if (mode) 2448c2ecf20Sopenharmony_ci val |= 0x40; 2458c2ecf20Sopenharmony_ci if (valid) 2468c2ecf20Sopenharmony_ci val |= 0x01; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, LGDT3305_TP_CTRL_1, val); 2498c2ecf20Sopenharmony_ci if (lg_fail(ret)) 2508c2ecf20Sopenharmony_ci goto fail; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ret = lgdt3305_soft_reset(state); 2538c2ecf20Sopenharmony_cifail: 2548c2ecf20Sopenharmony_ci return ret; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int lgdt3305_set_modulation(struct lgdt3305_state *state, 2588c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci u8 opermode; 2618c2ecf20Sopenharmony_ci int ret; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci lg_dbg("\n"); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_1, &opermode); 2668c2ecf20Sopenharmony_ci if (lg_fail(ret)) 2678c2ecf20Sopenharmony_ci goto fail; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci opermode &= ~0x03; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci switch (p->modulation) { 2728c2ecf20Sopenharmony_ci case VSB_8: 2738c2ecf20Sopenharmony_ci opermode |= 0x03; 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case QAM_64: 2768c2ecf20Sopenharmony_ci opermode |= 0x00; 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci case QAM_256: 2798c2ecf20Sopenharmony_ci opermode |= 0x01; 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci return -EINVAL; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_1, opermode); 2858c2ecf20Sopenharmony_cifail: 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int lgdt3305_set_filter_extension(struct lgdt3305_state *state, 2908c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int val; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci switch (p->modulation) { 2958c2ecf20Sopenharmony_ci case VSB_8: 2968c2ecf20Sopenharmony_ci val = 0; 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci case QAM_64: 2998c2ecf20Sopenharmony_ci case QAM_256: 3008c2ecf20Sopenharmony_ci val = 1; 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci default: 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci lg_dbg("val = %d\n", val); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return lgdt3305_set_reg_bit(state, 0x043f, 2, val); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int lgdt3305_passband_digital_agc(struct lgdt3305_state *state, 3138c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci u16 agc_ref; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci switch (p->modulation) { 3188c2ecf20Sopenharmony_ci case VSB_8: 3198c2ecf20Sopenharmony_ci agc_ref = 0x32c4; 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci case QAM_64: 3228c2ecf20Sopenharmony_ci agc_ref = 0x2a00; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci case QAM_256: 3258c2ecf20Sopenharmony_ci agc_ref = 0x2a80; 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci default: 3288c2ecf20Sopenharmony_ci return -EINVAL; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci lg_dbg("agc ref: 0x%04x\n", agc_ref); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_1, agc_ref >> 8); 3348c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_2, agc_ref & 0xff); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int lgdt3305_rfagc_loop(struct lgdt3305_state *state, 3408c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci u16 ifbw, rfbw, agcdelay; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci switch (p->modulation) { 3458c2ecf20Sopenharmony_ci case VSB_8: 3468c2ecf20Sopenharmony_ci agcdelay = 0x04c0; 3478c2ecf20Sopenharmony_ci rfbw = 0x8000; 3488c2ecf20Sopenharmony_ci ifbw = 0x8000; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci case QAM_64: 3518c2ecf20Sopenharmony_ci case QAM_256: 3528c2ecf20Sopenharmony_ci agcdelay = 0x046b; 3538c2ecf20Sopenharmony_ci rfbw = 0x8889; 3548c2ecf20Sopenharmony_ci /* FIXME: investigate optimal ifbw & rfbw values for the 3558c2ecf20Sopenharmony_ci * DT3304 and re-write this switch..case block */ 3568c2ecf20Sopenharmony_ci if (state->cfg->demod_chip == LGDT3304) 3578c2ecf20Sopenharmony_ci ifbw = 0x6666; 3588c2ecf20Sopenharmony_ci else /* (state->cfg->demod_chip == LGDT3305) */ 3598c2ecf20Sopenharmony_ci ifbw = 0x8888; 3608c2ecf20Sopenharmony_ci break; 3618c2ecf20Sopenharmony_ci default: 3628c2ecf20Sopenharmony_ci return -EINVAL; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (state->cfg->rf_agc_loop) { 3668c2ecf20Sopenharmony_ci lg_dbg("agcdelay: 0x%04x, rfbw: 0x%04x\n", agcdelay, rfbw); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* rf agc loop filter bandwidth */ 3698c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_1, 3708c2ecf20Sopenharmony_ci agcdelay >> 8); 3718c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_2, 3728c2ecf20Sopenharmony_ci agcdelay & 0xff); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_1, 3758c2ecf20Sopenharmony_ci rfbw >> 8); 3768c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_2, 3778c2ecf20Sopenharmony_ci rfbw & 0xff); 3788c2ecf20Sopenharmony_ci } else { 3798c2ecf20Sopenharmony_ci lg_dbg("ifbw: 0x%04x\n", ifbw); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* if agc loop filter bandwidth */ 3828c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_IFBW_1, ifbw >> 8); 3838c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_IFBW_2, ifbw & 0xff); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int lgdt3305_agc_setup(struct lgdt3305_state *state, 3908c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci int lockdten, acqen; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci switch (p->modulation) { 3958c2ecf20Sopenharmony_ci case VSB_8: 3968c2ecf20Sopenharmony_ci lockdten = 0; 3978c2ecf20Sopenharmony_ci acqen = 0; 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case QAM_64: 4008c2ecf20Sopenharmony_ci case QAM_256: 4018c2ecf20Sopenharmony_ci lockdten = 1; 4028c2ecf20Sopenharmony_ci acqen = 1; 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci default: 4058c2ecf20Sopenharmony_ci return -EINVAL; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* control agc function */ 4118c2ecf20Sopenharmony_ci switch (state->cfg->demod_chip) { 4128c2ecf20Sopenharmony_ci case LGDT3304: 4138c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1); 4148c2ecf20Sopenharmony_ci lgdt3305_set_reg_bit(state, 0x030e, 2, acqen); 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci case LGDT3305: 4178c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); 4188c2ecf20Sopenharmony_ci lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci default: 4218c2ecf20Sopenharmony_ci return -EINVAL; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return lgdt3305_rfagc_loop(state, p); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic int lgdt3305_set_agc_power_ref(struct lgdt3305_state *state, 4288c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci u16 usref = 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci switch (p->modulation) { 4338c2ecf20Sopenharmony_ci case VSB_8: 4348c2ecf20Sopenharmony_ci if (state->cfg->usref_8vsb) 4358c2ecf20Sopenharmony_ci usref = state->cfg->usref_8vsb; 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci case QAM_64: 4388c2ecf20Sopenharmony_ci if (state->cfg->usref_qam64) 4398c2ecf20Sopenharmony_ci usref = state->cfg->usref_qam64; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case QAM_256: 4428c2ecf20Sopenharmony_ci if (state->cfg->usref_qam256) 4438c2ecf20Sopenharmony_ci usref = state->cfg->usref_qam256; 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci default: 4468c2ecf20Sopenharmony_ci return -EINVAL; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (usref) { 4508c2ecf20Sopenharmony_ci lg_dbg("set manual mode: 0x%04x\n", usref); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 3, 1); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_1, 4558c2ecf20Sopenharmony_ci 0xff & (usref >> 8)); 4568c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_2, 4578c2ecf20Sopenharmony_ci 0xff & (usref >> 0)); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int lgdt3305_spectral_inversion(struct lgdt3305_state *state, 4658c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p, 4668c2ecf20Sopenharmony_ci int inversion) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci int ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci lg_dbg("(%d)\n", inversion); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci switch (p->modulation) { 4738c2ecf20Sopenharmony_ci case VSB_8: 4748c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, LGDT3305_CR_CTRL_7, 4758c2ecf20Sopenharmony_ci inversion ? 0xf9 : 0x79); 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci case QAM_64: 4788c2ecf20Sopenharmony_ci case QAM_256: 4798c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, LGDT3305_FEC_BLOCK_CTRL, 4808c2ecf20Sopenharmony_ci inversion ? 0xfd : 0xff); 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci default: 4838c2ecf20Sopenharmony_ci ret = -EINVAL; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int lgdt3305_set_if(struct lgdt3305_state *state, 4898c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci u16 if_freq_khz; 4928c2ecf20Sopenharmony_ci u8 nco1, nco2, nco3, nco4; 4938c2ecf20Sopenharmony_ci u64 nco; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci switch (p->modulation) { 4968c2ecf20Sopenharmony_ci case VSB_8: 4978c2ecf20Sopenharmony_ci if_freq_khz = state->cfg->vsb_if_khz; 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci case QAM_64: 5008c2ecf20Sopenharmony_ci case QAM_256: 5018c2ecf20Sopenharmony_ci if_freq_khz = state->cfg->qam_if_khz; 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci default: 5048c2ecf20Sopenharmony_ci return -EINVAL; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci nco = if_freq_khz / 10; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci switch (p->modulation) { 5108c2ecf20Sopenharmony_ci case VSB_8: 5118c2ecf20Sopenharmony_ci nco <<= 24; 5128c2ecf20Sopenharmony_ci do_div(nco, 625); 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci case QAM_64: 5158c2ecf20Sopenharmony_ci case QAM_256: 5168c2ecf20Sopenharmony_ci nco <<= 28; 5178c2ecf20Sopenharmony_ci do_div(nco, 625); 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci default: 5208c2ecf20Sopenharmony_ci return -EINVAL; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci nco1 = (nco >> 24) & 0x3f; 5248c2ecf20Sopenharmony_ci nco1 |= 0x40; 5258c2ecf20Sopenharmony_ci nco2 = (nco >> 16) & 0xff; 5268c2ecf20Sopenharmony_ci nco3 = (nco >> 8) & 0xff; 5278c2ecf20Sopenharmony_ci nco4 = nco & 0xff; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, nco1); 5308c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, nco2); 5318c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, nco3); 5328c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, nco4); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci lg_dbg("%d KHz -> [%02x%02x%02x%02x]\n", 5358c2ecf20Sopenharmony_ci if_freq_khz, nco1, nco2, nco3, nco4); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (state->cfg->deny_i2c_rptr) 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci lg_dbg("(%d)\n", enable); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_2, 5, 5528c2ecf20Sopenharmony_ci enable ? 0 : 1); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int lgdt3305_sleep(struct dvb_frontend *fe) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 5588c2ecf20Sopenharmony_ci u8 gen_ctrl_3, gen_ctrl_4; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci lg_dbg("\n"); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci gen_ctrl_3 = read_reg(state, LGDT3305_GEN_CTRL_3); 5638c2ecf20Sopenharmony_ci gen_ctrl_4 = read_reg(state, LGDT3305_GEN_CTRL_4); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* hold in software reset while sleeping */ 5668c2ecf20Sopenharmony_ci gen_ctrl_3 &= ~0x01; 5678c2ecf20Sopenharmony_ci /* tristate the IF-AGC pin */ 5688c2ecf20Sopenharmony_ci gen_ctrl_3 |= 0x02; 5698c2ecf20Sopenharmony_ci /* tristate the RF-AGC pin */ 5708c2ecf20Sopenharmony_ci gen_ctrl_3 |= 0x04; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* disable vsb/qam module */ 5738c2ecf20Sopenharmony_ci gen_ctrl_4 &= ~0x01; 5748c2ecf20Sopenharmony_ci /* disable adc module */ 5758c2ecf20Sopenharmony_ci gen_ctrl_4 &= ~0x02; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_3, gen_ctrl_3); 5788c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_4, gen_ctrl_4); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int lgdt3305_init(struct dvb_frontend *fe) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 5868c2ecf20Sopenharmony_ci int ret; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci static struct lgdt3305_reg lgdt3304_init_data[] = { 5898c2ecf20Sopenharmony_ci { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, 5908c2ecf20Sopenharmony_ci { .reg = 0x000d, .val = 0x02, }, 5918c2ecf20Sopenharmony_ci { .reg = 0x000e, .val = 0x02, }, 5928c2ecf20Sopenharmony_ci { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, 5938c2ecf20Sopenharmony_ci { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, 5948c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, 5958c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, 5968c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, 5978c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, 5988c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, }, 5998c2ecf20Sopenharmony_ci { .reg = 0x0112, .val = 0x17, }, 6008c2ecf20Sopenharmony_ci { .reg = 0x0113, .val = 0x15, }, 6018c2ecf20Sopenharmony_ci { .reg = 0x0114, .val = 0x18, }, 6028c2ecf20Sopenharmony_ci { .reg = 0x0115, .val = 0xff, }, 6038c2ecf20Sopenharmony_ci { .reg = 0x0116, .val = 0x3c, }, 6048c2ecf20Sopenharmony_ci { .reg = 0x0214, .val = 0x67, }, 6058c2ecf20Sopenharmony_ci { .reg = 0x0424, .val = 0x8d, }, 6068c2ecf20Sopenharmony_ci { .reg = 0x0427, .val = 0x12, }, 6078c2ecf20Sopenharmony_ci { .reg = 0x0428, .val = 0x4f, }, 6088c2ecf20Sopenharmony_ci { .reg = LGDT3305_IFBW_1, .val = 0x80, }, 6098c2ecf20Sopenharmony_ci { .reg = LGDT3305_IFBW_2, .val = 0x00, }, 6108c2ecf20Sopenharmony_ci { .reg = 0x030a, .val = 0x08, }, 6118c2ecf20Sopenharmony_ci { .reg = 0x030b, .val = 0x9b, }, 6128c2ecf20Sopenharmony_ci { .reg = 0x030d, .val = 0x00, }, 6138c2ecf20Sopenharmony_ci { .reg = 0x030e, .val = 0x1c, }, 6148c2ecf20Sopenharmony_ci { .reg = 0x0314, .val = 0xe1, }, 6158c2ecf20Sopenharmony_ci { .reg = 0x000d, .val = 0x82, }, 6168c2ecf20Sopenharmony_ci { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, 6178c2ecf20Sopenharmony_ci { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, 6188c2ecf20Sopenharmony_ci }; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci static struct lgdt3305_reg lgdt3305_init_data[] = { 6218c2ecf20Sopenharmony_ci { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, 6228c2ecf20Sopenharmony_ci { .reg = LGDT3305_GEN_CTRL_2, .val = 0xb0, }, 6238c2ecf20Sopenharmony_ci { .reg = LGDT3305_GEN_CTRL_3, .val = 0x01, }, 6248c2ecf20Sopenharmony_ci { .reg = LGDT3305_GEN_CONTROL, .val = 0x6f, }, 6258c2ecf20Sopenharmony_ci { .reg = LGDT3305_GEN_CTRL_4, .val = 0x03, }, 6268c2ecf20Sopenharmony_ci { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, 6278c2ecf20Sopenharmony_ci { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, 6288c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, 6298c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, 6308c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, 6318c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, 6328c2ecf20Sopenharmony_ci { .reg = LGDT3305_CR_CTRL_7, .val = 0x79, }, 6338c2ecf20Sopenharmony_ci { .reg = LGDT3305_AGC_POWER_REF_1, .val = 0x32, }, 6348c2ecf20Sopenharmony_ci { .reg = LGDT3305_AGC_POWER_REF_2, .val = 0xc4, }, 6358c2ecf20Sopenharmony_ci { .reg = LGDT3305_AGC_DELAY_PT_1, .val = 0x0d, }, 6368c2ecf20Sopenharmony_ci { .reg = LGDT3305_AGC_DELAY_PT_2, .val = 0x30, }, 6378c2ecf20Sopenharmony_ci { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1, .val = 0x80, }, 6388c2ecf20Sopenharmony_ci { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2, .val = 0x00, }, 6398c2ecf20Sopenharmony_ci { .reg = LGDT3305_IFBW_1, .val = 0x80, }, 6408c2ecf20Sopenharmony_ci { .reg = LGDT3305_IFBW_2, .val = 0x00, }, 6418c2ecf20Sopenharmony_ci { .reg = LGDT3305_AGC_CTRL_1, .val = 0x30, }, 6428c2ecf20Sopenharmony_ci { .reg = LGDT3305_AGC_CTRL_4, .val = 0x61, }, 6438c2ecf20Sopenharmony_ci { .reg = LGDT3305_FEC_BLOCK_CTRL, .val = 0xff, }, 6448c2ecf20Sopenharmony_ci { .reg = LGDT3305_TP_CTRL_1, .val = 0x1b, }, 6458c2ecf20Sopenharmony_ci }; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci lg_dbg("\n"); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci switch (state->cfg->demod_chip) { 6508c2ecf20Sopenharmony_ci case LGDT3304: 6518c2ecf20Sopenharmony_ci ret = lgdt3305_write_regs(state, lgdt3304_init_data, 6528c2ecf20Sopenharmony_ci ARRAY_SIZE(lgdt3304_init_data)); 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci case LGDT3305: 6558c2ecf20Sopenharmony_ci ret = lgdt3305_write_regs(state, lgdt3305_init_data, 6568c2ecf20Sopenharmony_ci ARRAY_SIZE(lgdt3305_init_data)); 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci default: 6598c2ecf20Sopenharmony_ci ret = -EINVAL; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci if (lg_fail(ret)) 6628c2ecf20Sopenharmony_ci goto fail; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ret = lgdt3305_soft_reset(state); 6658c2ecf20Sopenharmony_cifail: 6668c2ecf20Sopenharmony_ci return ret; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic int lgdt3304_set_parameters(struct dvb_frontend *fe) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 6728c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 6738c2ecf20Sopenharmony_ci int ret; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci lg_dbg("(%d, %d)\n", p->frequency, p->modulation); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 6788c2ecf20Sopenharmony_ci ret = fe->ops.tuner_ops.set_params(fe); 6798c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 6808c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 6818c2ecf20Sopenharmony_ci if (lg_fail(ret)) 6828c2ecf20Sopenharmony_ci goto fail; 6838c2ecf20Sopenharmony_ci state->current_frequency = p->frequency; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci ret = lgdt3305_set_modulation(state, p); 6878c2ecf20Sopenharmony_ci if (lg_fail(ret)) 6888c2ecf20Sopenharmony_ci goto fail; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci ret = lgdt3305_passband_digital_agc(state, p); 6918c2ecf20Sopenharmony_ci if (lg_fail(ret)) 6928c2ecf20Sopenharmony_ci goto fail; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci ret = lgdt3305_agc_setup(state, p); 6958c2ecf20Sopenharmony_ci if (lg_fail(ret)) 6968c2ecf20Sopenharmony_ci goto fail; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */ 6998c2ecf20Sopenharmony_ci switch (p->modulation) { 7008c2ecf20Sopenharmony_ci case VSB_8: 7018c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, 0x030d, 0x00); 7028c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f); 7038c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c); 7048c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac); 7058c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba); 7068c2ecf20Sopenharmony_ci break; 7078c2ecf20Sopenharmony_ci case QAM_64: 7088c2ecf20Sopenharmony_ci case QAM_256: 7098c2ecf20Sopenharmony_ci lgdt3305_write_reg(state, 0x030d, 0x14); 7108c2ecf20Sopenharmony_ci ret = lgdt3305_set_if(state, p); 7118c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7128c2ecf20Sopenharmony_ci goto fail; 7138c2ecf20Sopenharmony_ci break; 7148c2ecf20Sopenharmony_ci default: 7158c2ecf20Sopenharmony_ci return -EINVAL; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci ret = lgdt3305_spectral_inversion(state, p, 7208c2ecf20Sopenharmony_ci state->cfg->spectral_inversion 7218c2ecf20Sopenharmony_ci ? 1 : 0); 7228c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7238c2ecf20Sopenharmony_ci goto fail; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci state->current_modulation = p->modulation; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); 7288c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7298c2ecf20Sopenharmony_ci goto fail; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ 7328c2ecf20Sopenharmony_ci ret = lgdt3305_mpeg_mode_polarity(state); 7338c2ecf20Sopenharmony_cifail: 7348c2ecf20Sopenharmony_ci return ret; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic int lgdt3305_set_parameters(struct dvb_frontend *fe) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 7408c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 7418c2ecf20Sopenharmony_ci int ret; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci lg_dbg("(%d, %d)\n", p->frequency, p->modulation); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 7468c2ecf20Sopenharmony_ci ret = fe->ops.tuner_ops.set_params(fe); 7478c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 7488c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 7498c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7508c2ecf20Sopenharmony_ci goto fail; 7518c2ecf20Sopenharmony_ci state->current_frequency = p->frequency; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci ret = lgdt3305_set_modulation(state, p); 7558c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7568c2ecf20Sopenharmony_ci goto fail; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci ret = lgdt3305_passband_digital_agc(state, p); 7598c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7608c2ecf20Sopenharmony_ci goto fail; 7618c2ecf20Sopenharmony_ci ret = lgdt3305_set_agc_power_ref(state, p); 7628c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7638c2ecf20Sopenharmony_ci goto fail; 7648c2ecf20Sopenharmony_ci ret = lgdt3305_agc_setup(state, p); 7658c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7668c2ecf20Sopenharmony_ci goto fail; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* low if */ 7698c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, LGDT3305_GEN_CONTROL, 0x2f); 7708c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7718c2ecf20Sopenharmony_ci goto fail; 7728c2ecf20Sopenharmony_ci ret = lgdt3305_set_reg_bit(state, LGDT3305_CR_CTR_FREQ_1, 6, 1); 7738c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7748c2ecf20Sopenharmony_ci goto fail; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci ret = lgdt3305_set_if(state, p); 7778c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7788c2ecf20Sopenharmony_ci goto fail; 7798c2ecf20Sopenharmony_ci ret = lgdt3305_spectral_inversion(state, p, 7808c2ecf20Sopenharmony_ci state->cfg->spectral_inversion 7818c2ecf20Sopenharmony_ci ? 1 : 0); 7828c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7838c2ecf20Sopenharmony_ci goto fail; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci ret = lgdt3305_set_filter_extension(state, p); 7868c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7878c2ecf20Sopenharmony_ci goto fail; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci state->current_modulation = p->modulation; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); 7928c2ecf20Sopenharmony_ci if (lg_fail(ret)) 7938c2ecf20Sopenharmony_ci goto fail; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ 7968c2ecf20Sopenharmony_ci ret = lgdt3305_mpeg_mode_polarity(state); 7978c2ecf20Sopenharmony_cifail: 7988c2ecf20Sopenharmony_ci return ret; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int lgdt3305_get_frontend(struct dvb_frontend *fe, 8028c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci lg_dbg("\n"); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci p->modulation = state->current_modulation; 8098c2ecf20Sopenharmony_ci p->frequency = state->current_frequency; 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic int lgdt3305_read_cr_lock_status(struct lgdt3305_state *state, 8168c2ecf20Sopenharmony_ci int *locked) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci u8 val; 8198c2ecf20Sopenharmony_ci int ret; 8208c2ecf20Sopenharmony_ci char *cr_lock_state = ""; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci *locked = 0; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci ret = lgdt3305_read_reg(state, LGDT3305_CR_LOCK_STATUS, &val); 8258c2ecf20Sopenharmony_ci if (lg_fail(ret)) 8268c2ecf20Sopenharmony_ci goto fail; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci switch (state->current_modulation) { 8298c2ecf20Sopenharmony_ci case QAM_256: 8308c2ecf20Sopenharmony_ci case QAM_64: 8318c2ecf20Sopenharmony_ci if (val & (1 << 1)) 8328c2ecf20Sopenharmony_ci *locked = 1; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci switch (val & 0x07) { 8358c2ecf20Sopenharmony_ci case 0: 8368c2ecf20Sopenharmony_ci cr_lock_state = "QAM UNLOCK"; 8378c2ecf20Sopenharmony_ci break; 8388c2ecf20Sopenharmony_ci case 4: 8398c2ecf20Sopenharmony_ci cr_lock_state = "QAM 1stLock"; 8408c2ecf20Sopenharmony_ci break; 8418c2ecf20Sopenharmony_ci case 6: 8428c2ecf20Sopenharmony_ci cr_lock_state = "QAM 2ndLock"; 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci case 7: 8458c2ecf20Sopenharmony_ci cr_lock_state = "QAM FinalLock"; 8468c2ecf20Sopenharmony_ci break; 8478c2ecf20Sopenharmony_ci default: 8488c2ecf20Sopenharmony_ci cr_lock_state = "CLOCKQAM-INVALID!"; 8498c2ecf20Sopenharmony_ci break; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci break; 8528c2ecf20Sopenharmony_ci case VSB_8: 8538c2ecf20Sopenharmony_ci if (val & (1 << 7)) { 8548c2ecf20Sopenharmony_ci *locked = 1; 8558c2ecf20Sopenharmony_ci cr_lock_state = "CLOCKVSB"; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci break; 8588c2ecf20Sopenharmony_ci default: 8598c2ecf20Sopenharmony_ci ret = -EINVAL; 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci lg_dbg("(%d) %s\n", *locked, cr_lock_state); 8628c2ecf20Sopenharmony_cifail: 8638c2ecf20Sopenharmony_ci return ret; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int lgdt3305_read_fec_lock_status(struct lgdt3305_state *state, 8678c2ecf20Sopenharmony_ci int *locked) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci u8 val; 8708c2ecf20Sopenharmony_ci int ret, mpeg_lock, fec_lock, viterbi_lock; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci *locked = 0; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci switch (state->current_modulation) { 8758c2ecf20Sopenharmony_ci case QAM_256: 8768c2ecf20Sopenharmony_ci case QAM_64: 8778c2ecf20Sopenharmony_ci ret = lgdt3305_read_reg(state, 8788c2ecf20Sopenharmony_ci LGDT3305_FEC_LOCK_STATUS, &val); 8798c2ecf20Sopenharmony_ci if (lg_fail(ret)) 8808c2ecf20Sopenharmony_ci goto fail; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci mpeg_lock = (val & (1 << 0)) ? 1 : 0; 8838c2ecf20Sopenharmony_ci fec_lock = (val & (1 << 2)) ? 1 : 0; 8848c2ecf20Sopenharmony_ci viterbi_lock = (val & (1 << 3)) ? 1 : 0; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci *locked = mpeg_lock && fec_lock && viterbi_lock; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci lg_dbg("(%d) %s%s%s\n", *locked, 8898c2ecf20Sopenharmony_ci mpeg_lock ? "mpeg lock " : "", 8908c2ecf20Sopenharmony_ci fec_lock ? "fec lock " : "", 8918c2ecf20Sopenharmony_ci viterbi_lock ? "viterbi lock" : ""); 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci case VSB_8: 8948c2ecf20Sopenharmony_ci default: 8958c2ecf20Sopenharmony_ci ret = -EINVAL; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_cifail: 8988c2ecf20Sopenharmony_ci return ret; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cistatic int lgdt3305_read_status(struct dvb_frontend *fe, enum fe_status *status) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 9048c2ecf20Sopenharmony_ci u8 val; 9058c2ecf20Sopenharmony_ci int ret, signal, inlock, nofecerr, snrgood, 9068c2ecf20Sopenharmony_ci cr_lock, fec_lock, sync_lock; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci *status = 0; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ret = lgdt3305_read_reg(state, LGDT3305_GEN_STATUS, &val); 9118c2ecf20Sopenharmony_ci if (lg_fail(ret)) 9128c2ecf20Sopenharmony_ci goto fail; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci signal = (val & (1 << 4)) ? 1 : 0; 9158c2ecf20Sopenharmony_ci inlock = (val & (1 << 3)) ? 0 : 1; 9168c2ecf20Sopenharmony_ci sync_lock = (val & (1 << 2)) ? 1 : 0; 9178c2ecf20Sopenharmony_ci nofecerr = (val & (1 << 1)) ? 1 : 0; 9188c2ecf20Sopenharmony_ci snrgood = (val & (1 << 0)) ? 1 : 0; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci lg_dbg("%s%s%s%s%s\n", 9218c2ecf20Sopenharmony_ci signal ? "SIGNALEXIST " : "", 9228c2ecf20Sopenharmony_ci inlock ? "INLOCK " : "", 9238c2ecf20Sopenharmony_ci sync_lock ? "SYNCLOCK " : "", 9248c2ecf20Sopenharmony_ci nofecerr ? "NOFECERR " : "", 9258c2ecf20Sopenharmony_ci snrgood ? "SNRGOOD " : ""); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci ret = lgdt3305_read_cr_lock_status(state, &cr_lock); 9288c2ecf20Sopenharmony_ci if (lg_fail(ret)) 9298c2ecf20Sopenharmony_ci goto fail; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (signal) 9328c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 9338c2ecf20Sopenharmony_ci if (cr_lock) 9348c2ecf20Sopenharmony_ci *status |= FE_HAS_CARRIER; 9358c2ecf20Sopenharmony_ci if (nofecerr) 9368c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 9378c2ecf20Sopenharmony_ci if (sync_lock) 9388c2ecf20Sopenharmony_ci *status |= FE_HAS_SYNC; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci switch (state->current_modulation) { 9418c2ecf20Sopenharmony_ci case QAM_256: 9428c2ecf20Sopenharmony_ci case QAM_64: 9438c2ecf20Sopenharmony_ci /* signal bit is unreliable on the DT3304 in QAM mode */ 9448c2ecf20Sopenharmony_ci if (((LGDT3304 == state->cfg->demod_chip)) && (cr_lock)) 9458c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci ret = lgdt3305_read_fec_lock_status(state, &fec_lock); 9488c2ecf20Sopenharmony_ci if (lg_fail(ret)) 9498c2ecf20Sopenharmony_ci goto fail; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (fec_lock) 9528c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK; 9538c2ecf20Sopenharmony_ci break; 9548c2ecf20Sopenharmony_ci case VSB_8: 9558c2ecf20Sopenharmony_ci if (inlock) 9568c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK; 9578c2ecf20Sopenharmony_ci break; 9588c2ecf20Sopenharmony_ci default: 9598c2ecf20Sopenharmony_ci ret = -EINVAL; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_cifail: 9628c2ecf20Sopenharmony_ci return ret; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci/* borrowed from lgdt330x.c */ 9688c2ecf20Sopenharmony_cistatic u32 calculate_snr(u32 mse, u32 c) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci if (mse == 0) /* no signal */ 9718c2ecf20Sopenharmony_ci return 0; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci mse = intlog10(mse); 9748c2ecf20Sopenharmony_ci if (mse > c) { 9758c2ecf20Sopenharmony_ci /* Negative SNR, which is possible, but realisticly the 9768c2ecf20Sopenharmony_ci demod will lose lock before the signal gets this bad. The 9778c2ecf20Sopenharmony_ci API only allows for unsigned values, so just return 0 */ 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci return 10*(c - mse); 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic int lgdt3305_read_snr(struct dvb_frontend *fe, u16 *snr) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 9868c2ecf20Sopenharmony_ci u32 noise; /* noise value */ 9878c2ecf20Sopenharmony_ci u32 c; /* per-modulation SNR calculation constant */ 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci switch (state->current_modulation) { 9908c2ecf20Sopenharmony_ci case VSB_8: 9918c2ecf20Sopenharmony_ci#ifdef USE_PTMSE 9928c2ecf20Sopenharmony_ci /* Use Phase Tracker Mean-Square Error Register */ 9938c2ecf20Sopenharmony_ci /* SNR for ranges from -13.11 to +44.08 */ 9948c2ecf20Sopenharmony_ci noise = ((read_reg(state, LGDT3305_PT_MSE_1) & 0x07) << 16) | 9958c2ecf20Sopenharmony_ci (read_reg(state, LGDT3305_PT_MSE_2) << 8) | 9968c2ecf20Sopenharmony_ci (read_reg(state, LGDT3305_PT_MSE_3) & 0xff); 9978c2ecf20Sopenharmony_ci c = 73957994; /* log10(25*32^2)*2^24 */ 9988c2ecf20Sopenharmony_ci#else 9998c2ecf20Sopenharmony_ci /* Use Equalizer Mean-Square Error Register */ 10008c2ecf20Sopenharmony_ci /* SNR for ranges from -16.12 to +44.08 */ 10018c2ecf20Sopenharmony_ci noise = ((read_reg(state, LGDT3305_EQ_MSE_1) & 0x0f) << 16) | 10028c2ecf20Sopenharmony_ci (read_reg(state, LGDT3305_EQ_MSE_2) << 8) | 10038c2ecf20Sopenharmony_ci (read_reg(state, LGDT3305_EQ_MSE_3) & 0xff); 10048c2ecf20Sopenharmony_ci c = 73957994; /* log10(25*32^2)*2^24 */ 10058c2ecf20Sopenharmony_ci#endif 10068c2ecf20Sopenharmony_ci break; 10078c2ecf20Sopenharmony_ci case QAM_64: 10088c2ecf20Sopenharmony_ci case QAM_256: 10098c2ecf20Sopenharmony_ci noise = (read_reg(state, LGDT3305_CR_MSE_1) << 8) | 10108c2ecf20Sopenharmony_ci (read_reg(state, LGDT3305_CR_MSE_2) & 0xff); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci c = (state->current_modulation == QAM_64) ? 10138c2ecf20Sopenharmony_ci 97939837 : 98026066; 10148c2ecf20Sopenharmony_ci /* log10(688128)*2^24 and log10(696320)*2^24 */ 10158c2ecf20Sopenharmony_ci break; 10168c2ecf20Sopenharmony_ci default: 10178c2ecf20Sopenharmony_ci return -EINVAL; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci state->snr = calculate_snr(noise, c); 10208c2ecf20Sopenharmony_ci /* report SNR in dB * 10 */ 10218c2ecf20Sopenharmony_ci *snr = (state->snr / ((1 << 24) / 10)); 10228c2ecf20Sopenharmony_ci lg_dbg("noise = 0x%08x, snr = %d.%02d dB\n", noise, 10238c2ecf20Sopenharmony_ci state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci return 0; 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic int lgdt3305_read_signal_strength(struct dvb_frontend *fe, 10298c2ecf20Sopenharmony_ci u16 *strength) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci /* borrowed from lgdt330x.c 10328c2ecf20Sopenharmony_ci * 10338c2ecf20Sopenharmony_ci * Calculate strength from SNR up to 35dB 10348c2ecf20Sopenharmony_ci * Even though the SNR can go higher than 35dB, 10358c2ecf20Sopenharmony_ci * there is some comfort factor in having a range of 10368c2ecf20Sopenharmony_ci * strong signals that can show at 100% 10378c2ecf20Sopenharmony_ci */ 10388c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 10398c2ecf20Sopenharmony_ci u16 snr; 10408c2ecf20Sopenharmony_ci int ret; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci *strength = 0; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci ret = fe->ops.read_snr(fe, &snr); 10458c2ecf20Sopenharmony_ci if (lg_fail(ret)) 10468c2ecf20Sopenharmony_ci goto fail; 10478c2ecf20Sopenharmony_ci /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ 10488c2ecf20Sopenharmony_ci /* scale the range 0 - 35*2^24 into 0 - 65535 */ 10498c2ecf20Sopenharmony_ci if (state->snr >= 8960 * 0x10000) 10508c2ecf20Sopenharmony_ci *strength = 0xffff; 10518c2ecf20Sopenharmony_ci else 10528c2ecf20Sopenharmony_ci *strength = state->snr / 8960; 10538c2ecf20Sopenharmony_cifail: 10548c2ecf20Sopenharmony_ci return ret; 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic int lgdt3305_read_ber(struct dvb_frontend *fe, u32 *ber) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci *ber = 0; 10628c2ecf20Sopenharmony_ci return 0; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic int lgdt3305_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci *ucblocks = 10708c2ecf20Sopenharmony_ci (read_reg(state, LGDT3305_FEC_PKT_ERR_1) << 8) | 10718c2ecf20Sopenharmony_ci (read_reg(state, LGDT3305_FEC_PKT_ERR_2) & 0xff); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci return 0; 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic int lgdt3305_get_tune_settings(struct dvb_frontend *fe, 10778c2ecf20Sopenharmony_ci struct dvb_frontend_tune_settings 10788c2ecf20Sopenharmony_ci *fe_tune_settings) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci fe_tune_settings->min_delay_ms = 500; 10818c2ecf20Sopenharmony_ci lg_dbg("\n"); 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistatic void lgdt3305_release(struct dvb_frontend *fe) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci struct lgdt3305_state *state = fe->demodulator_priv; 10888c2ecf20Sopenharmony_ci lg_dbg("\n"); 10898c2ecf20Sopenharmony_ci kfree(state); 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops lgdt3304_ops; 10938c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops lgdt3305_ops; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistruct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, 10968c2ecf20Sopenharmony_ci struct i2c_adapter *i2c_adap) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct lgdt3305_state *state = NULL; 10998c2ecf20Sopenharmony_ci int ret; 11008c2ecf20Sopenharmony_ci u8 val; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci lg_dbg("(%d-%04x)\n", 11038c2ecf20Sopenharmony_ci i2c_adap ? i2c_adapter_id(i2c_adap) : 0, 11048c2ecf20Sopenharmony_ci config ? config->i2c_addr : 0); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct lgdt3305_state), GFP_KERNEL); 11078c2ecf20Sopenharmony_ci if (state == NULL) 11088c2ecf20Sopenharmony_ci goto fail; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci state->cfg = config; 11118c2ecf20Sopenharmony_ci state->i2c_adap = i2c_adap; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci switch (config->demod_chip) { 11148c2ecf20Sopenharmony_ci case LGDT3304: 11158c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &lgdt3304_ops, 11168c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 11178c2ecf20Sopenharmony_ci break; 11188c2ecf20Sopenharmony_ci case LGDT3305: 11198c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &lgdt3305_ops, 11208c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 11218c2ecf20Sopenharmony_ci break; 11228c2ecf20Sopenharmony_ci default: 11238c2ecf20Sopenharmony_ci goto fail; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* verify that we're talking to a lg dt3304/5 */ 11288c2ecf20Sopenharmony_ci ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); 11298c2ecf20Sopenharmony_ci if ((lg_fail(ret)) | (val == 0)) 11308c2ecf20Sopenharmony_ci goto fail; 11318c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, 0x0808, 0x80); 11328c2ecf20Sopenharmony_ci if (lg_fail(ret)) 11338c2ecf20Sopenharmony_ci goto fail; 11348c2ecf20Sopenharmony_ci ret = lgdt3305_read_reg(state, 0x0808, &val); 11358c2ecf20Sopenharmony_ci if ((lg_fail(ret)) | (val != 0x80)) 11368c2ecf20Sopenharmony_ci goto fail; 11378c2ecf20Sopenharmony_ci ret = lgdt3305_write_reg(state, 0x0808, 0x00); 11388c2ecf20Sopenharmony_ci if (lg_fail(ret)) 11398c2ecf20Sopenharmony_ci goto fail; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci state->current_frequency = -1; 11428c2ecf20Sopenharmony_ci state->current_modulation = -1; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return &state->frontend; 11458c2ecf20Sopenharmony_cifail: 11468c2ecf20Sopenharmony_ci lg_warn("unable to detect %s hardware\n", 11478c2ecf20Sopenharmony_ci config->demod_chip ? "LGDT3304" : "LGDT3305"); 11488c2ecf20Sopenharmony_ci kfree(state); 11498c2ecf20Sopenharmony_ci return NULL; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lgdt3305_attach); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops lgdt3304_ops = { 11548c2ecf20Sopenharmony_ci .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, 11558c2ecf20Sopenharmony_ci .info = { 11568c2ecf20Sopenharmony_ci .name = "LG Electronics LGDT3304 VSB/QAM Frontend", 11578c2ecf20Sopenharmony_ci .frequency_min_hz = 54 * MHz, 11588c2ecf20Sopenharmony_ci .frequency_max_hz = 858 * MHz, 11598c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 62500, 11608c2ecf20Sopenharmony_ci .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB 11618c2ecf20Sopenharmony_ci }, 11628c2ecf20Sopenharmony_ci .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, 11638c2ecf20Sopenharmony_ci .init = lgdt3305_init, 11648c2ecf20Sopenharmony_ci .sleep = lgdt3305_sleep, 11658c2ecf20Sopenharmony_ci .set_frontend = lgdt3304_set_parameters, 11668c2ecf20Sopenharmony_ci .get_frontend = lgdt3305_get_frontend, 11678c2ecf20Sopenharmony_ci .get_tune_settings = lgdt3305_get_tune_settings, 11688c2ecf20Sopenharmony_ci .read_status = lgdt3305_read_status, 11698c2ecf20Sopenharmony_ci .read_ber = lgdt3305_read_ber, 11708c2ecf20Sopenharmony_ci .read_signal_strength = lgdt3305_read_signal_strength, 11718c2ecf20Sopenharmony_ci .read_snr = lgdt3305_read_snr, 11728c2ecf20Sopenharmony_ci .read_ucblocks = lgdt3305_read_ucblocks, 11738c2ecf20Sopenharmony_ci .release = lgdt3305_release, 11748c2ecf20Sopenharmony_ci}; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops lgdt3305_ops = { 11778c2ecf20Sopenharmony_ci .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, 11788c2ecf20Sopenharmony_ci .info = { 11798c2ecf20Sopenharmony_ci .name = "LG Electronics LGDT3305 VSB/QAM Frontend", 11808c2ecf20Sopenharmony_ci .frequency_min_hz = 54 * MHz, 11818c2ecf20Sopenharmony_ci .frequency_max_hz = 858 * MHz, 11828c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 62500, 11838c2ecf20Sopenharmony_ci .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB 11848c2ecf20Sopenharmony_ci }, 11858c2ecf20Sopenharmony_ci .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, 11868c2ecf20Sopenharmony_ci .init = lgdt3305_init, 11878c2ecf20Sopenharmony_ci .sleep = lgdt3305_sleep, 11888c2ecf20Sopenharmony_ci .set_frontend = lgdt3305_set_parameters, 11898c2ecf20Sopenharmony_ci .get_frontend = lgdt3305_get_frontend, 11908c2ecf20Sopenharmony_ci .get_tune_settings = lgdt3305_get_tune_settings, 11918c2ecf20Sopenharmony_ci .read_status = lgdt3305_read_status, 11928c2ecf20Sopenharmony_ci .read_ber = lgdt3305_read_ber, 11938c2ecf20Sopenharmony_ci .read_signal_strength = lgdt3305_read_signal_strength, 11948c2ecf20Sopenharmony_ci .read_snr = lgdt3305_read_snr, 11958c2ecf20Sopenharmony_ci .read_ucblocks = lgdt3305_read_ucblocks, 11968c2ecf20Sopenharmony_ci .release = lgdt3305_release, 11978c2ecf20Sopenharmony_ci}; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); 12008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); 12018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12028c2ecf20Sopenharmony_ciMODULE_VERSION("0.2"); 1203