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