From 7e5ce6515d0deb76a49dcb4112a6dff5d950bfb6 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Mon, 3 Aug 2009 13:43:40 -0300 Subject: [PATCH] V4L/DVB (12898): DiB0070: Update to latest internal release General update of the dib0070-driver based on DiBcom's latest release. New driver features can enable better performance in some reception situations. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_devices.c | 7 +- drivers/media/dvb/frontends/dib0070.c | 673 ++++++++++++++++------------ drivers/media/dvb/frontends/dib0070.h | 11 + 3 files changed, 407 insertions(+), 284 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 10a89b0..10ade26 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1098,11 +1098,13 @@ static struct dibx000_agc_config dib7070_agc_config = { static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) { + deb_info("reset: %d", onoff); return dib7000p_set_gpio(fe, 8, 0, !onoff); } static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) { + deb_info("sleep: %d", onoff); return dib7000p_set_gpio(fe, 9, 0, onoff); } @@ -1112,13 +1114,14 @@ static struct dib0070_config dib7070p_dib0070_config[2] = { .reset = dib7070_tuner_reset, .sleep = dib7070_tuner_sleep, .clock_khz = 12000, - .clock_pad_drive = 4 + .clock_pad_drive = 4, + .charge_pump = 2, }, { .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, .reset = dib7070_tuner_reset, .sleep = dib7070_tuner_sleep, .clock_khz = 12000, - + .charge_pump = 2, } }; diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c index da92cbe..2eb9bdb 100644 --- a/drivers/media/dvb/frontends/dib0070.c +++ b/drivers/media/dvb/frontends/dib0070.c @@ -1,12 +1,29 @@ /* * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. * - * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) + * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/) * * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * This code is more or less generated from another driver, please + * excuse some codingstyle oddities. + * */ + #include #include @@ -19,19 +36,57 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); -#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB0070: "); printk(args); printk("\n"); } } while (0) +#define dprintk(args...) do { \ + if (debug) { \ + printk(KERN_DEBUG "DiB0070: "); \ + printk(args); \ + printk("\n"); \ + } \ +} while (0) #define DIB0070_P1D 0x00 #define DIB0070_P1F 0x01 #define DIB0070_P1G 0x03 #define DIB0070S_P1A 0x02 +enum frontend_tune_state { + CT_TUNER_START = 10, + CT_TUNER_STEP_0, + CT_TUNER_STEP_1, + CT_TUNER_STEP_2, + CT_TUNER_STEP_3, + CT_TUNER_STEP_4, + CT_TUNER_STEP_5, + CT_TUNER_STEP_6, + CT_TUNER_STEP_7, + CT_TUNER_STOP, +}; + +#define FE_CALLBACK_TIME_NEVER 0xffffffff + struct dib0070_state { struct i2c_adapter *i2c; struct dvb_frontend *fe; const struct dib0070_config *cfg; u16 wbd_ff_offset; u8 revision; + + enum frontend_tune_state tune_state; + u32 current_rf; + + /* for the captrim binary search */ + s8 step; + u16 adc_diff; + + s8 captrim; + s8 fcaptrim; + u16 lo4; + + const struct dib0070_tuning *current_tune_table_index; + const struct dib0070_lna_match *lna_match; + + u8 wbd_gain_current; + u16 wbd_offset_3_3[2]; }; static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg) @@ -59,55 +114,71 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) return 0; } -#define HARD_RESET(state) do { if (state->cfg->reset) { state->cfg->reset(state->fe,1); msleep(10); state->cfg->reset(state->fe,0); msleep(10); } } while (0) +#define HARD_RESET(state) do { \ + state->cfg->sleep(state->fe, 0); \ + if (state->cfg->reset) { \ + state->cfg->reset(state->fe,1); msleep(10); \ + state->cfg->reset(state->fe,0); msleep(10); \ + } \ +} while (0) static int dib0070_set_bandwidth(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) { - struct dib0070_state *st = fe->tuner_priv; - u16 tmp = 0; - tmp = dib0070_read_reg(st, 0x02) & 0x3fff; - - switch(BANDWIDTH_TO_KHZ(ch->u.ofdm.bandwidth)) { - case 8000: - tmp |= (0 << 14); - break; - case 7000: - tmp |= (1 << 14); - break; - case 6000: - tmp |= (2 << 14); - break; - case 5000: - default: - tmp |= (3 << 14); - break; - } - dib0070_write_reg(st, 0x02, tmp); + struct dib0070_state *st = fe->tuner_priv; + u16 tmp = dib0070_read_reg(st, 0x02) & 0x3fff; + + if (fe->dtv_property_cache.bandwidth_hz/1000 > 7000) + tmp |= (0 << 14); + else if (fe->dtv_property_cache.bandwidth_hz/1000 > 6000) + tmp |= (1 << 14); + else if (fe->dtv_property_cache.bandwidth_hz/1000 > 5000) + tmp |= (2 << 14); + else + tmp |= (3 << 14); + + dib0070_write_reg(st, 0x02, tmp); + + /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ + if (fe->dtv_property_cache.delivery_system == SYS_ISDBT) { + u16 value = dib0070_read_reg(st, 0x17); + + dib0070_write_reg(st, 0x17, value & 0xfffc); + tmp = dib0070_read_reg(st, 0x01) & 0x01ff; + dib0070_write_reg(st, 0x01, tmp | (60 << 9)); + + dib0070_write_reg(st, 0x17, value); + } return 0; } -static void dib0070_captrim(struct dib0070_state *st, u16 LO4) +static int dib0070_captrim(struct dib0070_state *st, enum frontend_tune_state *tune_state) { - int8_t captrim, fcaptrim, step_sign, step; - u16 adc, adc_diff = 3000; + int8_t step_sign; + u16 adc; + int ret = 0; + if (*tune_state == CT_TUNER_STEP_0) { + dib0070_write_reg(st, 0x0f, 0xed10); + dib0070_write_reg(st, 0x17, 0x0034); - dib0070_write_reg(st, 0x0f, 0xed10); - dib0070_write_reg(st, 0x17, 0x0034); + dib0070_write_reg(st, 0x18, 0x0032); + st->step = st->captrim = st->fcaptrim = 64; + st->adc_diff = 3000; + ret = 20; - dib0070_write_reg(st, 0x18, 0x0032); - msleep(2); + *tune_state = CT_TUNER_STEP_1; + } else if (*tune_state == CT_TUNER_STEP_1) { + st->step /= 2; + dib0070_write_reg(st, 0x14, st->lo4 | st->captrim); + ret = 15; - step = captrim = fcaptrim = 64; + *tune_state = CT_TUNER_STEP_2; + } else if (*tune_state == CT_TUNER_STEP_2) { - do { - step /= 2; - dib0070_write_reg(st, 0x14, LO4 | captrim); - msleep(1); adc = dib0070_read_reg(st, 0x19); - dprintk( "CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", captrim, adc, (u32) adc*(u32)1800/(u32)1024); + dprintk( "CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", st->captrim, adc, (u32) adc*(u32)1800/(u32)1024); if (adc >= 400) { adc -= 400; @@ -117,266 +188,305 @@ static void dib0070_captrim(struct dib0070_state *st, u16 LO4) step_sign = 1; } - if (adc < adc_diff) { - dprintk( "CAPTRIM=%hd is closer to target (%hd/%hd)", captrim, adc, adc_diff); - adc_diff = adc; - fcaptrim = captrim; + if (adc < st->adc_diff) { + dprintk( "CAPTRIM=%hd is closer to target (%hd/%hd)", st->captrim, adc, st->adc_diff); + st->adc_diff = adc; + st->fcaptrim = st->captrim; } - captrim += (step_sign * step); - } while (step >= 1); + st->captrim += (step_sign * st->step); + + if (st->step >= 1) + *tune_state = CT_TUNER_STEP_1; + else + *tune_state = CT_TUNER_STEP_3; + + } else if (*tune_state == CT_TUNER_STEP_3) { + dib0070_write_reg(st, 0x14, st->lo4 | st->fcaptrim); + dib0070_write_reg(st, 0x18, 0x07ff); + *tune_state = CT_TUNER_STEP_4; + } - dib0070_write_reg(st, 0x14, LO4 | fcaptrim); - dib0070_write_reg(st, 0x18, 0x07ff); + return ret; } -#define LPF 100 // define for the loop filter 100kHz by default 16-07-06 -#define LO4_SET_VCO_HFDIV(l, v, h) l |= ((v) << 11) | ((h) << 7) -#define LO4_SET_SD(l, s) l |= ((s) << 14) | ((s) << 12) -#define LO4_SET_CTRIM(l, c) l |= (c) << 10 -static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) +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) { - struct dib0070_state *st = fe->tuner_priv; - u32 freq = ch->frequency/1000 + (BAND_OF_FREQUENCY(ch->frequency/1000) == BAND_VHF ? st->cfg->freq_offset_khz_vhf : st->cfg->freq_offset_khz_uhf); + struct dib0070_state *state = fe->tuner_priv; + u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); + dprintk( "CTRL_LO5: 0x%x", lo5); + return dib0070_write_reg(state, 0x15, lo5); +} - u8 band = BAND_OF_FREQUENCY(freq), c; +struct dib0070_tuning +{ + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 switch_trim; + u8 vco_band; + u8 hfdiv; + u8 vco_multi; + u8 presc; + u8 wbdmux; + u16 tuner_enable; +}; - /*******************VCO***********************************/ - u16 lo4 = 0; +struct dib0070_lna_match +{ + u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ + u8 lna_band; +}; - u8 REFDIV, PRESC = 2; - u32 FBDiv, Rest, FREF, VCOF_kHz; - u16 Num, Den; - /*******************FrontEnd******************************/ - u16 value = 0; +static const struct dib0070_tuning dib0070s_tuning_table[] = - dprintk( "Tuning for Band: %hd (%d kHz)", band, freq); +{ + { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */ + { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 }, + { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 }, + { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */ + { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, + { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, + { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */ +}; +static const struct dib0070_tuning dib0070_tuning_table[] = - dib0070_write_reg(st, 0x17, 0x30); +{ + { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */ + { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */ + { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 }, + { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 }, + { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */ + { 699999, 2, 0 ,1, 4, 2, 2, 0x4000 | 0x0800 }, + { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 }, + { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */ +}; - dib0070_set_bandwidth(fe, ch); /* c is used as HF */ - switch (st->revision) { - case DIB0070S_P1A: - switch (band) { - case BAND_LBAND: - LO4_SET_VCO_HFDIV(lo4, 1, 1); - c = 2; - break; - case BAND_SBAND: - LO4_SET_VCO_HFDIV(lo4, 0, 0); - LO4_SET_CTRIM(lo4, 1); - c = 1; - break; - case BAND_UHF: - default: - if (freq < 570000) { - LO4_SET_VCO_HFDIV(lo4, 1, 3); - PRESC = 6; c = 6; - } else if (freq < 680000) { - LO4_SET_VCO_HFDIV(lo4, 0, 2); - c = 4; - } else { - LO4_SET_VCO_HFDIV(lo4, 1, 2); - c = 4; - } - break; - } break; - - case DIB0070_P1G: - case DIB0070_P1F: - default: - switch (band) { - case BAND_FM: - LO4_SET_VCO_HFDIV(lo4, 0, 7); - c = 24; - break; - case BAND_LBAND: - LO4_SET_VCO_HFDIV(lo4, 1, 0); - c = 2; - break; - case BAND_VHF: - if (freq < 180000) { - LO4_SET_VCO_HFDIV(lo4, 0, 3); - c = 16; - } else if (freq < 190000) { - LO4_SET_VCO_HFDIV(lo4, 1, 3); - c = 16; - } else { - LO4_SET_VCO_HFDIV(lo4, 0, 6); - c = 12; - } - break; - - case BAND_UHF: - default: - if (freq < 570000) { - LO4_SET_VCO_HFDIV(lo4, 1, 5); - c = 6; - } else if (freq < 700000) { - LO4_SET_VCO_HFDIV(lo4, 0, 1); - c = 4; - } else { - LO4_SET_VCO_HFDIV(lo4, 1, 1); - c = 4; - } - break; - } - break; - } +static const struct dib0070_lna_match dib0070_lna_flip_chip[] = - dprintk( "HFDIV code: %hd", (lo4 >> 7) & 0xf); - dprintk( "VCO = %hd", (lo4 >> 11) & 0x3); +{ + { 180000, 0 }, /* VHF */ + { 188000, 1 }, + { 196400, 2 }, + { 250000, 3 }, + { 550000, 0 }, /* UHF */ + { 590000, 1 }, + { 666000, 3 }, + { 864000, 5 }, + { 1500000, 0 }, /* LBAND or everything higher than UHF */ + { 1600000, 1 }, + { 2000000, 3 }, + { 0xffffffff, 7 }, +}; +static const struct dib0070_lna_match dib0070_lna[] = - VCOF_kHz = (c * freq) * 2; - dprintk( "VCOF in kHz: %d ((%hd*%d) << 1))",VCOF_kHz, c, freq); +{ + { 180000, 0 }, /* VHF */ + { 188000, 1 }, + { 196400, 2 }, + { 250000, 3 }, + { 550000, 2 }, /* UHF */ + { 650000, 3 }, + { 750000, 5 }, + { 850000, 6 }, + { 864000, 7 }, + { 1500000, 0 }, /* LBAND or everything higher than UHF */ + { 1600000, 1 }, + { 2000000, 3 }, + { 0xffffffff, 7 }, +}; - switch (band) { - case BAND_VHF: - REFDIV = (u8) ((st->cfg->clock_khz + 9999) / 10000); - break; - case BAND_FM: - REFDIV = (u8) ((st->cfg->clock_khz) / 1000); - break; - default: - REFDIV = (u8) ( st->cfg->clock_khz / 10000); - break; - } - FREF = st->cfg->clock_khz / REFDIV; +#define LPF 100 // define for the loop filter 100kHz by default 16-07-06 +static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch) +{ + struct dib0070_state *st = fe->tuner_priv; + + const struct dib0070_tuning *tune; + const struct dib0070_lna_match *lna_match; + + enum frontend_tune_state *tune_state = &st->tune_state; + int ret = 10; /* 1ms is the default delay most of the time */ - dprintk( "REFDIV: %hd, FREF: %d", REFDIV, FREF); + u8 band = (u8)BAND_OF_FREQUENCY(ch->frequency/1000); + u32 freq = ch->frequency/1000 + (band == BAND_VHF ? st->cfg->freq_offset_khz_vhf : st->cfg->freq_offset_khz_uhf); + + +#ifdef CONFIG_STANDARD_ISDBT + if (fe->dtv_property_cache.delivery_system == SYS_ISDBT && ch->u.isdbt.sb_mode == 1) + if ( ( (ch->u.isdbt.sb_conn_total_seg % 2) && (ch->u.isdbt.sb_wanted_seg == ((ch->u.isdbt.sb_conn_total_seg/2) + 1) ) ) || + ( ( (ch->u.isdbt.sb_conn_total_seg % 2) == 0) && (ch->u.isdbt.sb_wanted_seg == (ch->u.isdbt.sb_conn_total_seg/2) ) ) || + ( ( (ch->u.isdbt.sb_conn_total_seg % 2) == 0) && (ch->u.isdbt.sb_wanted_seg == ((ch->u.isdbt.sb_conn_total_seg/2)+1))) ) + freq += 850; +#endif + if (st->current_rf != freq) { + switch (st->revision) { - case DIB0070S_P1A: - FBDiv = (VCOF_kHz / PRESC / FREF); - Rest = (VCOF_kHz / PRESC) - FBDiv * FREF; - break; - - case DIB0070_P1G: - case DIB0070_P1F: - default: - FBDiv = (freq / (FREF / 2)); - Rest = 2 * freq - FBDiv * FREF; - break; + case DIB0070S_P1A: + tune = dib0070s_tuning_table; + lna_match = dib0070_lna; + break; + default: + tune = dib0070_tuning_table; + if (st->cfg->flip_chip) + lna_match = dib0070_lna_flip_chip; + else + lna_match = dib0070_lna; + break; } + while (freq > tune->max_freq) /* find the right one */ + tune++; + while (freq > lna_match->max_freq) /* find the right one */ + lna_match++; + st->current_tune_table_index = tune; + st->lna_match = lna_match; + } - if (Rest < LPF) Rest = 0; - else if (Rest < 2 * LPF) Rest = 2 * LPF; - else if (Rest > (FREF - LPF)) { Rest = 0 ; FBDiv += 1; } - else if (Rest > (FREF - 2 * LPF)) Rest = FREF - 2 * LPF; - Rest = (Rest * 6528) / (FREF / 10); - dprintk( "FBDIV: %d, Rest: %d", FBDiv, Rest); + if (*tune_state == CT_TUNER_START) { + dprintk( "Tuning for Band: %hd (%d kHz)", band, freq); + if (st->current_rf != freq) { + u8 REFDIV; + u32 FBDiv, Rest, FREF, VCOF_kHz; + u8 Den; - Num = 0; - Den = 1; + st->current_rf = freq; + st->lo4 = (st->current_tune_table_index->vco_band << 11) | (st->current_tune_table_index->hfdiv << 7); - if (Rest > 0) { - LO4_SET_SD(lo4, 1); - Den = 255; - Num = (u16)Rest; - } - dprintk( "Num: %hd, Den: %hd, SD: %hd",Num, Den, (lo4 >> 12) & 0x1); + dib0070_write_reg(st, 0x17, 0x30); - dib0070_write_reg(st, 0x11, (u16)FBDiv); + VCOF_kHz = st->current_tune_table_index->vco_multi * freq * 2; + switch (band) { + case BAND_VHF: + REFDIV = (u8) ((st->cfg->clock_khz + 9999) / 10000); + break; + case BAND_FM: + REFDIV = (u8) ((st->cfg->clock_khz) / 1000); + break; + default: + REFDIV = (u8) ( st->cfg->clock_khz / 10000); + break; + } + FREF = st->cfg->clock_khz / REFDIV; - dib0070_write_reg(st, 0x12, (Den << 8) | REFDIV); - dib0070_write_reg(st, 0x13, Num); + switch (st->revision) { + case DIB0070S_P1A: + FBDiv = (VCOF_kHz / st->current_tune_table_index->presc / FREF); + Rest = (VCOF_kHz / st->current_tune_table_index->presc) - FBDiv * FREF; + break; + case DIB0070_P1G: + case DIB0070_P1F: + default: + FBDiv = (freq / (FREF / 2)); + Rest = 2 * freq - FBDiv * FREF; + break; + } - value = 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001; - switch (band) { - case BAND_UHF: value |= 0x4000 | 0x0800; break; - case BAND_LBAND: value |= 0x2000 | 0x0400; break; - default: value |= 0x8000 | 0x1000; break; - } - dib0070_write_reg(st, 0x20, value); + if (Rest < LPF) Rest = 0; + else if (Rest < 2 * LPF) Rest = 2 * LPF; + else if (Rest > (FREF - LPF)) { Rest = 0 ; FBDiv += 1; } + else if (Rest > (FREF - 2 * LPF)) Rest = FREF - 2 * LPF; + Rest = (Rest * 6528) / (FREF / 10); - dib0070_captrim(st, lo4); - if (st->revision == DIB0070S_P1A) { - if (band == BAND_SBAND) - dib0070_write_reg(st, 0x15, 0x16e2); - else - dib0070_write_reg(st, 0x15, 0x56e5); - } + Den = 1; + if (Rest > 0) { + st->lo4 |= (1 << 14) | (1 << 12); + Den = 255; + } + dib0070_write_reg(st, 0x11, (u16)FBDiv); + dib0070_write_reg(st, 0x12, (Den << 8) | REFDIV); + dib0070_write_reg(st, 0x13, (u16) Rest); - switch (band) { - case BAND_UHF: value = 0x7c82; break; - case BAND_LBAND: value = 0x7c84; break; - default: value = 0x7c81; break; - } - dib0070_write_reg(st, 0x0f, value); - dib0070_write_reg(st, 0x06, 0x3fff); + if (st->revision == DIB0070S_P1A) { - /* Front End */ - /* c == TUNE, value = SWITCH */ - c = 0; - value = 0; - switch (band) { - case BAND_FM: - c = 0; value = 1; - break; + if (band == BAND_SBAND) { + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + dib0070_write_reg(st, 0x1d,0xFFFF); + } else + dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); + } - case BAND_VHF: - if (freq <= 180000) c = 0; - else if (freq <= 188200) c = 1; - else if (freq <= 196400) c = 2; - else c = 3; - value = 1; - break; - case BAND_LBAND: - if (freq <= 1500000) c = 0; - else if (freq <= 1600000) c = 1; - else c = 3; - break; + dib0070_write_reg(st, 0x20, 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | st->current_tune_table_index->tuner_enable); - case BAND_SBAND: - c = 7; - dib0070_write_reg(st, 0x1d,0xFFFF); - break; + dprintk( "REFDIV: %hd, FREF: %d", REFDIV, FREF); + dprintk( "FBDIV: %d, Rest: %d", FBDiv, Rest); + dprintk( "Num: %hd, Den: %hd, SD: %hd",(u16) Rest, Den, (st->lo4 >> 12) & 0x1); + dprintk( "HFDIV code: %hd", st->current_tune_table_index->hfdiv); + dprintk( "VCO = %hd", st->current_tune_table_index->vco_band); + dprintk( "VCOF: ((%hd*%d) << 1))", st->current_tune_table_index->vco_multi, freq); - case BAND_UHF: - default: - if (st->cfg->flip_chip) { - if (freq <= 550000) c = 0; - else if (freq <= 590000) c = 1; - else if (freq <= 666000) c = 3; - else c = 5; - } else { - if (freq <= 550000) c = 2; - else if (freq <= 650000) c = 3; - else if (freq <= 750000) c = 5; - else if (freq <= 850000) c = 6; - else c = 7; - } - value = 2; - break; + *tune_state = CT_TUNER_STEP_0; + } else { /* we are already tuned to this frequency - the configuration is correct */ + ret = 50; /* wakeup time */ + *tune_state = CT_TUNER_STEP_5; + } + } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { + + ret = dib0070_captrim(st, tune_state); + + } else if (*tune_state == CT_TUNER_STEP_4) { + const struct dib0070_wbd_gain_cfg *tmp = st->cfg->wbd_gain; + if (tmp != NULL) { + while (freq/1000 > tmp->freq) /* find the right one */ + tmp++; + dib0070_write_reg(st, 0x0f, (0 << 15) | (1 << 14) | (3 << 12) | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) | (st->current_tune_table_index->wbdmux << 0)); + st->wbd_gain_current = tmp->wbd_gain_val; + } else { + dib0070_write_reg(st, 0x0f, (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (st->current_tune_table_index->wbdmux << 0)); + st->wbd_gain_current = 6; } - /* default: LNA_MATCH=7, BIAS=3 */ - dib0070_write_reg(st, 0x07, (value << 11) | (7 << 8) | (c << 3) | (3 << 0)); - dib0070_write_reg(st, 0x08, (c << 10) | (3 << 7) | (127)); + dib0070_write_reg(st, 0x06, 0x3fff); + dib0070_write_reg(st, 0x07, (st->current_tune_table_index->switch_trim << 11) | (7 << 8) | (st->lna_match->lna_band << 3) | (3 << 0)); + dib0070_write_reg(st, 0x08, (st->lna_match->lna_band << 10) | (3 << 7) | (127)); dib0070_write_reg(st, 0x0d, 0x0d80); dib0070_write_reg(st, 0x18, 0x07ff); dib0070_write_reg(st, 0x17, 0x0033); - return 0; + + *tune_state = CT_TUNER_STEP_5; + } else if (*tune_state == CT_TUNER_STEP_5) { + dib0070_set_bandwidth(fe, ch); + *tune_state = CT_TUNER_STOP; + } else { + ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ + } + return ret; +} + + +static int dib0070_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +{ + struct dib0070_state *state = fe->tuner_priv; + uint32_t ret; + + state->tune_state = CT_TUNER_START; + + do { + ret = dib0070_tune_digital(fe, p); + if (ret != FE_CALLBACK_TIME_NEVER) + msleep(ret/10); + else + break; + } while (state->tune_state != CT_TUNER_STOP); + + return 0; } static int dib0070_wakeup(struct dvb_frontend *fe) @@ -395,7 +505,7 @@ static int dib0070_sleep(struct dvb_frontend *fe) return 0; } -static u16 dib0070_p1f_defaults[] = +static const u16 dib0070_p1f_defaults[] = { 7, 0x02, @@ -434,45 +544,40 @@ static u16 dib0070_p1f_defaults[] = 0, }; -static void dib0070_wbd_calibration(struct dvb_frontend *fe) +static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain) { - u16 wbd_offs; - struct dib0070_state *state = fe->tuner_priv; - - if (state->cfg->sleep) - state->cfg->sleep(fe, 0); - - dib0070_write_reg(state, 0x0f, 0x6d81); - dib0070_write_reg(state, 0x20, 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); - msleep(9); - wbd_offs = dib0070_read_reg(state, 0x19); - dib0070_write_reg(state, 0x20, 0); - state->wbd_ff_offset = ((wbd_offs * 8 * 18 / 33 + 1) / 2); - dprintk( "WBDStart = %d (Vargen) - FF = %hd", (u32) wbd_offs * 1800/1024, state->wbd_ff_offset); - - if (state->cfg->sleep) - state->cfg->sleep(fe, 1); + u16 tuner_en = dib0070_read_reg(state, 0x20); + u16 offset; + + dib0070_write_reg(state, 0x18, 0x07ff); + dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); + dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); + msleep(9); + offset = dib0070_read_reg(state, 0x19); + dib0070_write_reg(state, 0x20, tuner_en); + return offset; +} +static void dib0070_wbd_offset_calibration(struct dib0070_state *state) +{ + u8 gain; + for (gain = 6; gain < 8; gain++) { + state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); + dprintk( "Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]); + } } u16 dib0070_wbd_offset(struct dvb_frontend *fe) { struct dib0070_state *st = fe->tuner_priv; - return st->wbd_ff_offset; + return st->wbd_offset_3_3[st->wbd_gain_current - 6]; } EXPORT_SYMBOL(dib0070_wbd_offset); -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) -{ - struct dib0070_state *state = fe->tuner_priv; - u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); - dprintk( "CTRL_LO5: 0x%x", lo5); - return dib0070_write_reg(state, 0x15, lo5); -} - #define pgm_read_word(w) (*w) -static int dib0070_reset(struct dib0070_state *state) +static int dib0070_reset(struct dvb_frontend *fe) { + struct dib0070_state *state = fe->tuner_priv; u16 l, r, *n; HARD_RESET(state); @@ -482,6 +587,8 @@ static int dib0070_reset(struct dib0070_state *state) if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1) state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff; else +#else +#warning forcing SBAND #endif state->revision = DIB0070S_P1A; @@ -511,24 +618,27 @@ static int dib0070_reset(struct dib0070_state *state) else r = 2; + r |= state->cfg->osc_buffer_state << 3; dib0070_write_reg(state, 0x10, r); - dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 4)); + dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5)); if (state->cfg->invert_iq) { r = dib0070_read_reg(state, 0x02) & 0xffdf; dib0070_write_reg(state, 0x02, r | (1 << 5)); } - - if (state->revision == DIB0070S_P1A) - dib0070_set_ctrl_lo5(state->fe, 4, 7, 3, 1); - else - dib0070_set_ctrl_lo5(state->fe, 4, 4, 2, 0); + if (state->revision == DIB0070S_P1A) + dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); + else + dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter); dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8); - return 0; + + dib0070_wbd_offset_calibration(state); + + return 0; } @@ -539,7 +649,7 @@ static int dib0070_release(struct dvb_frontend *fe) return 0; } -static struct dvb_tuner_ops dib0070_ops = { +static const struct dvb_tuner_ops dib0070_ops = { .info = { .name = "DiBcom DiB0070", .frequency_min = 45000000, @@ -550,7 +660,8 @@ static struct dvb_tuner_ops dib0070_ops = { .init = dib0070_wakeup, .sleep = dib0070_sleep, - .set_params = dib0070_tune_digital, + .set_params = dib0070_tune, + // .get_frequency = dib0070_get_frequency, // .get_bandwidth = dib0070_get_bandwidth }; @@ -566,11 +677,9 @@ struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter state->fe = fe; fe->tuner_priv = state; - if (dib0070_reset(state) != 0) + if (dib0070_reset(fe) != 0) goto free_mem; - dib0070_wbd_calibration(fe); - printk(KERN_INFO "DiB0070: successfully identified\n"); memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops)); diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h index 9670f5d..8c1c51c 100644 --- a/drivers/media/dvb/frontends/dib0070.h +++ b/drivers/media/dvb/frontends/dib0070.h @@ -15,6 +15,11 @@ struct i2c_adapter; #define DEFAULT_DIB0070_I2C_ADDRESS 0x60 +struct dib0070_wbd_gain_cfg { + u16 freq; + u16 wbd_gain_val; +}; + struct dib0070_config { u8 i2c_address; @@ -35,6 +40,12 @@ struct dib0070_config { u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ u8 flip_chip; + u8 enable_third_order_filter; + u8 charge_pump; + + const struct dib0070_wbd_gain_cfg * wbd_gain; + + u8 vga_filter; }; #if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) -- 2.7.4