1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 29a0bf528SMauro Carvalho Chehab /* 39a0bf528SMauro Carvalho Chehab * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM 49a0bf528SMauro Carvalho Chehab * 59a0bf528SMauro Carvalho Chehab * Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.org> 69a0bf528SMauro Carvalho Chehab * 79a0bf528SMauro Carvalho Chehab * LGDT3304 support by Jarod Wilson <jarod@redhat.com> 89a0bf528SMauro Carvalho Chehab */ 99a0bf528SMauro Carvalho Chehab 109a0bf528SMauro Carvalho Chehab #include <asm/div64.h> 119a0bf528SMauro Carvalho Chehab #include <linux/dvb/frontend.h> 129a0bf528SMauro Carvalho Chehab #include <linux/slab.h> 13f97fa3dcSAndy Shevchenko #include <linux/int_log.h> 149a0bf528SMauro Carvalho Chehab #include "lgdt3305.h" 159a0bf528SMauro Carvalho Chehab 169a0bf528SMauro Carvalho Chehab static int debug; 179a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644); 189a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); 199a0bf528SMauro Carvalho Chehab 209a0bf528SMauro Carvalho Chehab #define DBG_INFO 1 219a0bf528SMauro Carvalho Chehab #define DBG_REG 2 229a0bf528SMauro Carvalho Chehab 239a0bf528SMauro Carvalho Chehab #define lg_printk(kern, fmt, arg...) \ 249a0bf528SMauro Carvalho Chehab printk(kern "%s: " fmt, __func__, ##arg) 259a0bf528SMauro Carvalho Chehab 269a0bf528SMauro Carvalho Chehab #define lg_info(fmt, arg...) printk(KERN_INFO "lgdt3305: " fmt, ##arg) 279a0bf528SMauro Carvalho Chehab #define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) 289a0bf528SMauro Carvalho Chehab #define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) 299a0bf528SMauro Carvalho Chehab #define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ 309a0bf528SMauro Carvalho Chehab lg_printk(KERN_DEBUG, fmt, ##arg) 319a0bf528SMauro Carvalho Chehab #define lg_reg(fmt, arg...) if (debug & DBG_REG) \ 329a0bf528SMauro Carvalho Chehab lg_printk(KERN_DEBUG, fmt, ##arg) 339a0bf528SMauro Carvalho Chehab 349a0bf528SMauro Carvalho Chehab #define lg_fail(ret) \ 359a0bf528SMauro Carvalho Chehab ({ \ 369a0bf528SMauro Carvalho Chehab int __ret; \ 379a0bf528SMauro Carvalho Chehab __ret = (ret < 0); \ 389a0bf528SMauro Carvalho Chehab if (__ret) \ 399a0bf528SMauro Carvalho Chehab lg_err("error %d on line %d\n", ret, __LINE__); \ 409a0bf528SMauro Carvalho Chehab __ret; \ 419a0bf528SMauro Carvalho Chehab }) 429a0bf528SMauro Carvalho Chehab 439a0bf528SMauro Carvalho Chehab struct lgdt3305_state { 449a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c_adap; 459a0bf528SMauro Carvalho Chehab const struct lgdt3305_config *cfg; 469a0bf528SMauro Carvalho Chehab 479a0bf528SMauro Carvalho Chehab struct dvb_frontend frontend; 489a0bf528SMauro Carvalho Chehab 490df289a2SMauro Carvalho Chehab enum fe_modulation current_modulation; 509a0bf528SMauro Carvalho Chehab u32 current_frequency; 519a0bf528SMauro Carvalho Chehab u32 snr; 529a0bf528SMauro Carvalho Chehab }; 539a0bf528SMauro Carvalho Chehab 549a0bf528SMauro Carvalho Chehab /* ------------------------------------------------------------------------ */ 559a0bf528SMauro Carvalho Chehab 569a0bf528SMauro Carvalho Chehab /* FIXME: verify & document the LGDT3304 registers */ 579a0bf528SMauro Carvalho Chehab 589a0bf528SMauro Carvalho Chehab #define LGDT3305_GEN_CTRL_1 0x0000 599a0bf528SMauro Carvalho Chehab #define LGDT3305_GEN_CTRL_2 0x0001 609a0bf528SMauro Carvalho Chehab #define LGDT3305_GEN_CTRL_3 0x0002 619a0bf528SMauro Carvalho Chehab #define LGDT3305_GEN_STATUS 0x0003 629a0bf528SMauro Carvalho Chehab #define LGDT3305_GEN_CONTROL 0x0007 639a0bf528SMauro Carvalho Chehab #define LGDT3305_GEN_CTRL_4 0x000a 649a0bf528SMauro Carvalho Chehab #define LGDT3305_DGTL_AGC_REF_1 0x0012 659a0bf528SMauro Carvalho Chehab #define LGDT3305_DGTL_AGC_REF_2 0x0013 669a0bf528SMauro Carvalho Chehab #define LGDT3305_CR_CTR_FREQ_1 0x0106 679a0bf528SMauro Carvalho Chehab #define LGDT3305_CR_CTR_FREQ_2 0x0107 689a0bf528SMauro Carvalho Chehab #define LGDT3305_CR_CTR_FREQ_3 0x0108 699a0bf528SMauro Carvalho Chehab #define LGDT3305_CR_CTR_FREQ_4 0x0109 709a0bf528SMauro Carvalho Chehab #define LGDT3305_CR_MSE_1 0x011b 719a0bf528SMauro Carvalho Chehab #define LGDT3305_CR_MSE_2 0x011c 729a0bf528SMauro Carvalho Chehab #define LGDT3305_CR_LOCK_STATUS 0x011d 739a0bf528SMauro Carvalho Chehab #define LGDT3305_CR_CTRL_7 0x0126 749a0bf528SMauro Carvalho Chehab #define LGDT3305_AGC_POWER_REF_1 0x0300 759a0bf528SMauro Carvalho Chehab #define LGDT3305_AGC_POWER_REF_2 0x0301 769a0bf528SMauro Carvalho Chehab #define LGDT3305_AGC_DELAY_PT_1 0x0302 779a0bf528SMauro Carvalho Chehab #define LGDT3305_AGC_DELAY_PT_2 0x0303 789a0bf528SMauro Carvalho Chehab #define LGDT3305_RFAGC_LOOP_FLTR_BW_1 0x0306 799a0bf528SMauro Carvalho Chehab #define LGDT3305_RFAGC_LOOP_FLTR_BW_2 0x0307 809a0bf528SMauro Carvalho Chehab #define LGDT3305_IFBW_1 0x0308 819a0bf528SMauro Carvalho Chehab #define LGDT3305_IFBW_2 0x0309 829a0bf528SMauro Carvalho Chehab #define LGDT3305_AGC_CTRL_1 0x030c 839a0bf528SMauro Carvalho Chehab #define LGDT3305_AGC_CTRL_4 0x0314 849a0bf528SMauro Carvalho Chehab #define LGDT3305_EQ_MSE_1 0x0413 859a0bf528SMauro Carvalho Chehab #define LGDT3305_EQ_MSE_2 0x0414 869a0bf528SMauro Carvalho Chehab #define LGDT3305_EQ_MSE_3 0x0415 879a0bf528SMauro Carvalho Chehab #define LGDT3305_PT_MSE_1 0x0417 889a0bf528SMauro Carvalho Chehab #define LGDT3305_PT_MSE_2 0x0418 899a0bf528SMauro Carvalho Chehab #define LGDT3305_PT_MSE_3 0x0419 909a0bf528SMauro Carvalho Chehab #define LGDT3305_FEC_BLOCK_CTRL 0x0504 919a0bf528SMauro Carvalho Chehab #define LGDT3305_FEC_LOCK_STATUS 0x050a 929a0bf528SMauro Carvalho Chehab #define LGDT3305_FEC_PKT_ERR_1 0x050c 939a0bf528SMauro Carvalho Chehab #define LGDT3305_FEC_PKT_ERR_2 0x050d 949a0bf528SMauro Carvalho Chehab #define LGDT3305_TP_CTRL_1 0x050e 959a0bf528SMauro Carvalho Chehab #define LGDT3305_BERT_PERIOD 0x0801 969a0bf528SMauro Carvalho Chehab #define LGDT3305_BERT_ERROR_COUNT_1 0x080a 979a0bf528SMauro Carvalho Chehab #define LGDT3305_BERT_ERROR_COUNT_2 0x080b 989a0bf528SMauro Carvalho Chehab #define LGDT3305_BERT_ERROR_COUNT_3 0x080c 999a0bf528SMauro Carvalho Chehab #define LGDT3305_BERT_ERROR_COUNT_4 0x080d 1009a0bf528SMauro Carvalho Chehab 1019a0bf528SMauro Carvalho Chehab static int lgdt3305_write_reg(struct lgdt3305_state *state, u16 reg, u8 val) 1029a0bf528SMauro Carvalho Chehab { 1039a0bf528SMauro Carvalho Chehab int ret; 1049a0bf528SMauro Carvalho Chehab u8 buf[] = { reg >> 8, reg & 0xff, val }; 1059a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { 1069a0bf528SMauro Carvalho Chehab .addr = state->cfg->i2c_addr, .flags = 0, 1079a0bf528SMauro Carvalho Chehab .buf = buf, .len = 3, 1089a0bf528SMauro Carvalho Chehab }; 1099a0bf528SMauro Carvalho Chehab 1109a0bf528SMauro Carvalho Chehab lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); 1119a0bf528SMauro Carvalho Chehab 1129a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c_adap, &msg, 1); 1139a0bf528SMauro Carvalho Chehab 1149a0bf528SMauro Carvalho Chehab if (ret != 1) { 1159a0bf528SMauro Carvalho Chehab lg_err("error (addr %02x %02x <- %02x, err = %i)\n", 1169a0bf528SMauro Carvalho Chehab msg.buf[0], msg.buf[1], msg.buf[2], ret); 1179a0bf528SMauro Carvalho Chehab if (ret < 0) 1189a0bf528SMauro Carvalho Chehab return ret; 1199a0bf528SMauro Carvalho Chehab else 1209a0bf528SMauro Carvalho Chehab return -EREMOTEIO; 1219a0bf528SMauro Carvalho Chehab } 1229a0bf528SMauro Carvalho Chehab return 0; 1239a0bf528SMauro Carvalho Chehab } 1249a0bf528SMauro Carvalho Chehab 1259a0bf528SMauro Carvalho Chehab static int lgdt3305_read_reg(struct lgdt3305_state *state, u16 reg, u8 *val) 1269a0bf528SMauro Carvalho Chehab { 1279a0bf528SMauro Carvalho Chehab int ret; 1289a0bf528SMauro Carvalho Chehab u8 reg_buf[] = { reg >> 8, reg & 0xff }; 1299a0bf528SMauro Carvalho Chehab struct i2c_msg msg[] = { 1309a0bf528SMauro Carvalho Chehab { .addr = state->cfg->i2c_addr, 1319a0bf528SMauro Carvalho Chehab .flags = 0, .buf = reg_buf, .len = 2 }, 1329a0bf528SMauro Carvalho Chehab { .addr = state->cfg->i2c_addr, 1339a0bf528SMauro Carvalho Chehab .flags = I2C_M_RD, .buf = val, .len = 1 }, 1349a0bf528SMauro Carvalho Chehab }; 1359a0bf528SMauro Carvalho Chehab 1369a0bf528SMauro Carvalho Chehab lg_reg("reg: 0x%04x\n", reg); 1379a0bf528SMauro Carvalho Chehab 1389a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c_adap, msg, 2); 1399a0bf528SMauro Carvalho Chehab 1409a0bf528SMauro Carvalho Chehab if (ret != 2) { 1419a0bf528SMauro Carvalho Chehab lg_err("error (addr %02x reg %04x error (ret == %i)\n", 1429a0bf528SMauro Carvalho Chehab state->cfg->i2c_addr, reg, ret); 1439a0bf528SMauro Carvalho Chehab if (ret < 0) 1449a0bf528SMauro Carvalho Chehab return ret; 1459a0bf528SMauro Carvalho Chehab else 1469a0bf528SMauro Carvalho Chehab return -EREMOTEIO; 1479a0bf528SMauro Carvalho Chehab } 1489a0bf528SMauro Carvalho Chehab return 0; 1499a0bf528SMauro Carvalho Chehab } 1509a0bf528SMauro Carvalho Chehab 1519a0bf528SMauro Carvalho Chehab #define read_reg(state, reg) \ 1529a0bf528SMauro Carvalho Chehab ({ \ 1539a0bf528SMauro Carvalho Chehab u8 __val; \ 1549a0bf528SMauro Carvalho Chehab int ret = lgdt3305_read_reg(state, reg, &__val); \ 1559a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) \ 1569a0bf528SMauro Carvalho Chehab __val = 0; \ 1579a0bf528SMauro Carvalho Chehab __val; \ 1589a0bf528SMauro Carvalho Chehab }) 1599a0bf528SMauro Carvalho Chehab 1609a0bf528SMauro Carvalho Chehab static int lgdt3305_set_reg_bit(struct lgdt3305_state *state, 1619a0bf528SMauro Carvalho Chehab u16 reg, int bit, int onoff) 1629a0bf528SMauro Carvalho Chehab { 1639a0bf528SMauro Carvalho Chehab u8 val; 1649a0bf528SMauro Carvalho Chehab int ret; 1659a0bf528SMauro Carvalho Chehab 1669a0bf528SMauro Carvalho Chehab lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); 1679a0bf528SMauro Carvalho Chehab 1689a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_reg(state, reg, &val); 1699a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 1709a0bf528SMauro Carvalho Chehab goto fail; 1719a0bf528SMauro Carvalho Chehab 1729a0bf528SMauro Carvalho Chehab val &= ~(1 << bit); 1739a0bf528SMauro Carvalho Chehab val |= (onoff & 1) << bit; 1749a0bf528SMauro Carvalho Chehab 1759a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, reg, val); 1769a0bf528SMauro Carvalho Chehab fail: 1779a0bf528SMauro Carvalho Chehab return ret; 1789a0bf528SMauro Carvalho Chehab } 1799a0bf528SMauro Carvalho Chehab 1809a0bf528SMauro Carvalho Chehab struct lgdt3305_reg { 1819a0bf528SMauro Carvalho Chehab u16 reg; 1829a0bf528SMauro Carvalho Chehab u8 val; 1839a0bf528SMauro Carvalho Chehab }; 1849a0bf528SMauro Carvalho Chehab 1859a0bf528SMauro Carvalho Chehab static int lgdt3305_write_regs(struct lgdt3305_state *state, 1869a0bf528SMauro Carvalho Chehab struct lgdt3305_reg *regs, int len) 1879a0bf528SMauro Carvalho Chehab { 1889a0bf528SMauro Carvalho Chehab int i, ret; 1899a0bf528SMauro Carvalho Chehab 1909a0bf528SMauro Carvalho Chehab lg_reg("writing %d registers...\n", len); 1919a0bf528SMauro Carvalho Chehab 1929a0bf528SMauro Carvalho Chehab for (i = 0; i < len - 1; i++) { 1939a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, regs[i].reg, regs[i].val); 1949a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 1959a0bf528SMauro Carvalho Chehab return ret; 1969a0bf528SMauro Carvalho Chehab } 1979a0bf528SMauro Carvalho Chehab return 0; 1989a0bf528SMauro Carvalho Chehab } 1999a0bf528SMauro Carvalho Chehab 2009a0bf528SMauro Carvalho Chehab /* ------------------------------------------------------------------------ */ 2019a0bf528SMauro Carvalho Chehab 2029a0bf528SMauro Carvalho Chehab static int lgdt3305_soft_reset(struct lgdt3305_state *state) 2039a0bf528SMauro Carvalho Chehab { 2049a0bf528SMauro Carvalho Chehab int ret; 2059a0bf528SMauro Carvalho Chehab 2069a0bf528SMauro Carvalho Chehab lg_dbg("\n"); 2079a0bf528SMauro Carvalho Chehab 2089a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 0); 2099a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 2109a0bf528SMauro Carvalho Chehab goto fail; 2119a0bf528SMauro Carvalho Chehab 2129a0bf528SMauro Carvalho Chehab msleep(20); 2139a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 1); 2149a0bf528SMauro Carvalho Chehab fail: 2159a0bf528SMauro Carvalho Chehab return ret; 2169a0bf528SMauro Carvalho Chehab } 2179a0bf528SMauro Carvalho Chehab 2189a0bf528SMauro Carvalho Chehab static inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state, 2199a0bf528SMauro Carvalho Chehab enum lgdt3305_mpeg_mode mode) 2209a0bf528SMauro Carvalho Chehab { 2219a0bf528SMauro Carvalho Chehab lg_dbg("(%d)\n", mode); 2229a0bf528SMauro Carvalho Chehab return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode); 2239a0bf528SMauro Carvalho Chehab } 2249a0bf528SMauro Carvalho Chehab 225bdba90dfSMichael Ira Krufky static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state) 2269a0bf528SMauro Carvalho Chehab { 2279a0bf528SMauro Carvalho Chehab u8 val; 2289a0bf528SMauro Carvalho Chehab int ret; 229bdba90dfSMichael Ira Krufky enum lgdt3305_tp_clock_edge edge = state->cfg->tpclk_edge; 23027f7ef7cSMichael Ira Krufky enum lgdt3305_tp_clock_mode mode = state->cfg->tpclk_mode; 231bdba90dfSMichael Ira Krufky enum lgdt3305_tp_valid_polarity valid = state->cfg->tpvalid_polarity; 2329a0bf528SMauro Carvalho Chehab 2339a0bf528SMauro Carvalho Chehab lg_dbg("edge = %d, valid = %d\n", edge, valid); 2349a0bf528SMauro Carvalho Chehab 2359a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_reg(state, LGDT3305_TP_CTRL_1, &val); 2369a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 2379a0bf528SMauro Carvalho Chehab goto fail; 2389a0bf528SMauro Carvalho Chehab 2399a0bf528SMauro Carvalho Chehab val &= ~0x09; 2409a0bf528SMauro Carvalho Chehab 2419a0bf528SMauro Carvalho Chehab if (edge) 2429a0bf528SMauro Carvalho Chehab val |= 0x08; 24327f7ef7cSMichael Ira Krufky if (mode) 24427f7ef7cSMichael Ira Krufky val |= 0x40; 2459a0bf528SMauro Carvalho Chehab if (valid) 2469a0bf528SMauro Carvalho Chehab val |= 0x01; 2479a0bf528SMauro Carvalho Chehab 2489a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, LGDT3305_TP_CTRL_1, val); 2499a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 2509a0bf528SMauro Carvalho Chehab goto fail; 2519a0bf528SMauro Carvalho Chehab 2529a0bf528SMauro Carvalho Chehab ret = lgdt3305_soft_reset(state); 2539a0bf528SMauro Carvalho Chehab fail: 2549a0bf528SMauro Carvalho Chehab return ret; 2559a0bf528SMauro Carvalho Chehab } 2569a0bf528SMauro Carvalho Chehab 2579a0bf528SMauro Carvalho Chehab static int lgdt3305_set_modulation(struct lgdt3305_state *state, 2589a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p) 2599a0bf528SMauro Carvalho Chehab { 2609a0bf528SMauro Carvalho Chehab u8 opermode; 2619a0bf528SMauro Carvalho Chehab int ret; 2629a0bf528SMauro Carvalho Chehab 2639a0bf528SMauro Carvalho Chehab lg_dbg("\n"); 2649a0bf528SMauro Carvalho Chehab 2659a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_1, &opermode); 2669a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 2679a0bf528SMauro Carvalho Chehab goto fail; 2689a0bf528SMauro Carvalho Chehab 2699a0bf528SMauro Carvalho Chehab opermode &= ~0x03; 2709a0bf528SMauro Carvalho Chehab 2719a0bf528SMauro Carvalho Chehab switch (p->modulation) { 2729a0bf528SMauro Carvalho Chehab case VSB_8: 2739a0bf528SMauro Carvalho Chehab opermode |= 0x03; 2749a0bf528SMauro Carvalho Chehab break; 2759a0bf528SMauro Carvalho Chehab case QAM_64: 2769a0bf528SMauro Carvalho Chehab opermode |= 0x00; 2779a0bf528SMauro Carvalho Chehab break; 2789a0bf528SMauro Carvalho Chehab case QAM_256: 2799a0bf528SMauro Carvalho Chehab opermode |= 0x01; 2809a0bf528SMauro Carvalho Chehab break; 2819a0bf528SMauro Carvalho Chehab default: 2829a0bf528SMauro Carvalho Chehab return -EINVAL; 2839a0bf528SMauro Carvalho Chehab } 2849a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_1, opermode); 2859a0bf528SMauro Carvalho Chehab fail: 2869a0bf528SMauro Carvalho Chehab return ret; 2879a0bf528SMauro Carvalho Chehab } 2889a0bf528SMauro Carvalho Chehab 2899a0bf528SMauro Carvalho Chehab static int lgdt3305_set_filter_extension(struct lgdt3305_state *state, 2909a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p) 2919a0bf528SMauro Carvalho Chehab { 2929a0bf528SMauro Carvalho Chehab int val; 2939a0bf528SMauro Carvalho Chehab 2949a0bf528SMauro Carvalho Chehab switch (p->modulation) { 2959a0bf528SMauro Carvalho Chehab case VSB_8: 2969a0bf528SMauro Carvalho Chehab val = 0; 2979a0bf528SMauro Carvalho Chehab break; 2989a0bf528SMauro Carvalho Chehab case QAM_64: 2999a0bf528SMauro Carvalho Chehab case QAM_256: 3009a0bf528SMauro Carvalho Chehab val = 1; 3019a0bf528SMauro Carvalho Chehab break; 3029a0bf528SMauro Carvalho Chehab default: 3039a0bf528SMauro Carvalho Chehab return -EINVAL; 3049a0bf528SMauro Carvalho Chehab } 3059a0bf528SMauro Carvalho Chehab lg_dbg("val = %d\n", val); 3069a0bf528SMauro Carvalho Chehab 3079a0bf528SMauro Carvalho Chehab return lgdt3305_set_reg_bit(state, 0x043f, 2, val); 3089a0bf528SMauro Carvalho Chehab } 3099a0bf528SMauro Carvalho Chehab 3109a0bf528SMauro Carvalho Chehab /* ------------------------------------------------------------------------ */ 3119a0bf528SMauro Carvalho Chehab 3129a0bf528SMauro Carvalho Chehab static int lgdt3305_passband_digital_agc(struct lgdt3305_state *state, 3139a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p) 3149a0bf528SMauro Carvalho Chehab { 3159a0bf528SMauro Carvalho Chehab u16 agc_ref; 3169a0bf528SMauro Carvalho Chehab 3179a0bf528SMauro Carvalho Chehab switch (p->modulation) { 3189a0bf528SMauro Carvalho Chehab case VSB_8: 3199a0bf528SMauro Carvalho Chehab agc_ref = 0x32c4; 3209a0bf528SMauro Carvalho Chehab break; 3219a0bf528SMauro Carvalho Chehab case QAM_64: 3229a0bf528SMauro Carvalho Chehab agc_ref = 0x2a00; 3239a0bf528SMauro Carvalho Chehab break; 3249a0bf528SMauro Carvalho Chehab case QAM_256: 3259a0bf528SMauro Carvalho Chehab agc_ref = 0x2a80; 3269a0bf528SMauro Carvalho Chehab break; 3279a0bf528SMauro Carvalho Chehab default: 3289a0bf528SMauro Carvalho Chehab return -EINVAL; 3299a0bf528SMauro Carvalho Chehab } 3309a0bf528SMauro Carvalho Chehab 3319a0bf528SMauro Carvalho Chehab lg_dbg("agc ref: 0x%04x\n", agc_ref); 3329a0bf528SMauro Carvalho Chehab 3339a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_1, agc_ref >> 8); 3349a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_2, agc_ref & 0xff); 3359a0bf528SMauro Carvalho Chehab 3369a0bf528SMauro Carvalho Chehab return 0; 3379a0bf528SMauro Carvalho Chehab } 3389a0bf528SMauro Carvalho Chehab 3399a0bf528SMauro Carvalho Chehab static int lgdt3305_rfagc_loop(struct lgdt3305_state *state, 3409a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p) 3419a0bf528SMauro Carvalho Chehab { 3429a0bf528SMauro Carvalho Chehab u16 ifbw, rfbw, agcdelay; 3439a0bf528SMauro Carvalho Chehab 3449a0bf528SMauro Carvalho Chehab switch (p->modulation) { 3459a0bf528SMauro Carvalho Chehab case VSB_8: 3469a0bf528SMauro Carvalho Chehab agcdelay = 0x04c0; 3479a0bf528SMauro Carvalho Chehab rfbw = 0x8000; 3489a0bf528SMauro Carvalho Chehab ifbw = 0x8000; 3499a0bf528SMauro Carvalho Chehab break; 3509a0bf528SMauro Carvalho Chehab case QAM_64: 3519a0bf528SMauro Carvalho Chehab case QAM_256: 3529a0bf528SMauro Carvalho Chehab agcdelay = 0x046b; 3539a0bf528SMauro Carvalho Chehab rfbw = 0x8889; 3549a0bf528SMauro Carvalho Chehab /* FIXME: investigate optimal ifbw & rfbw values for the 3559a0bf528SMauro Carvalho Chehab * DT3304 and re-write this switch..case block */ 3569a0bf528SMauro Carvalho Chehab if (state->cfg->demod_chip == LGDT3304) 3579a0bf528SMauro Carvalho Chehab ifbw = 0x6666; 3589a0bf528SMauro Carvalho Chehab else /* (state->cfg->demod_chip == LGDT3305) */ 3599a0bf528SMauro Carvalho Chehab ifbw = 0x8888; 3609a0bf528SMauro Carvalho Chehab break; 3619a0bf528SMauro Carvalho Chehab default: 3629a0bf528SMauro Carvalho Chehab return -EINVAL; 3639a0bf528SMauro Carvalho Chehab } 3649a0bf528SMauro Carvalho Chehab 3659a0bf528SMauro Carvalho Chehab if (state->cfg->rf_agc_loop) { 3669a0bf528SMauro Carvalho Chehab lg_dbg("agcdelay: 0x%04x, rfbw: 0x%04x\n", agcdelay, rfbw); 3679a0bf528SMauro Carvalho Chehab 3689a0bf528SMauro Carvalho Chehab /* rf agc loop filter bandwidth */ 3699a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_1, 3709a0bf528SMauro Carvalho Chehab agcdelay >> 8); 3719a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_2, 3729a0bf528SMauro Carvalho Chehab agcdelay & 0xff); 3739a0bf528SMauro Carvalho Chehab 3749a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_1, 3759a0bf528SMauro Carvalho Chehab rfbw >> 8); 3769a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_2, 3779a0bf528SMauro Carvalho Chehab rfbw & 0xff); 3789a0bf528SMauro Carvalho Chehab } else { 3799a0bf528SMauro Carvalho Chehab lg_dbg("ifbw: 0x%04x\n", ifbw); 3809a0bf528SMauro Carvalho Chehab 3819a0bf528SMauro Carvalho Chehab /* if agc loop filter bandwidth */ 3829a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_IFBW_1, ifbw >> 8); 3839a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_IFBW_2, ifbw & 0xff); 3849a0bf528SMauro Carvalho Chehab } 3859a0bf528SMauro Carvalho Chehab 3869a0bf528SMauro Carvalho Chehab return 0; 3879a0bf528SMauro Carvalho Chehab } 3889a0bf528SMauro Carvalho Chehab 3899a0bf528SMauro Carvalho Chehab static int lgdt3305_agc_setup(struct lgdt3305_state *state, 3909a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p) 3919a0bf528SMauro Carvalho Chehab { 3929a0bf528SMauro Carvalho Chehab int lockdten, acqen; 3939a0bf528SMauro Carvalho Chehab 3949a0bf528SMauro Carvalho Chehab switch (p->modulation) { 3959a0bf528SMauro Carvalho Chehab case VSB_8: 3969a0bf528SMauro Carvalho Chehab lockdten = 0; 3979a0bf528SMauro Carvalho Chehab acqen = 0; 3989a0bf528SMauro Carvalho Chehab break; 3999a0bf528SMauro Carvalho Chehab case QAM_64: 4009a0bf528SMauro Carvalho Chehab case QAM_256: 4019a0bf528SMauro Carvalho Chehab lockdten = 1; 4029a0bf528SMauro Carvalho Chehab acqen = 1; 4039a0bf528SMauro Carvalho Chehab break; 4049a0bf528SMauro Carvalho Chehab default: 4059a0bf528SMauro Carvalho Chehab return -EINVAL; 4069a0bf528SMauro Carvalho Chehab } 4079a0bf528SMauro Carvalho Chehab 4089a0bf528SMauro Carvalho Chehab lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); 4099a0bf528SMauro Carvalho Chehab 4109a0bf528SMauro Carvalho Chehab /* control agc function */ 4119a0bf528SMauro Carvalho Chehab switch (state->cfg->demod_chip) { 4129a0bf528SMauro Carvalho Chehab case LGDT3304: 4139a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1); 4149a0bf528SMauro Carvalho Chehab lgdt3305_set_reg_bit(state, 0x030e, 2, acqen); 4159a0bf528SMauro Carvalho Chehab break; 4169a0bf528SMauro Carvalho Chehab case LGDT3305: 4179a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); 4189a0bf528SMauro Carvalho Chehab lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); 4199a0bf528SMauro Carvalho Chehab break; 4209a0bf528SMauro Carvalho Chehab default: 4219a0bf528SMauro Carvalho Chehab return -EINVAL; 4229a0bf528SMauro Carvalho Chehab } 4239a0bf528SMauro Carvalho Chehab 4249a0bf528SMauro Carvalho Chehab return lgdt3305_rfagc_loop(state, p); 4259a0bf528SMauro Carvalho Chehab } 4269a0bf528SMauro Carvalho Chehab 4279a0bf528SMauro Carvalho Chehab static int lgdt3305_set_agc_power_ref(struct lgdt3305_state *state, 4289a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p) 4299a0bf528SMauro Carvalho Chehab { 4309a0bf528SMauro Carvalho Chehab u16 usref = 0; 4319a0bf528SMauro Carvalho Chehab 4329a0bf528SMauro Carvalho Chehab switch (p->modulation) { 4339a0bf528SMauro Carvalho Chehab case VSB_8: 4349a0bf528SMauro Carvalho Chehab if (state->cfg->usref_8vsb) 4359a0bf528SMauro Carvalho Chehab usref = state->cfg->usref_8vsb; 4369a0bf528SMauro Carvalho Chehab break; 4379a0bf528SMauro Carvalho Chehab case QAM_64: 4389a0bf528SMauro Carvalho Chehab if (state->cfg->usref_qam64) 4399a0bf528SMauro Carvalho Chehab usref = state->cfg->usref_qam64; 4409a0bf528SMauro Carvalho Chehab break; 4419a0bf528SMauro Carvalho Chehab case QAM_256: 4429a0bf528SMauro Carvalho Chehab if (state->cfg->usref_qam256) 4439a0bf528SMauro Carvalho Chehab usref = state->cfg->usref_qam256; 4449a0bf528SMauro Carvalho Chehab break; 4459a0bf528SMauro Carvalho Chehab default: 4469a0bf528SMauro Carvalho Chehab return -EINVAL; 4479a0bf528SMauro Carvalho Chehab } 4489a0bf528SMauro Carvalho Chehab 4499a0bf528SMauro Carvalho Chehab if (usref) { 4509a0bf528SMauro Carvalho Chehab lg_dbg("set manual mode: 0x%04x\n", usref); 4519a0bf528SMauro Carvalho Chehab 4529a0bf528SMauro Carvalho Chehab lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 3, 1); 4539a0bf528SMauro Carvalho Chehab 4549a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_1, 4559a0bf528SMauro Carvalho Chehab 0xff & (usref >> 8)); 4569a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_2, 4579a0bf528SMauro Carvalho Chehab 0xff & (usref >> 0)); 4589a0bf528SMauro Carvalho Chehab } 4599a0bf528SMauro Carvalho Chehab return 0; 4609a0bf528SMauro Carvalho Chehab } 4619a0bf528SMauro Carvalho Chehab 4629a0bf528SMauro Carvalho Chehab /* ------------------------------------------------------------------------ */ 4639a0bf528SMauro Carvalho Chehab 4649a0bf528SMauro Carvalho Chehab static int lgdt3305_spectral_inversion(struct lgdt3305_state *state, 4659a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p, 4669a0bf528SMauro Carvalho Chehab int inversion) 4679a0bf528SMauro Carvalho Chehab { 4689a0bf528SMauro Carvalho Chehab int ret; 4699a0bf528SMauro Carvalho Chehab 4709a0bf528SMauro Carvalho Chehab lg_dbg("(%d)\n", inversion); 4719a0bf528SMauro Carvalho Chehab 4729a0bf528SMauro Carvalho Chehab switch (p->modulation) { 4739a0bf528SMauro Carvalho Chehab case VSB_8: 4749a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, LGDT3305_CR_CTRL_7, 4759a0bf528SMauro Carvalho Chehab inversion ? 0xf9 : 0x79); 4769a0bf528SMauro Carvalho Chehab break; 4779a0bf528SMauro Carvalho Chehab case QAM_64: 4789a0bf528SMauro Carvalho Chehab case QAM_256: 4799a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, LGDT3305_FEC_BLOCK_CTRL, 4809a0bf528SMauro Carvalho Chehab inversion ? 0xfd : 0xff); 4819a0bf528SMauro Carvalho Chehab break; 4829a0bf528SMauro Carvalho Chehab default: 4839a0bf528SMauro Carvalho Chehab ret = -EINVAL; 4849a0bf528SMauro Carvalho Chehab } 4859a0bf528SMauro Carvalho Chehab return ret; 4869a0bf528SMauro Carvalho Chehab } 4879a0bf528SMauro Carvalho Chehab 4889a0bf528SMauro Carvalho Chehab static int lgdt3305_set_if(struct lgdt3305_state *state, 4899a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p) 4909a0bf528SMauro Carvalho Chehab { 4919a0bf528SMauro Carvalho Chehab u16 if_freq_khz; 4929a0bf528SMauro Carvalho Chehab u8 nco1, nco2, nco3, nco4; 4939a0bf528SMauro Carvalho Chehab u64 nco; 4949a0bf528SMauro Carvalho Chehab 4959a0bf528SMauro Carvalho Chehab switch (p->modulation) { 4969a0bf528SMauro Carvalho Chehab case VSB_8: 4979a0bf528SMauro Carvalho Chehab if_freq_khz = state->cfg->vsb_if_khz; 4989a0bf528SMauro Carvalho Chehab break; 4999a0bf528SMauro Carvalho Chehab case QAM_64: 5009a0bf528SMauro Carvalho Chehab case QAM_256: 5019a0bf528SMauro Carvalho Chehab if_freq_khz = state->cfg->qam_if_khz; 5029a0bf528SMauro Carvalho Chehab break; 5039a0bf528SMauro Carvalho Chehab default: 5049a0bf528SMauro Carvalho Chehab return -EINVAL; 5059a0bf528SMauro Carvalho Chehab } 5069a0bf528SMauro Carvalho Chehab 5079a0bf528SMauro Carvalho Chehab nco = if_freq_khz / 10; 5089a0bf528SMauro Carvalho Chehab 5099a0bf528SMauro Carvalho Chehab switch (p->modulation) { 5109a0bf528SMauro Carvalho Chehab case VSB_8: 5119a0bf528SMauro Carvalho Chehab nco <<= 24; 5129a0bf528SMauro Carvalho Chehab do_div(nco, 625); 5139a0bf528SMauro Carvalho Chehab break; 5149a0bf528SMauro Carvalho Chehab case QAM_64: 5159a0bf528SMauro Carvalho Chehab case QAM_256: 5169a0bf528SMauro Carvalho Chehab nco <<= 28; 5179a0bf528SMauro Carvalho Chehab do_div(nco, 625); 5189a0bf528SMauro Carvalho Chehab break; 5199a0bf528SMauro Carvalho Chehab default: 5209a0bf528SMauro Carvalho Chehab return -EINVAL; 5219a0bf528SMauro Carvalho Chehab } 5229a0bf528SMauro Carvalho Chehab 5239a0bf528SMauro Carvalho Chehab nco1 = (nco >> 24) & 0x3f; 5249a0bf528SMauro Carvalho Chehab nco1 |= 0x40; 5259a0bf528SMauro Carvalho Chehab nco2 = (nco >> 16) & 0xff; 5269a0bf528SMauro Carvalho Chehab nco3 = (nco >> 8) & 0xff; 5279a0bf528SMauro Carvalho Chehab nco4 = nco & 0xff; 5289a0bf528SMauro Carvalho Chehab 5299a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, nco1); 5309a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, nco2); 5319a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, nco3); 5329a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, nco4); 5339a0bf528SMauro Carvalho Chehab 5349a0bf528SMauro Carvalho Chehab lg_dbg("%d KHz -> [%02x%02x%02x%02x]\n", 5359a0bf528SMauro Carvalho Chehab if_freq_khz, nco1, nco2, nco3, nco4); 5369a0bf528SMauro Carvalho Chehab 5379a0bf528SMauro Carvalho Chehab return 0; 5389a0bf528SMauro Carvalho Chehab } 5399a0bf528SMauro Carvalho Chehab 5409a0bf528SMauro Carvalho Chehab /* ------------------------------------------------------------------------ */ 5419a0bf528SMauro Carvalho Chehab 5429a0bf528SMauro Carvalho Chehab static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 5439a0bf528SMauro Carvalho Chehab { 5449a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 5459a0bf528SMauro Carvalho Chehab 5469a0bf528SMauro Carvalho Chehab if (state->cfg->deny_i2c_rptr) 5479a0bf528SMauro Carvalho Chehab return 0; 5489a0bf528SMauro Carvalho Chehab 5499a0bf528SMauro Carvalho Chehab lg_dbg("(%d)\n", enable); 5509a0bf528SMauro Carvalho Chehab 5519a0bf528SMauro Carvalho Chehab return lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_2, 5, 5529a0bf528SMauro Carvalho Chehab enable ? 0 : 1); 5539a0bf528SMauro Carvalho Chehab } 5549a0bf528SMauro Carvalho Chehab 5559a0bf528SMauro Carvalho Chehab static int lgdt3305_sleep(struct dvb_frontend *fe) 5569a0bf528SMauro Carvalho Chehab { 5579a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 5589a0bf528SMauro Carvalho Chehab u8 gen_ctrl_3, gen_ctrl_4; 5599a0bf528SMauro Carvalho Chehab 5609a0bf528SMauro Carvalho Chehab lg_dbg("\n"); 5619a0bf528SMauro Carvalho Chehab 5629a0bf528SMauro Carvalho Chehab gen_ctrl_3 = read_reg(state, LGDT3305_GEN_CTRL_3); 5639a0bf528SMauro Carvalho Chehab gen_ctrl_4 = read_reg(state, LGDT3305_GEN_CTRL_4); 5649a0bf528SMauro Carvalho Chehab 5659a0bf528SMauro Carvalho Chehab /* hold in software reset while sleeping */ 5669a0bf528SMauro Carvalho Chehab gen_ctrl_3 &= ~0x01; 5679a0bf528SMauro Carvalho Chehab /* tristate the IF-AGC pin */ 5689a0bf528SMauro Carvalho Chehab gen_ctrl_3 |= 0x02; 5699a0bf528SMauro Carvalho Chehab /* tristate the RF-AGC pin */ 5709a0bf528SMauro Carvalho Chehab gen_ctrl_3 |= 0x04; 5719a0bf528SMauro Carvalho Chehab 5729a0bf528SMauro Carvalho Chehab /* disable vsb/qam module */ 5739a0bf528SMauro Carvalho Chehab gen_ctrl_4 &= ~0x01; 5749a0bf528SMauro Carvalho Chehab /* disable adc module */ 5759a0bf528SMauro Carvalho Chehab gen_ctrl_4 &= ~0x02; 5769a0bf528SMauro Carvalho Chehab 5779a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_3, gen_ctrl_3); 5789a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_4, gen_ctrl_4); 5799a0bf528SMauro Carvalho Chehab 5809a0bf528SMauro Carvalho Chehab return 0; 5819a0bf528SMauro Carvalho Chehab } 5829a0bf528SMauro Carvalho Chehab 5839a0bf528SMauro Carvalho Chehab static int lgdt3305_init(struct dvb_frontend *fe) 5849a0bf528SMauro Carvalho Chehab { 5859a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 5869a0bf528SMauro Carvalho Chehab int ret; 5879a0bf528SMauro Carvalho Chehab 5889a0bf528SMauro Carvalho Chehab static struct lgdt3305_reg lgdt3304_init_data[] = { 5899a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, 5909a0bf528SMauro Carvalho Chehab { .reg = 0x000d, .val = 0x02, }, 5919a0bf528SMauro Carvalho Chehab { .reg = 0x000e, .val = 0x02, }, 5929a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, 5939a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, 5949a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, 5959a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, 5969a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, 5979a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, 5989a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, }, 5999a0bf528SMauro Carvalho Chehab { .reg = 0x0112, .val = 0x17, }, 6009a0bf528SMauro Carvalho Chehab { .reg = 0x0113, .val = 0x15, }, 6019a0bf528SMauro Carvalho Chehab { .reg = 0x0114, .val = 0x18, }, 6029a0bf528SMauro Carvalho Chehab { .reg = 0x0115, .val = 0xff, }, 6039a0bf528SMauro Carvalho Chehab { .reg = 0x0116, .val = 0x3c, }, 6049a0bf528SMauro Carvalho Chehab { .reg = 0x0214, .val = 0x67, }, 6059a0bf528SMauro Carvalho Chehab { .reg = 0x0424, .val = 0x8d, }, 6069a0bf528SMauro Carvalho Chehab { .reg = 0x0427, .val = 0x12, }, 6079a0bf528SMauro Carvalho Chehab { .reg = 0x0428, .val = 0x4f, }, 6089a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_IFBW_1, .val = 0x80, }, 6099a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_IFBW_2, .val = 0x00, }, 6109a0bf528SMauro Carvalho Chehab { .reg = 0x030a, .val = 0x08, }, 6119a0bf528SMauro Carvalho Chehab { .reg = 0x030b, .val = 0x9b, }, 6129a0bf528SMauro Carvalho Chehab { .reg = 0x030d, .val = 0x00, }, 6139a0bf528SMauro Carvalho Chehab { .reg = 0x030e, .val = 0x1c, }, 6149a0bf528SMauro Carvalho Chehab { .reg = 0x0314, .val = 0xe1, }, 6159a0bf528SMauro Carvalho Chehab { .reg = 0x000d, .val = 0x82, }, 6169a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, 6179a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, 6189a0bf528SMauro Carvalho Chehab }; 6199a0bf528SMauro Carvalho Chehab 6209a0bf528SMauro Carvalho Chehab static struct lgdt3305_reg lgdt3305_init_data[] = { 6219a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, 6229a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_GEN_CTRL_2, .val = 0xb0, }, 6239a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_GEN_CTRL_3, .val = 0x01, }, 6249a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_GEN_CONTROL, .val = 0x6f, }, 6259a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_GEN_CTRL_4, .val = 0x03, }, 6269a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, 6279a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, 6289a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, 6299a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, 6309a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, 6319a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, 6329a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_CR_CTRL_7, .val = 0x79, }, 6339a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_AGC_POWER_REF_1, .val = 0x32, }, 6349a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_AGC_POWER_REF_2, .val = 0xc4, }, 6359a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_AGC_DELAY_PT_1, .val = 0x0d, }, 6369a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_AGC_DELAY_PT_2, .val = 0x30, }, 6379a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1, .val = 0x80, }, 6389a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2, .val = 0x00, }, 6399a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_IFBW_1, .val = 0x80, }, 6409a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_IFBW_2, .val = 0x00, }, 6419a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_AGC_CTRL_1, .val = 0x30, }, 6429a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_AGC_CTRL_4, .val = 0x61, }, 6439a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_FEC_BLOCK_CTRL, .val = 0xff, }, 6449a0bf528SMauro Carvalho Chehab { .reg = LGDT3305_TP_CTRL_1, .val = 0x1b, }, 6459a0bf528SMauro Carvalho Chehab }; 6469a0bf528SMauro Carvalho Chehab 6479a0bf528SMauro Carvalho Chehab lg_dbg("\n"); 6489a0bf528SMauro Carvalho Chehab 6499a0bf528SMauro Carvalho Chehab switch (state->cfg->demod_chip) { 6509a0bf528SMauro Carvalho Chehab case LGDT3304: 6519a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_regs(state, lgdt3304_init_data, 6529a0bf528SMauro Carvalho Chehab ARRAY_SIZE(lgdt3304_init_data)); 6539a0bf528SMauro Carvalho Chehab break; 6549a0bf528SMauro Carvalho Chehab case LGDT3305: 6559a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_regs(state, lgdt3305_init_data, 6569a0bf528SMauro Carvalho Chehab ARRAY_SIZE(lgdt3305_init_data)); 6579a0bf528SMauro Carvalho Chehab break; 6589a0bf528SMauro Carvalho Chehab default: 6599a0bf528SMauro Carvalho Chehab ret = -EINVAL; 6609a0bf528SMauro Carvalho Chehab } 6619a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 6629a0bf528SMauro Carvalho Chehab goto fail; 6639a0bf528SMauro Carvalho Chehab 6649a0bf528SMauro Carvalho Chehab ret = lgdt3305_soft_reset(state); 6659a0bf528SMauro Carvalho Chehab fail: 6669a0bf528SMauro Carvalho Chehab return ret; 6679a0bf528SMauro Carvalho Chehab } 6689a0bf528SMauro Carvalho Chehab 6699a0bf528SMauro Carvalho Chehab static int lgdt3304_set_parameters(struct dvb_frontend *fe) 6709a0bf528SMauro Carvalho Chehab { 6719a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p = &fe->dtv_property_cache; 6729a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 6739a0bf528SMauro Carvalho Chehab int ret; 6749a0bf528SMauro Carvalho Chehab 6759a0bf528SMauro Carvalho Chehab lg_dbg("(%d, %d)\n", p->frequency, p->modulation); 6769a0bf528SMauro Carvalho Chehab 6779a0bf528SMauro Carvalho Chehab if (fe->ops.tuner_ops.set_params) { 6789a0bf528SMauro Carvalho Chehab ret = fe->ops.tuner_ops.set_params(fe); 6799a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 6809a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); 6819a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 6829a0bf528SMauro Carvalho Chehab goto fail; 6839a0bf528SMauro Carvalho Chehab state->current_frequency = p->frequency; 6849a0bf528SMauro Carvalho Chehab } 6859a0bf528SMauro Carvalho Chehab 6869a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_modulation(state, p); 6879a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 6889a0bf528SMauro Carvalho Chehab goto fail; 6899a0bf528SMauro Carvalho Chehab 6909a0bf528SMauro Carvalho Chehab ret = lgdt3305_passband_digital_agc(state, p); 6919a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 6929a0bf528SMauro Carvalho Chehab goto fail; 6939a0bf528SMauro Carvalho Chehab 6949a0bf528SMauro Carvalho Chehab ret = lgdt3305_agc_setup(state, p); 6959a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 6969a0bf528SMauro Carvalho Chehab goto fail; 6979a0bf528SMauro Carvalho Chehab 6989a0bf528SMauro Carvalho Chehab /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */ 6999a0bf528SMauro Carvalho Chehab switch (p->modulation) { 7009a0bf528SMauro Carvalho Chehab case VSB_8: 7019a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, 0x030d, 0x00); 7029a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f); 7039a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c); 7049a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac); 7059a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba); 7069a0bf528SMauro Carvalho Chehab break; 7079a0bf528SMauro Carvalho Chehab case QAM_64: 7089a0bf528SMauro Carvalho Chehab case QAM_256: 7099a0bf528SMauro Carvalho Chehab lgdt3305_write_reg(state, 0x030d, 0x14); 7109a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_if(state, p); 7119a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7129a0bf528SMauro Carvalho Chehab goto fail; 7139a0bf528SMauro Carvalho Chehab break; 7149a0bf528SMauro Carvalho Chehab default: 7159a0bf528SMauro Carvalho Chehab return -EINVAL; 7169a0bf528SMauro Carvalho Chehab } 7179a0bf528SMauro Carvalho Chehab 7189a0bf528SMauro Carvalho Chehab 7199a0bf528SMauro Carvalho Chehab ret = lgdt3305_spectral_inversion(state, p, 7209a0bf528SMauro Carvalho Chehab state->cfg->spectral_inversion 7219a0bf528SMauro Carvalho Chehab ? 1 : 0); 7229a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7239a0bf528SMauro Carvalho Chehab goto fail; 7249a0bf528SMauro Carvalho Chehab 7259a0bf528SMauro Carvalho Chehab state->current_modulation = p->modulation; 7269a0bf528SMauro Carvalho Chehab 7279a0bf528SMauro Carvalho Chehab ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); 7289a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7299a0bf528SMauro Carvalho Chehab goto fail; 7309a0bf528SMauro Carvalho Chehab 7319a0bf528SMauro Carvalho Chehab /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ 732bdba90dfSMichael Ira Krufky ret = lgdt3305_mpeg_mode_polarity(state); 7339a0bf528SMauro Carvalho Chehab fail: 7349a0bf528SMauro Carvalho Chehab return ret; 7359a0bf528SMauro Carvalho Chehab } 7369a0bf528SMauro Carvalho Chehab 7379a0bf528SMauro Carvalho Chehab static int lgdt3305_set_parameters(struct dvb_frontend *fe) 7389a0bf528SMauro Carvalho Chehab { 7399a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p = &fe->dtv_property_cache; 7409a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 7419a0bf528SMauro Carvalho Chehab int ret; 7429a0bf528SMauro Carvalho Chehab 7439a0bf528SMauro Carvalho Chehab lg_dbg("(%d, %d)\n", p->frequency, p->modulation); 7449a0bf528SMauro Carvalho Chehab 7459a0bf528SMauro Carvalho Chehab if (fe->ops.tuner_ops.set_params) { 7469a0bf528SMauro Carvalho Chehab ret = fe->ops.tuner_ops.set_params(fe); 7479a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 7489a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); 7499a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7509a0bf528SMauro Carvalho Chehab goto fail; 7519a0bf528SMauro Carvalho Chehab state->current_frequency = p->frequency; 7529a0bf528SMauro Carvalho Chehab } 7539a0bf528SMauro Carvalho Chehab 7549a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_modulation(state, p); 7559a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7569a0bf528SMauro Carvalho Chehab goto fail; 7579a0bf528SMauro Carvalho Chehab 7589a0bf528SMauro Carvalho Chehab ret = lgdt3305_passband_digital_agc(state, p); 7599a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7609a0bf528SMauro Carvalho Chehab goto fail; 7619a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_agc_power_ref(state, p); 7629a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7639a0bf528SMauro Carvalho Chehab goto fail; 7649a0bf528SMauro Carvalho Chehab ret = lgdt3305_agc_setup(state, p); 7659a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7669a0bf528SMauro Carvalho Chehab goto fail; 7679a0bf528SMauro Carvalho Chehab 7689a0bf528SMauro Carvalho Chehab /* low if */ 7699a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, LGDT3305_GEN_CONTROL, 0x2f); 7709a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7719a0bf528SMauro Carvalho Chehab goto fail; 7729a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_reg_bit(state, LGDT3305_CR_CTR_FREQ_1, 6, 1); 7739a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7749a0bf528SMauro Carvalho Chehab goto fail; 7759a0bf528SMauro Carvalho Chehab 7769a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_if(state, p); 7779a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7789a0bf528SMauro Carvalho Chehab goto fail; 7799a0bf528SMauro Carvalho Chehab ret = lgdt3305_spectral_inversion(state, p, 7809a0bf528SMauro Carvalho Chehab state->cfg->spectral_inversion 7819a0bf528SMauro Carvalho Chehab ? 1 : 0); 7829a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7839a0bf528SMauro Carvalho Chehab goto fail; 7849a0bf528SMauro Carvalho Chehab 7859a0bf528SMauro Carvalho Chehab ret = lgdt3305_set_filter_extension(state, p); 7869a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7879a0bf528SMauro Carvalho Chehab goto fail; 7889a0bf528SMauro Carvalho Chehab 7899a0bf528SMauro Carvalho Chehab state->current_modulation = p->modulation; 7909a0bf528SMauro Carvalho Chehab 7919a0bf528SMauro Carvalho Chehab ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); 7929a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 7939a0bf528SMauro Carvalho Chehab goto fail; 7949a0bf528SMauro Carvalho Chehab 7959a0bf528SMauro Carvalho Chehab /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ 796bdba90dfSMichael Ira Krufky ret = lgdt3305_mpeg_mode_polarity(state); 7979a0bf528SMauro Carvalho Chehab fail: 7989a0bf528SMauro Carvalho Chehab return ret; 7999a0bf528SMauro Carvalho Chehab } 8009a0bf528SMauro Carvalho Chehab 8017e3e68bcSMauro Carvalho Chehab static int lgdt3305_get_frontend(struct dvb_frontend *fe, 8027e3e68bcSMauro Carvalho Chehab struct dtv_frontend_properties *p) 8039a0bf528SMauro Carvalho Chehab { 8049a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 8059a0bf528SMauro Carvalho Chehab 8069a0bf528SMauro Carvalho Chehab lg_dbg("\n"); 8079a0bf528SMauro Carvalho Chehab 8089a0bf528SMauro Carvalho Chehab p->modulation = state->current_modulation; 8099a0bf528SMauro Carvalho Chehab p->frequency = state->current_frequency; 8109a0bf528SMauro Carvalho Chehab return 0; 8119a0bf528SMauro Carvalho Chehab } 8129a0bf528SMauro Carvalho Chehab 8139a0bf528SMauro Carvalho Chehab /* ------------------------------------------------------------------------ */ 8149a0bf528SMauro Carvalho Chehab 8159a0bf528SMauro Carvalho Chehab static int lgdt3305_read_cr_lock_status(struct lgdt3305_state *state, 8169a0bf528SMauro Carvalho Chehab int *locked) 8179a0bf528SMauro Carvalho Chehab { 8189a0bf528SMauro Carvalho Chehab u8 val; 8199a0bf528SMauro Carvalho Chehab int ret; 8209a0bf528SMauro Carvalho Chehab char *cr_lock_state = ""; 8219a0bf528SMauro Carvalho Chehab 8229a0bf528SMauro Carvalho Chehab *locked = 0; 8239a0bf528SMauro Carvalho Chehab 8249a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_reg(state, LGDT3305_CR_LOCK_STATUS, &val); 8259a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 8269a0bf528SMauro Carvalho Chehab goto fail; 8279a0bf528SMauro Carvalho Chehab 8289a0bf528SMauro Carvalho Chehab switch (state->current_modulation) { 8299a0bf528SMauro Carvalho Chehab case QAM_256: 8309a0bf528SMauro Carvalho Chehab case QAM_64: 8319a0bf528SMauro Carvalho Chehab if (val & (1 << 1)) 8329a0bf528SMauro Carvalho Chehab *locked = 1; 8339a0bf528SMauro Carvalho Chehab 8349a0bf528SMauro Carvalho Chehab switch (val & 0x07) { 8359a0bf528SMauro Carvalho Chehab case 0: 8369a0bf528SMauro Carvalho Chehab cr_lock_state = "QAM UNLOCK"; 8379a0bf528SMauro Carvalho Chehab break; 8389a0bf528SMauro Carvalho Chehab case 4: 8399a0bf528SMauro Carvalho Chehab cr_lock_state = "QAM 1stLock"; 8409a0bf528SMauro Carvalho Chehab break; 8419a0bf528SMauro Carvalho Chehab case 6: 8429a0bf528SMauro Carvalho Chehab cr_lock_state = "QAM 2ndLock"; 8439a0bf528SMauro Carvalho Chehab break; 8449a0bf528SMauro Carvalho Chehab case 7: 8459a0bf528SMauro Carvalho Chehab cr_lock_state = "QAM FinalLock"; 8469a0bf528SMauro Carvalho Chehab break; 8479a0bf528SMauro Carvalho Chehab default: 8489a0bf528SMauro Carvalho Chehab cr_lock_state = "CLOCKQAM-INVALID!"; 8499a0bf528SMauro Carvalho Chehab break; 8509a0bf528SMauro Carvalho Chehab } 8519a0bf528SMauro Carvalho Chehab break; 8529a0bf528SMauro Carvalho Chehab case VSB_8: 8539a0bf528SMauro Carvalho Chehab if (val & (1 << 7)) { 8549a0bf528SMauro Carvalho Chehab *locked = 1; 8559a0bf528SMauro Carvalho Chehab cr_lock_state = "CLOCKVSB"; 8569a0bf528SMauro Carvalho Chehab } 8579a0bf528SMauro Carvalho Chehab break; 8589a0bf528SMauro Carvalho Chehab default: 8599a0bf528SMauro Carvalho Chehab ret = -EINVAL; 8609a0bf528SMauro Carvalho Chehab } 8619a0bf528SMauro Carvalho Chehab lg_dbg("(%d) %s\n", *locked, cr_lock_state); 8629a0bf528SMauro Carvalho Chehab fail: 8639a0bf528SMauro Carvalho Chehab return ret; 8649a0bf528SMauro Carvalho Chehab } 8659a0bf528SMauro Carvalho Chehab 8669a0bf528SMauro Carvalho Chehab static int lgdt3305_read_fec_lock_status(struct lgdt3305_state *state, 8679a0bf528SMauro Carvalho Chehab int *locked) 8689a0bf528SMauro Carvalho Chehab { 8699a0bf528SMauro Carvalho Chehab u8 val; 8709a0bf528SMauro Carvalho Chehab int ret, mpeg_lock, fec_lock, viterbi_lock; 8719a0bf528SMauro Carvalho Chehab 8729a0bf528SMauro Carvalho Chehab *locked = 0; 8739a0bf528SMauro Carvalho Chehab 8749a0bf528SMauro Carvalho Chehab switch (state->current_modulation) { 8759a0bf528SMauro Carvalho Chehab case QAM_256: 8769a0bf528SMauro Carvalho Chehab case QAM_64: 8779a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_reg(state, 8789a0bf528SMauro Carvalho Chehab LGDT3305_FEC_LOCK_STATUS, &val); 8799a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 8809a0bf528SMauro Carvalho Chehab goto fail; 8819a0bf528SMauro Carvalho Chehab 8829a0bf528SMauro Carvalho Chehab mpeg_lock = (val & (1 << 0)) ? 1 : 0; 8839a0bf528SMauro Carvalho Chehab fec_lock = (val & (1 << 2)) ? 1 : 0; 8849a0bf528SMauro Carvalho Chehab viterbi_lock = (val & (1 << 3)) ? 1 : 0; 8859a0bf528SMauro Carvalho Chehab 8869a0bf528SMauro Carvalho Chehab *locked = mpeg_lock && fec_lock && viterbi_lock; 8879a0bf528SMauro Carvalho Chehab 8889a0bf528SMauro Carvalho Chehab lg_dbg("(%d) %s%s%s\n", *locked, 8899a0bf528SMauro Carvalho Chehab mpeg_lock ? "mpeg lock " : "", 8909a0bf528SMauro Carvalho Chehab fec_lock ? "fec lock " : "", 8919a0bf528SMauro Carvalho Chehab viterbi_lock ? "viterbi lock" : ""); 8929a0bf528SMauro Carvalho Chehab break; 8939a0bf528SMauro Carvalho Chehab case VSB_8: 8949a0bf528SMauro Carvalho Chehab default: 8959a0bf528SMauro Carvalho Chehab ret = -EINVAL; 8969a0bf528SMauro Carvalho Chehab } 8979a0bf528SMauro Carvalho Chehab fail: 8989a0bf528SMauro Carvalho Chehab return ret; 8999a0bf528SMauro Carvalho Chehab } 9009a0bf528SMauro Carvalho Chehab 9010df289a2SMauro Carvalho Chehab static int lgdt3305_read_status(struct dvb_frontend *fe, enum fe_status *status) 9029a0bf528SMauro Carvalho Chehab { 9039a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 9049a0bf528SMauro Carvalho Chehab u8 val; 9059a0bf528SMauro Carvalho Chehab int ret, signal, inlock, nofecerr, snrgood, 9069a0bf528SMauro Carvalho Chehab cr_lock, fec_lock, sync_lock; 9079a0bf528SMauro Carvalho Chehab 9089a0bf528SMauro Carvalho Chehab *status = 0; 9099a0bf528SMauro Carvalho Chehab 9109a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_reg(state, LGDT3305_GEN_STATUS, &val); 9119a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 9129a0bf528SMauro Carvalho Chehab goto fail; 9139a0bf528SMauro Carvalho Chehab 9149a0bf528SMauro Carvalho Chehab signal = (val & (1 << 4)) ? 1 : 0; 9159a0bf528SMauro Carvalho Chehab inlock = (val & (1 << 3)) ? 0 : 1; 9169a0bf528SMauro Carvalho Chehab sync_lock = (val & (1 << 2)) ? 1 : 0; 9179a0bf528SMauro Carvalho Chehab nofecerr = (val & (1 << 1)) ? 1 : 0; 9189a0bf528SMauro Carvalho Chehab snrgood = (val & (1 << 0)) ? 1 : 0; 9199a0bf528SMauro Carvalho Chehab 9209a0bf528SMauro Carvalho Chehab lg_dbg("%s%s%s%s%s\n", 9219a0bf528SMauro Carvalho Chehab signal ? "SIGNALEXIST " : "", 9229a0bf528SMauro Carvalho Chehab inlock ? "INLOCK " : "", 9239a0bf528SMauro Carvalho Chehab sync_lock ? "SYNCLOCK " : "", 9249a0bf528SMauro Carvalho Chehab nofecerr ? "NOFECERR " : "", 9259a0bf528SMauro Carvalho Chehab snrgood ? "SNRGOOD " : ""); 9269a0bf528SMauro Carvalho Chehab 9279a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_cr_lock_status(state, &cr_lock); 9289a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 9299a0bf528SMauro Carvalho Chehab goto fail; 9309a0bf528SMauro Carvalho Chehab 9319a0bf528SMauro Carvalho Chehab if (signal) 9329a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SIGNAL; 9339a0bf528SMauro Carvalho Chehab if (cr_lock) 9349a0bf528SMauro Carvalho Chehab *status |= FE_HAS_CARRIER; 9359a0bf528SMauro Carvalho Chehab if (nofecerr) 9369a0bf528SMauro Carvalho Chehab *status |= FE_HAS_VITERBI; 9379a0bf528SMauro Carvalho Chehab if (sync_lock) 9389a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SYNC; 9399a0bf528SMauro Carvalho Chehab 9409a0bf528SMauro Carvalho Chehab switch (state->current_modulation) { 9419a0bf528SMauro Carvalho Chehab case QAM_256: 9429a0bf528SMauro Carvalho Chehab case QAM_64: 9439a0bf528SMauro Carvalho Chehab /* signal bit is unreliable on the DT3304 in QAM mode */ 9449a0bf528SMauro Carvalho Chehab if (((LGDT3304 == state->cfg->demod_chip)) && (cr_lock)) 9459a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SIGNAL; 9469a0bf528SMauro Carvalho Chehab 9479a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_fec_lock_status(state, &fec_lock); 9489a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 9499a0bf528SMauro Carvalho Chehab goto fail; 9509a0bf528SMauro Carvalho Chehab 9519a0bf528SMauro Carvalho Chehab if (fec_lock) 9529a0bf528SMauro Carvalho Chehab *status |= FE_HAS_LOCK; 9539a0bf528SMauro Carvalho Chehab break; 9549a0bf528SMauro Carvalho Chehab case VSB_8: 9559a0bf528SMauro Carvalho Chehab if (inlock) 9569a0bf528SMauro Carvalho Chehab *status |= FE_HAS_LOCK; 9579a0bf528SMauro Carvalho Chehab break; 9589a0bf528SMauro Carvalho Chehab default: 9599a0bf528SMauro Carvalho Chehab ret = -EINVAL; 9609a0bf528SMauro Carvalho Chehab } 9619a0bf528SMauro Carvalho Chehab fail: 9629a0bf528SMauro Carvalho Chehab return ret; 9639a0bf528SMauro Carvalho Chehab } 9649a0bf528SMauro Carvalho Chehab 9659a0bf528SMauro Carvalho Chehab /* ------------------------------------------------------------------------ */ 9669a0bf528SMauro Carvalho Chehab 9679a0bf528SMauro Carvalho Chehab /* borrowed from lgdt330x.c */ 9689a0bf528SMauro Carvalho Chehab static u32 calculate_snr(u32 mse, u32 c) 9699a0bf528SMauro Carvalho Chehab { 9709a0bf528SMauro Carvalho Chehab if (mse == 0) /* no signal */ 9719a0bf528SMauro Carvalho Chehab return 0; 9729a0bf528SMauro Carvalho Chehab 9739a0bf528SMauro Carvalho Chehab mse = intlog10(mse); 9749a0bf528SMauro Carvalho Chehab if (mse > c) { 9759a0bf528SMauro Carvalho Chehab /* Negative SNR, which is possible, but realisticly the 9769a0bf528SMauro Carvalho Chehab demod will lose lock before the signal gets this bad. The 9779a0bf528SMauro Carvalho Chehab API only allows for unsigned values, so just return 0 */ 9789a0bf528SMauro Carvalho Chehab return 0; 9799a0bf528SMauro Carvalho Chehab } 9809a0bf528SMauro Carvalho Chehab return 10*(c - mse); 9819a0bf528SMauro Carvalho Chehab } 9829a0bf528SMauro Carvalho Chehab 9839a0bf528SMauro Carvalho Chehab static int lgdt3305_read_snr(struct dvb_frontend *fe, u16 *snr) 9849a0bf528SMauro Carvalho Chehab { 9859a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 9869a0bf528SMauro Carvalho Chehab u32 noise; /* noise value */ 9879a0bf528SMauro Carvalho Chehab u32 c; /* per-modulation SNR calculation constant */ 9889a0bf528SMauro Carvalho Chehab 9899a0bf528SMauro Carvalho Chehab switch (state->current_modulation) { 9909a0bf528SMauro Carvalho Chehab case VSB_8: 9919a0bf528SMauro Carvalho Chehab #ifdef USE_PTMSE 9929a0bf528SMauro Carvalho Chehab /* Use Phase Tracker Mean-Square Error Register */ 9939a0bf528SMauro Carvalho Chehab /* SNR for ranges from -13.11 to +44.08 */ 9949a0bf528SMauro Carvalho Chehab noise = ((read_reg(state, LGDT3305_PT_MSE_1) & 0x07) << 16) | 9959a0bf528SMauro Carvalho Chehab (read_reg(state, LGDT3305_PT_MSE_2) << 8) | 9969a0bf528SMauro Carvalho Chehab (read_reg(state, LGDT3305_PT_MSE_3) & 0xff); 9979a0bf528SMauro Carvalho Chehab c = 73957994; /* log10(25*32^2)*2^24 */ 9989a0bf528SMauro Carvalho Chehab #else 9999a0bf528SMauro Carvalho Chehab /* Use Equalizer Mean-Square Error Register */ 10009a0bf528SMauro Carvalho Chehab /* SNR for ranges from -16.12 to +44.08 */ 10019a0bf528SMauro Carvalho Chehab noise = ((read_reg(state, LGDT3305_EQ_MSE_1) & 0x0f) << 16) | 10029a0bf528SMauro Carvalho Chehab (read_reg(state, LGDT3305_EQ_MSE_2) << 8) | 10039a0bf528SMauro Carvalho Chehab (read_reg(state, LGDT3305_EQ_MSE_3) & 0xff); 10049a0bf528SMauro Carvalho Chehab c = 73957994; /* log10(25*32^2)*2^24 */ 10059a0bf528SMauro Carvalho Chehab #endif 10069a0bf528SMauro Carvalho Chehab break; 10079a0bf528SMauro Carvalho Chehab case QAM_64: 10089a0bf528SMauro Carvalho Chehab case QAM_256: 10099a0bf528SMauro Carvalho Chehab noise = (read_reg(state, LGDT3305_CR_MSE_1) << 8) | 10109a0bf528SMauro Carvalho Chehab (read_reg(state, LGDT3305_CR_MSE_2) & 0xff); 10119a0bf528SMauro Carvalho Chehab 10129a0bf528SMauro Carvalho Chehab c = (state->current_modulation == QAM_64) ? 10139a0bf528SMauro Carvalho Chehab 97939837 : 98026066; 10149a0bf528SMauro Carvalho Chehab /* log10(688128)*2^24 and log10(696320)*2^24 */ 10159a0bf528SMauro Carvalho Chehab break; 10169a0bf528SMauro Carvalho Chehab default: 10179a0bf528SMauro Carvalho Chehab return -EINVAL; 10189a0bf528SMauro Carvalho Chehab } 10199a0bf528SMauro Carvalho Chehab state->snr = calculate_snr(noise, c); 10209a0bf528SMauro Carvalho Chehab /* report SNR in dB * 10 */ 10219a0bf528SMauro Carvalho Chehab *snr = (state->snr / ((1 << 24) / 10)); 10229a0bf528SMauro Carvalho Chehab lg_dbg("noise = 0x%08x, snr = %d.%02d dB\n", noise, 10239a0bf528SMauro Carvalho Chehab state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); 10249a0bf528SMauro Carvalho Chehab 10259a0bf528SMauro Carvalho Chehab return 0; 10269a0bf528SMauro Carvalho Chehab } 10279a0bf528SMauro Carvalho Chehab 10289a0bf528SMauro Carvalho Chehab static int lgdt3305_read_signal_strength(struct dvb_frontend *fe, 10299a0bf528SMauro Carvalho Chehab u16 *strength) 10309a0bf528SMauro Carvalho Chehab { 10319a0bf528SMauro Carvalho Chehab /* borrowed from lgdt330x.c 10329a0bf528SMauro Carvalho Chehab * 10339a0bf528SMauro Carvalho Chehab * Calculate strength from SNR up to 35dB 10349a0bf528SMauro Carvalho Chehab * Even though the SNR can go higher than 35dB, 10359a0bf528SMauro Carvalho Chehab * there is some comfort factor in having a range of 10369a0bf528SMauro Carvalho Chehab * strong signals that can show at 100% 10379a0bf528SMauro Carvalho Chehab */ 10389a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 10399a0bf528SMauro Carvalho Chehab u16 snr; 10409a0bf528SMauro Carvalho Chehab int ret; 10419a0bf528SMauro Carvalho Chehab 10429a0bf528SMauro Carvalho Chehab *strength = 0; 10439a0bf528SMauro Carvalho Chehab 10449a0bf528SMauro Carvalho Chehab ret = fe->ops.read_snr(fe, &snr); 10459a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 10469a0bf528SMauro Carvalho Chehab goto fail; 10479a0bf528SMauro Carvalho Chehab /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ 10489a0bf528SMauro Carvalho Chehab /* scale the range 0 - 35*2^24 into 0 - 65535 */ 10499a0bf528SMauro Carvalho Chehab if (state->snr >= 8960 * 0x10000) 10509a0bf528SMauro Carvalho Chehab *strength = 0xffff; 10519a0bf528SMauro Carvalho Chehab else 10529a0bf528SMauro Carvalho Chehab *strength = state->snr / 8960; 10539a0bf528SMauro Carvalho Chehab fail: 10549a0bf528SMauro Carvalho Chehab return ret; 10559a0bf528SMauro Carvalho Chehab } 10569a0bf528SMauro Carvalho Chehab 10579a0bf528SMauro Carvalho Chehab /* ------------------------------------------------------------------------ */ 10589a0bf528SMauro Carvalho Chehab 10599a0bf528SMauro Carvalho Chehab static int lgdt3305_read_ber(struct dvb_frontend *fe, u32 *ber) 10609a0bf528SMauro Carvalho Chehab { 10619a0bf528SMauro Carvalho Chehab *ber = 0; 10629a0bf528SMauro Carvalho Chehab return 0; 10639a0bf528SMauro Carvalho Chehab } 10649a0bf528SMauro Carvalho Chehab 10659a0bf528SMauro Carvalho Chehab static int lgdt3305_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 10669a0bf528SMauro Carvalho Chehab { 10679a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 10689a0bf528SMauro Carvalho Chehab 10699a0bf528SMauro Carvalho Chehab *ucblocks = 10709a0bf528SMauro Carvalho Chehab (read_reg(state, LGDT3305_FEC_PKT_ERR_1) << 8) | 10719a0bf528SMauro Carvalho Chehab (read_reg(state, LGDT3305_FEC_PKT_ERR_2) & 0xff); 10729a0bf528SMauro Carvalho Chehab 10739a0bf528SMauro Carvalho Chehab return 0; 10749a0bf528SMauro Carvalho Chehab } 10759a0bf528SMauro Carvalho Chehab 10769a0bf528SMauro Carvalho Chehab static int lgdt3305_get_tune_settings(struct dvb_frontend *fe, 10779a0bf528SMauro Carvalho Chehab struct dvb_frontend_tune_settings 10789a0bf528SMauro Carvalho Chehab *fe_tune_settings) 10799a0bf528SMauro Carvalho Chehab { 10809a0bf528SMauro Carvalho Chehab fe_tune_settings->min_delay_ms = 500; 10819a0bf528SMauro Carvalho Chehab lg_dbg("\n"); 10829a0bf528SMauro Carvalho Chehab return 0; 10839a0bf528SMauro Carvalho Chehab } 10849a0bf528SMauro Carvalho Chehab 10859a0bf528SMauro Carvalho Chehab static void lgdt3305_release(struct dvb_frontend *fe) 10869a0bf528SMauro Carvalho Chehab { 10879a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = fe->demodulator_priv; 10889a0bf528SMauro Carvalho Chehab lg_dbg("\n"); 10899a0bf528SMauro Carvalho Chehab kfree(state); 10909a0bf528SMauro Carvalho Chehab } 10919a0bf528SMauro Carvalho Chehab 1092bd336e63SMax Kellermann static const struct dvb_frontend_ops lgdt3304_ops; 1093bd336e63SMax Kellermann static const struct dvb_frontend_ops lgdt3305_ops; 10949a0bf528SMauro Carvalho Chehab 10959a0bf528SMauro Carvalho Chehab struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, 10969a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c_adap) 10979a0bf528SMauro Carvalho Chehab { 10989a0bf528SMauro Carvalho Chehab struct lgdt3305_state *state = NULL; 10999a0bf528SMauro Carvalho Chehab int ret; 11009a0bf528SMauro Carvalho Chehab u8 val; 11019a0bf528SMauro Carvalho Chehab 11029a0bf528SMauro Carvalho Chehab lg_dbg("(%d-%04x)\n", 11039a0bf528SMauro Carvalho Chehab i2c_adap ? i2c_adapter_id(i2c_adap) : 0, 11049a0bf528SMauro Carvalho Chehab config ? config->i2c_addr : 0); 11059a0bf528SMauro Carvalho Chehab 11069a0bf528SMauro Carvalho Chehab state = kzalloc(sizeof(struct lgdt3305_state), GFP_KERNEL); 11079a0bf528SMauro Carvalho Chehab if (state == NULL) 11089a0bf528SMauro Carvalho Chehab goto fail; 11099a0bf528SMauro Carvalho Chehab 11109a0bf528SMauro Carvalho Chehab state->cfg = config; 11119a0bf528SMauro Carvalho Chehab state->i2c_adap = i2c_adap; 11129a0bf528SMauro Carvalho Chehab 11139a0bf528SMauro Carvalho Chehab switch (config->demod_chip) { 11149a0bf528SMauro Carvalho Chehab case LGDT3304: 11159a0bf528SMauro Carvalho Chehab memcpy(&state->frontend.ops, &lgdt3304_ops, 11169a0bf528SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops)); 11179a0bf528SMauro Carvalho Chehab break; 11189a0bf528SMauro Carvalho Chehab case LGDT3305: 11199a0bf528SMauro Carvalho Chehab memcpy(&state->frontend.ops, &lgdt3305_ops, 11209a0bf528SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops)); 11219a0bf528SMauro Carvalho Chehab break; 11229a0bf528SMauro Carvalho Chehab default: 11239a0bf528SMauro Carvalho Chehab goto fail; 11249a0bf528SMauro Carvalho Chehab } 11259a0bf528SMauro Carvalho Chehab state->frontend.demodulator_priv = state; 11269a0bf528SMauro Carvalho Chehab 11279a0bf528SMauro Carvalho Chehab /* verify that we're talking to a lg dt3304/5 */ 11289a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); 11299a0bf528SMauro Carvalho Chehab if ((lg_fail(ret)) | (val == 0)) 11309a0bf528SMauro Carvalho Chehab goto fail; 11319a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, 0x0808, 0x80); 11329a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 11339a0bf528SMauro Carvalho Chehab goto fail; 11349a0bf528SMauro Carvalho Chehab ret = lgdt3305_read_reg(state, 0x0808, &val); 11359a0bf528SMauro Carvalho Chehab if ((lg_fail(ret)) | (val != 0x80)) 11369a0bf528SMauro Carvalho Chehab goto fail; 11379a0bf528SMauro Carvalho Chehab ret = lgdt3305_write_reg(state, 0x0808, 0x00); 11389a0bf528SMauro Carvalho Chehab if (lg_fail(ret)) 11399a0bf528SMauro Carvalho Chehab goto fail; 11409a0bf528SMauro Carvalho Chehab 11419a0bf528SMauro Carvalho Chehab state->current_frequency = -1; 11429a0bf528SMauro Carvalho Chehab state->current_modulation = -1; 11439a0bf528SMauro Carvalho Chehab 11449a0bf528SMauro Carvalho Chehab return &state->frontend; 11459a0bf528SMauro Carvalho Chehab fail: 11469a0bf528SMauro Carvalho Chehab lg_warn("unable to detect %s hardware\n", 11479a0bf528SMauro Carvalho Chehab config->demod_chip ? "LGDT3304" : "LGDT3305"); 11489a0bf528SMauro Carvalho Chehab kfree(state); 11499a0bf528SMauro Carvalho Chehab return NULL; 11509a0bf528SMauro Carvalho Chehab } 1151*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(lgdt3305_attach); 11529a0bf528SMauro Carvalho Chehab 1153bd336e63SMax Kellermann static const struct dvb_frontend_ops lgdt3304_ops = { 11549a0bf528SMauro Carvalho Chehab .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, 11559a0bf528SMauro Carvalho Chehab .info = { 11569a0bf528SMauro Carvalho Chehab .name = "LG Electronics LGDT3304 VSB/QAM Frontend", 1157f1b1eabfSMauro Carvalho Chehab .frequency_min_hz = 54 * MHz, 1158f1b1eabfSMauro Carvalho Chehab .frequency_max_hz = 858 * MHz, 1159f1b1eabfSMauro Carvalho Chehab .frequency_stepsize_hz = 62500, 11609a0bf528SMauro Carvalho Chehab .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB 11619a0bf528SMauro Carvalho Chehab }, 11629a0bf528SMauro Carvalho Chehab .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, 11639a0bf528SMauro Carvalho Chehab .init = lgdt3305_init, 1164c9af5c15SShuah Khan .sleep = lgdt3305_sleep, 11659a0bf528SMauro Carvalho Chehab .set_frontend = lgdt3304_set_parameters, 11669a0bf528SMauro Carvalho Chehab .get_frontend = lgdt3305_get_frontend, 11679a0bf528SMauro Carvalho Chehab .get_tune_settings = lgdt3305_get_tune_settings, 11689a0bf528SMauro Carvalho Chehab .read_status = lgdt3305_read_status, 11699a0bf528SMauro Carvalho Chehab .read_ber = lgdt3305_read_ber, 11709a0bf528SMauro Carvalho Chehab .read_signal_strength = lgdt3305_read_signal_strength, 11719a0bf528SMauro Carvalho Chehab .read_snr = lgdt3305_read_snr, 11729a0bf528SMauro Carvalho Chehab .read_ucblocks = lgdt3305_read_ucblocks, 11739a0bf528SMauro Carvalho Chehab .release = lgdt3305_release, 11749a0bf528SMauro Carvalho Chehab }; 11759a0bf528SMauro Carvalho Chehab 1176bd336e63SMax Kellermann static const struct dvb_frontend_ops lgdt3305_ops = { 11779a0bf528SMauro Carvalho Chehab .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, 11789a0bf528SMauro Carvalho Chehab .info = { 11799a0bf528SMauro Carvalho Chehab .name = "LG Electronics LGDT3305 VSB/QAM Frontend", 1180f1b1eabfSMauro Carvalho Chehab .frequency_min_hz = 54 * MHz, 1181f1b1eabfSMauro Carvalho Chehab .frequency_max_hz = 858 * MHz, 1182f1b1eabfSMauro Carvalho Chehab .frequency_stepsize_hz = 62500, 11839a0bf528SMauro Carvalho Chehab .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB 11849a0bf528SMauro Carvalho Chehab }, 11859a0bf528SMauro Carvalho Chehab .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, 11869a0bf528SMauro Carvalho Chehab .init = lgdt3305_init, 11879a0bf528SMauro Carvalho Chehab .sleep = lgdt3305_sleep, 11889a0bf528SMauro Carvalho Chehab .set_frontend = lgdt3305_set_parameters, 11899a0bf528SMauro Carvalho Chehab .get_frontend = lgdt3305_get_frontend, 11909a0bf528SMauro Carvalho Chehab .get_tune_settings = lgdt3305_get_tune_settings, 11919a0bf528SMauro Carvalho Chehab .read_status = lgdt3305_read_status, 11929a0bf528SMauro Carvalho Chehab .read_ber = lgdt3305_read_ber, 11939a0bf528SMauro Carvalho Chehab .read_signal_strength = lgdt3305_read_signal_strength, 11949a0bf528SMauro Carvalho Chehab .read_snr = lgdt3305_read_snr, 11959a0bf528SMauro Carvalho Chehab .read_ucblocks = lgdt3305_read_ucblocks, 11969a0bf528SMauro Carvalho Chehab .release = lgdt3305_release, 11979a0bf528SMauro Carvalho Chehab }; 11989a0bf528SMauro Carvalho Chehab 11999a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); 12009a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); 12019a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 12029a0bf528SMauro Carvalho Chehab MODULE_VERSION("0.2"); 1203