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