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 + -
显示快捷键?