brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · 81e65ac Raw
504 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Fitipower FC0012 tuner driver4 *5 * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>6 */7 8#include "fc0012.h"9#include "fc0012-priv.h"10 11static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val)12{13	u8 buf[2] = {reg, val};14	struct i2c_msg msg = {15		.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 216	};17 18	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {19		dev_err(&priv->i2c->dev,20			"%s: I2C write reg failed, reg: %02x, val: %02x\n",21			KBUILD_MODNAME, reg, val);22		return -EREMOTEIO;23	}24	return 0;25}26 27static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val)28{29	struct i2c_msg msg[2] = {30		{ .addr = priv->cfg->i2c_address, .flags = 0,31			.buf = &reg, .len = 1 },32		{ .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,33			.buf = val, .len = 1 },34	};35 36	if (i2c_transfer(priv->i2c, msg, 2) != 2) {37		dev_err(&priv->i2c->dev,38			"%s: I2C read reg failed, reg: %02x\n",39			KBUILD_MODNAME, reg);40		return -EREMOTEIO;41	}42	return 0;43}44 45static void fc0012_release(struct dvb_frontend *fe)46{47	kfree(fe->tuner_priv);48	fe->tuner_priv = NULL;49}50 51static int fc0012_init(struct dvb_frontend *fe)52{53	struct fc0012_priv *priv = fe->tuner_priv;54	int i, ret = 0;55	unsigned char reg[] = {56		0x00,	/* dummy reg. 0 */57		0x05,	/* reg. 0x01 */58		0x10,	/* reg. 0x02 */59		0x00,	/* reg. 0x03 */60		0x00,	/* reg. 0x04 */61		0x0f,	/* reg. 0x05: may also be 0x0a */62		0x00,	/* reg. 0x06: divider 2, VCO slow */63		0x00,	/* reg. 0x07: may also be 0x0f */64		0xff,	/* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,65			   Loop Bw 1/8 */66		0x6e,	/* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */67		0xb8,	/* reg. 0x0a: Disable LO Test Buffer */68		0x82,	/* reg. 0x0b: Output Clock is same as clock frequency,69			   may also be 0x83 */70		0xfc,	/* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */71		0x02,	/* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */72		0x00,	/* reg. 0x0e */73		0x00,	/* reg. 0x0f */74		0x00,	/* reg. 0x10: may also be 0x0d */75		0x00,	/* reg. 0x11 */76		0x1f,	/* reg. 0x12: Set to maximum gain */77		0x08,	/* reg. 0x13: Set to Middle Gain: 0x08,78			   Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */79		0x00,	/* reg. 0x14 */80		0x04,	/* reg. 0x15: Enable LNA COMPS */81	};82 83	switch (priv->cfg->xtal_freq) {84	case FC_XTAL_27_MHZ:85	case FC_XTAL_28_8_MHZ:86		reg[0x07] |= 0x20;87		break;88	case FC_XTAL_36_MHZ:89	default:90		break;91	}92 93	if (priv->cfg->dual_master)94		reg[0x0c] |= 0x02;95 96	if (priv->cfg->loop_through)97		reg[0x09] |= 0x01;98 99	if (fe->ops.i2c_gate_ctrl)100		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */101 102	for (i = 1; i < sizeof(reg); i++) {103		ret = fc0012_writereg(priv, i, reg[i]);104		if (ret)105			break;106	}107 108	if (fe->ops.i2c_gate_ctrl)109		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */110 111	if (ret)112		dev_err(&priv->i2c->dev, "%s: fc0012_writereg failed: %d\n",113				KBUILD_MODNAME, ret);114 115	return ret;116}117 118static int fc0012_set_params(struct dvb_frontend *fe)119{120	struct fc0012_priv *priv = fe->tuner_priv;121	int i, ret = 0;122	struct dtv_frontend_properties *p = &fe->dtv_property_cache;123	u32 freq = p->frequency / 1000;124	u32 delsys = p->delivery_system;125	unsigned char reg[7], am, pm, multi, tmp;126	unsigned long f_vco;127	unsigned short xtal_freq_khz_2, xin, xdiv;128	bool vco_select = false;129 130	if (fe->callback) {131		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,132			FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1));133		if (ret)134			goto exit;135	}136 137	switch (priv->cfg->xtal_freq) {138	case FC_XTAL_27_MHZ:139		xtal_freq_khz_2 = 27000 / 2;140		break;141	case FC_XTAL_36_MHZ:142		xtal_freq_khz_2 = 36000 / 2;143		break;144	case FC_XTAL_28_8_MHZ:145	default:146		xtal_freq_khz_2 = 28800 / 2;147		break;148	}149 150	/* select frequency divider and the frequency of VCO */151	if (freq < 37084) {		/* freq * 96 < 3560000 */152		multi = 96;153		reg[5] = 0x82;154		reg[6] = 0x00;155	} else if (freq < 55625) {	/* freq * 64 < 3560000 */156		multi = 64;157		reg[5] = 0x82;158		reg[6] = 0x02;159	} else if (freq < 74167) {	/* freq * 48 < 3560000 */160		multi = 48;161		reg[5] = 0x42;162		reg[6] = 0x00;163	} else if (freq < 111250) {	/* freq * 32 < 3560000 */164		multi = 32;165		reg[5] = 0x42;166		reg[6] = 0x02;167	} else if (freq < 148334) {	/* freq * 24 < 3560000 */168		multi = 24;169		reg[5] = 0x22;170		reg[6] = 0x00;171	} else if (freq < 222500) {	/* freq * 16 < 3560000 */172		multi = 16;173		reg[5] = 0x22;174		reg[6] = 0x02;175	} else if (freq < 296667) {	/* freq * 12 < 3560000 */176		multi = 12;177		reg[5] = 0x12;178		reg[6] = 0x00;179	} else if (freq < 445000) {	/* freq * 8 < 3560000 */180		multi = 8;181		reg[5] = 0x12;182		reg[6] = 0x02;183	} else if (freq < 593334) {	/* freq * 6 < 3560000 */184		multi = 6;185		reg[5] = 0x0a;186		reg[6] = 0x00;187	} else {188		multi = 4;189		reg[5] = 0x0a;190		reg[6] = 0x02;191	}192 193	f_vco = freq * multi;194 195	if (f_vco >= 3060000) {196		reg[6] |= 0x08;197		vco_select = true;198	}199 200	if (freq >= 45000) {201		/* From divided value (XDIV) determined the FA and FP value */202		xdiv = (unsigned short)(f_vco / xtal_freq_khz_2);203		if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2))204			xdiv++;205 206		pm = (unsigned char)(xdiv / 8);207		am = (unsigned char)(xdiv - (8 * pm));208 209		if (am < 2) {210			reg[1] = am + 8;211			reg[2] = pm - 1;212		} else {213			reg[1] = am;214			reg[2] = pm;215		}216	} else {217		/* fix for frequency less than 45 MHz */218		reg[1] = 0x06;219		reg[2] = 0x11;220	}221 222	/* fix clock out */223	reg[6] |= 0x20;224 225	/* From VCO frequency determines the XIN ( fractional part of Delta226	   Sigma PLL) and divided value (XDIV) */227	xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2);228	xin = (xin << 15) / xtal_freq_khz_2;229	if (xin >= 16384)230		xin += 32768;231 232	reg[3] = xin >> 8;	/* xin with 9 bit resolution */233	reg[4] = xin & 0xff;234 235	if (delsys == SYS_DVBT) {236		reg[6] &= 0x3f;	/* bits 6 and 7 describe the bandwidth */237		switch (p->bandwidth_hz) {238		case 6000000:239			reg[6] |= 0x80;240			break;241		case 7000000:242			reg[6] |= 0x40;243			break;244		case 8000000:245		default:246			break;247		}248	} else {249		dev_err(&priv->i2c->dev, "%s: modulation type not supported!\n",250				KBUILD_MODNAME);251		return -EINVAL;252	}253 254	/* modified for Realtek demod */255	reg[5] |= 0x07;256 257	if (fe->ops.i2c_gate_ctrl)258		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */259 260	for (i = 1; i <= 6; i++) {261		ret = fc0012_writereg(priv, i, reg[i]);262		if (ret)263			goto exit;264	}265 266	/* VCO Calibration */267	ret = fc0012_writereg(priv, 0x0e, 0x80);268	if (!ret)269		ret = fc0012_writereg(priv, 0x0e, 0x00);270 271	/* VCO Re-Calibration if needed */272	if (!ret)273		ret = fc0012_writereg(priv, 0x0e, 0x00);274 275	if (!ret) {276		msleep(10);277		ret = fc0012_readreg(priv, 0x0e, &tmp);278	}279	if (ret)280		goto exit;281 282	/* vco selection */283	tmp &= 0x3f;284 285	if (vco_select) {286		if (tmp > 0x3c) {287			reg[6] &= ~0x08;288			ret = fc0012_writereg(priv, 0x06, reg[6]);289			if (!ret)290				ret = fc0012_writereg(priv, 0x0e, 0x80);291			if (!ret)292				ret = fc0012_writereg(priv, 0x0e, 0x00);293		}294	} else {295		if (tmp < 0x02) {296			reg[6] |= 0x08;297			ret = fc0012_writereg(priv, 0x06, reg[6]);298			if (!ret)299				ret = fc0012_writereg(priv, 0x0e, 0x80);300			if (!ret)301				ret = fc0012_writereg(priv, 0x0e, 0x00);302		}303	}304 305	priv->frequency = p->frequency;306	priv->bandwidth = p->bandwidth_hz;307 308exit:309	if (fe->ops.i2c_gate_ctrl)310		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */311	if (ret)312		dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n",313				KBUILD_MODNAME, __func__, ret);314	return ret;315}316 317static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency)318{319	struct fc0012_priv *priv = fe->tuner_priv;320	*frequency = priv->frequency;321	return 0;322}323 324static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)325{326	*frequency = 0; /* Zero-IF */327	return 0;328}329 330static int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)331{332	struct fc0012_priv *priv = fe->tuner_priv;333	*bandwidth = priv->bandwidth;334	return 0;335}336 337#define INPUT_ADC_LEVEL	-8338 339static int fc0012_get_rf_strength(struct dvb_frontend *fe, u16 *strength)340{341	struct fc0012_priv *priv = fe->tuner_priv;342	int ret;343	unsigned char tmp;344	int int_temp, lna_gain, int_lna, tot_agc_gain, power;345	static const int fc0012_lna_gain_table[] = {346		/* low gain */347		-63, -58, -99, -73,348		-63, -65, -54, -60,349		/* middle gain */350		 71,  70,  68,  67,351		 65,  63,  61,  58,352		/* high gain */353		197, 191, 188, 186,354		184, 182, 181, 179,355	};356 357	if (fe->ops.i2c_gate_ctrl)358		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */359 360	ret = fc0012_writereg(priv, 0x12, 0x00);361	if (ret)362		goto err;363 364	ret = fc0012_readreg(priv, 0x12, &tmp);365	if (ret)366		goto err;367	int_temp = tmp;368 369	ret = fc0012_readreg(priv, 0x13, &tmp);370	if (ret)371		goto err;372	lna_gain = tmp & 0x1f;373 374	if (fe->ops.i2c_gate_ctrl)375		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */376 377	if (lna_gain < ARRAY_SIZE(fc0012_lna_gain_table)) {378		int_lna = fc0012_lna_gain_table[lna_gain];379		tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 +380				(int_temp & 0x1f)) * 2;381		power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10;382 383		if (power >= 45)384			*strength = 255;	/* 100% */385		else if (power < -95)386			*strength = 0;387		else388			*strength = (power + 95) * 255 / 140;389 390		*strength |= *strength << 8;391	} else {392		ret = -1;393	}394 395	goto exit;396 397err:398	if (fe->ops.i2c_gate_ctrl)399		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */400exit:401	if (ret)402		dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n",403				KBUILD_MODNAME, __func__, ret);404	return ret;405}406 407static const struct dvb_tuner_ops fc0012_tuner_ops = {408	.info = {409		.name              = "Fitipower FC0012",410 411		.frequency_min_hz  =  37 * MHz,	/* estimate */412		.frequency_max_hz  = 862 * MHz,	/* estimate */413	},414 415	.release	= fc0012_release,416 417	.init		= fc0012_init,418 419	.set_params	= fc0012_set_params,420 421	.get_frequency	= fc0012_get_frequency,422	.get_if_frequency = fc0012_get_if_frequency,423	.get_bandwidth	= fc0012_get_bandwidth,424 425	.get_rf_strength = fc0012_get_rf_strength,426};427 428struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe,429	struct i2c_adapter *i2c, const struct fc0012_config *cfg)430{431	struct fc0012_priv *priv;432	int ret;433	u8 chip_id;434 435	if (fe->ops.i2c_gate_ctrl)436		fe->ops.i2c_gate_ctrl(fe, 1);437 438	priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL);439	if (!priv) {440		ret = -ENOMEM;441		dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);442		goto err;443	}444 445	priv->cfg = cfg;446	priv->i2c = i2c;447 448	/* check if the tuner is there */449	ret = fc0012_readreg(priv, 0x00, &chip_id);450	if (ret < 0)451		goto err;452 453	dev_dbg(&i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);454 455	switch (chip_id) {456	case 0xa1:457		break;458	default:459		ret = -ENODEV;460		goto err;461	}462 463	dev_info(&i2c->dev, "%s: Fitipower FC0012 successfully identified\n",464			KBUILD_MODNAME);465 466	if (priv->cfg->loop_through) {467		ret = fc0012_writereg(priv, 0x09, 0x6f);468		if (ret < 0)469			goto err;470	}471 472	/*473	 * TODO: Clock out en or div?474	 * For dual tuner configuration clearing bit [0] is required.475	 */476	if (priv->cfg->clock_out) {477		ret =  fc0012_writereg(priv, 0x0b, 0x82);478		if (ret < 0)479			goto err;480	}481 482	fe->tuner_priv = priv;483	memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops,484		sizeof(struct dvb_tuner_ops));485 486err:487	if (fe->ops.i2c_gate_ctrl)488		fe->ops.i2c_gate_ctrl(fe, 0);489 490	if (ret) {491		dev_dbg(&i2c->dev, "%s: failed: %d\n", __func__, ret);492		kfree(priv);493		return NULL;494	}495 496	return fe;497}498EXPORT_SYMBOL_GPL(fc0012_attach);499 500MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver");501MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>");502MODULE_LICENSE("GPL");503MODULE_VERSION("0.6");504