tda18271-fe.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,227 行 · 第 1/2 页

C
1,227
字号
}static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe){	struct tda18271_priv *priv = fe->tuner_priv;	unsigned int i;	int ret;	tda_info("tda18271: performing RF tracking filter calibration\n");	/* wait for die temperature stabilization */	msleep(200);	ret = tda18271_powerscan_init(fe);	if (tda_fail(ret))		goto fail;	/* rf band calibration */	for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) {		ret =		tda18271_rf_tracking_filters_init(fe, 1000 *						  priv->rf_cal_state[i].rfmax);		if (tda_fail(ret))			goto fail;	}	priv->tm_rfcal = tda18271_read_thermometer(fe);fail:	return ret;}/* ------------------------------------------------------------------ */static int tda18271c2_rf_cal_init(struct dvb_frontend *fe){	struct tda18271_priv *priv = fe->tuner_priv;	unsigned char *regs = priv->tda18271_regs;	int ret;	/* test RF_CAL_OK to see if we need init */	if ((regs[R_EP1] & 0x10) == 0)		priv->cal_initialized = false;	if (priv->cal_initialized)		return 0;	ret = tda18271_calc_rf_filter_curve(fe);	if (tda_fail(ret))		goto fail;	ret = tda18271_por(fe);	if (tda_fail(ret))		goto fail;	tda_info("tda18271: RF tracking filter calibration complete\n");	priv->cal_initialized = true;	goto end;fail:	tda_info("tda18271: RF tracking filter calibration failed!\n");end:	return ret;}static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe,						     u32 freq, u32 bw){	struct tda18271_priv *priv = fe->tuner_priv;	unsigned char *regs = priv->tda18271_regs;	int ret;	u32 N = 0;	/* calculate bp filter */	tda18271_calc_bp_filter(fe, &freq);	tda18271_write_regs(fe, R_EP1, 1);	regs[R_EB4]  &= 0x07;	regs[R_EB4]  |= 0x60;	tda18271_write_regs(fe, R_EB4, 1);	regs[R_EB7]   = 0x60;	tda18271_write_regs(fe, R_EB7, 1);	regs[R_EB14]  = 0x00;	tda18271_write_regs(fe, R_EB14, 1);	regs[R_EB20]  = 0xcc;	tda18271_write_regs(fe, R_EB20, 1);	/* set cal mode to RF tracking filter calibration */	regs[R_EP4]  |= 0x03;	/* calculate cal pll */	switch (priv->mode) {	case TDA18271_ANALOG:		N = freq - 1250000;		break;	case TDA18271_DIGITAL:		N = freq + bw / 2;		break;	}	tda18271_calc_cal_pll(fe, N);	/* calculate main pll */	switch (priv->mode) {	case TDA18271_ANALOG:		N = freq - 250000;		break;	case TDA18271_DIGITAL:		N = freq + bw / 2 + 1000000;		break;	}	tda18271_calc_main_pll(fe, N);	ret = tda18271_write_regs(fe, R_EP3, 11);	if (tda_fail(ret))		return ret;	msleep(5); /* RF tracking filter calibration initialization */	/* search for K,M,CO for RF calibration */	tda18271_calc_km(fe, &freq);	tda18271_write_regs(fe, R_EB13, 1);	/* search for rf band */	tda18271_calc_rf_band(fe, &freq);	/* search for gain taper */	tda18271_calc_gain_taper(fe, &freq);	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);	regs[R_EB4]  &= 0x07;	regs[R_EB4]  |= 0x40;	tda18271_write_regs(fe, R_EB4, 1);	regs[R_EB7]   = 0x40;	tda18271_write_regs(fe, R_EB7, 1);	msleep(10); /* pll locking */	regs[R_EB20]  = 0xec;	tda18271_write_regs(fe, R_EB20, 1);	msleep(60); /* RF tracking filter calibration completion */	regs[R_EP4]  &= ~0x03; /* set cal mode to normal */	tda18271_write_regs(fe, R_EP4, 1);	tda18271_write_regs(fe, R_EP1, 1);	/* RF tracking filter correction for VHF_Low band */	if (0 == tda18271_calc_rf_cal(fe, &freq))		tda18271_write_regs(fe, R_EB14, 1);	return 0;}/* ------------------------------------------------------------------ */static int tda18271_ir_cal_init(struct dvb_frontend *fe){	struct tda18271_priv *priv = fe->tuner_priv;	unsigned char *regs = priv->tda18271_regs;	int ret;	ret = tda18271_read_regs(fe);	if (tda_fail(ret))		goto fail;	/* test IR_CAL_OK to see if we need init */	if ((regs[R_EP1] & 0x08) == 0)		ret = tda18271_init_regs(fe);fail:	return ret;}static int tda18271_init(struct dvb_frontend *fe){	struct tda18271_priv *priv = fe->tuner_priv;	int ret;	mutex_lock(&priv->lock);	/* power up */	ret = tda18271_set_standby_mode(fe, 0, 0, 0);	if (tda_fail(ret))		goto fail;	/* initialization */	ret = tda18271_ir_cal_init(fe);	if (tda_fail(ret))		goto fail;	if (priv->id == TDA18271HDC2)		tda18271c2_rf_cal_init(fe);fail:	mutex_unlock(&priv->lock);	return ret;}static int tda18271_tune(struct dvb_frontend *fe,			 struct tda18271_std_map_item *map, u32 freq, u32 bw){	struct tda18271_priv *priv = fe->tuner_priv;	int ret;	tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n",		freq, map->if_freq, bw, map->agc_mode, map->std);	ret = tda18271_init(fe);	if (tda_fail(ret))		goto fail;	mutex_lock(&priv->lock);	switch (priv->id) {	case TDA18271HDC1:		tda18271c1_rf_tracking_filter_calibration(fe, freq, bw);		break;	case TDA18271HDC2:		tda18271c2_rf_tracking_filters_correction(fe, freq);		break;	}	ret = tda18271_channel_configuration(fe, map, freq, bw);	mutex_unlock(&priv->lock);fail:	return ret;}/* ------------------------------------------------------------------ */static int tda18271_set_params(struct dvb_frontend *fe,			       struct dvb_frontend_parameters *params){	struct tda18271_priv *priv = fe->tuner_priv;	struct tda18271_std_map *std_map = &priv->std;	struct tda18271_std_map_item *map;	int ret;	u32 bw, freq = params->frequency;	priv->mode = TDA18271_DIGITAL;	if (fe->ops.info.type == FE_ATSC) {		switch (params->u.vsb.modulation) {		case VSB_8:		case VSB_16:			map = &std_map->atsc_6;			break;		case QAM_64:		case QAM_256:			map = &std_map->qam_6;			break;		default:			tda_warn("modulation not set!\n");			return -EINVAL;		}#if 0 /* keep */		/* userspace request is already center adjusted */		freq += 1750000; /* Adjust to center (+1.75MHZ) */#endif		bw = 6000000;	} else if (fe->ops.info.type == FE_OFDM) {		switch (params->u.ofdm.bandwidth) {		case BANDWIDTH_6_MHZ:			bw = 6000000;			map = &std_map->dvbt_6;			break;		case BANDWIDTH_7_MHZ:			bw = 7000000;			map = &std_map->dvbt_7;			break;		case BANDWIDTH_8_MHZ:			bw = 8000000;			map = &std_map->dvbt_8;			break;		default:			tda_warn("bandwidth not set!\n");			return -EINVAL;		}	} else {		tda_warn("modulation type not supported!\n");		return -EINVAL;	}	/* When tuning digital, the analog demod must be tri-stated */	if (fe->ops.analog_ops.standby)		fe->ops.analog_ops.standby(fe);	ret = tda18271_tune(fe, map, freq, bw);	if (tda_fail(ret))		goto fail;	priv->frequency = freq;	priv->bandwidth = (fe->ops.info.type == FE_OFDM) ?		params->u.ofdm.bandwidth : 0;fail:	return ret;}static int tda18271_set_analog_params(struct dvb_frontend *fe,				      struct analog_parameters *params){	struct tda18271_priv *priv = fe->tuner_priv;	struct tda18271_std_map *std_map = &priv->std;	struct tda18271_std_map_item *map;	char *mode;	int ret;	u32 freq = params->frequency * 62500;	priv->mode = TDA18271_ANALOG;	if (params->mode == V4L2_TUNER_RADIO) {		freq = freq / 1000;		map = &std_map->fm_radio;		mode = "fm";	} else if (params->std & V4L2_STD_MN) {		map = &std_map->atv_mn;		mode = "MN";	} else if (params->std & V4L2_STD_B) {		map = &std_map->atv_b;		mode = "B";	} else if (params->std & V4L2_STD_GH) {		map = &std_map->atv_gh;		mode = "GH";	} else if (params->std & V4L2_STD_PAL_I) {		map = &std_map->atv_i;		mode = "I";	} else if (params->std & V4L2_STD_DK) {		map = &std_map->atv_dk;		mode = "DK";	} else if (params->std & V4L2_STD_SECAM_L) {		map = &std_map->atv_l;		mode = "L";	} else if (params->std & V4L2_STD_SECAM_LC) {		map = &std_map->atv_lc;		mode = "L'";	} else {		map = &std_map->atv_i;		mode = "xx";	}	tda_dbg("setting tda18271 to system %s\n", mode);	ret = tda18271_tune(fe, map, freq, 0);	if (tda_fail(ret))		goto fail;	priv->frequency = freq;	priv->bandwidth = 0;fail:	return ret;}static int tda18271_sleep(struct dvb_frontend *fe){	struct tda18271_priv *priv = fe->tuner_priv;	int ret;	mutex_lock(&priv->lock);	/* standby mode w/ slave tuner output	 * & loop thru & xtal oscillator on */	ret = tda18271_set_standby_mode(fe, 1, 0, 0);	mutex_unlock(&priv->lock);	return ret;}static int tda18271_release(struct dvb_frontend *fe){	struct tda18271_priv *priv = fe->tuner_priv;	mutex_lock(&tda18271_list_mutex);	if (priv)		hybrid_tuner_release_state(priv);	mutex_unlock(&tda18271_list_mutex);	fe->tuner_priv = NULL;	return 0;}static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency){	struct tda18271_priv *priv = fe->tuner_priv;	*frequency = priv->frequency;	return 0;}static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth){	struct tda18271_priv *priv = fe->tuner_priv;	*bandwidth = priv->bandwidth;	return 0;}/* ------------------------------------------------------------------ */#define tda18271_update_std(std_cfg, name) do {				\	if (map->std_cfg.if_freq +					\		map->std_cfg.agc_mode + map->std_cfg.std +		\		map->std_cfg.if_lvl + map->std_cfg.rfagc_top > 0) {	\		tda_dbg("Using custom std config for %s\n", name);	\		memcpy(&std->std_cfg, &map->std_cfg,			\			sizeof(struct tda18271_std_map_item));		\	} } while (0)#define tda18271_dump_std_item(std_cfg, name) do {			\	tda_dbg("(%s) if_freq = %d, agc_mode = %d, std = %d, "		\		"if_lvl = %d, rfagc_top = 0x%02x\n",			\		name, std->std_cfg.if_freq,				\		std->std_cfg.agc_mode, std->std_cfg.std,		\		std->std_cfg.if_lvl, std->std_cfg.rfagc_top);		\	} while (0)static int tda18271_dump_std_map(struct dvb_frontend *fe){	struct tda18271_priv *priv = fe->tuner_priv;	struct tda18271_std_map *std = &priv->std;	tda_dbg("========== STANDARD MAP SETTINGS ==========\n");	tda18271_dump_std_item(fm_radio, "  fm  ");	tda18271_dump_std_item(atv_b,  "atv b ");	tda18271_dump_std_item(atv_dk, "atv dk");	tda18271_dump_std_item(atv_gh, "atv gh");	tda18271_dump_std_item(atv_i,  "atv i ");	tda18271_dump_std_item(atv_l,  "atv l ");	tda18271_dump_std_item(atv_lc, "atv l'");	tda18271_dump_std_item(atv_mn, "atv mn");	tda18271_dump_std_item(atsc_6, "atsc 6");	tda18271_dump_std_item(dvbt_6, "dvbt 6");	tda18271_dump_std_item(dvbt_7, "dvbt 7");	tda18271_dump_std_item(dvbt_8, "dvbt 8");	tda18271_dump_std_item(qam_6,  "qam 6 ");	tda18271_dump_std_item(qam_8,  "qam 8 ");	return 0;}static int tda18271_update_std_map(struct dvb_frontend *fe,				   struct tda18271_std_map *map){	struct tda18271_priv *priv = fe->tuner_priv;	struct tda18271_std_map *std = &priv->std;	if (!map)		return -EINVAL;	tda18271_update_std(fm_radio, "fm");	tda18271_update_std(atv_b,  "atv b");	tda18271_update_std(atv_dk, "atv dk");	tda18271_update_std(atv_gh, "atv gh");	tda18271_update_std(atv_i,  "atv i");	tda18271_update_std(atv_l,  "atv l");	tda18271_update_std(atv_lc, "atv l'");	tda18271_update_std(atv_mn, "atv mn");	tda18271_update_std(atsc_6, "atsc 6");	tda18271_update_std(dvbt_6, "dvbt 6");	tda18271_update_std(dvbt_7, "dvbt 7");	tda18271_update_std(dvbt_8, "dvbt 8");	tda18271_update_std(qam_6,  "qam 6");	tda18271_update_std(qam_8,  "qam 8");	return 0;}static int tda18271_get_id(struct dvb_frontend *fe){	struct tda18271_priv *priv = fe->tuner_priv;	unsigned char *regs = priv->tda18271_regs;	char *name;	int ret = 0;	mutex_lock(&priv->lock);	tda18271_read_regs(fe);	mutex_unlock(&priv->lock);	switch (regs[R_ID] & 0x7f) {	case 3:		name = "TDA18271HD/C1";		priv->id = TDA18271HDC1;		break;	case 4:		name = "TDA18271HD/C2";		priv->id = TDA18271HDC2;		break;	default:		name = "Unknown device";		ret = -EINVAL;		break;	}	tda_info("%s detected @ %d-%04x%s\n", name,		 i2c_adapter_id(priv->i2c_props.adap),		 priv->i2c_props.addr,		 (0 == ret) ? "" : ", device not supported.");	return ret;}static struct dvb_tuner_ops tda18271_tuner_ops = {	.info = {		.name = "NXP TDA18271HD",		.frequency_min  =  45000000,		.frequency_max  = 864000000,		.frequency_step =     62500	},	.init              = tda18271_init,	.sleep             = tda18271_sleep,	.set_params        = tda18271_set_params,	.set_analog_params = tda18271_set_analog_params,	.release           = tda18271_release,	.get_frequency     = tda18271_get_frequency,	.get_bandwidth     = tda18271_get_bandwidth,};struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,				     struct i2c_adapter *i2c,				     struct tda18271_config *cfg){	struct tda18271_priv *priv = NULL;	int instance;	mutex_lock(&tda18271_list_mutex);	instance = hybrid_tuner_request_state(struct tda18271_priv, priv,					      hybrid_tuner_instance_list,					      i2c, addr, "tda18271");	switch (instance) {	case 0:		goto fail;	case 1:		/* new tuner instance */		priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;		priv->role = (cfg) ? cfg->role : TDA18271_MASTER;		priv->cal_initialized = false;		mutex_init(&priv->lock);		fe->tuner_priv = priv;		if (cfg)			priv->small_i2c = cfg->small_i2c;		if (tda_fail(tda18271_get_id(fe)))			goto fail;		if (tda_fail(tda18271_assign_map_layout(fe)))			goto fail;		mutex_lock(&priv->lock);		tda18271_init_regs(fe);		if ((tda18271_cal_on_startup) && (priv->id == TDA18271HDC2))			tda18271c2_rf_cal_init(fe);		mutex_unlock(&priv->lock);		break;	default:		/* existing tuner instance */		fe->tuner_priv = priv;		/* allow dvb driver to override i2c gate setting */		if ((cfg) && (cfg->gate != TDA18271_GATE_ANALOG))			priv->gate = cfg->gate;		break;	}	/* override default std map with values in config struct */	if ((cfg) && (cfg->std_map))		tda18271_update_std_map(fe, cfg->std_map);	mutex_unlock(&tda18271_list_mutex);	memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops,	       sizeof(struct dvb_tuner_ops));	if (tda18271_debug & (DBG_MAP | DBG_ADV))		tda18271_dump_std_map(fe);	return fe;fail:	mutex_unlock(&tda18271_list_mutex);	tda18271_release(fe);	return NULL;}EXPORT_SYMBOL_GPL(tda18271_attach);MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");MODULE_LICENSE("GPL");MODULE_VERSION("0.3");/* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- * Local variables: * c-basic-offset: 8 * End: */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?