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