brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · 1006a27 Raw
617 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Fitipower FC0013 tuner driver4 *5 * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>6 * partially based on driver code from Fitipower7 * Copyright (C) 2010 Fitipower Integrated Technology Inc8 */9 10#include "fc0013.h"11#include "fc0013-priv.h"12 13static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val)14{15	u8 buf[2] = {reg, val};16	struct i2c_msg msg = {17		.addr = priv->addr, .flags = 0, .buf = buf, .len = 218	};19 20	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {21		err("I2C write reg failed, reg: %02x, val: %02x", reg, val);22		return -EREMOTEIO;23	}24	return 0;25}26 27static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val)28{29	struct i2c_msg msg[2] = {30		{ .addr = priv->addr, .flags = 0, .buf = &reg, .len = 1 },31		{ .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 },32	};33 34	if (i2c_transfer(priv->i2c, msg, 2) != 2) {35		err("I2C read reg failed, reg: %02x", reg);36		return -EREMOTEIO;37	}38	return 0;39}40 41static void fc0013_release(struct dvb_frontend *fe)42{43	kfree(fe->tuner_priv);44	fe->tuner_priv = NULL;45}46 47static int fc0013_init(struct dvb_frontend *fe)48{49	struct fc0013_priv *priv = fe->tuner_priv;50	int i, ret = 0;51	unsigned char reg[] = {52		0x00,	/* reg. 0x00: dummy */53		0x09,	/* reg. 0x01 */54		0x16,	/* reg. 0x02 */55		0x00,	/* reg. 0x03 */56		0x00,	/* reg. 0x04 */57		0x17,	/* reg. 0x05 */58		0x02,	/* reg. 0x06 */59		0x0a,	/* reg. 0x07: CHECK */60		0xff,	/* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,61			   Loop Bw 1/8 */62		0x6f,	/* reg. 0x09: enable LoopThrough */63		0xb8,	/* reg. 0x0a: Disable LO Test Buffer */64		0x82,	/* reg. 0x0b: CHECK */65		0xfc,	/* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */66		0x01,	/* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */67		0x00,	/* reg. 0x0e */68		0x00,	/* reg. 0x0f */69		0x00,	/* reg. 0x10 */70		0x00,	/* reg. 0x11 */71		0x00,	/* reg. 0x12 */72		0x00,	/* reg. 0x13 */73		0x50,	/* reg. 0x14: DVB-t High Gain, UHF.74			   Middle Gain: 0x48, Low Gain: 0x40 */75		0x01,	/* reg. 0x15 */76	};77 78	switch (priv->xtal_freq) {79	case FC_XTAL_27_MHZ:80	case FC_XTAL_28_8_MHZ:81		reg[0x07] |= 0x20;82		break;83	case FC_XTAL_36_MHZ:84	default:85		break;86	}87 88	if (priv->dual_master)89		reg[0x0c] |= 0x02;90 91	if (fe->ops.i2c_gate_ctrl)92		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */93 94	for (i = 1; i < sizeof(reg); i++) {95		ret = fc0013_writereg(priv, i, reg[i]);96		if (ret)97			break;98	}99 100	if (fe->ops.i2c_gate_ctrl)101		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */102 103	if (ret)104		err("fc0013_writereg failed: %d", ret);105 106	return ret;107}108 109static int fc0013_sleep(struct dvb_frontend *fe)110{111	/* nothing to do here */112	return 0;113}114 115int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val)116{117	struct fc0013_priv *priv = fe->tuner_priv;118	int ret;119	u8 rc_cal;120	int val;121 122	if (fe->ops.i2c_gate_ctrl)123		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */124 125	/* push rc_cal value, get rc_cal value */126	ret = fc0013_writereg(priv, 0x10, 0x00);127	if (ret)128		goto error_out;129 130	/* get rc_cal value */131	ret = fc0013_readreg(priv, 0x10, &rc_cal);132	if (ret)133		goto error_out;134 135	rc_cal &= 0x0f;136 137	val = (int)rc_cal + rc_val;138 139	/* forcing rc_cal */140	ret = fc0013_writereg(priv, 0x0d, 0x11);141	if (ret)142		goto error_out;143 144	/* modify rc_cal value */145	if (val > 15)146		ret = fc0013_writereg(priv, 0x10, 0x0f);147	else if (val < 0)148		ret = fc0013_writereg(priv, 0x10, 0x00);149	else150		ret = fc0013_writereg(priv, 0x10, (u8)val);151 152error_out:153	if (fe->ops.i2c_gate_ctrl)154		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */155 156	return ret;157}158EXPORT_SYMBOL(fc0013_rc_cal_add);159 160int fc0013_rc_cal_reset(struct dvb_frontend *fe)161{162	struct fc0013_priv *priv = fe->tuner_priv;163	int ret;164 165	if (fe->ops.i2c_gate_ctrl)166		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */167 168	ret = fc0013_writereg(priv, 0x0d, 0x01);169	if (!ret)170		ret = fc0013_writereg(priv, 0x10, 0x00);171 172	if (fe->ops.i2c_gate_ctrl)173		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */174 175	return ret;176}177EXPORT_SYMBOL(fc0013_rc_cal_reset);178 179static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq)180{181	int ret;182	u8 tmp;183 184	ret = fc0013_readreg(priv, 0x1d, &tmp);185	if (ret)186		goto error_out;187	tmp &= 0xe3;188	if (freq <= 177500) {		/* VHF Track: 7 */189		ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c);190	} else if (freq <= 184500) {	/* VHF Track: 6 */191		ret = fc0013_writereg(priv, 0x1d, tmp | 0x18);192	} else if (freq <= 191500) {	/* VHF Track: 5 */193		ret = fc0013_writereg(priv, 0x1d, tmp | 0x14);194	} else if (freq <= 198500) {	/* VHF Track: 4 */195		ret = fc0013_writereg(priv, 0x1d, tmp | 0x10);196	} else if (freq <= 205500) {	/* VHF Track: 3 */197		ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c);198	} else if (freq <= 219500) {	/* VHF Track: 2 */199		ret = fc0013_writereg(priv, 0x1d, tmp | 0x08);200	} else if (freq < 300000) {	/* VHF Track: 1 */201		ret = fc0013_writereg(priv, 0x1d, tmp | 0x04);202	} else {			/* UHF and GPS */203		ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c);204	}205error_out:206	return ret;207}208 209static int fc0013_set_params(struct dvb_frontend *fe)210{211	struct fc0013_priv *priv = fe->tuner_priv;212	int i, ret = 0;213	struct dtv_frontend_properties *p = &fe->dtv_property_cache;214	u32 freq = p->frequency / 1000;215	u32 delsys = p->delivery_system;216	unsigned char reg[7], am, pm, multi, tmp;217	unsigned long f_vco;218	unsigned short xtal_freq_khz_2, xin, xdiv;219	bool vco_select = false;220 221	if (fe->callback) {222		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,223			FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1));224		if (ret)225			goto exit;226	}227 228	switch (priv->xtal_freq) {229	case FC_XTAL_27_MHZ:230		xtal_freq_khz_2 = 27000 / 2;231		break;232	case FC_XTAL_36_MHZ:233		xtal_freq_khz_2 = 36000 / 2;234		break;235	case FC_XTAL_28_8_MHZ:236	default:237		xtal_freq_khz_2 = 28800 / 2;238		break;239	}240 241	if (fe->ops.i2c_gate_ctrl)242		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */243 244	/* set VHF track */245	ret = fc0013_set_vhf_track(priv, freq);246	if (ret)247		goto exit;248 249	if (freq < 300000) {250		/* enable VHF filter */251		ret = fc0013_readreg(priv, 0x07, &tmp);252		if (ret)253			goto exit;254		ret = fc0013_writereg(priv, 0x07, tmp | 0x10);255		if (ret)256			goto exit;257 258		/* disable UHF & disable GPS */259		ret = fc0013_readreg(priv, 0x14, &tmp);260		if (ret)261			goto exit;262		ret = fc0013_writereg(priv, 0x14, tmp & 0x1f);263		if (ret)264			goto exit;265	} else if (freq <= 862000) {266		/* disable VHF filter */267		ret = fc0013_readreg(priv, 0x07, &tmp);268		if (ret)269			goto exit;270		ret = fc0013_writereg(priv, 0x07, tmp & 0xef);271		if (ret)272			goto exit;273 274		/* enable UHF & disable GPS */275		ret = fc0013_readreg(priv, 0x14, &tmp);276		if (ret)277			goto exit;278		ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40);279		if (ret)280			goto exit;281	} else {282		/* disable VHF filter */283		ret = fc0013_readreg(priv, 0x07, &tmp);284		if (ret)285			goto exit;286		ret = fc0013_writereg(priv, 0x07, tmp & 0xef);287		if (ret)288			goto exit;289 290		/* disable UHF & enable GPS */291		ret = fc0013_readreg(priv, 0x14, &tmp);292		if (ret)293			goto exit;294		ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20);295		if (ret)296			goto exit;297	}298 299	/* select frequency divider and the frequency of VCO */300	if (freq < 37084) {		/* freq * 96 < 3560000 */301		multi = 96;302		reg[5] = 0x82;303		reg[6] = 0x00;304	} else if (freq < 55625) {	/* freq * 64 < 3560000 */305		multi = 64;306		reg[5] = 0x02;307		reg[6] = 0x02;308	} else if (freq < 74167) {	/* freq * 48 < 3560000 */309		multi = 48;310		reg[5] = 0x42;311		reg[6] = 0x00;312	} else if (freq < 111250) {	/* freq * 32 < 3560000 */313		multi = 32;314		reg[5] = 0x82;315		reg[6] = 0x02;316	} else if (freq < 148334) {	/* freq * 24 < 3560000 */317		multi = 24;318		reg[5] = 0x22;319		reg[6] = 0x00;320	} else if (freq < 222500) {	/* freq * 16 < 3560000 */321		multi = 16;322		reg[5] = 0x42;323		reg[6] = 0x02;324	} else if (freq < 296667) {	/* freq * 12 < 3560000 */325		multi = 12;326		reg[5] = 0x12;327		reg[6] = 0x00;328	} else if (freq < 445000) {	/* freq * 8 < 3560000 */329		multi = 8;330		reg[5] = 0x22;331		reg[6] = 0x02;332	} else if (freq < 593334) {	/* freq * 6 < 3560000 */333		multi = 6;334		reg[5] = 0x0a;335		reg[6] = 0x00;336	} else if (freq < 950000) {	/* freq * 4 < 3800000 */337		multi = 4;338		reg[5] = 0x12;339		reg[6] = 0x02;340	} else {341		multi = 2;342		reg[5] = 0x0a;343		reg[6] = 0x02;344	}345 346	f_vco = freq * multi;347 348	if (f_vco >= 3060000) {349		reg[6] |= 0x08;350		vco_select = true;351	}352 353	if (freq >= 45000) {354		/* From divided value (XDIV) determined the FA and FP value */355		xdiv = (unsigned short)(f_vco / xtal_freq_khz_2);356		if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2))357			xdiv++;358 359		pm = (unsigned char)(xdiv / 8);360		am = (unsigned char)(xdiv - (8 * pm));361 362		if (am < 2) {363			reg[1] = am + 8;364			reg[2] = pm - 1;365		} else {366			reg[1] = am;367			reg[2] = pm;368		}369	} else {370		/* fix for frequency less than 45 MHz */371		reg[1] = 0x06;372		reg[2] = 0x11;373	}374 375	/* fix clock out */376	reg[6] |= 0x20;377 378	/* From VCO frequency determines the XIN ( fractional part of Delta379	   Sigma PLL) and divided value (XDIV) */380	xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2);381	xin = (xin << 15) / xtal_freq_khz_2;382	if (xin >= 16384)383		xin += 32768;384 385	reg[3] = xin >> 8;386	reg[4] = xin & 0xff;387 388	if (delsys == SYS_DVBT) {389		reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */390		switch (p->bandwidth_hz) {391		case 6000000:392			reg[6] |= 0x80;393			break;394		case 7000000:395			reg[6] |= 0x40;396			break;397		case 8000000:398		default:399			break;400		}401	} else {402		err("%s: modulation type not supported!", __func__);403		return -EINVAL;404	}405 406	/* modified for Realtek demod */407	reg[5] |= 0x07;408 409	for (i = 1; i <= 6; i++) {410		ret = fc0013_writereg(priv, i, reg[i]);411		if (ret)412			goto exit;413	}414 415	ret = fc0013_readreg(priv, 0x11, &tmp);416	if (ret)417		goto exit;418	if (multi == 64)419		ret = fc0013_writereg(priv, 0x11, tmp | 0x04);420	else421		ret = fc0013_writereg(priv, 0x11, tmp & 0xfb);422	if (ret)423		goto exit;424 425	/* VCO Calibration */426	ret = fc0013_writereg(priv, 0x0e, 0x80);427	if (!ret)428		ret = fc0013_writereg(priv, 0x0e, 0x00);429 430	/* VCO Re-Calibration if needed */431	if (!ret)432		ret = fc0013_writereg(priv, 0x0e, 0x00);433 434	if (!ret) {435		msleep(10);436		ret = fc0013_readreg(priv, 0x0e, &tmp);437	}438	if (ret)439		goto exit;440 441	/* vco selection */442	tmp &= 0x3f;443 444	if (vco_select) {445		if (tmp > 0x3c) {446			reg[6] &= ~0x08;447			ret = fc0013_writereg(priv, 0x06, reg[6]);448			if (!ret)449				ret = fc0013_writereg(priv, 0x0e, 0x80);450			if (!ret)451				ret = fc0013_writereg(priv, 0x0e, 0x00);452		}453	} else {454		if (tmp < 0x02) {455			reg[6] |= 0x08;456			ret = fc0013_writereg(priv, 0x06, reg[6]);457			if (!ret)458				ret = fc0013_writereg(priv, 0x0e, 0x80);459			if (!ret)460				ret = fc0013_writereg(priv, 0x0e, 0x00);461		}462	}463 464	priv->frequency = p->frequency;465	priv->bandwidth = p->bandwidth_hz;466 467exit:468	if (fe->ops.i2c_gate_ctrl)469		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */470	if (ret)471		warn("%s: failed: %d", __func__, ret);472	return ret;473}474 475static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency)476{477	struct fc0013_priv *priv = fe->tuner_priv;478	*frequency = priv->frequency;479	return 0;480}481 482static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)483{484	/* always ? */485	*frequency = 0;486	return 0;487}488 489static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)490{491	struct fc0013_priv *priv = fe->tuner_priv;492	*bandwidth = priv->bandwidth;493	return 0;494}495 496#define INPUT_ADC_LEVEL	-8497 498static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength)499{500	struct fc0013_priv *priv = fe->tuner_priv;501	int ret;502	unsigned char tmp;503	int int_temp, lna_gain, int_lna, tot_agc_gain, power;504	static const int fc0013_lna_gain_table[] = {505		/* low gain */506		-63, -58, -99, -73,507		-63, -65, -54, -60,508		/* middle gain */509		 71,  70,  68,  67,510		 65,  63,  61,  58,511		/* high gain */512		197, 191, 188, 186,513		184, 182, 181, 179,514	};515 516	if (fe->ops.i2c_gate_ctrl)517		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */518 519	ret = fc0013_writereg(priv, 0x13, 0x00);520	if (ret)521		goto err;522 523	ret = fc0013_readreg(priv, 0x13, &tmp);524	if (ret)525		goto err;526	int_temp = tmp;527 528	ret = fc0013_readreg(priv, 0x14, &tmp);529	if (ret)530		goto err;531	lna_gain = tmp & 0x1f;532 533	if (fe->ops.i2c_gate_ctrl)534		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */535 536	if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) {537		int_lna = fc0013_lna_gain_table[lna_gain];538		tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 +539				(int_temp & 0x1f)) * 2;540		power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10;541 542		if (power >= 45)543			*strength = 255;	/* 100% */544		else if (power < -95)545			*strength = 0;546		else547			*strength = (power + 95) * 255 / 140;548 549		*strength |= *strength << 8;550	} else {551		ret = -1;552	}553 554	goto exit;555 556err:557	if (fe->ops.i2c_gate_ctrl)558		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */559exit:560	if (ret)561		warn("%s: failed: %d", __func__, ret);562	return ret;563}564 565static const struct dvb_tuner_ops fc0013_tuner_ops = {566	.info = {567		.name		  = "Fitipower FC0013",568 569		.frequency_min_hz =   37 * MHz,	/* estimate */570		.frequency_max_hz = 1680 * MHz,	/* CHECK */571	},572 573	.release	= fc0013_release,574 575	.init		= fc0013_init,576	.sleep		= fc0013_sleep,577 578	.set_params	= fc0013_set_params,579 580	.get_frequency	= fc0013_get_frequency,581	.get_if_frequency = fc0013_get_if_frequency,582	.get_bandwidth	= fc0013_get_bandwidth,583 584	.get_rf_strength = fc0013_get_rf_strength,585};586 587struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,588	struct i2c_adapter *i2c, u8 i2c_address, int dual_master,589	enum fc001x_xtal_freq xtal_freq)590{591	struct fc0013_priv *priv = NULL;592 593	priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL);594	if (priv == NULL)595		return NULL;596 597	priv->i2c = i2c;598	priv->dual_master = dual_master;599	priv->addr = i2c_address;600	priv->xtal_freq = xtal_freq;601 602	info("Fitipower FC0013 successfully attached.");603 604	fe->tuner_priv = priv;605 606	memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops,607		sizeof(struct dvb_tuner_ops));608 609	return fe;610}611EXPORT_SYMBOL_GPL(fc0013_attach);612 613MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver");614MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>");615MODULE_LICENSE("GPL");616MODULE_VERSION("0.2");617