Path: blob/master/drivers/media/dvb/frontends/dib0070.c
15112 views
/*1* Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner.2*3* Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/)4*5* This program is free software; you can redistribute it and/or6* modify it under the terms of the GNU General Public License as7* published by the Free Software Foundation; either version 2 of the8* License, or (at your option) any later version.9*10* This program is distributed in the hope that it will be useful, but11* WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13*14* GNU General Public License for more details.15*16* You should have received a copy of the GNU General Public License17* along with this program; if not, write to the Free Software18* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.19*20*21* This code is more or less generated from another driver, please22* excuse some codingstyle oddities.23*24*/2526#include <linux/kernel.h>27#include <linux/slab.h>28#include <linux/i2c.h>2930#include "dvb_frontend.h"3132#include "dib0070.h"33#include "dibx000_common.h"3435static int debug;36module_param(debug, int, 0644);37MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");3839#define dprintk(args...) do { \40if (debug) { \41printk(KERN_DEBUG "DiB0070: "); \42printk(args); \43printk("\n"); \44} \45} while (0)4647#define DIB0070_P1D 0x0048#define DIB0070_P1F 0x0149#define DIB0070_P1G 0x0350#define DIB0070S_P1A 0x025152struct dib0070_state {53struct i2c_adapter *i2c;54struct dvb_frontend *fe;55const struct dib0070_config *cfg;56u16 wbd_ff_offset;57u8 revision;5859enum frontend_tune_state tune_state;60u32 current_rf;6162/* for the captrim binary search */63s8 step;64u16 adc_diff;6566s8 captrim;67s8 fcaptrim;68u16 lo4;6970const struct dib0070_tuning *current_tune_table_index;71const struct dib0070_lna_match *lna_match;7273u8 wbd_gain_current;74u16 wbd_offset_3_3[2];7576/* for the I2C transfer */77struct i2c_msg msg[2];78u8 i2c_write_buffer[3];79u8 i2c_read_buffer[2];80};8182static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)83{84state->i2c_write_buffer[0] = reg;8586memset(state->msg, 0, 2 * sizeof(struct i2c_msg));87state->msg[0].addr = state->cfg->i2c_address;88state->msg[0].flags = 0;89state->msg[0].buf = state->i2c_write_buffer;90state->msg[0].len = 1;91state->msg[1].addr = state->cfg->i2c_address;92state->msg[1].flags = I2C_M_RD;93state->msg[1].buf = state->i2c_read_buffer;94state->msg[1].len = 2;9596if (i2c_transfer(state->i2c, state->msg, 2) != 2) {97printk(KERN_WARNING "DiB0070 I2C read failed\n");98return 0;99}100return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];101}102103static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)104{105state->i2c_write_buffer[0] = reg;106state->i2c_write_buffer[1] = val >> 8;107state->i2c_write_buffer[2] = val & 0xff;108109memset(state->msg, 0, sizeof(struct i2c_msg));110state->msg[0].addr = state->cfg->i2c_address;111state->msg[0].flags = 0;112state->msg[0].buf = state->i2c_write_buffer;113state->msg[0].len = 3;114115if (i2c_transfer(state->i2c, state->msg, 1) != 1) {116printk(KERN_WARNING "DiB0070 I2C write failed\n");117return -EREMOTEIO;118}119return 0;120}121122#define HARD_RESET(state) do { \123state->cfg->sleep(state->fe, 0); \124if (state->cfg->reset) { \125state->cfg->reset(state->fe,1); msleep(10); \126state->cfg->reset(state->fe,0); msleep(10); \127} \128} while (0)129130static int dib0070_set_bandwidth(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)131{132struct dib0070_state *state = fe->tuner_priv;133u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff;134135if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000)136tmp |= (0 << 14);137else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000)138tmp |= (1 << 14);139else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000)140tmp |= (2 << 14);141else142tmp |= (3 << 14);143144dib0070_write_reg(state, 0x02, tmp);145146/* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */147if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) {148u16 value = dib0070_read_reg(state, 0x17);149150dib0070_write_reg(state, 0x17, value & 0xfffc);151tmp = dib0070_read_reg(state, 0x01) & 0x01ff;152dib0070_write_reg(state, 0x01, tmp | (60 << 9));153154dib0070_write_reg(state, 0x17, value);155}156return 0;157}158159static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state)160{161int8_t step_sign;162u16 adc;163int ret = 0;164165if (*tune_state == CT_TUNER_STEP_0) {166167dib0070_write_reg(state, 0x0f, 0xed10);168dib0070_write_reg(state, 0x17, 0x0034);169170dib0070_write_reg(state, 0x18, 0x0032);171state->step = state->captrim = state->fcaptrim = 64;172state->adc_diff = 3000;173ret = 20;174175*tune_state = CT_TUNER_STEP_1;176} else if (*tune_state == CT_TUNER_STEP_1) {177state->step /= 2;178dib0070_write_reg(state, 0x14, state->lo4 | state->captrim);179ret = 15;180181*tune_state = CT_TUNER_STEP_2;182} else if (*tune_state == CT_TUNER_STEP_2) {183184adc = dib0070_read_reg(state, 0x19);185186dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc*(u32)1800/(u32)1024);187188if (adc >= 400) {189adc -= 400;190step_sign = -1;191} else {192adc = 400 - adc;193step_sign = 1;194}195196if (adc < state->adc_diff) {197dprintk("CAPTRIM=%hd is closer to target (%hd/%hd)", state->captrim, adc, state->adc_diff);198state->adc_diff = adc;199state->fcaptrim = state->captrim;200201202203}204state->captrim += (step_sign * state->step);205206if (state->step >= 1)207*tune_state = CT_TUNER_STEP_1;208else209*tune_state = CT_TUNER_STEP_3;210211} else if (*tune_state == CT_TUNER_STEP_3) {212dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim);213dib0070_write_reg(state, 0x18, 0x07ff);214*tune_state = CT_TUNER_STEP_4;215}216217return ret;218}219220static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt)221{222struct dib0070_state *state = fe->tuner_priv;223u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0);224dprintk("CTRL_LO5: 0x%x", lo5);225return dib0070_write_reg(state, 0x15, lo5);226}227228void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)229{230struct dib0070_state *state = fe->tuner_priv;231232if (open) {233dib0070_write_reg(state, 0x1b, 0xff00);234dib0070_write_reg(state, 0x1a, 0x0000);235} else {236dib0070_write_reg(state, 0x1b, 0x4112);237if (state->cfg->vga_filter != 0) {238dib0070_write_reg(state, 0x1a, state->cfg->vga_filter);239dprintk("vga filter register is set to %x", state->cfg->vga_filter);240} else241dib0070_write_reg(state, 0x1a, 0x0009);242}243}244245EXPORT_SYMBOL(dib0070_ctrl_agc_filter);246struct dib0070_tuning {247u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */248u8 switch_trim;249u8 vco_band;250u8 hfdiv;251u8 vco_multi;252u8 presc;253u8 wbdmux;254u16 tuner_enable;255};256257struct dib0070_lna_match {258u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */259u8 lna_band;260};261262static const struct dib0070_tuning dib0070s_tuning_table[] = {263{ 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */264{ 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 },265{ 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 },266{ 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */267{ 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },268{ 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },269{ 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */270};271272static const struct dib0070_tuning dib0070_tuning_table[] = {273{ 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */274{ 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */275{ 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 },276{ 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 },277{ 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */278{ 699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800 },279{ 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 },280{ 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */281};282283static const struct dib0070_lna_match dib0070_lna_flip_chip[] = {284{ 180000, 0 }, /* VHF */285{ 188000, 1 },286{ 196400, 2 },287{ 250000, 3 },288{ 550000, 0 }, /* UHF */289{ 590000, 1 },290{ 666000, 3 },291{ 864000, 5 },292{ 1500000, 0 }, /* LBAND or everything higher than UHF */293{ 1600000, 1 },294{ 2000000, 3 },295{ 0xffffffff, 7 },296};297298static const struct dib0070_lna_match dib0070_lna[] = {299{ 180000, 0 }, /* VHF */300{ 188000, 1 },301{ 196400, 2 },302{ 250000, 3 },303{ 550000, 2 }, /* UHF */304{ 650000, 3 },305{ 750000, 5 },306{ 850000, 6 },307{ 864000, 7 },308{ 1500000, 0 }, /* LBAND or everything higher than UHF */309{ 1600000, 1 },310{ 2000000, 3 },311{ 0xffffffff, 7 },312};313314#define LPF 100315static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)316{317struct dib0070_state *state = fe->tuner_priv;318319const struct dib0070_tuning *tune;320const struct dib0070_lna_match *lna_match;321322enum frontend_tune_state *tune_state = &state->tune_state;323int ret = 10; /* 1ms is the default delay most of the time */324325u8 band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000);326u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf);327328#ifdef CONFIG_SYS_ISDBT329if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)330if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2)331&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))332|| (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)333&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2)))334|| (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)335&& (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))))336freq += 850;337#endif338if (state->current_rf != freq) {339340switch (state->revision) {341case DIB0070S_P1A:342tune = dib0070s_tuning_table;343lna_match = dib0070_lna;344break;345default:346tune = dib0070_tuning_table;347if (state->cfg->flip_chip)348lna_match = dib0070_lna_flip_chip;349else350lna_match = dib0070_lna;351break;352}353while (freq > tune->max_freq) /* find the right one */354tune++;355while (freq > lna_match->max_freq) /* find the right one */356lna_match++;357358state->current_tune_table_index = tune;359state->lna_match = lna_match;360}361362if (*tune_state == CT_TUNER_START) {363dprintk("Tuning for Band: %hd (%d kHz)", band, freq);364if (state->current_rf != freq) {365u8 REFDIV;366u32 FBDiv, Rest, FREF, VCOF_kHz;367u8 Den;368369state->current_rf = freq;370state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);371372373dib0070_write_reg(state, 0x17, 0x30);374375376VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;377378switch (band) {379case BAND_VHF:380REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000);381break;382case BAND_FM:383REFDIV = (u8) ((state->cfg->clock_khz) / 1000);384break;385default:386REFDIV = (u8) (state->cfg->clock_khz / 10000);387break;388}389FREF = state->cfg->clock_khz / REFDIV;390391392393switch (state->revision) {394case DIB0070S_P1A:395FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF);396Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF;397break;398399case DIB0070_P1G:400case DIB0070_P1F:401default:402FBDiv = (freq / (FREF / 2));403Rest = 2 * freq - FBDiv * FREF;404break;405}406407if (Rest < LPF)408Rest = 0;409else if (Rest < 2 * LPF)410Rest = 2 * LPF;411else if (Rest > (FREF - LPF)) {412Rest = 0;413FBDiv += 1;414} else if (Rest > (FREF - 2 * LPF))415Rest = FREF - 2 * LPF;416Rest = (Rest * 6528) / (FREF / 10);417418Den = 1;419if (Rest > 0) {420state->lo4 |= (1 << 14) | (1 << 12);421Den = 255;422}423424425dib0070_write_reg(state, 0x11, (u16)FBDiv);426dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV);427dib0070_write_reg(state, 0x13, (u16) Rest);428429if (state->revision == DIB0070S_P1A) {430431if (band == BAND_SBAND) {432dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);433dib0070_write_reg(state, 0x1d, 0xFFFF);434} else435dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1);436}437438dib0070_write_reg(state, 0x20,4390x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);440441dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF);442dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest);443dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1);444dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv);445dprintk("VCO = %hd", state->current_tune_table_index->vco_band);446dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq);447448*tune_state = CT_TUNER_STEP_0;449} else { /* we are already tuned to this frequency - the configuration is correct */450ret = 50; /* wakeup time */451*tune_state = CT_TUNER_STEP_5;452}453} else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {454455ret = dib0070_captrim(state, tune_state);456457} else if (*tune_state == CT_TUNER_STEP_4) {458const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;459if (tmp != NULL) {460while (freq/1000 > tmp->freq) /* find the right one */461tmp++;462dib0070_write_reg(state, 0x0f,463(0 << 15) | (1 << 14) | (3 << 12)464| (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7)465| (state->current_tune_table_index->wbdmux << 0));466state->wbd_gain_current = tmp->wbd_gain_val;467} else {468dib0070_write_reg(state, 0x0f,469(0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (state->current_tune_table_index->470wbdmux << 0));471state->wbd_gain_current = 6;472}473474dib0070_write_reg(state, 0x06, 0x3fff);475dib0070_write_reg(state, 0x07,476(state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0));477dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127));478dib0070_write_reg(state, 0x0d, 0x0d80);479480481dib0070_write_reg(state, 0x18, 0x07ff);482dib0070_write_reg(state, 0x17, 0x0033);483484485*tune_state = CT_TUNER_STEP_5;486} else if (*tune_state == CT_TUNER_STEP_5) {487dib0070_set_bandwidth(fe, ch);488*tune_state = CT_TUNER_STOP;489} else {490ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */491}492return ret;493}494495496static int dib0070_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)497{498struct dib0070_state *state = fe->tuner_priv;499uint32_t ret;500501state->tune_state = CT_TUNER_START;502503do {504ret = dib0070_tune_digital(fe, p);505if (ret != FE_CALLBACK_TIME_NEVER)506msleep(ret/10);507else508break;509} while (state->tune_state != CT_TUNER_STOP);510511return 0;512}513514static int dib0070_wakeup(struct dvb_frontend *fe)515{516struct dib0070_state *state = fe->tuner_priv;517if (state->cfg->sleep)518state->cfg->sleep(fe, 0);519return 0;520}521522static int dib0070_sleep(struct dvb_frontend *fe)523{524struct dib0070_state *state = fe->tuner_priv;525if (state->cfg->sleep)526state->cfg->sleep(fe, 1);527return 0;528}529530u8 dib0070_get_rf_output(struct dvb_frontend *fe)531{532struct dib0070_state *state = fe->tuner_priv;533return (dib0070_read_reg(state, 0x07) >> 11) & 0x3;534}535EXPORT_SYMBOL(dib0070_get_rf_output);536537int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no)538{539struct dib0070_state *state = fe->tuner_priv;540u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff;541if (no > 3)542no = 3;543if (no < 1)544no = 1;545return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11));546}547EXPORT_SYMBOL(dib0070_set_rf_output);548549static const u16 dib0070_p1f_defaults[] =550551{5527, 0x02,5530x0008,5540x0000,5550x0000,5560x0000,5570x0000,5580x0002,5590x0100,5605613, 0x0d,5620x0d80,5630x0001,5640x0000,5655664, 0x11,5670x0000,5680x0103,5690x0000,5700x0000,5715723, 0x16,5730x0004 | 0x0040,5740x0030,5750x07ff,5765776, 0x1b,5780x4112,5790xff00,5800xc07f,5810x0000,5820x0180,5830x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,5845850,586};587588static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain)589{590u16 tuner_en = dib0070_read_reg(state, 0x20);591u16 offset;592593dib0070_write_reg(state, 0x18, 0x07ff);594dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);595dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0));596msleep(9);597offset = dib0070_read_reg(state, 0x19);598dib0070_write_reg(state, 0x20, tuner_en);599return offset;600}601602static void dib0070_wbd_offset_calibration(struct dib0070_state *state)603{604u8 gain;605for (gain = 6; gain < 8; gain++) {606state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2);607dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]);608}609}610611u16 dib0070_wbd_offset(struct dvb_frontend *fe)612{613struct dib0070_state *state = fe->tuner_priv;614const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;615u32 freq = fe->dtv_property_cache.frequency/1000;616617if (tmp != NULL) {618while (freq/1000 > tmp->freq) /* find the right one */619tmp++;620state->wbd_gain_current = tmp->wbd_gain_val;621} else622state->wbd_gain_current = 6;623624return state->wbd_offset_3_3[state->wbd_gain_current - 6];625}626EXPORT_SYMBOL(dib0070_wbd_offset);627628#define pgm_read_word(w) (*w)629static int dib0070_reset(struct dvb_frontend *fe)630{631struct dib0070_state *state = fe->tuner_priv;632u16 l, r, *n;633634HARD_RESET(state);635636637#ifndef FORCE_SBAND_TUNER638if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1)639state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff;640else641#else642#warning forcing SBAND643#endif644state->revision = DIB0070S_P1A;645646/* P1F or not */647dprintk("Revision: %x", state->revision);648649if (state->revision == DIB0070_P1D) {650dprintk("Error: this driver is not to be used meant for P1D or earlier");651return -EINVAL;652}653654n = (u16 *) dib0070_p1f_defaults;655l = pgm_read_word(n++);656while (l) {657r = pgm_read_word(n++);658do {659dib0070_write_reg(state, (u8)r, pgm_read_word(n++));660r++;661} while (--l);662l = pgm_read_word(n++);663}664665if (state->cfg->force_crystal_mode != 0)666r = state->cfg->force_crystal_mode;667else if (state->cfg->clock_khz >= 24000)668r = 1;669else670r = 2;671672673r |= state->cfg->osc_buffer_state << 3;674675dib0070_write_reg(state, 0x10, r);676dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5));677678if (state->cfg->invert_iq) {679r = dib0070_read_reg(state, 0x02) & 0xffdf;680dib0070_write_reg(state, 0x02, r | (1 << 5));681}682683if (state->revision == DIB0070S_P1A)684dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);685else686dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter);687688dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8);689690dib0070_wbd_offset_calibration(state);691692return 0;693}694695static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency)696{697struct dib0070_state *state = fe->tuner_priv;698699*frequency = 1000 * state->current_rf;700return 0;701}702703static int dib0070_release(struct dvb_frontend *fe)704{705kfree(fe->tuner_priv);706fe->tuner_priv = NULL;707return 0;708}709710static const struct dvb_tuner_ops dib0070_ops = {711.info = {712.name = "DiBcom DiB0070",713.frequency_min = 45000000,714.frequency_max = 860000000,715.frequency_step = 1000,716},717.release = dib0070_release,718719.init = dib0070_wakeup,720.sleep = dib0070_sleep,721.set_params = dib0070_tune,722723.get_frequency = dib0070_get_frequency,724// .get_bandwidth = dib0070_get_bandwidth725};726727struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)728{729struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL);730if (state == NULL)731return NULL;732733state->cfg = cfg;734state->i2c = i2c;735state->fe = fe;736fe->tuner_priv = state;737738if (dib0070_reset(fe) != 0)739goto free_mem;740741printk(KERN_INFO "DiB0070: successfully identified\n");742memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));743744fe->tuner_priv = state;745return fe;746747free_mem:748kfree(state);749fe->tuner_priv = NULL;750return NULL;751}752EXPORT_SYMBOL(dib0070_attach);753754MODULE_AUTHOR("Patrick Boettcher <[email protected]>");755MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner");756MODULE_LICENSE("GPL");757758759