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 
dib0070_read_reg(struct dib0070_state * state,u8 reg)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 
dib0070_write_reg(struct dib0070_state * state,u8 reg,u16 val)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 
dib0070_set_bandwidth(struct dvb_frontend * fe)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 
dib0070_captrim(struct dib0070_state * state,enum frontend_tune_state * tune_state)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 
192f54def5bSSean Young 		dprintk("CAPTRIM=%d; ADC = %hd (ADC) & %dmV\n", state->captrim,
193f54def5bSSean Young 			adc, (u32)adc * (u32)1800 / (u32)1024);
1949a0bf528SMauro Carvalho Chehab 
1959a0bf528SMauro Carvalho Chehab 		if (adc >= 400) {
1969a0bf528SMauro Carvalho Chehab 			adc -= 400;
1979a0bf528SMauro Carvalho Chehab 			step_sign = -1;
1989a0bf528SMauro Carvalho Chehab 		} else {
1999a0bf528SMauro Carvalho Chehab 			adc = 400 - adc;
2009a0bf528SMauro Carvalho Chehab 			step_sign = 1;
2019a0bf528SMauro Carvalho Chehab 		}
2029a0bf528SMauro Carvalho Chehab 
2039a0bf528SMauro Carvalho Chehab 		if (adc < state->adc_diff) {
204f54def5bSSean Young 			dprintk("CAPTRIM=%d is closer to target (%hd/%hd)\n",
205f54def5bSSean Young 				state->captrim, adc, state->adc_diff);
2069a0bf528SMauro Carvalho Chehab 			state->adc_diff = adc;
2079a0bf528SMauro Carvalho Chehab 			state->fcaptrim = state->captrim;
2089a0bf528SMauro Carvalho Chehab 		}
2099a0bf528SMauro Carvalho Chehab 		state->captrim += (step_sign * state->step);
2109a0bf528SMauro Carvalho Chehab 
2119a0bf528SMauro Carvalho Chehab 		if (state->step >= 1)
2129a0bf528SMauro Carvalho Chehab 			*tune_state = CT_TUNER_STEP_1;
2139a0bf528SMauro Carvalho Chehab 		else
2149a0bf528SMauro Carvalho Chehab 			*tune_state = CT_TUNER_STEP_3;
2159a0bf528SMauro Carvalho Chehab 
2169a0bf528SMauro Carvalho Chehab 	} else if (*tune_state == CT_TUNER_STEP_3) {
2179a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim);
2189a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x18, 0x07ff);
2199a0bf528SMauro Carvalho Chehab 		*tune_state = CT_TUNER_STEP_4;
2209a0bf528SMauro Carvalho Chehab 	}
2219a0bf528SMauro Carvalho Chehab 
2229a0bf528SMauro Carvalho Chehab 	return ret;
2239a0bf528SMauro Carvalho Chehab }
2249a0bf528SMauro Carvalho Chehab 
dib0070_set_ctrl_lo5(struct dvb_frontend * fe,u8 vco_bias_trim,u8 hf_div_trim,u8 cp_current,u8 third_order_filt)2259a0bf528SMauro 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)
2269a0bf528SMauro Carvalho Chehab {
2279a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
2289a0bf528SMauro 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);
229f3f8ef22SMauro Carvalho Chehab 
230fb11cbd1SMauro Carvalho Chehab 	dprintk("CTRL_LO5: 0x%x\n", lo5);
2319a0bf528SMauro Carvalho Chehab 	return dib0070_write_reg(state, 0x15, lo5);
2329a0bf528SMauro Carvalho Chehab }
2339a0bf528SMauro Carvalho Chehab 
dib0070_ctrl_agc_filter(struct dvb_frontend * fe,u8 open)2349a0bf528SMauro Carvalho Chehab void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)
2359a0bf528SMauro Carvalho Chehab {
2369a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
2379a0bf528SMauro Carvalho Chehab 
2389a0bf528SMauro Carvalho Chehab 	if (open) {
2399a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x1b, 0xff00);
2409a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x1a, 0x0000);
2419a0bf528SMauro Carvalho Chehab 	} else {
2429a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x1b, 0x4112);
2439a0bf528SMauro Carvalho Chehab 		if (state->cfg->vga_filter != 0) {
2449a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x1a, state->cfg->vga_filter);
245fb11cbd1SMauro Carvalho Chehab 			dprintk("vga filter register is set to %x\n", state->cfg->vga_filter);
2469a0bf528SMauro Carvalho Chehab 		} else
2479a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x1a, 0x0009);
2489a0bf528SMauro Carvalho Chehab 	}
2499a0bf528SMauro Carvalho Chehab }
2509a0bf528SMauro Carvalho Chehab 
2519a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_ctrl_agc_filter);
2529a0bf528SMauro Carvalho Chehab struct dib0070_tuning {
2539a0bf528SMauro Carvalho Chehab 	u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
2549a0bf528SMauro Carvalho Chehab 	u8 switch_trim;
2559a0bf528SMauro Carvalho Chehab 	u8 vco_band;
2569a0bf528SMauro Carvalho Chehab 	u8 hfdiv;
2579a0bf528SMauro Carvalho Chehab 	u8 vco_multi;
2589a0bf528SMauro Carvalho Chehab 	u8 presc;
2599a0bf528SMauro Carvalho Chehab 	u8 wbdmux;
2609a0bf528SMauro Carvalho Chehab 	u16 tuner_enable;
2619a0bf528SMauro Carvalho Chehab };
2629a0bf528SMauro Carvalho Chehab 
2639a0bf528SMauro Carvalho Chehab struct dib0070_lna_match {
2649a0bf528SMauro Carvalho Chehab 	u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
2659a0bf528SMauro Carvalho Chehab 	u8 lna_band;
2669a0bf528SMauro Carvalho Chehab };
2679a0bf528SMauro Carvalho Chehab 
2689a0bf528SMauro Carvalho Chehab static const struct dib0070_tuning dib0070s_tuning_table[] = {
2699a0bf528SMauro Carvalho Chehab 	{     570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */
2709a0bf528SMauro Carvalho Chehab 	{     700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 },
2719a0bf528SMauro Carvalho Chehab 	{     863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 },
2729a0bf528SMauro Carvalho Chehab 	{    1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */
2739a0bf528SMauro Carvalho Chehab 	{    1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
2749a0bf528SMauro Carvalho Chehab 	{    2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
2759a0bf528SMauro Carvalho Chehab 	{ 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */
2769a0bf528SMauro Carvalho Chehab };
2779a0bf528SMauro Carvalho Chehab 
2789a0bf528SMauro Carvalho Chehab static const struct dib0070_tuning dib0070_tuning_table[] = {
2799a0bf528SMauro Carvalho Chehab 	{     115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */
2809a0bf528SMauro Carvalho Chehab 	{     179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */
2819a0bf528SMauro Carvalho Chehab 	{     189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 },
2829a0bf528SMauro Carvalho Chehab 	{     250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 },
2839a0bf528SMauro Carvalho Chehab 	{     569999, 2, 1, 5,  6, 2, 2, 0x4000 | 0x0800 }, /* UHF */
2849a0bf528SMauro Carvalho Chehab 	{     699999, 2, 0, 1,  4, 2, 2, 0x4000 | 0x0800 },
2859a0bf528SMauro Carvalho Chehab 	{     863999, 2, 1, 1,  4, 2, 2, 0x4000 | 0x0800 },
2869a0bf528SMauro Carvalho Chehab 	{ 0xffffffff, 0, 1, 0,  2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */
2879a0bf528SMauro Carvalho Chehab };
2889a0bf528SMauro Carvalho Chehab 
2899a0bf528SMauro Carvalho Chehab static const struct dib0070_lna_match dib0070_lna_flip_chip[] = {
2909a0bf528SMauro Carvalho Chehab 	{     180000, 0 }, /* VHF */
2919a0bf528SMauro Carvalho Chehab 	{     188000, 1 },
2929a0bf528SMauro Carvalho Chehab 	{     196400, 2 },
2939a0bf528SMauro Carvalho Chehab 	{     250000, 3 },
2949a0bf528SMauro Carvalho Chehab 	{     550000, 0 }, /* UHF */
2959a0bf528SMauro Carvalho Chehab 	{     590000, 1 },
2969a0bf528SMauro Carvalho Chehab 	{     666000, 3 },
2979a0bf528SMauro Carvalho Chehab 	{     864000, 5 },
2989a0bf528SMauro Carvalho Chehab 	{    1500000, 0 }, /* LBAND or everything higher than UHF */
2999a0bf528SMauro Carvalho Chehab 	{    1600000, 1 },
3009a0bf528SMauro Carvalho Chehab 	{    2000000, 3 },
3019a0bf528SMauro Carvalho Chehab 	{ 0xffffffff, 7 },
3029a0bf528SMauro Carvalho Chehab };
3039a0bf528SMauro Carvalho Chehab 
3049a0bf528SMauro Carvalho Chehab static const struct dib0070_lna_match dib0070_lna[] = {
3059a0bf528SMauro Carvalho Chehab 	{     180000, 0 }, /* VHF */
3069a0bf528SMauro Carvalho Chehab 	{     188000, 1 },
3079a0bf528SMauro Carvalho Chehab 	{     196400, 2 },
3089a0bf528SMauro Carvalho Chehab 	{     250000, 3 },
3099a0bf528SMauro Carvalho Chehab 	{     550000, 2 }, /* UHF */
3109a0bf528SMauro Carvalho Chehab 	{     650000, 3 },
3119a0bf528SMauro Carvalho Chehab 	{     750000, 5 },
3129a0bf528SMauro Carvalho Chehab 	{     850000, 6 },
3139a0bf528SMauro Carvalho Chehab 	{     864000, 7 },
3149a0bf528SMauro Carvalho Chehab 	{    1500000, 0 }, /* LBAND or everything higher than UHF */
3159a0bf528SMauro Carvalho Chehab 	{    1600000, 1 },
3169a0bf528SMauro Carvalho Chehab 	{    2000000, 3 },
3179a0bf528SMauro Carvalho Chehab 	{ 0xffffffff, 7 },
3189a0bf528SMauro Carvalho Chehab };
3199a0bf528SMauro Carvalho Chehab 
3209a0bf528SMauro Carvalho Chehab #define LPF	100
dib0070_tune_digital(struct dvb_frontend * fe)3219a0bf528SMauro Carvalho Chehab static int dib0070_tune_digital(struct dvb_frontend *fe)
3229a0bf528SMauro Carvalho Chehab {
3239a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
3249a0bf528SMauro Carvalho Chehab 
3259a0bf528SMauro Carvalho Chehab 	const struct dib0070_tuning *tune;
3269a0bf528SMauro Carvalho Chehab 	const struct dib0070_lna_match *lna_match;
3279a0bf528SMauro Carvalho Chehab 
3289a0bf528SMauro Carvalho Chehab 	enum frontend_tune_state *tune_state = &state->tune_state;
3299a0bf528SMauro Carvalho Chehab 	int ret = 10; /* 1ms is the default delay most of the time */
3309a0bf528SMauro Carvalho Chehab 
3319a0bf528SMauro Carvalho Chehab 	u8  band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000);
3329a0bf528SMauro 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);
3339a0bf528SMauro Carvalho Chehab 
3349a0bf528SMauro Carvalho Chehab #ifdef CONFIG_SYS_ISDBT
3359a0bf528SMauro Carvalho Chehab 	if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
3369a0bf528SMauro Carvalho Chehab 			if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2)
3379a0bf528SMauro Carvalho Chehab 			&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
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)))
3409a0bf528SMauro Carvalho Chehab 			|| (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
3419a0bf528SMauro Carvalho Chehab 				&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))))
3429a0bf528SMauro Carvalho Chehab 				freq += 850;
3439a0bf528SMauro Carvalho Chehab #endif
3449a0bf528SMauro Carvalho Chehab 	if (state->current_rf != freq) {
3459a0bf528SMauro Carvalho Chehab 
3469a0bf528SMauro Carvalho Chehab 		switch (state->revision) {
3479a0bf528SMauro Carvalho Chehab 		case DIB0070S_P1A:
3489a0bf528SMauro Carvalho Chehab 		tune = dib0070s_tuning_table;
3499a0bf528SMauro Carvalho Chehab 		lna_match = dib0070_lna;
3509a0bf528SMauro Carvalho Chehab 		break;
3519a0bf528SMauro Carvalho Chehab 		default:
3529a0bf528SMauro Carvalho Chehab 		tune = dib0070_tuning_table;
3539a0bf528SMauro Carvalho Chehab 		if (state->cfg->flip_chip)
3549a0bf528SMauro Carvalho Chehab 			lna_match = dib0070_lna_flip_chip;
3559a0bf528SMauro Carvalho Chehab 		else
3569a0bf528SMauro Carvalho Chehab 			lna_match = dib0070_lna;
3579a0bf528SMauro Carvalho Chehab 		break;
3589a0bf528SMauro Carvalho Chehab 		}
3599a0bf528SMauro Carvalho Chehab 		while (freq > tune->max_freq) /* find the right one */
3609a0bf528SMauro Carvalho Chehab 			tune++;
3619a0bf528SMauro Carvalho Chehab 		while (freq > lna_match->max_freq) /* find the right one */
3629a0bf528SMauro Carvalho Chehab 			lna_match++;
3639a0bf528SMauro Carvalho Chehab 
3649a0bf528SMauro Carvalho Chehab 		state->current_tune_table_index = tune;
3659a0bf528SMauro Carvalho Chehab 		state->lna_match = lna_match;
3669a0bf528SMauro Carvalho Chehab 	}
3679a0bf528SMauro Carvalho Chehab 
3689a0bf528SMauro Carvalho Chehab 	if (*tune_state == CT_TUNER_START) {
369f54def5bSSean Young 		dprintk("Tuning for Band: %d (%d kHz)\n", band, freq);
3709a0bf528SMauro Carvalho Chehab 		if (state->current_rf != freq) {
3719a0bf528SMauro Carvalho Chehab 			u8 REFDIV;
3729a0bf528SMauro Carvalho Chehab 			u32 FBDiv, Rest, FREF, VCOF_kHz;
3739a0bf528SMauro Carvalho Chehab 			u8 Den;
3749a0bf528SMauro Carvalho Chehab 
3759a0bf528SMauro Carvalho Chehab 			state->current_rf = freq;
3769a0bf528SMauro Carvalho Chehab 			state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);
3779a0bf528SMauro Carvalho Chehab 
3789a0bf528SMauro Carvalho Chehab 
3799a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x17, 0x30);
3809a0bf528SMauro Carvalho Chehab 
3819a0bf528SMauro Carvalho Chehab 
3829a0bf528SMauro Carvalho Chehab 			VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;
3839a0bf528SMauro Carvalho Chehab 
3849a0bf528SMauro Carvalho Chehab 			switch (band) {
3859a0bf528SMauro Carvalho Chehab 			case BAND_VHF:
3869a0bf528SMauro Carvalho Chehab 				REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000);
3879a0bf528SMauro Carvalho Chehab 				break;
3889a0bf528SMauro Carvalho Chehab 			case BAND_FM:
3899a0bf528SMauro Carvalho Chehab 				REFDIV = (u8) ((state->cfg->clock_khz) / 1000);
3909a0bf528SMauro Carvalho Chehab 				break;
3919a0bf528SMauro Carvalho Chehab 			default:
3929a0bf528SMauro Carvalho Chehab 				REFDIV = (u8) (state->cfg->clock_khz  / 10000);
3939a0bf528SMauro Carvalho Chehab 				break;
3949a0bf528SMauro Carvalho Chehab 			}
3959a0bf528SMauro Carvalho Chehab 			FREF = state->cfg->clock_khz / REFDIV;
3969a0bf528SMauro Carvalho Chehab 
3979a0bf528SMauro Carvalho Chehab 
3989a0bf528SMauro Carvalho Chehab 
3999a0bf528SMauro Carvalho Chehab 			switch (state->revision) {
4009a0bf528SMauro Carvalho Chehab 			case DIB0070S_P1A:
4019a0bf528SMauro Carvalho Chehab 				FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF);
4029a0bf528SMauro Carvalho Chehab 				Rest  = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF;
4039a0bf528SMauro Carvalho Chehab 				break;
4049a0bf528SMauro Carvalho Chehab 
4059a0bf528SMauro Carvalho Chehab 			case DIB0070_P1G:
4069a0bf528SMauro Carvalho Chehab 			case DIB0070_P1F:
4079a0bf528SMauro Carvalho Chehab 			default:
4089a0bf528SMauro Carvalho Chehab 				FBDiv = (freq / (FREF / 2));
4099a0bf528SMauro Carvalho Chehab 				Rest  = 2 * freq - FBDiv * FREF;
4109a0bf528SMauro Carvalho Chehab 				break;
4119a0bf528SMauro Carvalho Chehab 			}
4129a0bf528SMauro Carvalho Chehab 
4139a0bf528SMauro Carvalho Chehab 			if (Rest < LPF)
4149a0bf528SMauro Carvalho Chehab 				Rest = 0;
4159a0bf528SMauro Carvalho Chehab 			else if (Rest < 2 * LPF)
4169a0bf528SMauro Carvalho Chehab 				Rest = 2 * LPF;
4179a0bf528SMauro Carvalho Chehab 			else if (Rest > (FREF - LPF)) {
4189a0bf528SMauro Carvalho Chehab 				Rest = 0;
4199a0bf528SMauro Carvalho Chehab 				FBDiv += 1;
4209a0bf528SMauro Carvalho Chehab 			} else if (Rest > (FREF - 2 * LPF))
4219a0bf528SMauro Carvalho Chehab 				Rest = FREF - 2 * LPF;
4229a0bf528SMauro Carvalho Chehab 			Rest = (Rest * 6528) / (FREF / 10);
4239a0bf528SMauro Carvalho Chehab 
4249a0bf528SMauro Carvalho Chehab 			Den = 1;
4259a0bf528SMauro Carvalho Chehab 			if (Rest > 0) {
4269a0bf528SMauro Carvalho Chehab 				state->lo4 |= (1 << 14) | (1 << 12);
4279a0bf528SMauro Carvalho Chehab 				Den = 255;
4289a0bf528SMauro Carvalho Chehab 			}
4299a0bf528SMauro Carvalho Chehab 
4309a0bf528SMauro Carvalho Chehab 
4319a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x11, (u16)FBDiv);
4329a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV);
4339a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x13, (u16) Rest);
4349a0bf528SMauro Carvalho Chehab 
4359a0bf528SMauro Carvalho Chehab 			if (state->revision == DIB0070S_P1A) {
4369a0bf528SMauro Carvalho Chehab 
4379a0bf528SMauro Carvalho Chehab 				if (band == BAND_SBAND) {
4389a0bf528SMauro Carvalho Chehab 					dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
4399a0bf528SMauro Carvalho Chehab 					dib0070_write_reg(state, 0x1d, 0xFFFF);
4409a0bf528SMauro Carvalho Chehab 				} else
4419a0bf528SMauro Carvalho Chehab 					dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1);
4429a0bf528SMauro Carvalho Chehab 			}
4439a0bf528SMauro Carvalho Chehab 
4449a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x20,
4459a0bf528SMauro Carvalho Chehab 				0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);
4469a0bf528SMauro Carvalho Chehab 
447f54def5bSSean Young 			dprintk("REFDIV: %u, FREF: %d\n", REFDIV, FREF);
448fb11cbd1SMauro Carvalho Chehab 			dprintk("FBDIV: %d, Rest: %d\n", FBDiv, Rest);
449f54def5bSSean Young 			dprintk("Num: %u, Den: %u, SD: %d\n", (u16)Rest, Den,
450f54def5bSSean Young 				(state->lo4 >> 12) & 0x1);
451f54def5bSSean Young 			dprintk("HFDIV code: %u\n",
452f54def5bSSean Young 				state->current_tune_table_index->hfdiv);
453f54def5bSSean Young 			dprintk("VCO = %u\n",
454f54def5bSSean Young 				state->current_tune_table_index->vco_band);
455f54def5bSSean Young 			dprintk("VCOF: ((%u*%d) << 1))\n",
456f54def5bSSean Young 				state->current_tune_table_index->vco_multi,
457f54def5bSSean Young 				freq);
4589a0bf528SMauro Carvalho Chehab 
4599a0bf528SMauro Carvalho Chehab 			*tune_state = CT_TUNER_STEP_0;
4609a0bf528SMauro Carvalho Chehab 		} else { /* we are already tuned to this frequency - the configuration is correct  */
4619a0bf528SMauro Carvalho Chehab 			ret = 50; /* wakeup time */
4629a0bf528SMauro Carvalho Chehab 			*tune_state = CT_TUNER_STEP_5;
4639a0bf528SMauro Carvalho Chehab 		}
4649a0bf528SMauro Carvalho Chehab 	} else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {
4659a0bf528SMauro Carvalho Chehab 
4669a0bf528SMauro Carvalho Chehab 		ret = dib0070_captrim(state, tune_state);
4679a0bf528SMauro Carvalho Chehab 
4689a0bf528SMauro Carvalho Chehab 	} else if (*tune_state == CT_TUNER_STEP_4) {
4699a0bf528SMauro Carvalho Chehab 		const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
4709a0bf528SMauro Carvalho Chehab 		if (tmp != NULL) {
4719a0bf528SMauro Carvalho Chehab 			while (freq/1000 > tmp->freq) /* find the right one */
4729a0bf528SMauro Carvalho Chehab 				tmp++;
4739a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x0f,
4749a0bf528SMauro Carvalho Chehab 				(0 << 15) | (1 << 14) | (3 << 12)
4759a0bf528SMauro Carvalho Chehab 				| (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7)
4769a0bf528SMauro Carvalho Chehab 				| (state->current_tune_table_index->wbdmux << 0));
4779a0bf528SMauro Carvalho Chehab 			state->wbd_gain_current = tmp->wbd_gain_val;
4789a0bf528SMauro Carvalho Chehab 		} else {
4799a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, 0x0f,
480f3f8ef22SMauro Carvalho Chehab 					  (0 << 15) | (1 << 14) | (3 << 12)
481f3f8ef22SMauro Carvalho Chehab 					  | (6 << 9) | (0 << 8) | (1 << 7)
482f3f8ef22SMauro Carvalho Chehab 					  | (state->current_tune_table_index->wbdmux << 0));
4839a0bf528SMauro Carvalho Chehab 			state->wbd_gain_current = 6;
4849a0bf528SMauro Carvalho Chehab 		}
4859a0bf528SMauro Carvalho Chehab 
4869a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x06, 0x3fff);
4879a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x07,
4889a0bf528SMauro Carvalho Chehab 				  (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0));
4899a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127));
4909a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x0d, 0x0d80);
4919a0bf528SMauro Carvalho Chehab 
4929a0bf528SMauro Carvalho Chehab 
4939a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x18,   0x07ff);
4949a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x17, 0x0033);
4959a0bf528SMauro Carvalho Chehab 
4969a0bf528SMauro Carvalho Chehab 
4979a0bf528SMauro Carvalho Chehab 		*tune_state = CT_TUNER_STEP_5;
4989a0bf528SMauro Carvalho Chehab 	} else if (*tune_state == CT_TUNER_STEP_5) {
4999a0bf528SMauro Carvalho Chehab 		dib0070_set_bandwidth(fe);
5009a0bf528SMauro Carvalho Chehab 		*tune_state = CT_TUNER_STOP;
5019a0bf528SMauro Carvalho Chehab 	} else {
5029a0bf528SMauro Carvalho Chehab 		ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */
5039a0bf528SMauro Carvalho Chehab 	}
5049a0bf528SMauro Carvalho Chehab 	return ret;
5059a0bf528SMauro Carvalho Chehab }
5069a0bf528SMauro Carvalho Chehab 
5079a0bf528SMauro Carvalho Chehab 
dib0070_tune(struct dvb_frontend * fe)5089a0bf528SMauro Carvalho Chehab static int dib0070_tune(struct dvb_frontend *fe)
5099a0bf528SMauro Carvalho Chehab {
5109a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5119a0bf528SMauro Carvalho Chehab 	uint32_t ret;
5129a0bf528SMauro Carvalho Chehab 
5139a0bf528SMauro Carvalho Chehab 	state->tune_state = CT_TUNER_START;
5149a0bf528SMauro Carvalho Chehab 
5159a0bf528SMauro Carvalho Chehab 	do {
5169a0bf528SMauro Carvalho Chehab 		ret = dib0070_tune_digital(fe);
5179a0bf528SMauro Carvalho Chehab 		if (ret != FE_CALLBACK_TIME_NEVER)
5189a0bf528SMauro Carvalho Chehab 			msleep(ret/10);
5199a0bf528SMauro Carvalho Chehab 		else
5209a0bf528SMauro Carvalho Chehab 		break;
5219a0bf528SMauro Carvalho Chehab 	} while (state->tune_state != CT_TUNER_STOP);
5229a0bf528SMauro Carvalho Chehab 
5239a0bf528SMauro Carvalho Chehab 	return 0;
5249a0bf528SMauro Carvalho Chehab }
5259a0bf528SMauro Carvalho Chehab 
dib0070_wakeup(struct dvb_frontend * fe)5269a0bf528SMauro Carvalho Chehab static int dib0070_wakeup(struct dvb_frontend *fe)
5279a0bf528SMauro Carvalho Chehab {
5289a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5299a0bf528SMauro Carvalho Chehab 	if (state->cfg->sleep)
5309a0bf528SMauro Carvalho Chehab 		state->cfg->sleep(fe, 0);
5319a0bf528SMauro Carvalho Chehab 	return 0;
5329a0bf528SMauro Carvalho Chehab }
5339a0bf528SMauro Carvalho Chehab 
dib0070_sleep(struct dvb_frontend * fe)5349a0bf528SMauro Carvalho Chehab static int dib0070_sleep(struct dvb_frontend *fe)
5359a0bf528SMauro Carvalho Chehab {
5369a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5379a0bf528SMauro Carvalho Chehab 	if (state->cfg->sleep)
5389a0bf528SMauro Carvalho Chehab 		state->cfg->sleep(fe, 1);
5399a0bf528SMauro Carvalho Chehab 	return 0;
5409a0bf528SMauro Carvalho Chehab }
5419a0bf528SMauro Carvalho Chehab 
dib0070_get_rf_output(struct dvb_frontend * fe)5429a0bf528SMauro Carvalho Chehab u8 dib0070_get_rf_output(struct dvb_frontend *fe)
5439a0bf528SMauro Carvalho Chehab {
5449a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5459a0bf528SMauro Carvalho Chehab 	return (dib0070_read_reg(state, 0x07) >> 11) & 0x3;
5469a0bf528SMauro Carvalho Chehab }
5479a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_get_rf_output);
5489a0bf528SMauro Carvalho Chehab 
dib0070_set_rf_output(struct dvb_frontend * fe,u8 no)5499a0bf528SMauro Carvalho Chehab int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no)
5509a0bf528SMauro Carvalho Chehab {
5519a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
5529a0bf528SMauro Carvalho Chehab 	u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff;
5539a0bf528SMauro Carvalho Chehab 	if (no > 3)
5549a0bf528SMauro Carvalho Chehab 		no = 3;
5559a0bf528SMauro Carvalho Chehab 	if (no < 1)
5569a0bf528SMauro Carvalho Chehab 		no = 1;
5579a0bf528SMauro Carvalho Chehab 	return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11));
5589a0bf528SMauro Carvalho Chehab }
5599a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_set_rf_output);
5609a0bf528SMauro Carvalho Chehab 
5619a0bf528SMauro Carvalho Chehab static const u16 dib0070_p1f_defaults[] =
5629a0bf528SMauro Carvalho Chehab 
5639a0bf528SMauro Carvalho Chehab {
5649a0bf528SMauro Carvalho Chehab 	7, 0x02,
5659a0bf528SMauro Carvalho Chehab 		0x0008,
5669a0bf528SMauro Carvalho Chehab 		0x0000,
5679a0bf528SMauro Carvalho Chehab 		0x0000,
5689a0bf528SMauro Carvalho Chehab 		0x0000,
5699a0bf528SMauro Carvalho Chehab 		0x0000,
5709a0bf528SMauro Carvalho Chehab 		0x0002,
5719a0bf528SMauro Carvalho Chehab 		0x0100,
5729a0bf528SMauro Carvalho Chehab 
5739a0bf528SMauro Carvalho Chehab 	3, 0x0d,
5749a0bf528SMauro Carvalho Chehab 		0x0d80,
5759a0bf528SMauro Carvalho Chehab 		0x0001,
5769a0bf528SMauro Carvalho Chehab 		0x0000,
5779a0bf528SMauro Carvalho Chehab 
5789a0bf528SMauro Carvalho Chehab 	4, 0x11,
5799a0bf528SMauro Carvalho Chehab 		0x0000,
5809a0bf528SMauro Carvalho Chehab 		0x0103,
5819a0bf528SMauro Carvalho Chehab 		0x0000,
5829a0bf528SMauro Carvalho Chehab 		0x0000,
5839a0bf528SMauro Carvalho Chehab 
5849a0bf528SMauro Carvalho Chehab 	3, 0x16,
5859a0bf528SMauro Carvalho Chehab 		0x0004 | 0x0040,
5869a0bf528SMauro Carvalho Chehab 		0x0030,
5879a0bf528SMauro Carvalho Chehab 		0x07ff,
5889a0bf528SMauro Carvalho Chehab 
5899a0bf528SMauro Carvalho Chehab 	6, 0x1b,
5909a0bf528SMauro Carvalho Chehab 		0x4112,
5919a0bf528SMauro Carvalho Chehab 		0xff00,
5929a0bf528SMauro Carvalho Chehab 		0xc07f,
5939a0bf528SMauro Carvalho Chehab 		0x0000,
5949a0bf528SMauro Carvalho Chehab 		0x0180,
5959a0bf528SMauro Carvalho Chehab 		0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,
5969a0bf528SMauro Carvalho Chehab 
5979a0bf528SMauro Carvalho Chehab 	0,
5989a0bf528SMauro Carvalho Chehab };
5999a0bf528SMauro Carvalho Chehab 
dib0070_read_wbd_offset(struct dib0070_state * state,u8 gain)6009a0bf528SMauro Carvalho Chehab static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain)
6019a0bf528SMauro Carvalho Chehab {
6029a0bf528SMauro Carvalho Chehab 	u16 tuner_en = dib0070_read_reg(state, 0x20);
6039a0bf528SMauro Carvalho Chehab 	u16 offset;
6049a0bf528SMauro Carvalho Chehab 
6059a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x18, 0x07ff);
6069a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
6079a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0));
6089a0bf528SMauro Carvalho Chehab 	msleep(9);
6099a0bf528SMauro Carvalho Chehab 	offset = dib0070_read_reg(state, 0x19);
6109a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x20, tuner_en);
6119a0bf528SMauro Carvalho Chehab 	return offset;
6129a0bf528SMauro Carvalho Chehab }
6139a0bf528SMauro Carvalho Chehab 
dib0070_wbd_offset_calibration(struct dib0070_state * state)6149a0bf528SMauro Carvalho Chehab static void dib0070_wbd_offset_calibration(struct dib0070_state *state)
6159a0bf528SMauro Carvalho Chehab {
6169a0bf528SMauro Carvalho Chehab 	u8 gain;
6179a0bf528SMauro Carvalho Chehab 	for (gain = 6; gain < 8; gain++) {
6189a0bf528SMauro Carvalho Chehab 		state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2);
619fb11cbd1SMauro Carvalho Chehab 		dprintk("Gain: %d, WBDOffset (3.3V) = %hd\n", gain, state->wbd_offset_3_3[gain-6]);
6209a0bf528SMauro Carvalho Chehab 	}
6219a0bf528SMauro Carvalho Chehab }
6229a0bf528SMauro Carvalho Chehab 
dib0070_wbd_offset(struct dvb_frontend * fe)6239a0bf528SMauro Carvalho Chehab u16 dib0070_wbd_offset(struct dvb_frontend *fe)
6249a0bf528SMauro Carvalho Chehab {
6259a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
6269a0bf528SMauro Carvalho Chehab 	const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
6279a0bf528SMauro Carvalho Chehab 	u32 freq = fe->dtv_property_cache.frequency/1000;
6289a0bf528SMauro Carvalho Chehab 
6299a0bf528SMauro Carvalho Chehab 	if (tmp != NULL) {
6309a0bf528SMauro Carvalho Chehab 		while (freq/1000 > tmp->freq) /* find the right one */
6319a0bf528SMauro Carvalho Chehab 			tmp++;
6329a0bf528SMauro Carvalho Chehab 		state->wbd_gain_current = tmp->wbd_gain_val;
6339a0bf528SMauro Carvalho Chehab 	} else
6349a0bf528SMauro Carvalho Chehab 		state->wbd_gain_current = 6;
6359a0bf528SMauro Carvalho Chehab 
6369a0bf528SMauro Carvalho Chehab 	return state->wbd_offset_3_3[state->wbd_gain_current - 6];
6379a0bf528SMauro Carvalho Chehab }
6389a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(dib0070_wbd_offset);
6399a0bf528SMauro Carvalho Chehab 
6409a0bf528SMauro Carvalho Chehab #define pgm_read_word(w) (*w)
dib0070_reset(struct dvb_frontend * fe)6419a0bf528SMauro Carvalho Chehab static int dib0070_reset(struct dvb_frontend *fe)
6429a0bf528SMauro Carvalho Chehab {
6439a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
6449a0bf528SMauro Carvalho Chehab 	u16 l, r, *n;
6459a0bf528SMauro Carvalho Chehab 
6469a0bf528SMauro Carvalho Chehab 	HARD_RESET(state);
6479a0bf528SMauro Carvalho Chehab 
6489a0bf528SMauro Carvalho Chehab 
6499a0bf528SMauro Carvalho Chehab #ifndef FORCE_SBAND_TUNER
6509a0bf528SMauro Carvalho Chehab 	if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1)
6519a0bf528SMauro Carvalho Chehab 		state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff;
6529a0bf528SMauro Carvalho Chehab 	else
6539a0bf528SMauro Carvalho Chehab #else
6549a0bf528SMauro Carvalho Chehab #warning forcing SBAND
6559a0bf528SMauro Carvalho Chehab #endif
6569a0bf528SMauro Carvalho Chehab 	state->revision = DIB0070S_P1A;
6579a0bf528SMauro Carvalho Chehab 
6589a0bf528SMauro Carvalho Chehab 	/* P1F or not */
659fb11cbd1SMauro Carvalho Chehab 	dprintk("Revision: %x\n", state->revision);
6609a0bf528SMauro Carvalho Chehab 
6619a0bf528SMauro Carvalho Chehab 	if (state->revision == DIB0070_P1D) {
662fb11cbd1SMauro Carvalho Chehab 		dprintk("Error: this driver is not to be used meant for P1D or earlier\n");
6639a0bf528SMauro Carvalho Chehab 		return -EINVAL;
6649a0bf528SMauro Carvalho Chehab 	}
6659a0bf528SMauro Carvalho Chehab 
6669a0bf528SMauro Carvalho Chehab 	n = (u16 *) dib0070_p1f_defaults;
6679a0bf528SMauro Carvalho Chehab 	l = pgm_read_word(n++);
6689a0bf528SMauro Carvalho Chehab 	while (l) {
6699a0bf528SMauro Carvalho Chehab 		r = pgm_read_word(n++);
6709a0bf528SMauro Carvalho Chehab 		do {
6719a0bf528SMauro Carvalho Chehab 			dib0070_write_reg(state, (u8)r, pgm_read_word(n++));
6729a0bf528SMauro Carvalho Chehab 			r++;
6739a0bf528SMauro Carvalho Chehab 		} while (--l);
6749a0bf528SMauro Carvalho Chehab 		l = pgm_read_word(n++);
6759a0bf528SMauro Carvalho Chehab 	}
6769a0bf528SMauro Carvalho Chehab 
6779a0bf528SMauro Carvalho Chehab 	if (state->cfg->force_crystal_mode != 0)
6789a0bf528SMauro Carvalho Chehab 		r = state->cfg->force_crystal_mode;
6799a0bf528SMauro Carvalho Chehab 	else if (state->cfg->clock_khz >= 24000)
6809a0bf528SMauro Carvalho Chehab 		r = 1;
6819a0bf528SMauro Carvalho Chehab 	else
6829a0bf528SMauro Carvalho Chehab 		r = 2;
6839a0bf528SMauro Carvalho Chehab 
6849a0bf528SMauro Carvalho Chehab 
6859a0bf528SMauro Carvalho Chehab 	r |= state->cfg->osc_buffer_state << 3;
6869a0bf528SMauro Carvalho Chehab 
6879a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x10, r);
6889a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5));
6899a0bf528SMauro Carvalho Chehab 
6909a0bf528SMauro Carvalho Chehab 	if (state->cfg->invert_iq) {
6919a0bf528SMauro Carvalho Chehab 		r = dib0070_read_reg(state, 0x02) & 0xffdf;
6929a0bf528SMauro Carvalho Chehab 		dib0070_write_reg(state, 0x02, r | (1 << 5));
6939a0bf528SMauro Carvalho Chehab 	}
6949a0bf528SMauro Carvalho Chehab 
6959a0bf528SMauro Carvalho Chehab 	if (state->revision == DIB0070S_P1A)
6969a0bf528SMauro Carvalho Chehab 		dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
6979a0bf528SMauro Carvalho Chehab 	else
698f3f8ef22SMauro Carvalho Chehab 		dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump,
699f3f8ef22SMauro Carvalho Chehab 				     state->cfg->enable_third_order_filter);
7009a0bf528SMauro Carvalho Chehab 
7019a0bf528SMauro Carvalho Chehab 	dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8);
7029a0bf528SMauro Carvalho Chehab 
7039a0bf528SMauro Carvalho Chehab 	dib0070_wbd_offset_calibration(state);
7049a0bf528SMauro Carvalho Chehab 
7059a0bf528SMauro Carvalho Chehab 	return 0;
7069a0bf528SMauro Carvalho Chehab }
7079a0bf528SMauro Carvalho Chehab 
dib0070_get_frequency(struct dvb_frontend * fe,u32 * frequency)7089a0bf528SMauro Carvalho Chehab static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency)
7099a0bf528SMauro Carvalho Chehab {
7109a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = fe->tuner_priv;
7119a0bf528SMauro Carvalho Chehab 
7129a0bf528SMauro Carvalho Chehab 	*frequency = 1000 * state->current_rf;
7139a0bf528SMauro Carvalho Chehab 	return 0;
7149a0bf528SMauro Carvalho Chehab }
7159a0bf528SMauro Carvalho Chehab 
dib0070_release(struct dvb_frontend * fe)716f2709c20SMauro Carvalho Chehab static void dib0070_release(struct dvb_frontend *fe)
717f2709c20SMauro Carvalho Chehab {
718f2709c20SMauro Carvalho Chehab 	kfree(fe->tuner_priv);
719f2709c20SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
720f2709c20SMauro Carvalho Chehab }
721f2709c20SMauro Carvalho Chehab 
7229a0bf528SMauro Carvalho Chehab static const struct dvb_tuner_ops dib0070_ops = {
7239a0bf528SMauro Carvalho Chehab 	.info = {
7249a0bf528SMauro Carvalho Chehab 		.name              = "DiBcom DiB0070",
725a3f90c75SMauro Carvalho Chehab 		.frequency_min_hz  =  45 * MHz,
726a3f90c75SMauro Carvalho Chehab 		.frequency_max_hz  = 860 * MHz,
727a3f90c75SMauro Carvalho Chehab 		.frequency_step_hz =   1 * kHz,
7289a0bf528SMauro Carvalho Chehab 	},
729f2709c20SMauro Carvalho Chehab 	.release       = dib0070_release,
7309a0bf528SMauro Carvalho Chehab 
7319a0bf528SMauro Carvalho Chehab 	.init          = dib0070_wakeup,
7329a0bf528SMauro Carvalho Chehab 	.sleep         = dib0070_sleep,
7339a0bf528SMauro Carvalho Chehab 	.set_params    = dib0070_tune,
7349a0bf528SMauro Carvalho Chehab 
7359a0bf528SMauro Carvalho Chehab 	.get_frequency = dib0070_get_frequency,
7369a0bf528SMauro Carvalho Chehab //      .get_bandwidth = dib0070_get_bandwidth
7379a0bf528SMauro Carvalho Chehab };
7389a0bf528SMauro Carvalho Chehab 
dib0070_attach(struct dvb_frontend * fe,struct i2c_adapter * i2c,struct dib0070_config * cfg)7399a0bf528SMauro Carvalho Chehab struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)
7409a0bf528SMauro Carvalho Chehab {
7419a0bf528SMauro Carvalho Chehab 	struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL);
7429a0bf528SMauro Carvalho Chehab 	if (state == NULL)
7439a0bf528SMauro Carvalho Chehab 		return NULL;
7449a0bf528SMauro Carvalho Chehab 
7459a0bf528SMauro Carvalho Chehab 	state->cfg = cfg;
7469a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
7479a0bf528SMauro Carvalho Chehab 	state->fe  = fe;
7489a0bf528SMauro Carvalho Chehab 	mutex_init(&state->i2c_buffer_lock);
7499a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = state;
7509a0bf528SMauro Carvalho Chehab 
7519a0bf528SMauro Carvalho Chehab 	if (dib0070_reset(fe) != 0)
7529a0bf528SMauro Carvalho Chehab 		goto free_mem;
7539a0bf528SMauro Carvalho Chehab 
754fb11cbd1SMauro Carvalho Chehab 	pr_info("DiB0070: successfully identified\n");
7559a0bf528SMauro Carvalho Chehab 	memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));
7569a0bf528SMauro Carvalho Chehab 
7579a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = state;
7589a0bf528SMauro Carvalho Chehab 	return fe;
7599a0bf528SMauro Carvalho Chehab 
7609a0bf528SMauro Carvalho Chehab free_mem:
7619a0bf528SMauro Carvalho Chehab 	kfree(state);
7629a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
7639a0bf528SMauro Carvalho Chehab 	return NULL;
7649a0bf528SMauro Carvalho Chehab }
765*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(dib0070_attach);
7669a0bf528SMauro Carvalho Chehab 
76799e44da7SPatrick Boettcher MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
7689a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner");
7699a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
770