18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This code is more or less generated from another driver, please
88c2ecf20Sopenharmony_ci * excuse some codingstyle oddities.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/i2c.h>
168c2ecf20Sopenharmony_ci#include <linux/mutex.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "dib0070.h"
218c2ecf20Sopenharmony_ci#include "dibx000_common.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int debug;
248c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define dprintk(fmt, arg...) do {					\
288c2ecf20Sopenharmony_ci	if (debug)							\
298c2ecf20Sopenharmony_ci		printk(KERN_DEBUG pr_fmt("%s: " fmt),			\
308c2ecf20Sopenharmony_ci		       __func__, ##arg);				\
318c2ecf20Sopenharmony_ci} while (0)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define DIB0070_P1D  0x00
348c2ecf20Sopenharmony_ci#define DIB0070_P1F  0x01
358c2ecf20Sopenharmony_ci#define DIB0070_P1G  0x03
368c2ecf20Sopenharmony_ci#define DIB0070S_P1A 0x02
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct dib0070_state {
398c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c;
408c2ecf20Sopenharmony_ci	struct dvb_frontend *fe;
418c2ecf20Sopenharmony_ci	const struct dib0070_config *cfg;
428c2ecf20Sopenharmony_ci	u16 wbd_ff_offset;
438c2ecf20Sopenharmony_ci	u8 revision;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	enum frontend_tune_state tune_state;
468c2ecf20Sopenharmony_ci	u32 current_rf;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/* for the captrim binary search */
498c2ecf20Sopenharmony_ci	s8 step;
508c2ecf20Sopenharmony_ci	u16 adc_diff;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	s8 captrim;
538c2ecf20Sopenharmony_ci	s8 fcaptrim;
548c2ecf20Sopenharmony_ci	u16 lo4;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	const struct dib0070_tuning *current_tune_table_index;
578c2ecf20Sopenharmony_ci	const struct dib0070_lna_match *lna_match;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	u8  wbd_gain_current;
608c2ecf20Sopenharmony_ci	u16 wbd_offset_3_3[2];
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* for the I2C transfer */
638c2ecf20Sopenharmony_ci	struct i2c_msg msg[2];
648c2ecf20Sopenharmony_ci	u8 i2c_write_buffer[3];
658c2ecf20Sopenharmony_ci	u8 i2c_read_buffer[2];
668c2ecf20Sopenharmony_ci	struct mutex i2c_buffer_lock;
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic u16 dib0070_read_reg(struct dib0070_state *state, u8 reg)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	u16 ret;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
748c2ecf20Sopenharmony_ci		dprintk("could not acquire lock\n");
758c2ecf20Sopenharmony_ci		return 0;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	state->i2c_write_buffer[0] = reg;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
818c2ecf20Sopenharmony_ci	state->msg[0].addr = state->cfg->i2c_address;
828c2ecf20Sopenharmony_ci	state->msg[0].flags = 0;
838c2ecf20Sopenharmony_ci	state->msg[0].buf = state->i2c_write_buffer;
848c2ecf20Sopenharmony_ci	state->msg[0].len = 1;
858c2ecf20Sopenharmony_ci	state->msg[1].addr = state->cfg->i2c_address;
868c2ecf20Sopenharmony_ci	state->msg[1].flags = I2C_M_RD;
878c2ecf20Sopenharmony_ci	state->msg[1].buf = state->i2c_read_buffer;
888c2ecf20Sopenharmony_ci	state->msg[1].len = 2;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (i2c_transfer(state->i2c, state->msg, 2) != 2) {
918c2ecf20Sopenharmony_ci		pr_warn("DiB0070 I2C read failed\n");
928c2ecf20Sopenharmony_ci		ret = 0;
938c2ecf20Sopenharmony_ci	} else
948c2ecf20Sopenharmony_ci		ret = (state->i2c_read_buffer[0] << 8)
958c2ecf20Sopenharmony_ci			| state->i2c_read_buffer[1];
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	mutex_unlock(&state->i2c_buffer_lock);
988c2ecf20Sopenharmony_ci	return ret;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	int ret;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
1068c2ecf20Sopenharmony_ci		dprintk("could not acquire lock\n");
1078c2ecf20Sopenharmony_ci		return -EINVAL;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci	state->i2c_write_buffer[0] = reg;
1108c2ecf20Sopenharmony_ci	state->i2c_write_buffer[1] = val >> 8;
1118c2ecf20Sopenharmony_ci	state->i2c_write_buffer[2] = val & 0xff;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	memset(state->msg, 0, sizeof(struct i2c_msg));
1148c2ecf20Sopenharmony_ci	state->msg[0].addr = state->cfg->i2c_address;
1158c2ecf20Sopenharmony_ci	state->msg[0].flags = 0;
1168c2ecf20Sopenharmony_ci	state->msg[0].buf = state->i2c_write_buffer;
1178c2ecf20Sopenharmony_ci	state->msg[0].len = 3;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (i2c_transfer(state->i2c, state->msg, 1) != 1) {
1208c2ecf20Sopenharmony_ci		pr_warn("DiB0070 I2C write failed\n");
1218c2ecf20Sopenharmony_ci		ret = -EREMOTEIO;
1228c2ecf20Sopenharmony_ci	} else
1238c2ecf20Sopenharmony_ci		ret = 0;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	mutex_unlock(&state->i2c_buffer_lock);
1268c2ecf20Sopenharmony_ci	return ret;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci#define HARD_RESET(state) do { \
1308c2ecf20Sopenharmony_ci    state->cfg->sleep(state->fe, 0); \
1318c2ecf20Sopenharmony_ci    if (state->cfg->reset) { \
1328c2ecf20Sopenharmony_ci	state->cfg->reset(state->fe,1); msleep(10); \
1338c2ecf20Sopenharmony_ci	state->cfg->reset(state->fe,0); msleep(10); \
1348c2ecf20Sopenharmony_ci    } \
1358c2ecf20Sopenharmony_ci} while (0)
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int dib0070_set_bandwidth(struct dvb_frontend *fe)
1388c2ecf20Sopenharmony_ci	{
1398c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
1408c2ecf20Sopenharmony_ci	u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000)
1438c2ecf20Sopenharmony_ci		tmp |= (0 << 14);
1448c2ecf20Sopenharmony_ci	else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000)
1458c2ecf20Sopenharmony_ci		tmp |= (1 << 14);
1468c2ecf20Sopenharmony_ci	else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000)
1478c2ecf20Sopenharmony_ci		tmp |= (2 << 14);
1488c2ecf20Sopenharmony_ci	else
1498c2ecf20Sopenharmony_ci		tmp |= (3 << 14);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	dib0070_write_reg(state, 0x02, tmp);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */
1548c2ecf20Sopenharmony_ci	if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) {
1558c2ecf20Sopenharmony_ci		u16 value = dib0070_read_reg(state, 0x17);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x17, value & 0xfffc);
1588c2ecf20Sopenharmony_ci		tmp = dib0070_read_reg(state, 0x01) & 0x01ff;
1598c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x01, tmp | (60 << 9));
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x17, value);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	int8_t step_sign;
1698c2ecf20Sopenharmony_ci	u16 adc;
1708c2ecf20Sopenharmony_ci	int ret = 0;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (*tune_state == CT_TUNER_STEP_0) {
1738c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x0f, 0xed10);
1748c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x17,    0x0034);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x18, 0x0032);
1778c2ecf20Sopenharmony_ci		state->step = state->captrim = state->fcaptrim = 64;
1788c2ecf20Sopenharmony_ci		state->adc_diff = 3000;
1798c2ecf20Sopenharmony_ci		ret = 20;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		*tune_state = CT_TUNER_STEP_1;
1828c2ecf20Sopenharmony_ci	} else if (*tune_state == CT_TUNER_STEP_1) {
1838c2ecf20Sopenharmony_ci		state->step /= 2;
1848c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x14, state->lo4 | state->captrim);
1858c2ecf20Sopenharmony_ci		ret = 15;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		*tune_state = CT_TUNER_STEP_2;
1888c2ecf20Sopenharmony_ci	} else if (*tune_state == CT_TUNER_STEP_2) {
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		adc = dib0070_read_reg(state, 0x19);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		dprintk("CAPTRIM=%d; ADC = %hd (ADC) & %dmV\n", state->captrim,
1938c2ecf20Sopenharmony_ci			adc, (u32)adc * (u32)1800 / (u32)1024);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		if (adc >= 400) {
1968c2ecf20Sopenharmony_ci			adc -= 400;
1978c2ecf20Sopenharmony_ci			step_sign = -1;
1988c2ecf20Sopenharmony_ci		} else {
1998c2ecf20Sopenharmony_ci			adc = 400 - adc;
2008c2ecf20Sopenharmony_ci			step_sign = 1;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		if (adc < state->adc_diff) {
2048c2ecf20Sopenharmony_ci			dprintk("CAPTRIM=%d is closer to target (%hd/%hd)\n",
2058c2ecf20Sopenharmony_ci				state->captrim, adc, state->adc_diff);
2068c2ecf20Sopenharmony_ci			state->adc_diff = adc;
2078c2ecf20Sopenharmony_ci			state->fcaptrim = state->captrim;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci		state->captrim += (step_sign * state->step);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		if (state->step >= 1)
2128c2ecf20Sopenharmony_ci			*tune_state = CT_TUNER_STEP_1;
2138c2ecf20Sopenharmony_ci		else
2148c2ecf20Sopenharmony_ci			*tune_state = CT_TUNER_STEP_3;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	} else if (*tune_state == CT_TUNER_STEP_3) {
2178c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim);
2188c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x18, 0x07ff);
2198c2ecf20Sopenharmony_ci		*tune_state = CT_TUNER_STEP_4;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return ret;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
2288c2ecf20Sopenharmony_ci	u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	dprintk("CTRL_LO5: 0x%x\n", lo5);
2318c2ecf20Sopenharmony_ci	return dib0070_write_reg(state, 0x15, lo5);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_civoid dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (open) {
2398c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x1b, 0xff00);
2408c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x1a, 0x0000);
2418c2ecf20Sopenharmony_ci	} else {
2428c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x1b, 0x4112);
2438c2ecf20Sopenharmony_ci		if (state->cfg->vga_filter != 0) {
2448c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x1a, state->cfg->vga_filter);
2458c2ecf20Sopenharmony_ci			dprintk("vga filter register is set to %x\n", state->cfg->vga_filter);
2468c2ecf20Sopenharmony_ci		} else
2478c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x1a, 0x0009);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dib0070_ctrl_agc_filter);
2528c2ecf20Sopenharmony_cistruct dib0070_tuning {
2538c2ecf20Sopenharmony_ci	u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
2548c2ecf20Sopenharmony_ci	u8 switch_trim;
2558c2ecf20Sopenharmony_ci	u8 vco_band;
2568c2ecf20Sopenharmony_ci	u8 hfdiv;
2578c2ecf20Sopenharmony_ci	u8 vco_multi;
2588c2ecf20Sopenharmony_ci	u8 presc;
2598c2ecf20Sopenharmony_ci	u8 wbdmux;
2608c2ecf20Sopenharmony_ci	u16 tuner_enable;
2618c2ecf20Sopenharmony_ci};
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistruct dib0070_lna_match {
2648c2ecf20Sopenharmony_ci	u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
2658c2ecf20Sopenharmony_ci	u8 lna_band;
2668c2ecf20Sopenharmony_ci};
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic const struct dib0070_tuning dib0070s_tuning_table[] = {
2698c2ecf20Sopenharmony_ci	{     570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */
2708c2ecf20Sopenharmony_ci	{     700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 },
2718c2ecf20Sopenharmony_ci	{     863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 },
2728c2ecf20Sopenharmony_ci	{    1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */
2738c2ecf20Sopenharmony_ci	{    1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
2748c2ecf20Sopenharmony_ci	{    2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
2758c2ecf20Sopenharmony_ci	{ 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */
2768c2ecf20Sopenharmony_ci};
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic const struct dib0070_tuning dib0070_tuning_table[] = {
2798c2ecf20Sopenharmony_ci	{     115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */
2808c2ecf20Sopenharmony_ci	{     179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */
2818c2ecf20Sopenharmony_ci	{     189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 },
2828c2ecf20Sopenharmony_ci	{     250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 },
2838c2ecf20Sopenharmony_ci	{     569999, 2, 1, 5,  6, 2, 2, 0x4000 | 0x0800 }, /* UHF */
2848c2ecf20Sopenharmony_ci	{     699999, 2, 0, 1,  4, 2, 2, 0x4000 | 0x0800 },
2858c2ecf20Sopenharmony_ci	{     863999, 2, 1, 1,  4, 2, 2, 0x4000 | 0x0800 },
2868c2ecf20Sopenharmony_ci	{ 0xffffffff, 0, 1, 0,  2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */
2878c2ecf20Sopenharmony_ci};
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic const struct dib0070_lna_match dib0070_lna_flip_chip[] = {
2908c2ecf20Sopenharmony_ci	{     180000, 0 }, /* VHF */
2918c2ecf20Sopenharmony_ci	{     188000, 1 },
2928c2ecf20Sopenharmony_ci	{     196400, 2 },
2938c2ecf20Sopenharmony_ci	{     250000, 3 },
2948c2ecf20Sopenharmony_ci	{     550000, 0 }, /* UHF */
2958c2ecf20Sopenharmony_ci	{     590000, 1 },
2968c2ecf20Sopenharmony_ci	{     666000, 3 },
2978c2ecf20Sopenharmony_ci	{     864000, 5 },
2988c2ecf20Sopenharmony_ci	{    1500000, 0 }, /* LBAND or everything higher than UHF */
2998c2ecf20Sopenharmony_ci	{    1600000, 1 },
3008c2ecf20Sopenharmony_ci	{    2000000, 3 },
3018c2ecf20Sopenharmony_ci	{ 0xffffffff, 7 },
3028c2ecf20Sopenharmony_ci};
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic const struct dib0070_lna_match dib0070_lna[] = {
3058c2ecf20Sopenharmony_ci	{     180000, 0 }, /* VHF */
3068c2ecf20Sopenharmony_ci	{     188000, 1 },
3078c2ecf20Sopenharmony_ci	{     196400, 2 },
3088c2ecf20Sopenharmony_ci	{     250000, 3 },
3098c2ecf20Sopenharmony_ci	{     550000, 2 }, /* UHF */
3108c2ecf20Sopenharmony_ci	{     650000, 3 },
3118c2ecf20Sopenharmony_ci	{     750000, 5 },
3128c2ecf20Sopenharmony_ci	{     850000, 6 },
3138c2ecf20Sopenharmony_ci	{     864000, 7 },
3148c2ecf20Sopenharmony_ci	{    1500000, 0 }, /* LBAND or everything higher than UHF */
3158c2ecf20Sopenharmony_ci	{    1600000, 1 },
3168c2ecf20Sopenharmony_ci	{    2000000, 3 },
3178c2ecf20Sopenharmony_ci	{ 0xffffffff, 7 },
3188c2ecf20Sopenharmony_ci};
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci#define LPF	100
3218c2ecf20Sopenharmony_cistatic int dib0070_tune_digital(struct dvb_frontend *fe)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	const struct dib0070_tuning *tune;
3268c2ecf20Sopenharmony_ci	const struct dib0070_lna_match *lna_match;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	enum frontend_tune_state *tune_state = &state->tune_state;
3298c2ecf20Sopenharmony_ci	int ret = 10; /* 1ms is the default delay most of the time */
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	u8  band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000);
3328c2ecf20Sopenharmony_ci	u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci#ifdef CONFIG_SYS_ISDBT
3358c2ecf20Sopenharmony_ci	if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
3368c2ecf20Sopenharmony_ci			if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2)
3378c2ecf20Sopenharmony_ci			&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
3388c2ecf20Sopenharmony_ci			|| (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
3398c2ecf20Sopenharmony_ci				&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2)))
3408c2ecf20Sopenharmony_ci			|| (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
3418c2ecf20Sopenharmony_ci				&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))))
3428c2ecf20Sopenharmony_ci				freq += 850;
3438c2ecf20Sopenharmony_ci#endif
3448c2ecf20Sopenharmony_ci	if (state->current_rf != freq) {
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		switch (state->revision) {
3478c2ecf20Sopenharmony_ci		case DIB0070S_P1A:
3488c2ecf20Sopenharmony_ci		tune = dib0070s_tuning_table;
3498c2ecf20Sopenharmony_ci		lna_match = dib0070_lna;
3508c2ecf20Sopenharmony_ci		break;
3518c2ecf20Sopenharmony_ci		default:
3528c2ecf20Sopenharmony_ci		tune = dib0070_tuning_table;
3538c2ecf20Sopenharmony_ci		if (state->cfg->flip_chip)
3548c2ecf20Sopenharmony_ci			lna_match = dib0070_lna_flip_chip;
3558c2ecf20Sopenharmony_ci		else
3568c2ecf20Sopenharmony_ci			lna_match = dib0070_lna;
3578c2ecf20Sopenharmony_ci		break;
3588c2ecf20Sopenharmony_ci		}
3598c2ecf20Sopenharmony_ci		while (freq > tune->max_freq) /* find the right one */
3608c2ecf20Sopenharmony_ci			tune++;
3618c2ecf20Sopenharmony_ci		while (freq > lna_match->max_freq) /* find the right one */
3628c2ecf20Sopenharmony_ci			lna_match++;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		state->current_tune_table_index = tune;
3658c2ecf20Sopenharmony_ci		state->lna_match = lna_match;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (*tune_state == CT_TUNER_START) {
3698c2ecf20Sopenharmony_ci		dprintk("Tuning for Band: %d (%d kHz)\n", band, freq);
3708c2ecf20Sopenharmony_ci		if (state->current_rf != freq) {
3718c2ecf20Sopenharmony_ci			u8 REFDIV;
3728c2ecf20Sopenharmony_ci			u32 FBDiv, Rest, FREF, VCOF_kHz;
3738c2ecf20Sopenharmony_ci			u8 Den;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci			state->current_rf = freq;
3768c2ecf20Sopenharmony_ci			state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x17, 0x30);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci			VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci			switch (band) {
3858c2ecf20Sopenharmony_ci			case BAND_VHF:
3868c2ecf20Sopenharmony_ci				REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000);
3878c2ecf20Sopenharmony_ci				break;
3888c2ecf20Sopenharmony_ci			case BAND_FM:
3898c2ecf20Sopenharmony_ci				REFDIV = (u8) ((state->cfg->clock_khz) / 1000);
3908c2ecf20Sopenharmony_ci				break;
3918c2ecf20Sopenharmony_ci			default:
3928c2ecf20Sopenharmony_ci				REFDIV = (u8) (state->cfg->clock_khz  / 10000);
3938c2ecf20Sopenharmony_ci				break;
3948c2ecf20Sopenharmony_ci			}
3958c2ecf20Sopenharmony_ci			FREF = state->cfg->clock_khz / REFDIV;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci			switch (state->revision) {
4008c2ecf20Sopenharmony_ci			case DIB0070S_P1A:
4018c2ecf20Sopenharmony_ci				FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF);
4028c2ecf20Sopenharmony_ci				Rest  = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF;
4038c2ecf20Sopenharmony_ci				break;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci			case DIB0070_P1G:
4068c2ecf20Sopenharmony_ci			case DIB0070_P1F:
4078c2ecf20Sopenharmony_ci			default:
4088c2ecf20Sopenharmony_ci				FBDiv = (freq / (FREF / 2));
4098c2ecf20Sopenharmony_ci				Rest  = 2 * freq - FBDiv * FREF;
4108c2ecf20Sopenharmony_ci				break;
4118c2ecf20Sopenharmony_ci			}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci			if (Rest < LPF)
4148c2ecf20Sopenharmony_ci				Rest = 0;
4158c2ecf20Sopenharmony_ci			else if (Rest < 2 * LPF)
4168c2ecf20Sopenharmony_ci				Rest = 2 * LPF;
4178c2ecf20Sopenharmony_ci			else if (Rest > (FREF - LPF)) {
4188c2ecf20Sopenharmony_ci				Rest = 0;
4198c2ecf20Sopenharmony_ci				FBDiv += 1;
4208c2ecf20Sopenharmony_ci			} else if (Rest > (FREF - 2 * LPF))
4218c2ecf20Sopenharmony_ci				Rest = FREF - 2 * LPF;
4228c2ecf20Sopenharmony_ci			Rest = (Rest * 6528) / (FREF / 10);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci			Den = 1;
4258c2ecf20Sopenharmony_ci			if (Rest > 0) {
4268c2ecf20Sopenharmony_ci				state->lo4 |= (1 << 14) | (1 << 12);
4278c2ecf20Sopenharmony_ci				Den = 255;
4288c2ecf20Sopenharmony_ci			}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x11, (u16)FBDiv);
4328c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV);
4338c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x13, (u16) Rest);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci			if (state->revision == DIB0070S_P1A) {
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci				if (band == BAND_SBAND) {
4388c2ecf20Sopenharmony_ci					dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
4398c2ecf20Sopenharmony_ci					dib0070_write_reg(state, 0x1d, 0xFFFF);
4408c2ecf20Sopenharmony_ci				} else
4418c2ecf20Sopenharmony_ci					dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1);
4428c2ecf20Sopenharmony_ci			}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x20,
4458c2ecf20Sopenharmony_ci				0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci			dprintk("REFDIV: %u, FREF: %d\n", REFDIV, FREF);
4488c2ecf20Sopenharmony_ci			dprintk("FBDIV: %d, Rest: %d\n", FBDiv, Rest);
4498c2ecf20Sopenharmony_ci			dprintk("Num: %u, Den: %u, SD: %d\n", (u16)Rest, Den,
4508c2ecf20Sopenharmony_ci				(state->lo4 >> 12) & 0x1);
4518c2ecf20Sopenharmony_ci			dprintk("HFDIV code: %u\n",
4528c2ecf20Sopenharmony_ci				state->current_tune_table_index->hfdiv);
4538c2ecf20Sopenharmony_ci			dprintk("VCO = %u\n",
4548c2ecf20Sopenharmony_ci				state->current_tune_table_index->vco_band);
4558c2ecf20Sopenharmony_ci			dprintk("VCOF: ((%u*%d) << 1))\n",
4568c2ecf20Sopenharmony_ci				state->current_tune_table_index->vco_multi,
4578c2ecf20Sopenharmony_ci				freq);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci			*tune_state = CT_TUNER_STEP_0;
4608c2ecf20Sopenharmony_ci		} else { /* we are already tuned to this frequency - the configuration is correct  */
4618c2ecf20Sopenharmony_ci			ret = 50; /* wakeup time */
4628c2ecf20Sopenharmony_ci			*tune_state = CT_TUNER_STEP_5;
4638c2ecf20Sopenharmony_ci		}
4648c2ecf20Sopenharmony_ci	} else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		ret = dib0070_captrim(state, tune_state);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	} else if (*tune_state == CT_TUNER_STEP_4) {
4698c2ecf20Sopenharmony_ci		const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
4708c2ecf20Sopenharmony_ci		if (tmp != NULL) {
4718c2ecf20Sopenharmony_ci			while (freq/1000 > tmp->freq) /* find the right one */
4728c2ecf20Sopenharmony_ci				tmp++;
4738c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x0f,
4748c2ecf20Sopenharmony_ci				(0 << 15) | (1 << 14) | (3 << 12)
4758c2ecf20Sopenharmony_ci				| (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7)
4768c2ecf20Sopenharmony_ci				| (state->current_tune_table_index->wbdmux << 0));
4778c2ecf20Sopenharmony_ci			state->wbd_gain_current = tmp->wbd_gain_val;
4788c2ecf20Sopenharmony_ci		} else {
4798c2ecf20Sopenharmony_ci			dib0070_write_reg(state, 0x0f,
4808c2ecf20Sopenharmony_ci					  (0 << 15) | (1 << 14) | (3 << 12)
4818c2ecf20Sopenharmony_ci					  | (6 << 9) | (0 << 8) | (1 << 7)
4828c2ecf20Sopenharmony_ci					  | (state->current_tune_table_index->wbdmux << 0));
4838c2ecf20Sopenharmony_ci			state->wbd_gain_current = 6;
4848c2ecf20Sopenharmony_ci		}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x06, 0x3fff);
4878c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x07,
4888c2ecf20Sopenharmony_ci				  (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0));
4898c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127));
4908c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x0d, 0x0d80);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x18,   0x07ff);
4948c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x17, 0x0033);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		*tune_state = CT_TUNER_STEP_5;
4988c2ecf20Sopenharmony_ci	} else if (*tune_state == CT_TUNER_STEP_5) {
4998c2ecf20Sopenharmony_ci		dib0070_set_bandwidth(fe);
5008c2ecf20Sopenharmony_ci		*tune_state = CT_TUNER_STOP;
5018c2ecf20Sopenharmony_ci	} else {
5028c2ecf20Sopenharmony_ci		ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci	return ret;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic int dib0070_tune(struct dvb_frontend *fe)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
5118c2ecf20Sopenharmony_ci	uint32_t ret;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	state->tune_state = CT_TUNER_START;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	do {
5168c2ecf20Sopenharmony_ci		ret = dib0070_tune_digital(fe);
5178c2ecf20Sopenharmony_ci		if (ret != FE_CALLBACK_TIME_NEVER)
5188c2ecf20Sopenharmony_ci			msleep(ret/10);
5198c2ecf20Sopenharmony_ci		else
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci	} while (state->tune_state != CT_TUNER_STOP);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	return 0;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic int dib0070_wakeup(struct dvb_frontend *fe)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
5298c2ecf20Sopenharmony_ci	if (state->cfg->sleep)
5308c2ecf20Sopenharmony_ci		state->cfg->sleep(fe, 0);
5318c2ecf20Sopenharmony_ci	return 0;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic int dib0070_sleep(struct dvb_frontend *fe)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
5378c2ecf20Sopenharmony_ci	if (state->cfg->sleep)
5388c2ecf20Sopenharmony_ci		state->cfg->sleep(fe, 1);
5398c2ecf20Sopenharmony_ci	return 0;
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ciu8 dib0070_get_rf_output(struct dvb_frontend *fe)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
5458c2ecf20Sopenharmony_ci	return (dib0070_read_reg(state, 0x07) >> 11) & 0x3;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dib0070_get_rf_output);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ciint dib0070_set_rf_output(struct dvb_frontend *fe, u8 no)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
5528c2ecf20Sopenharmony_ci	u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff;
5538c2ecf20Sopenharmony_ci	if (no > 3)
5548c2ecf20Sopenharmony_ci		no = 3;
5558c2ecf20Sopenharmony_ci	if (no < 1)
5568c2ecf20Sopenharmony_ci		no = 1;
5578c2ecf20Sopenharmony_ci	return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11));
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dib0070_set_rf_output);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic const u16 dib0070_p1f_defaults[] =
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	7, 0x02,
5658c2ecf20Sopenharmony_ci		0x0008,
5668c2ecf20Sopenharmony_ci		0x0000,
5678c2ecf20Sopenharmony_ci		0x0000,
5688c2ecf20Sopenharmony_ci		0x0000,
5698c2ecf20Sopenharmony_ci		0x0000,
5708c2ecf20Sopenharmony_ci		0x0002,
5718c2ecf20Sopenharmony_ci		0x0100,
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	3, 0x0d,
5748c2ecf20Sopenharmony_ci		0x0d80,
5758c2ecf20Sopenharmony_ci		0x0001,
5768c2ecf20Sopenharmony_ci		0x0000,
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	4, 0x11,
5798c2ecf20Sopenharmony_ci		0x0000,
5808c2ecf20Sopenharmony_ci		0x0103,
5818c2ecf20Sopenharmony_ci		0x0000,
5828c2ecf20Sopenharmony_ci		0x0000,
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	3, 0x16,
5858c2ecf20Sopenharmony_ci		0x0004 | 0x0040,
5868c2ecf20Sopenharmony_ci		0x0030,
5878c2ecf20Sopenharmony_ci		0x07ff,
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	6, 0x1b,
5908c2ecf20Sopenharmony_ci		0x4112,
5918c2ecf20Sopenharmony_ci		0xff00,
5928c2ecf20Sopenharmony_ci		0xc07f,
5938c2ecf20Sopenharmony_ci		0x0000,
5948c2ecf20Sopenharmony_ci		0x0180,
5958c2ecf20Sopenharmony_ci		0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	0,
5988c2ecf20Sopenharmony_ci};
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	u16 tuner_en = dib0070_read_reg(state, 0x20);
6038c2ecf20Sopenharmony_ci	u16 offset;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	dib0070_write_reg(state, 0x18, 0x07ff);
6068c2ecf20Sopenharmony_ci	dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
6078c2ecf20Sopenharmony_ci	dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0));
6088c2ecf20Sopenharmony_ci	msleep(9);
6098c2ecf20Sopenharmony_ci	offset = dib0070_read_reg(state, 0x19);
6108c2ecf20Sopenharmony_ci	dib0070_write_reg(state, 0x20, tuner_en);
6118c2ecf20Sopenharmony_ci	return offset;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic void dib0070_wbd_offset_calibration(struct dib0070_state *state)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	u8 gain;
6178c2ecf20Sopenharmony_ci	for (gain = 6; gain < 8; gain++) {
6188c2ecf20Sopenharmony_ci		state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2);
6198c2ecf20Sopenharmony_ci		dprintk("Gain: %d, WBDOffset (3.3V) = %hd\n", gain, state->wbd_offset_3_3[gain-6]);
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ciu16 dib0070_wbd_offset(struct dvb_frontend *fe)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
6268c2ecf20Sopenharmony_ci	const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
6278c2ecf20Sopenharmony_ci	u32 freq = fe->dtv_property_cache.frequency/1000;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	if (tmp != NULL) {
6308c2ecf20Sopenharmony_ci		while (freq/1000 > tmp->freq) /* find the right one */
6318c2ecf20Sopenharmony_ci			tmp++;
6328c2ecf20Sopenharmony_ci		state->wbd_gain_current = tmp->wbd_gain_val;
6338c2ecf20Sopenharmony_ci	} else
6348c2ecf20Sopenharmony_ci		state->wbd_gain_current = 6;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	return state->wbd_offset_3_3[state->wbd_gain_current - 6];
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dib0070_wbd_offset);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci#define pgm_read_word(w) (*w)
6418c2ecf20Sopenharmony_cistatic int dib0070_reset(struct dvb_frontend *fe)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
6448c2ecf20Sopenharmony_ci	u16 l, r, *n;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	HARD_RESET(state);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci#ifndef FORCE_SBAND_TUNER
6508c2ecf20Sopenharmony_ci	if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1)
6518c2ecf20Sopenharmony_ci		state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff;
6528c2ecf20Sopenharmony_ci	else
6538c2ecf20Sopenharmony_ci#else
6548c2ecf20Sopenharmony_ci#warning forcing SBAND
6558c2ecf20Sopenharmony_ci#endif
6568c2ecf20Sopenharmony_ci	state->revision = DIB0070S_P1A;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	/* P1F or not */
6598c2ecf20Sopenharmony_ci	dprintk("Revision: %x\n", state->revision);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (state->revision == DIB0070_P1D) {
6628c2ecf20Sopenharmony_ci		dprintk("Error: this driver is not to be used meant for P1D or earlier\n");
6638c2ecf20Sopenharmony_ci		return -EINVAL;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	n = (u16 *) dib0070_p1f_defaults;
6678c2ecf20Sopenharmony_ci	l = pgm_read_word(n++);
6688c2ecf20Sopenharmony_ci	while (l) {
6698c2ecf20Sopenharmony_ci		r = pgm_read_word(n++);
6708c2ecf20Sopenharmony_ci		do {
6718c2ecf20Sopenharmony_ci			dib0070_write_reg(state, (u8)r, pgm_read_word(n++));
6728c2ecf20Sopenharmony_ci			r++;
6738c2ecf20Sopenharmony_ci		} while (--l);
6748c2ecf20Sopenharmony_ci		l = pgm_read_word(n++);
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	if (state->cfg->force_crystal_mode != 0)
6788c2ecf20Sopenharmony_ci		r = state->cfg->force_crystal_mode;
6798c2ecf20Sopenharmony_ci	else if (state->cfg->clock_khz >= 24000)
6808c2ecf20Sopenharmony_ci		r = 1;
6818c2ecf20Sopenharmony_ci	else
6828c2ecf20Sopenharmony_ci		r = 2;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	r |= state->cfg->osc_buffer_state << 3;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	dib0070_write_reg(state, 0x10, r);
6888c2ecf20Sopenharmony_ci	dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5));
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (state->cfg->invert_iq) {
6918c2ecf20Sopenharmony_ci		r = dib0070_read_reg(state, 0x02) & 0xffdf;
6928c2ecf20Sopenharmony_ci		dib0070_write_reg(state, 0x02, r | (1 << 5));
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (state->revision == DIB0070S_P1A)
6968c2ecf20Sopenharmony_ci		dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
6978c2ecf20Sopenharmony_ci	else
6988c2ecf20Sopenharmony_ci		dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump,
6998c2ecf20Sopenharmony_ci				     state->cfg->enable_third_order_filter);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	dib0070_wbd_offset_calibration(state);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	return 0;
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	struct dib0070_state *state = fe->tuner_priv;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	*frequency = 1000 * state->current_rf;
7138c2ecf20Sopenharmony_ci	return 0;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic void dib0070_release(struct dvb_frontend *fe)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	kfree(fe->tuner_priv);
7198c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops dib0070_ops = {
7238c2ecf20Sopenharmony_ci	.info = {
7248c2ecf20Sopenharmony_ci		.name              = "DiBcom DiB0070",
7258c2ecf20Sopenharmony_ci		.frequency_min_hz  =  45 * MHz,
7268c2ecf20Sopenharmony_ci		.frequency_max_hz  = 860 * MHz,
7278c2ecf20Sopenharmony_ci		.frequency_step_hz =   1 * kHz,
7288c2ecf20Sopenharmony_ci	},
7298c2ecf20Sopenharmony_ci	.release       = dib0070_release,
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	.init          = dib0070_wakeup,
7328c2ecf20Sopenharmony_ci	.sleep         = dib0070_sleep,
7338c2ecf20Sopenharmony_ci	.set_params    = dib0070_tune,
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	.get_frequency = dib0070_get_frequency,
7368c2ecf20Sopenharmony_ci//      .get_bandwidth = dib0070_get_bandwidth
7378c2ecf20Sopenharmony_ci};
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_cistruct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL);
7428c2ecf20Sopenharmony_ci	if (state == NULL)
7438c2ecf20Sopenharmony_ci		return NULL;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	state->cfg = cfg;
7468c2ecf20Sopenharmony_ci	state->i2c = i2c;
7478c2ecf20Sopenharmony_ci	state->fe  = fe;
7488c2ecf20Sopenharmony_ci	mutex_init(&state->i2c_buffer_lock);
7498c2ecf20Sopenharmony_ci	fe->tuner_priv = state;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (dib0070_reset(fe) != 0)
7528c2ecf20Sopenharmony_ci		goto free_mem;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	pr_info("DiB0070: successfully identified\n");
7558c2ecf20Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	fe->tuner_priv = state;
7588c2ecf20Sopenharmony_ci	return fe;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cifree_mem:
7618c2ecf20Sopenharmony_ci	kfree(state);
7628c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
7638c2ecf20Sopenharmony_ci	return NULL;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dib0070_attach);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
7688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner");
7698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
770