1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab  * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner.
49a0bf528SMauro Carvalho Chehab  *
59a0bf528SMauro Carvalho Chehab  * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/)
69a0bf528SMauro Carvalho Chehab  *
79a0bf528SMauro Carvalho Chehab  * This code is more or less generated from another driver, please
89a0bf528SMauro Carvalho Chehab  * excuse some codingstyle oddities.
99a0bf528SMauro Carvalho Chehab  */
109a0bf528SMauro Carvalho Chehab 
11fb11cbd1SMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12fb11cbd1SMauro Carvalho Chehab 
139a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
149a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
159a0bf528SMauro Carvalho Chehab #include <linux/i2c.h>
169a0bf528SMauro Carvalho Chehab #include <linux/mutex.h>
179a0bf528SMauro Carvalho Chehab 
18fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
199a0bf528SMauro Carvalho Chehab 
209a0bf528SMauro Carvalho Chehab #include "dib0070.h"
219a0bf528SMauro Carvalho Chehab #include "dibx000_common.h"
229a0bf528SMauro Carvalho Chehab 
239a0bf528SMauro Carvalho Chehab static int debug;
249a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
259a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
269a0bf528SMauro Carvalho Chehab 
27fb11cbd1SMauro Carvalho Chehab #define dprintk(fmt, arg...) do {					\
28fb11cbd1SMauro Carvalho Chehab 	if (debug)							\
29fb11cbd1SMauro Carvalho Chehab 		printk(KERN_DEBUG pr_fmt("%s: " fmt),			\
30fb11cbd1SMauro Carvalho Chehab 		       __func__, ##arg);				\
319a0bf528SMauro Carvalho Chehab } while (0)
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab #define DIB0070_P1D  0x00
349a0bf528SMauro Carvalho Chehab #define DIB0070_P1F  0x01
359a0bf528SMauro Carvalho Chehab #define DIB0070_P1G  0x03
369a0bf528SMauro Carvalho Chehab #define DIB0070S_P1A 0x02
379a0bf528SMauro Carvalho Chehab 
389a0bf528SMauro Carvalho Chehab struct dib0070_state {
399a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
409a0bf528SMauro Carvalho Chehab 	struct dvb_frontend *fe;
419a0bf528SMauro Carvalho Chehab 	const struct dib0070_config *cfg;
429a0bf528SMauro Carvalho Chehab 	u16 wbd_ff_offset;
439a0bf528SMauro Carvalho Chehab 	u8 revision;
449a0bf528SMauro Carvalho Chehab 
459a0bf528SMauro Carvalho Chehab 	enum frontend_tune_state tune_state;
469a0bf528SMauro Carvalho Chehab 	u32 current_rf;
479a0bf528SMauro Carvalho Chehab 
489a0bf528SMauro Carvalho Chehab 	/* for the captrim binary search */
499a0bf528SMauro Carvalho Chehab 	s8 step;
509a0bf528SMauro Carvalho Chehab 	u16 adc_diff;
519a0bf528SMauro Carvalho Chehab 
529a0bf528SMauro Carvalho Chehab 	s8 captrim;
539a0bf528SMauro Carvalho Chehab 	s8 fcaptrim;
549a0bf528SMauro Carvalho Chehab 	u16 lo4;
559a0bf528SMauro Carvalho Chehab 
569a0bf528SMauro Carvalho Chehab 	const struct dib0070_tuning *current_tune_table_index;
579a0bf528SMauro Carvalho Chehab 	const struct dib0070_lna_match *lna_match;
589a0bf528SMauro Carvalho Chehab 
599a0bf528SMauro Carvalho Chehab 	u8  wbd_gain_current;
609a0bf528SMauro Carvalho Chehab 	u16 wbd_offset_3_3[2];
619a0bf528SMauro Carvalho Chehab 
629a0bf528SMauro Carvalho Chehab 	/* for the I2C transfer */
639a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[2];
649a0bf528SMauro Carvalho Chehab 	u8 i2c_write_buffer[3];
659a0bf528SMauro Carvalho Chehab 	u8 i2c_read_buffer[2];
669a0bf528SMauro Carvalho Chehab 	struct mutex i2c_buffer_lock;
679a0bf528SMauro Carvalho Chehab };
689a0bf528SMauro Carvalho Chehab 
699a0bf528SMauro Carvalho Chehab static u16 dib0070_read_reg(struct dib0070_state *state, u8 reg)
709a0bf528SMauro Carvalho Chehab {
719a0bf528SMauro Carvalho Chehab 	u16 ret;
729a0bf528SMauro Carvalho Chehab 
739a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
74fb11cbd1SMauro Carvalho Chehab 		dprintk("could not acquire lock\n");
759a0bf528SMauro Carvalho Chehab 		return 0;
769a0bf528SMauro Carvalho Chehab 	}
779a0bf528SMauro Carvalho Chehab 
789a0bf528SMauro Carvalho Chehab 	state->i2c_write_buffer[0] = reg;
799a0bf528SMauro Carvalho Chehab 
809a0bf528SMauro Carvalho Chehab 	memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
819a0bf528SMauro Carvalho Chehab 	state->msg[0].addr = state->cfg->i2c_address;
829a0bf528SMauro Carvalho Chehab 	state->msg[0].flags = 0;
839a0bf528SMauro Carvalho Chehab 	state->msg[0].buf = state->i2c_write_buffer;
849a0bf528SMauro Carvalho Chehab 	state->msg[0].len = 1;
859a0bf528SMauro Carvalho Chehab 	state->msg[1].addr = state->cfg->i2c_address;
869a0bf528SMauro Carvalho Chehab 	state->msg[1].flags = I2C_M_RD;
879a0bf528SMauro Carvalho Chehab 	state->msg[1].buf = state->i2c_read_buffer;
889a0bf528SMauro Carvalho Chehab 	state->msg[1].len = 2;
899a0bf528SMauro Carvalho Chehab 
909a0bf528SMauro Carvalho Chehab 	if (i2c_transfer(state->i2c, state->msg, 2) != 2) {
91fb11cbd1SMauro Carvalho Chehab 		pr_warn("DiB0070 I2C read failed\n");
929a0bf528SMauro Carvalho Chehab 		ret = 0;
939a0bf528SMauro Carvalho Chehab 	} else
949a0bf528SMauro Carvalho Chehab 		ret = (state->i2c_read_buffer[0] << 8)
959a0bf528SMauro Carvalho Chehab 			| state->i2c_read_buffer[1];
969a0bf528SMauro Carvalho Chehab 
979a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->i2c_buffer_lock);
989a0bf528SMauro Carvalho Chehab 	return ret;
999a0bf528SMauro Carvalho Chehab }
1009a0bf528SMauro Carvalho Chehab 
1019a0bf528SMauro Carvalho Chehab static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
1029a0bf528SMauro Carvalho Chehab {
1039a0bf528SMauro Carvalho Chehab 	int ret;
1049a0bf528SMauro Carvalho Chehab 
1059a0bf528SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
106fb11cbd1SMauro Carvalho Chehab 		dprintk("could not acquire lock\n");
1079a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1089a0bf528SMauro Carvalho Chehab 	}
1099a0bf528SMauro Carvalho Chehab 	state->i2c_write_buffer[0] = reg;
1109a0bf528SMauro Carvalho Chehab 	state->i2c_write_buffer[1] = val >> 8;
1119a0bf528SMauro Carvalho Chehab 	state->i2c_write_buffer[2] = val & 0xff;
1129a0bf528SMauro Carvalho Chehab 
1139a0bf528SMauro Carvalho Chehab 	memset(state->msg, 0, sizeof(struct i2c_msg));
1149a0bf528SMauro Carvalho Chehab 	state->msg[0].addr = state->cfg->i2c_address;
1159a0bf528SMauro Carvalho Chehab 	state->msg[0].flags = 0;
1169a0bf528SMauro Carvalho Chehab 	state->msg[0].buf = state->i2c_write_buffer;
1179a0bf528SMauro Carvalho Chehab 	state->msg[0].len = 3;
1189a0bf528SMauro Carvalho Chehab 
1199a0bf528SMauro Carvalho Chehab 	if (i2c_transfer(state->i2c, state->msg, 1) != 1) {
120fb11cbd1SMauro Carvalho Chehab 		pr_warn("DiB0070 I2C write failed\n");
1219a0bf528SMauro Carvalho Chehab 		ret = -EREMOTEIO;
1229a0bf528SMauro Carvalho Chehab 	} else
1239a0bf528SMauro Carvalho Chehab 		ret = 0;
1249a0bf528SMauro Carvalho Chehab 
1259a0bf528SMauro Carvalho Chehab 	mutex_unlock(&state->i2c_buffer_lock);
1269a0bf528SMauro Carvalho Chehab 	return ret;
1279a0bf528SMauro Carvalho Chehab }
1289a0bf528SMauro Carvalho Chehab 
1299a0bf528SMauro Carvalho Chehab #define HARD_RESET(state) do { \
1309a0bf528SMauro Carvalho Chehab     state->cfg->sleep(state->fe, 0); \
1319a0bf528SMauro Carvalho Chehab     if (state->cfg->reset) { \
1329a0bf528SMauro Carvalho Chehab 	state->cfg->reset(state->fe,1); msleep(10); \
1339a0bf528SMauro Carvalho Chehab 	state->cfg->reset(state->fe,0); msleep(10); \
1349a0bf528SMauro Carvalho Chehab     } \
1359a0bf528SMauro Carvalho Chehab } while (0)
1369a0bf528SMauro Carvalho Chehab 
1379a0bf528SMauro Carvalho Chehab static int dib0070_set_bandwidth(struct dvb_frontend *fe)
1389a0bf528SMauro Carvalho Chehab 	{
1399a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
1409a0bf528SMauro Carvalho Chehab 	u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff;
1419a0bf528SMauro Carvalho Chehab 
1429a0bf528SMauro Carvalho Chehab 	if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000)
1439a0bf528SMauro Carvalho Chehab 		tmp |= (0 << 14);
1449a0bf528SMauro Carvalho Chehab 	else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000)
1459a0bf528SMauro Carvalho Chehab 		tmp |= (1 << 14);
1469a0bf528SMauro Carvalho Chehab 	else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000)
1479a0bf528SMauro Carvalho Chehab 		tmp |= (2 << 14);
1489a0bf528SMauro Carvalho Chehab 	else
1499a0bf528SMauro Carvalho Chehab 		tmp |= (3 << 14);
1509a0bf528SMauro Carvalho Chehab 
1519a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x02, tmp);
1529a0bf528SMauro Carvalho Chehab 
1539a0bf528SMauro Carvalho Chehab 	/* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */
1549a0bf528SMauro Carvalho Chehab 	if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) {
1559a0bf528SMauro Carvalho Chehab 		u16 value = dib0070_read_reg(state, 0x17);
1569a0bf528SMauro Carvalho Chehab 
1579a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x17, value & 0xfffc);
1589a0bf528SMauro Carvalho Chehab 		tmp = dib0070_read_reg(state, 0x01) & 0x01ff;
1599a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x01, tmp | (60 << 9));
1609a0bf528SMauro Carvalho Chehab 
1619a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x17, value);
1629a0bf528SMauro Carvalho Chehab 	}
1639a0bf528SMauro Carvalho Chehab 	return 0;
1649a0bf528SMauro Carvalho Chehab }
1659a0bf528SMauro Carvalho Chehab 
1669a0bf528SMauro Carvalho Chehab static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state)
1679a0bf528SMauro Carvalho Chehab {
1689a0bf528SMauro Carvalho Chehab 	int8_t step_sign;
1699a0bf528SMauro Carvalho Chehab 	u16 adc;
1709a0bf528SMauro Carvalho Chehab 	int ret = 0;
1719a0bf528SMauro Carvalho Chehab 
1729a0bf528SMauro Carvalho Chehab 	if (*tune_state == CT_TUNER_STEP_0) {
1739a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x0f, 0xed10);
1749a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x17,    0x0034);
1759a0bf528SMauro Carvalho Chehab 
1769a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x18, 0x0032);
1779a0bf528SMauro Carvalho Chehab 		state->step = state->captrim = state->fcaptrim = 64;
1789a0bf528SMauro Carvalho Chehab 		state->adc_diff = 3000;
1799a0bf528SMauro Carvalho Chehab 		ret = 20;
1809a0bf528SMauro Carvalho Chehab 
1819a0bf528SMauro Carvalho Chehab 		*tune_state = CT_TUNER_STEP_1;
1829a0bf528SMauro Carvalho Chehab 	} else if (*tune_state == CT_TUNER_STEP_1) {
1839a0bf528SMauro Carvalho Chehab 		state->step /= 2;
1849a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x14, state->lo4 | state->captrim);
1859a0bf528SMauro Carvalho Chehab 		ret = 15;
1869a0bf528SMauro Carvalho Chehab 
1879a0bf528SMauro Carvalho Chehab 		*tune_state = CT_TUNER_STEP_2;
1889a0bf528SMauro Carvalho Chehab 	} else if (*tune_state == CT_TUNER_STEP_2) {
1899a0bf528SMauro Carvalho Chehab 
1909a0bf528SMauro Carvalho Chehab 		adc = dib0070_read_reg(state, 0x19);
1919a0bf528SMauro Carvalho Chehab 
192fb11cbd1SMauro Carvalho Chehab 		dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV\n", state->captrim, adc, (u32) adc*(u32)1800/(u32)1024);
1939a0bf528SMauro Carvalho Chehab 
1949a0bf528SMauro Carvalho Chehab 		if (adc >= 400) {
1959a0bf528SMauro Carvalho Chehab 			adc -= 400;
1969a0bf528SMauro Carvalho Chehab 			step_sign = -1;
1979a0bf528SMauro Carvalho Chehab 		} else {
1989a0bf528SMauro Carvalho Chehab 			adc = 400 - adc;
1999a0bf528SMauro Carvalho Chehab 			step_sign = 1;
2009a0bf528SMauro Carvalho Chehab 		}
2019a0bf528SMauro Carvalho Chehab 
2029a0bf528SMauro Carvalho Chehab 		if (adc < state->adc_diff) {
203fb11cbd1SMauro Carvalho Chehab 			dprintk("CAPTRIM=%hd is closer to target (%hd/%hd)\n", state->captrim, adc, state->adc_diff);
2049a0bf528SMauro Carvalho Chehab 			state->adc_diff = adc;
2059a0bf528SMauro Carvalho Chehab 			state->fcaptrim = state->captrim;
2069a0bf528SMauro Carvalho Chehab 		}
2079a0bf528SMauro Carvalho Chehab 		state->captrim += (step_sign * state->step);
2089a0bf528SMauro Carvalho Chehab 
2099a0bf528SMauro Carvalho Chehab 		if (state->step >= 1)
2109a0bf528SMauro Carvalho Chehab 			*tune_state = CT_TUNER_STEP_1;
2119a0bf528SMauro Carvalho Chehab 		else
2129a0bf528SMauro Carvalho Chehab 			*tune_state = CT_TUNER_STEP_3;
2139a0bf528SMauro Carvalho Chehab 
2149a0bf528SMauro Carvalho Chehab 	} else if (*tune_state == CT_TUNER_STEP_3) {
2159a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim);
2169a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x18, 0x07ff);
2179a0bf528SMauro Carvalho Chehab 		*tune_state = CT_TUNER_STEP_4;
2189a0bf528SMauro Carvalho Chehab 	}
2199a0bf528SMauro Carvalho Chehab 
2209a0bf528SMauro Carvalho Chehab 	return ret;
2219a0bf528SMauro Carvalho Chehab }
2229a0bf528SMauro Carvalho Chehab 
2239a0bf528SMauro Carvalho Chehab static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt)
2249a0bf528SMauro Carvalho Chehab {
2259a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
2269a0bf528SMauro Carvalho Chehab 	u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0);
227f3f8ef22SMauro Carvalho Chehab 
228fb11cbd1SMauro Carvalho Chehab 	dprintk("CTRL_LO5: 0x%x\n", lo5);
2299a0bf528SMauro Carvalho Chehab 	return dib0070_write_reg(state, 0x15, lo5);
2309a0bf528SMauro Carvalho Chehab }
2319a0bf528SMauro Carvalho Chehab 
2329a0bf528SMauro Carvalho Chehab void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)
2339a0bf528SMauro Carvalho Chehab {
2349a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
2359a0bf528SMauro Carvalho Chehab 
2369a0bf528SMauro Carvalho Chehab 	if (open) {
2379a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x1b, 0xff00);
2389a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x1a, 0x0000);
2399a0bf528SMauro Carvalho Chehab 	} else {
2409a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x1b, 0x4112);
2419a0bf528SMauro Carvalho Chehab 		if (state->cfg->vga_filter != 0) {
2429a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x1a, state->cfg->vga_filter);
243fb11cbd1SMauro Carvalho Chehab 			dprintk("vga filter register is set to %x\n", state->cfg->vga_filter);
2449a0bf528SMauro Carvalho Chehab 		} else
2459a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x1a, 0x0009);
2469a0bf528SMauro Carvalho Chehab 	}
2479a0bf528SMauro Carvalho Chehab }
2489a0bf528SMauro Carvalho Chehab 
2499a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_ctrl_agc_filter);
2509a0bf528SMauro Carvalho Chehab struct dib0070_tuning {
2519a0bf528SMauro Carvalho Chehab 	u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
2529a0bf528SMauro Carvalho Chehab 	u8 switch_trim;
2539a0bf528SMauro Carvalho Chehab 	u8 vco_band;
2549a0bf528SMauro Carvalho Chehab 	u8 hfdiv;
2559a0bf528SMauro Carvalho Chehab 	u8 vco_multi;
2569a0bf528SMauro Carvalho Chehab 	u8 presc;
2579a0bf528SMauro Carvalho Chehab 	u8 wbdmux;
2589a0bf528SMauro Carvalho Chehab 	u16 tuner_enable;
2599a0bf528SMauro Carvalho Chehab };
2609a0bf528SMauro Carvalho Chehab 
2619a0bf528SMauro Carvalho Chehab struct dib0070_lna_match {
2629a0bf528SMauro Carvalho Chehab 	u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
2639a0bf528SMauro Carvalho Chehab 	u8 lna_band;
2649a0bf528SMauro Carvalho Chehab };
2659a0bf528SMauro Carvalho Chehab 
2669a0bf528SMauro Carvalho Chehab static const struct dib0070_tuning dib0070s_tuning_table[] = {
2679a0bf528SMauro Carvalho Chehab 	{     570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */
2689a0bf528SMauro Carvalho Chehab 	{     700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 },
2699a0bf528SMauro Carvalho Chehab 	{     863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 },
2709a0bf528SMauro Carvalho Chehab 	{    1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */
2719a0bf528SMauro Carvalho Chehab 	{    1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
2729a0bf528SMauro Carvalho Chehab 	{    2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
2739a0bf528SMauro Carvalho Chehab 	{ 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */
2749a0bf528SMauro Carvalho Chehab };
2759a0bf528SMauro Carvalho Chehab 
2769a0bf528SMauro Carvalho Chehab static const struct dib0070_tuning dib0070_tuning_table[] = {
2779a0bf528SMauro Carvalho Chehab 	{     115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */
2789a0bf528SMauro Carvalho Chehab 	{     179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */
2799a0bf528SMauro Carvalho Chehab 	{     189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 },
2809a0bf528SMauro Carvalho Chehab 	{     250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 },
2819a0bf528SMauro Carvalho Chehab 	{     569999, 2, 1, 5,  6, 2, 2, 0x4000 | 0x0800 }, /* UHF */
2829a0bf528SMauro Carvalho Chehab 	{     699999, 2, 0, 1,  4, 2, 2, 0x4000 | 0x0800 },
2839a0bf528SMauro Carvalho Chehab 	{     863999, 2, 1, 1,  4, 2, 2, 0x4000 | 0x0800 },
2849a0bf528SMauro Carvalho Chehab 	{ 0xffffffff, 0, 1, 0,  2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */
2859a0bf528SMauro Carvalho Chehab };
2869a0bf528SMauro Carvalho Chehab 
2879a0bf528SMauro Carvalho Chehab static const struct dib0070_lna_match dib0070_lna_flip_chip[] = {
2889a0bf528SMauro Carvalho Chehab 	{     180000, 0 }, /* VHF */
2899a0bf528SMauro Carvalho Chehab 	{     188000, 1 },
2909a0bf528SMauro Carvalho Chehab 	{     196400, 2 },
2919a0bf528SMauro Carvalho Chehab 	{     250000, 3 },
2929a0bf528SMauro Carvalho Chehab 	{     550000, 0 }, /* UHF */
2939a0bf528SMauro Carvalho Chehab 	{     590000, 1 },
2949a0bf528SMauro Carvalho Chehab 	{     666000, 3 },
2959a0bf528SMauro Carvalho Chehab 	{     864000, 5 },
2969a0bf528SMauro Carvalho Chehab 	{    1500000, 0 }, /* LBAND or everything higher than UHF */
2979a0bf528SMauro Carvalho Chehab 	{    1600000, 1 },
2989a0bf528SMauro Carvalho Chehab 	{    2000000, 3 },
2999a0bf528SMauro Carvalho Chehab 	{ 0xffffffff, 7 },
3009a0bf528SMauro Carvalho Chehab };
3019a0bf528SMauro Carvalho Chehab 
3029a0bf528SMauro Carvalho Chehab static const struct dib0070_lna_match dib0070_lna[] = {
3039a0bf528SMauro Carvalho Chehab 	{     180000, 0 }, /* VHF */
3049a0bf528SMauro Carvalho Chehab 	{     188000, 1 },
3059a0bf528SMauro Carvalho Chehab 	{     196400, 2 },
3069a0bf528SMauro Carvalho Chehab 	{     250000, 3 },
3079a0bf528SMauro Carvalho Chehab 	{     550000, 2 }, /* UHF */
3089a0bf528SMauro Carvalho Chehab 	{     650000, 3 },
3099a0bf528SMauro Carvalho Chehab 	{     750000, 5 },
3109a0bf528SMauro Carvalho Chehab 	{     850000, 6 },
3119a0bf528SMauro Carvalho Chehab 	{     864000, 7 },
3129a0bf528SMauro Carvalho Chehab 	{    1500000, 0 }, /* LBAND or everything higher than UHF */
3139a0bf528SMauro Carvalho Chehab 	{    1600000, 1 },
3149a0bf528SMauro Carvalho Chehab 	{    2000000, 3 },
3159a0bf528SMauro Carvalho Chehab 	{ 0xffffffff, 7 },
3169a0bf528SMauro Carvalho Chehab };
3179a0bf528SMauro Carvalho Chehab 
3189a0bf528SMauro Carvalho Chehab #define LPF	100
3199a0bf528SMauro Carvalho Chehab static int dib0070_tune_digital(struct dvb_frontend *fe)
3209a0bf528SMauro Carvalho Chehab {
3219a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
3229a0bf528SMauro Carvalho Chehab 
3239a0bf528SMauro Carvalho Chehab 	const struct dib0070_tuning *tune;
3249a0bf528SMauro Carvalho Chehab 	const struct dib0070_lna_match *lna_match;
3259a0bf528SMauro Carvalho Chehab 
3269a0bf528SMauro Carvalho Chehab 	enum frontend_tune_state *tune_state = &state->tune_state;
3279a0bf528SMauro Carvalho Chehab 	int ret = 10; /* 1ms is the default delay most of the time */
3289a0bf528SMauro Carvalho Chehab 
3299a0bf528SMauro Carvalho Chehab 	u8  band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000);
3309a0bf528SMauro Carvalho Chehab 	u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf);
3319a0bf528SMauro Carvalho Chehab 
3329a0bf528SMauro Carvalho Chehab #ifdef CONFIG_SYS_ISDBT
3339a0bf528SMauro Carvalho Chehab 	if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
3349a0bf528SMauro Carvalho Chehab 			if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2)
3359a0bf528SMauro Carvalho Chehab 			&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
3369a0bf528SMauro Carvalho Chehab 			|| (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
3379a0bf528SMauro Carvalho Chehab 				&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2)))
3389a0bf528SMauro Carvalho Chehab 			|| (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
3399a0bf528SMauro Carvalho Chehab 				&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))))
3409a0bf528SMauro Carvalho Chehab 				freq += 850;
3419a0bf528SMauro Carvalho Chehab #endif
3429a0bf528SMauro Carvalho Chehab 	if (state->current_rf != freq) {
3439a0bf528SMauro Carvalho Chehab 
3449a0bf528SMauro Carvalho Chehab 		switch (state->revision) {
3459a0bf528SMauro Carvalho Chehab 		case DIB0070S_P1A:
3469a0bf528SMauro Carvalho Chehab 		tune = dib0070s_tuning_table;
3479a0bf528SMauro Carvalho Chehab 		lna_match = dib0070_lna;
3489a0bf528SMauro Carvalho Chehab 		break;
3499a0bf528SMauro Carvalho Chehab 		default:
3509a0bf528SMauro Carvalho Chehab 		tune = dib0070_tuning_table;
3519a0bf528SMauro Carvalho Chehab 		if (state->cfg->flip_chip)
3529a0bf528SMauro Carvalho Chehab 			lna_match = dib0070_lna_flip_chip;
3539a0bf528SMauro Carvalho Chehab 		else
3549a0bf528SMauro Carvalho Chehab 			lna_match = dib0070_lna;
3559a0bf528SMauro Carvalho Chehab 		break;
3569a0bf528SMauro Carvalho Chehab 		}
3579a0bf528SMauro Carvalho Chehab 		while (freq > tune->max_freq) /* find the right one */
3589a0bf528SMauro Carvalho Chehab 			tune++;
3599a0bf528SMauro Carvalho Chehab 		while (freq > lna_match->max_freq) /* find the right one */
3609a0bf528SMauro Carvalho Chehab 			lna_match++;
3619a0bf528SMauro Carvalho Chehab 
3629a0bf528SMauro Carvalho Chehab 		state->current_tune_table_index = tune;
3639a0bf528SMauro Carvalho Chehab 		state->lna_match = lna_match;
3649a0bf528SMauro Carvalho Chehab 	}
3659a0bf528SMauro Carvalho Chehab 
3669a0bf528SMauro Carvalho Chehab 	if (*tune_state == CT_TUNER_START) {
367fb11cbd1SMauro Carvalho Chehab 		dprintk("Tuning for Band: %hd (%d kHz)\n", band, freq);
3689a0bf528SMauro Carvalho Chehab 		if (state->current_rf != freq) {
3699a0bf528SMauro Carvalho Chehab 			u8 REFDIV;
3709a0bf528SMauro Carvalho Chehab 			u32 FBDiv, Rest, FREF, VCOF_kHz;
3719a0bf528SMauro Carvalho Chehab 			u8 Den;
3729a0bf528SMauro Carvalho Chehab 
3739a0bf528SMauro Carvalho Chehab 			state->current_rf = freq;
3749a0bf528SMauro Carvalho Chehab 			state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);
3759a0bf528SMauro Carvalho Chehab 
3769a0bf528SMauro Carvalho Chehab 
3779a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x17, 0x30);
3789a0bf528SMauro Carvalho Chehab 
3799a0bf528SMauro Carvalho Chehab 
3809a0bf528SMauro Carvalho Chehab 			VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;
3819a0bf528SMauro Carvalho Chehab 
3829a0bf528SMauro Carvalho Chehab 			switch (band) {
3839a0bf528SMauro Carvalho Chehab 			case BAND_VHF:
3849a0bf528SMauro Carvalho Chehab 				REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000);
3859a0bf528SMauro Carvalho Chehab 				break;
3869a0bf528SMauro Carvalho Chehab 			case BAND_FM:
3879a0bf528SMauro Carvalho Chehab 				REFDIV = (u8) ((state->cfg->clock_khz) / 1000);
3889a0bf528SMauro Carvalho Chehab 				break;
3899a0bf528SMauro Carvalho Chehab 			default:
3909a0bf528SMauro Carvalho Chehab 				REFDIV = (u8) (state->cfg->clock_khz  / 10000);
3919a0bf528SMauro Carvalho Chehab 				break;
3929a0bf528SMauro Carvalho Chehab 			}
3939a0bf528SMauro Carvalho Chehab 			FREF = state->cfg->clock_khz / REFDIV;
3949a0bf528SMauro Carvalho Chehab 
3959a0bf528SMauro Carvalho Chehab 
3969a0bf528SMauro Carvalho Chehab 
3979a0bf528SMauro Carvalho Chehab 			switch (state->revision) {
3989a0bf528SMauro Carvalho Chehab 			case DIB0070S_P1A:
3999a0bf528SMauro Carvalho Chehab 				FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF);
4009a0bf528SMauro Carvalho Chehab 				Rest  = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF;
4019a0bf528SMauro Carvalho Chehab 				break;
4029a0bf528SMauro Carvalho Chehab 
4039a0bf528SMauro Carvalho Chehab 			case DIB0070_P1G:
4049a0bf528SMauro Carvalho Chehab 			case DIB0070_P1F:
4059a0bf528SMauro Carvalho Chehab 			default:
4069a0bf528SMauro Carvalho Chehab 				FBDiv = (freq / (FREF / 2));
4079a0bf528SMauro Carvalho Chehab 				Rest  = 2 * freq - FBDiv * FREF;
4089a0bf528SMauro Carvalho Chehab 				break;
4099a0bf528SMauro Carvalho Chehab 			}
4109a0bf528SMauro Carvalho Chehab 
4119a0bf528SMauro Carvalho Chehab 			if (Rest < LPF)
4129a0bf528SMauro Carvalho Chehab 				Rest = 0;
4139a0bf528SMauro Carvalho Chehab 			else if (Rest < 2 * LPF)
4149a0bf528SMauro Carvalho Chehab 				Rest = 2 * LPF;
4159a0bf528SMauro Carvalho Chehab 			else if (Rest > (FREF - LPF)) {
4169a0bf528SMauro Carvalho Chehab 				Rest = 0;
4179a0bf528SMauro Carvalho Chehab 				FBDiv += 1;
4189a0bf528SMauro Carvalho Chehab 			} else if (Rest > (FREF - 2 * LPF))
4199a0bf528SMauro Carvalho Chehab 				Rest = FREF - 2 * LPF;
4209a0bf528SMauro Carvalho Chehab 			Rest = (Rest * 6528) / (FREF / 10);
4219a0bf528SMauro Carvalho Chehab 
4229a0bf528SMauro Carvalho Chehab 			Den = 1;
4239a0bf528SMauro Carvalho Chehab 			if (Rest > 0) {
4249a0bf528SMauro Carvalho Chehab 				state->lo4 |= (1 << 14) | (1 << 12);
4259a0bf528SMauro Carvalho Chehab 				Den = 255;
4269a0bf528SMauro Carvalho Chehab 			}
4279a0bf528SMauro Carvalho Chehab 
4289a0bf528SMauro Carvalho Chehab 
4299a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x11, (u16)FBDiv);
4309a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV);
4319a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x13, (u16) Rest);
4329a0bf528SMauro Carvalho Chehab 
4339a0bf528SMauro Carvalho Chehab 			if (state->revision == DIB0070S_P1A) {
4349a0bf528SMauro Carvalho Chehab 
4359a0bf528SMauro Carvalho Chehab 				if (band == BAND_SBAND) {
4369a0bf528SMauro Carvalho Chehab 					dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
4379a0bf528SMauro Carvalho Chehab 					dib0070_write_reg(state, 0x1d, 0xFFFF);
4389a0bf528SMauro Carvalho Chehab 				} else
4399a0bf528SMauro Carvalho Chehab 					dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1);
4409a0bf528SMauro Carvalho Chehab 			}
4419a0bf528SMauro Carvalho Chehab 
4429a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x20,
4439a0bf528SMauro Carvalho Chehab 				0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);
4449a0bf528SMauro Carvalho Chehab 
445fb11cbd1SMauro Carvalho Chehab 			dprintk("REFDIV: %hd, FREF: %d\n", REFDIV, FREF);
446fb11cbd1SMauro Carvalho Chehab 			dprintk("FBDIV: %d, Rest: %d\n", FBDiv, Rest);
447fb11cbd1SMauro Carvalho Chehab 			dprintk("Num: %hd, Den: %hd, SD: %hd\n", (u16) Rest, Den, (state->lo4 >> 12) & 0x1);
448fb11cbd1SMauro Carvalho Chehab 			dprintk("HFDIV code: %hd\n", state->current_tune_table_index->hfdiv);
449fb11cbd1SMauro Carvalho Chehab 			dprintk("VCO = %hd\n", state->current_tune_table_index->vco_band);
450fb11cbd1SMauro Carvalho Chehab 			dprintk("VCOF: ((%hd*%d) << 1))\n", state->current_tune_table_index->vco_multi, freq);
4519a0bf528SMauro Carvalho Chehab 
4529a0bf528SMauro Carvalho Chehab 			*tune_state = CT_TUNER_STEP_0;
4539a0bf528SMauro Carvalho Chehab 		} else { /* we are already tuned to this frequency - the configuration is correct  */
4549a0bf528SMauro Carvalho Chehab 			ret = 50; /* wakeup time */
4559a0bf528SMauro Carvalho Chehab 			*tune_state = CT_TUNER_STEP_5;
4569a0bf528SMauro Carvalho Chehab 		}
4579a0bf528SMauro Carvalho Chehab 	} else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {
4589a0bf528SMauro Carvalho Chehab 
4599a0bf528SMauro Carvalho Chehab 		ret = dib0070_captrim(state, tune_state);
4609a0bf528SMauro Carvalho Chehab 
4619a0bf528SMauro Carvalho Chehab 	} else if (*tune_state == CT_TUNER_STEP_4) {
4629a0bf528SMauro Carvalho Chehab 		const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
4639a0bf528SMauro Carvalho Chehab 		if (tmp != NULL) {
4649a0bf528SMauro Carvalho Chehab 			while (freq/1000 > tmp->freq) /* find the right one */
4659a0bf528SMauro Carvalho Chehab 				tmp++;
4669a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x0f,
4679a0bf528SMauro Carvalho Chehab 				(0 << 15) | (1 << 14) | (3 << 12)
4689a0bf528SMauro Carvalho Chehab 				| (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7)
4699a0bf528SMauro Carvalho Chehab 				| (state->current_tune_table_index->wbdmux << 0));
4709a0bf528SMauro Carvalho Chehab 			state->wbd_gain_current = tmp->wbd_gain_val;
4719a0bf528SMauro Carvalho Chehab 		} else {
4729a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x0f,
473f3f8ef22SMauro Carvalho Chehab 					  (0 << 15) | (1 << 14) | (3 << 12)
474f3f8ef22SMauro Carvalho Chehab 					  | (6 << 9) | (0 << 8) | (1 << 7)
475f3f8ef22SMauro Carvalho Chehab 					  | (state->current_tune_table_index->wbdmux << 0));
4769a0bf528SMauro Carvalho Chehab 			state->wbd_gain_current = 6;
4779a0bf528SMauro Carvalho Chehab 		}
4789a0bf528SMauro Carvalho Chehab 
4799a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x06, 0x3fff);
4809a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x07,
4819a0bf528SMauro Carvalho Chehab 				  (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0));
4829a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127));
4839a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x0d, 0x0d80);
4849a0bf528SMauro Carvalho Chehab 
4859a0bf528SMauro Carvalho Chehab 
4869a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x18,   0x07ff);
4879a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x17, 0x0033);
4889a0bf528SMauro Carvalho Chehab 
4899a0bf528SMauro Carvalho Chehab 
4909a0bf528SMauro Carvalho Chehab 		*tune_state = CT_TUNER_STEP_5;
4919a0bf528SMauro Carvalho Chehab 	} else if (*tune_state == CT_TUNER_STEP_5) {
4929a0bf528SMauro Carvalho Chehab 		dib0070_set_bandwidth(fe);
4939a0bf528SMauro Carvalho Chehab 		*tune_state = CT_TUNER_STOP;
4949a0bf528SMauro Carvalho Chehab 	} else {
4959a0bf528SMauro Carvalho Chehab 		ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */
4969a0bf528SMauro Carvalho Chehab 	}
4979a0bf528SMauro Carvalho Chehab 	return ret;
4989a0bf528SMauro Carvalho Chehab }
4999a0bf528SMauro Carvalho Chehab 
5009a0bf528SMauro Carvalho Chehab 
5019a0bf528SMauro Carvalho Chehab static int dib0070_tune(struct dvb_frontend *fe)
5029a0bf528SMauro Carvalho Chehab {
5039a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5049a0bf528SMauro Carvalho Chehab 	uint32_t ret;
5059a0bf528SMauro Carvalho Chehab 
5069a0bf528SMauro Carvalho Chehab 	state->tune_state = CT_TUNER_START;
5079a0bf528SMauro Carvalho Chehab 
5089a0bf528SMauro Carvalho Chehab 	do {
5099a0bf528SMauro Carvalho Chehab 		ret = dib0070_tune_digital(fe);
5109a0bf528SMauro Carvalho Chehab 		if (ret != FE_CALLBACK_TIME_NEVER)
5119a0bf528SMauro Carvalho Chehab 			msleep(ret/10);
5129a0bf528SMauro Carvalho Chehab 		else
5139a0bf528SMauro Carvalho Chehab 		break;
5149a0bf528SMauro Carvalho Chehab 	} while (state->tune_state != CT_TUNER_STOP);
5159a0bf528SMauro Carvalho Chehab 
5169a0bf528SMauro Carvalho Chehab 	return 0;
5179a0bf528SMauro Carvalho Chehab }
5189a0bf528SMauro Carvalho Chehab 
5199a0bf528SMauro Carvalho Chehab static int dib0070_wakeup(struct dvb_frontend *fe)
5209a0bf528SMauro Carvalho Chehab {
5219a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5229a0bf528SMauro Carvalho Chehab 	if (state->cfg->sleep)
5239a0bf528SMauro Carvalho Chehab 		state->cfg->sleep(fe, 0);
5249a0bf528SMauro Carvalho Chehab 	return 0;
5259a0bf528SMauro Carvalho Chehab }
5269a0bf528SMauro Carvalho Chehab 
5279a0bf528SMauro Carvalho Chehab static int dib0070_sleep(struct dvb_frontend *fe)
5289a0bf528SMauro Carvalho Chehab {
5299a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5309a0bf528SMauro Carvalho Chehab 	if (state->cfg->sleep)
5319a0bf528SMauro Carvalho Chehab 		state->cfg->sleep(fe, 1);
5329a0bf528SMauro Carvalho Chehab 	return 0;
5339a0bf528SMauro Carvalho Chehab }
5349a0bf528SMauro Carvalho Chehab 
5359a0bf528SMauro Carvalho Chehab u8 dib0070_get_rf_output(struct dvb_frontend *fe)
5369a0bf528SMauro Carvalho Chehab {
5379a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5389a0bf528SMauro Carvalho Chehab 	return (dib0070_read_reg(state, 0x07) >> 11) & 0x3;
5399a0bf528SMauro Carvalho Chehab }
5409a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_get_rf_output);
5419a0bf528SMauro Carvalho Chehab 
5429a0bf528SMauro Carvalho Chehab int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no)
5439a0bf528SMauro Carvalho Chehab {
5449a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5459a0bf528SMauro Carvalho Chehab 	u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff;
5469a0bf528SMauro Carvalho Chehab 	if (no > 3)
5479a0bf528SMauro Carvalho Chehab 		no = 3;
5489a0bf528SMauro Carvalho Chehab 	if (no < 1)
5499a0bf528SMauro Carvalho Chehab 		no = 1;
5509a0bf528SMauro Carvalho Chehab 	return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11));
5519a0bf528SMauro Carvalho Chehab }
5529a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_set_rf_output);
5539a0bf528SMauro Carvalho Chehab 
5549a0bf528SMauro Carvalho Chehab static const u16 dib0070_p1f_defaults[] =
5559a0bf528SMauro Carvalho Chehab 
5569a0bf528SMauro Carvalho Chehab {
5579a0bf528SMauro Carvalho Chehab 	7, 0x02,
5589a0bf528SMauro Carvalho Chehab 		0x0008,
5599a0bf528SMauro Carvalho Chehab 		0x0000,
5609a0bf528SMauro Carvalho Chehab 		0x0000,
5619a0bf528SMauro Carvalho Chehab 		0x0000,
5629a0bf528SMauro Carvalho Chehab 		0x0000,
5639a0bf528SMauro Carvalho Chehab 		0x0002,
5649a0bf528SMauro Carvalho Chehab 		0x0100,
5659a0bf528SMauro Carvalho Chehab 
5669a0bf528SMauro Carvalho Chehab 	3, 0x0d,
5679a0bf528SMauro Carvalho Chehab 		0x0d80,
5689a0bf528SMauro Carvalho Chehab 		0x0001,
5699a0bf528SMauro Carvalho Chehab 		0x0000,
5709a0bf528SMauro Carvalho Chehab 
5719a0bf528SMauro Carvalho Chehab 	4, 0x11,
5729a0bf528SMauro Carvalho Chehab 		0x0000,
5739a0bf528SMauro Carvalho Chehab 		0x0103,
5749a0bf528SMauro Carvalho Chehab 		0x0000,
5759a0bf528SMauro Carvalho Chehab 		0x0000,
5769a0bf528SMauro Carvalho Chehab 
5779a0bf528SMauro Carvalho Chehab 	3, 0x16,
5789a0bf528SMauro Carvalho Chehab 		0x0004 | 0x0040,
5799a0bf528SMauro Carvalho Chehab 		0x0030,
5809a0bf528SMauro Carvalho Chehab 		0x07ff,
5819a0bf528SMauro Carvalho Chehab 
5829a0bf528SMauro Carvalho Chehab 	6, 0x1b,
5839a0bf528SMauro Carvalho Chehab 		0x4112,
5849a0bf528SMauro Carvalho Chehab 		0xff00,
5859a0bf528SMauro Carvalho Chehab 		0xc07f,
5869a0bf528SMauro Carvalho Chehab 		0x0000,
5879a0bf528SMauro Carvalho Chehab 		0x0180,
5889a0bf528SMauro Carvalho Chehab 		0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,
5899a0bf528SMauro Carvalho Chehab 
5909a0bf528SMauro Carvalho Chehab 	0,
5919a0bf528SMauro Carvalho Chehab };
5929a0bf528SMauro Carvalho Chehab 
5939a0bf528SMauro Carvalho Chehab static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain)
5949a0bf528SMauro Carvalho Chehab {
5959a0bf528SMauro Carvalho Chehab 	u16 tuner_en = dib0070_read_reg(state, 0x20);
5969a0bf528SMauro Carvalho Chehab 	u16 offset;
5979a0bf528SMauro Carvalho Chehab 
5989a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x18, 0x07ff);
5999a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
6009a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0));
6019a0bf528SMauro Carvalho Chehab 	msleep(9);
6029a0bf528SMauro Carvalho Chehab 	offset = dib0070_read_reg(state, 0x19);
6039a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x20, tuner_en);
6049a0bf528SMauro Carvalho Chehab 	return offset;
6059a0bf528SMauro Carvalho Chehab }
6069a0bf528SMauro Carvalho Chehab 
6079a0bf528SMauro Carvalho Chehab static void dib0070_wbd_offset_calibration(struct dib0070_state *state)
6089a0bf528SMauro Carvalho Chehab {
6099a0bf528SMauro Carvalho Chehab 	u8 gain;
6109a0bf528SMauro Carvalho Chehab 	for (gain = 6; gain < 8; gain++) {
6119a0bf528SMauro Carvalho Chehab 		state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2);
612fb11cbd1SMauro Carvalho Chehab 		dprintk("Gain: %d, WBDOffset (3.3V) = %hd\n", gain, state->wbd_offset_3_3[gain-6]);
6139a0bf528SMauro Carvalho Chehab 	}
6149a0bf528SMauro Carvalho Chehab }
6159a0bf528SMauro Carvalho Chehab 
6169a0bf528SMauro Carvalho Chehab u16 dib0070_wbd_offset(struct dvb_frontend *fe)
6179a0bf528SMauro Carvalho Chehab {
6189a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
6199a0bf528SMauro Carvalho Chehab 	const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
6209a0bf528SMauro Carvalho Chehab 	u32 freq = fe->dtv_property_cache.frequency/1000;
6219a0bf528SMauro Carvalho Chehab 
6229a0bf528SMauro Carvalho Chehab 	if (tmp != NULL) {
6239a0bf528SMauro Carvalho Chehab 		while (freq/1000 > tmp->freq) /* find the right one */
6249a0bf528SMauro Carvalho Chehab 			tmp++;
6259a0bf528SMauro Carvalho Chehab 		state->wbd_gain_current = tmp->wbd_gain_val;
6269a0bf528SMauro Carvalho Chehab 	} else
6279a0bf528SMauro Carvalho Chehab 		state->wbd_gain_current = 6;
6289a0bf528SMauro Carvalho Chehab 
6299a0bf528SMauro Carvalho Chehab 	return state->wbd_offset_3_3[state->wbd_gain_current - 6];
6309a0bf528SMauro Carvalho Chehab }
6319a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_wbd_offset);
6329a0bf528SMauro Carvalho Chehab 
6339a0bf528SMauro Carvalho Chehab #define pgm_read_word(w) (*w)
6349a0bf528SMauro Carvalho Chehab static int dib0070_reset(struct dvb_frontend *fe)
6359a0bf528SMauro Carvalho Chehab {
6369a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
6379a0bf528SMauro Carvalho Chehab 	u16 l, r, *n;
6389a0bf528SMauro Carvalho Chehab 
6399a0bf528SMauro Carvalho Chehab 	HARD_RESET(state);
6409a0bf528SMauro Carvalho Chehab 
6419a0bf528SMauro Carvalho Chehab 
6429a0bf528SMauro Carvalho Chehab #ifndef FORCE_SBAND_TUNER
6439a0bf528SMauro Carvalho Chehab 	if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1)
6449a0bf528SMauro Carvalho Chehab 		state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff;
6459a0bf528SMauro Carvalho Chehab 	else
6469a0bf528SMauro Carvalho Chehab #else
6479a0bf528SMauro Carvalho Chehab #warning forcing SBAND
6489a0bf528SMauro Carvalho Chehab #endif
6499a0bf528SMauro Carvalho Chehab 	state->revision = DIB0070S_P1A;
6509a0bf528SMauro Carvalho Chehab 
6519a0bf528SMauro Carvalho Chehab 	/* P1F or not */
652fb11cbd1SMauro Carvalho Chehab 	dprintk("Revision: %x\n", state->revision);
6539a0bf528SMauro Carvalho Chehab 
6549a0bf528SMauro Carvalho Chehab 	if (state->revision == DIB0070_P1D) {
655fb11cbd1SMauro Carvalho Chehab 		dprintk("Error: this driver is not to be used meant for P1D or earlier\n");
6569a0bf528SMauro Carvalho Chehab 		return -EINVAL;
6579a0bf528SMauro Carvalho Chehab 	}
6589a0bf528SMauro Carvalho Chehab 
6599a0bf528SMauro Carvalho Chehab 	n = (u16 *) dib0070_p1f_defaults;
6609a0bf528SMauro Carvalho Chehab 	l = pgm_read_word(n++);
6619a0bf528SMauro Carvalho Chehab 	while (l) {
6629a0bf528SMauro Carvalho Chehab 		r = pgm_read_word(n++);
6639a0bf528SMauro Carvalho Chehab 		do {
6649a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, (u8)r, pgm_read_word(n++));
6659a0bf528SMauro Carvalho Chehab 			r++;
6669a0bf528SMauro Carvalho Chehab 		} while (--l);
6679a0bf528SMauro Carvalho Chehab 		l = pgm_read_word(n++);
6689a0bf528SMauro Carvalho Chehab 	}
6699a0bf528SMauro Carvalho Chehab 
6709a0bf528SMauro Carvalho Chehab 	if (state->cfg->force_crystal_mode != 0)
6719a0bf528SMauro Carvalho Chehab 		r = state->cfg->force_crystal_mode;
6729a0bf528SMauro Carvalho Chehab 	else if (state->cfg->clock_khz >= 24000)
6739a0bf528SMauro Carvalho Chehab 		r = 1;
6749a0bf528SMauro Carvalho Chehab 	else
6759a0bf528SMauro Carvalho Chehab 		r = 2;
6769a0bf528SMauro Carvalho Chehab 
6779a0bf528SMauro Carvalho Chehab 
6789a0bf528SMauro Carvalho Chehab 	r |= state->cfg->osc_buffer_state << 3;
6799a0bf528SMauro Carvalho Chehab 
6809a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x10, r);
6819a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5));
6829a0bf528SMauro Carvalho Chehab 
6839a0bf528SMauro Carvalho Chehab 	if (state->cfg->invert_iq) {
6849a0bf528SMauro Carvalho Chehab 		r = dib0070_read_reg(state, 0x02) & 0xffdf;
6859a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x02, r | (1 << 5));
6869a0bf528SMauro Carvalho Chehab 	}
6879a0bf528SMauro Carvalho Chehab 
6889a0bf528SMauro Carvalho Chehab 	if (state->revision == DIB0070S_P1A)
6899a0bf528SMauro Carvalho Chehab 		dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
6909a0bf528SMauro Carvalho Chehab 	else
691f3f8ef22SMauro Carvalho Chehab 		dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump,
692f3f8ef22SMauro Carvalho Chehab 				     state->cfg->enable_third_order_filter);
6939a0bf528SMauro Carvalho Chehab 
6949a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8);
6959a0bf528SMauro Carvalho Chehab 
6969a0bf528SMauro Carvalho Chehab 	dib0070_wbd_offset_calibration(state);
6979a0bf528SMauro Carvalho Chehab 
6989a0bf528SMauro Carvalho Chehab 	return 0;
6999a0bf528SMauro Carvalho Chehab }
7009a0bf528SMauro Carvalho Chehab 
7019a0bf528SMauro Carvalho Chehab static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency)
7029a0bf528SMauro Carvalho Chehab {
7039a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
7049a0bf528SMauro Carvalho Chehab 
7059a0bf528SMauro Carvalho Chehab 	*frequency = 1000 * state->current_rf;
7069a0bf528SMauro Carvalho Chehab 	return 0;
7079a0bf528SMauro Carvalho Chehab }
7089a0bf528SMauro Carvalho Chehab 
709f2709c20SMauro Carvalho Chehab static void dib0070_release(struct dvb_frontend *fe)
710f2709c20SMauro Carvalho Chehab {
711f2709c20SMauro Carvalho Chehab 	kfree(fe->tuner_priv);
712f2709c20SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
713f2709c20SMauro Carvalho Chehab }
714f2709c20SMauro Carvalho Chehab 
7159a0bf528SMauro Carvalho Chehab static const struct dvb_tuner_ops dib0070_ops = {
7169a0bf528SMauro Carvalho Chehab 	.info = {
7179a0bf528SMauro Carvalho Chehab 		.name              = "DiBcom DiB0070",
718a3f90c75SMauro Carvalho Chehab 		.frequency_min_hz  =  45 * MHz,
719a3f90c75SMauro Carvalho Chehab 		.frequency_max_hz  = 860 * MHz,
720a3f90c75SMauro Carvalho Chehab 		.frequency_step_hz =   1 * kHz,
7219a0bf528SMauro Carvalho Chehab 	},
722f2709c20SMauro Carvalho Chehab 	.release       = dib0070_release,
7239a0bf528SMauro Carvalho Chehab 
7249a0bf528SMauro Carvalho Chehab 	.init          = dib0070_wakeup,
7259a0bf528SMauro Carvalho Chehab 	.sleep         = dib0070_sleep,
7269a0bf528SMauro Carvalho Chehab 	.set_params    = dib0070_tune,
7279a0bf528SMauro Carvalho Chehab 
7289a0bf528SMauro Carvalho Chehab 	.get_frequency = dib0070_get_frequency,
7299a0bf528SMauro Carvalho Chehab //      .get_bandwidth = dib0070_get_bandwidth
7309a0bf528SMauro Carvalho Chehab };
7319a0bf528SMauro Carvalho Chehab 
7329a0bf528SMauro Carvalho Chehab struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)
7339a0bf528SMauro Carvalho Chehab {
7349a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL);
7359a0bf528SMauro Carvalho Chehab 	if (state == NULL)
7369a0bf528SMauro Carvalho Chehab 		return NULL;
7379a0bf528SMauro Carvalho Chehab 
7389a0bf528SMauro Carvalho Chehab 	state->cfg = cfg;
7399a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
7409a0bf528SMauro Carvalho Chehab 	state->fe  = fe;
7419a0bf528SMauro Carvalho Chehab 	mutex_init(&state->i2c_buffer_lock);
7429a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = state;
7439a0bf528SMauro Carvalho Chehab 
7449a0bf528SMauro Carvalho Chehab 	if (dib0070_reset(fe) != 0)
7459a0bf528SMauro Carvalho Chehab 		goto free_mem;
7469a0bf528SMauro Carvalho Chehab 
747fb11cbd1SMauro Carvalho Chehab 	pr_info("DiB0070: successfully identified\n");
7489a0bf528SMauro Carvalho Chehab 	memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));
7499a0bf528SMauro Carvalho Chehab 
7509a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = state;
7519a0bf528SMauro Carvalho Chehab 	return fe;
7529a0bf528SMauro Carvalho Chehab 
7539a0bf528SMauro Carvalho Chehab free_mem:
7549a0bf528SMauro Carvalho Chehab 	kfree(state);
7559a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
7569a0bf528SMauro Carvalho Chehab 	return NULL;
7579a0bf528SMauro Carvalho Chehab }
7589a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_attach);
7599a0bf528SMauro Carvalho Chehab 
76099e44da7SPatrick Boettcher MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
7619a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner");
7629a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
763