tda18271-fe.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,227 行 · 第 1/2 页
C
1,227 行
/* tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> 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; 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.*/#include <linux/delay.h>#include "compat.h"#include <linux/videodev2.h>#include "tda18271-priv.h"int tda18271_debug;module_param_named(debug, tda18271_debug, int, 0644);MODULE_PARM_DESC(debug, "set debug level " "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))");static int tda18271_cal_on_startup;module_param_named(cal, tda18271_cal_on_startup, int, 0644);MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");static DEFINE_MUTEX(tda18271_list_mutex);static LIST_HEAD(hybrid_tuner_instance_list);/*---------------------------------------------------------------------*/static inline int charge_pump_source(struct dvb_frontend *fe, int force){ struct tda18271_priv *priv = fe->tuner_priv; return tda18271_charge_pump_source(fe, (priv->role == TDA18271_SLAVE) ? TDA18271_CAL_PLL : TDA18271_MAIN_PLL, force);}static inline void tda18271_set_if_notch(struct dvb_frontend *fe){ struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; switch (priv->mode) { case TDA18271_ANALOG: regs[R_MPD] &= ~0x80; /* IF notch = 0 */ break; case TDA18271_DIGITAL: regs[R_MPD] |= 0x80; /* IF notch = 1 */ break; }}static int tda18271_channel_configuration(struct dvb_frontend *fe, struct tda18271_std_map_item *map, u32 freq, u32 bw){ struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; int ret; u32 N; /* update TV broadcast parameters */ /* set standard */ regs[R_EP3] &= ~0x1f; /* clear std bits */ regs[R_EP3] |= (map->agc_mode << 3) | map->std; if (priv->id == TDA18271HDC2) { /* set rfagc to high speed mode */ regs[R_EP3] &= ~0x04; } /* set cal mode to normal */ regs[R_EP4] &= ~0x03; /* update IF output level */ regs[R_EP4] &= ~0x1c; /* clear if level bits */ regs[R_EP4] |= (map->if_lvl << 2); /* update FM_RFn */ regs[R_EP4] &= ~0x80; regs[R_EP4] |= map->fm_rfn << 7; /* update rf top / if top */ regs[R_EB22] = 0x00; regs[R_EB22] |= map->rfagc_top; ret = tda18271_write_regs(fe, R_EB22, 1); if (tda_fail(ret)) goto fail; /* --------------------------------------------------------------- */ /* disable Power Level Indicator */ regs[R_EP1] |= 0x40; /* make sure thermometer is off */ regs[R_TM] &= ~0x10; /* frequency dependent parameters */ tda18271_calc_ir_measure(fe, &freq); tda18271_calc_bp_filter(fe, &freq); tda18271_calc_rf_band(fe, &freq); tda18271_calc_gain_taper(fe, &freq); /* --------------------------------------------------------------- */ /* dual tuner and agc1 extra configuration */ switch (priv->role) { case TDA18271_MASTER: regs[R_EB1] |= 0x04; /* main vco */ break; case TDA18271_SLAVE: regs[R_EB1] &= ~0x04; /* cal vco */ break; } /* agc1 always active */ regs[R_EB1] &= ~0x02; /* agc1 has priority on agc2 */ regs[R_EB1] &= ~0x01; ret = tda18271_write_regs(fe, R_EB1, 1); if (tda_fail(ret)) goto fail; /* --------------------------------------------------------------- */ N = map->if_freq * 1000 + freq; switch (priv->role) { case TDA18271_MASTER: tda18271_calc_main_pll(fe, N); tda18271_set_if_notch(fe); tda18271_write_regs(fe, R_MPD, 4); break; case TDA18271_SLAVE: tda18271_calc_cal_pll(fe, N); tda18271_write_regs(fe, R_CPD, 4); regs[R_MPD] = regs[R_CPD] & 0x7f; tda18271_set_if_notch(fe); tda18271_write_regs(fe, R_MPD, 1); break; } ret = tda18271_write_regs(fe, R_TM, 7); if (tda_fail(ret)) goto fail; /* force charge pump source */ charge_pump_source(fe, 1); msleep(1); /* return pll to normal operation */ charge_pump_source(fe, 0); msleep(20); if (priv->id == TDA18271HDC2) { /* set rfagc to normal speed mode */ if (map->fm_rfn) regs[R_EP3] &= ~0x04; else regs[R_EP3] |= 0x04; ret = tda18271_write_regs(fe, R_EP3, 1); }fail: return ret;}static int tda18271_read_thermometer(struct dvb_frontend *fe){ struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; int tm; /* switch thermometer on */ regs[R_TM] |= 0x10; tda18271_write_regs(fe, R_TM, 1); /* read thermometer info */ tda18271_read_regs(fe); if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) || (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) { if ((regs[R_TM] & 0x20) == 0x20) regs[R_TM] &= ~0x20; else regs[R_TM] |= 0x20; tda18271_write_regs(fe, R_TM, 1); msleep(10); /* temperature sensing */ /* read thermometer info */ tda18271_read_regs(fe); } tm = tda18271_lookup_thermometer(fe); /* switch thermometer off */ regs[R_TM] &= ~0x10; tda18271_write_regs(fe, R_TM, 1); /* set CAL mode to normal */ regs[R_EP4] &= ~0x03; tda18271_write_regs(fe, R_EP4, 1); return tm;}/* ------------------------------------------------------------------ */static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, u32 freq){ struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; int tm_current, rfcal_comp, approx, i, ret; u8 dc_over_dt, rf_tab; /* power up */ ret = tda18271_set_standby_mode(fe, 0, 0, 0); if (tda_fail(ret)) goto fail; /* read die current temperature */ tm_current = tda18271_read_thermometer(fe); /* frequency dependent parameters */ tda18271_calc_rf_cal(fe, &freq); rf_tab = regs[R_EB14]; i = tda18271_lookup_rf_band(fe, &freq, NULL); if (tda_fail(i)) return i; if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { approx = map[i].rf_a1 * (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab; } else { approx = map[i].rf_a2 * (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab; } if (approx < 0) approx = 0; if (approx > 255) approx = 255; tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); /* calculate temperature compensation */ rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal); regs[R_EB14] = approx + rfcal_comp; ret = tda18271_write_regs(fe, R_EB14, 1);fail: return ret;}static int tda18271_por(struct dvb_frontend *fe){ struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; int ret; /* power up detector 1 */ regs[R_EB12] &= ~0x20; ret = tda18271_write_regs(fe, R_EB12, 1); if (tda_fail(ret)) goto fail; regs[R_EB18] &= ~0x80; /* turn agc1 loop on */ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ ret = tda18271_write_regs(fe, R_EB18, 1); if (tda_fail(ret)) goto fail; regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */ /* POR mode */ ret = tda18271_set_standby_mode(fe, 1, 0, 0); if (tda_fail(ret)) goto fail; /* disable 1.5 MHz low pass filter */ regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */ regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */ ret = tda18271_write_regs(fe, R_EB21, 3);fail: return ret;}static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq){ struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; u32 N; /* set CAL mode to normal */ regs[R_EP4] &= ~0x03; tda18271_write_regs(fe, R_EP4, 1); /* switch off agc1 */ regs[R_EP3] |= 0x40; /* sm_lt = 1 */ regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */ tda18271_write_regs(fe, R_EB18, 1); /* frequency dependent parameters */ tda18271_calc_bp_filter(fe, &freq); tda18271_calc_gain_taper(fe, &freq); tda18271_calc_rf_band(fe, &freq); tda18271_calc_km(fe, &freq); tda18271_write_regs(fe, R_EP1, 3); tda18271_write_regs(fe, R_EB13, 1); /* main pll charge pump source */ tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1); /* cal pll charge pump source */ tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 1); /* force dcdc converter to 0 V */ regs[R_EB14] = 0x00; tda18271_write_regs(fe, R_EB14, 1); /* disable plls lock */ regs[R_EB20] &= ~0x20; tda18271_write_regs(fe, R_EB20, 1); /* set CAL mode to RF tracking filter calibration */ regs[R_EP4] |= 0x03; tda18271_write_regs(fe, R_EP4, 2); /* --------------------------------------------------------------- */ /* set the internal calibration signal */ N = freq; tda18271_calc_cal_pll(fe, N); tda18271_write_regs(fe, R_CPD, 4); /* downconvert internal calibration */ N += 1000000; tda18271_calc_main_pll(fe, N); tda18271_write_regs(fe, R_MPD, 4); msleep(5); tda18271_write_regs(fe, R_EP2, 1); tda18271_write_regs(fe, R_EP1, 1); tda18271_write_regs(fe, R_EP2, 1); tda18271_write_regs(fe, R_EP1, 1); /* --------------------------------------------------------------- */ /* normal operation for the main pll */ tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0); /* normal operation for the cal pll */ tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 0); msleep(10); /* plls locking */ /* launch the rf tracking filters calibration */ regs[R_EB20] |= 0x20; tda18271_write_regs(fe, R_EB20, 1); msleep(60); /* calibration */ /* --------------------------------------------------------------- */ /* set CAL mode to normal */ regs[R_EP4] &= ~0x03; /* switch on agc1 */ regs[R_EP3] &= ~0x40; /* sm_lt = 0 */ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ tda18271_write_regs(fe, R_EB18, 1); tda18271_write_regs(fe, R_EP3, 2); /* synchronization */ tda18271_write_regs(fe, R_EP1, 1); /* get calibration result */ tda18271_read_extended(fe); return regs[R_EB14];}static int tda18271_powerscan(struct dvb_frontend *fe, u32 *freq_in, u32 *freq_out){ struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; int sgn, bcal, count, wait, ret; u8 cid_target; u16 count_limit; u32 freq; freq = *freq_in; tda18271_calc_rf_band(fe, &freq); tda18271_calc_rf_cal(fe, &freq); tda18271_calc_gain_taper(fe, &freq); tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit); tda18271_write_regs(fe, R_EP2, 1); tda18271_write_regs(fe, R_EB14, 1); /* downconvert frequency */ freq += 1000000; tda18271_calc_main_pll(fe, freq); tda18271_write_regs(fe, R_MPD, 4); msleep(5); /* pll locking */ /* detection mode */ regs[R_EP4] &= ~0x03; regs[R_EP4] |= 0x01; tda18271_write_regs(fe, R_EP4, 1); /* launch power detection measurement */ tda18271_write_regs(fe, R_EP2, 1); /* read power detection info, stored in EB10 */ ret = tda18271_read_extended(fe); if (tda_fail(ret)) return ret; /* algorithm initialization */ sgn = 1; *freq_out = *freq_in; bcal = 0; count = 0; wait = false; while ((regs[R_EB10] & 0x3f) < cid_target) { /* downconvert updated freq to 1 MHz */ freq = *freq_in + (sgn * count) + 1000000; tda18271_calc_main_pll(fe, freq); tda18271_write_regs(fe, R_MPD, 4); if (wait) { msleep(5); /* pll locking */ wait = false; } else udelay(100); /* pll locking */ /* launch power detection measurement */ tda18271_write_regs(fe, R_EP2, 1); /* read power detection info, stored in EB10 */ ret = tda18271_read_extended(fe); if (tda_fail(ret)) return ret; count += 200; if (count <= count_limit) continue; if (sgn <= 0) break; sgn = -1 * sgn; count = 200; wait = true; } if ((regs[R_EB10] & 0x3f) >= cid_target) { bcal = 1; *freq_out = freq - 1000000; } else bcal = 0; tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n", bcal, *freq_in, *freq_out, freq); return bcal;}static int tda18271_powerscan_init(struct dvb_frontend *fe){ struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; int ret; /* set standard to digital */ regs[R_EP3] &= ~0x1f; /* clear std bits */ regs[R_EP3] |= 0x12; /* set cal mode to normal */ regs[R_EP4] &= ~0x03; /* update IF output level */ regs[R_EP4] &= ~0x1c; /* clear if level bits */ ret = tda18271_write_regs(fe, R_EP3, 2); if (tda_fail(ret)) goto fail; regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ ret = tda18271_write_regs(fe, R_EB18, 1); if (tda_fail(ret)) goto fail; regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */ /* 1.5 MHz low pass filter */ regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */ regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */ ret = tda18271_write_regs(fe, R_EB21, 3);fail: return ret;}static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq){ struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; int bcal, rf, i;#define RF1 0#define RF2 1#define RF3 2 u32 rf_default[3]; u32 rf_freq[3]; u8 prog_cal[3]; u8 prog_tab[3]; i = tda18271_lookup_rf_band(fe, &freq, NULL); if (tda_fail(i)) return i; rf_default[RF1] = 1000 * map[i].rf1_def; rf_default[RF2] = 1000 * map[i].rf2_def; rf_default[RF3] = 1000 * map[i].rf3_def; for (rf = RF1; rf <= RF3; rf++) { if (0 == rf_default[rf]) return 0; tda_cal("freq = %d, rf = %d\n", freq, rf); /* look for optimized calibration frequency */ bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]); if (tda_fail(bcal)) return bcal; tda18271_calc_rf_cal(fe, &rf_freq[rf]); prog_tab[rf] = regs[R_EB14]; if (1 == bcal) prog_cal[rf] = tda18271_calibrate_rf(fe, rf_freq[rf]); else prog_cal[rf] = prog_tab[rf]; switch (rf) { case RF1: map[i].rf_a1 = 0; map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1]; map[i].rf1 = rf_freq[RF1] / 1000; break; case RF2: map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] - prog_cal[RF1] + prog_tab[RF1]) / ((rf_freq[RF2] - rf_freq[RF1]) / 1000); map[i].rf2 = rf_freq[RF2] / 1000; break; case RF3: map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] - prog_cal[RF2] + prog_tab[RF2]) / ((rf_freq[RF3] - rf_freq[RF2]) / 1000); map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2]; map[i].rf3 = rf_freq[RF3] / 1000; break; default: BUG(); } } return 0;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?