162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Fitipower FC0011 tuner driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Michael Buesch <m@bues.ch>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Derived from FC0012 tuner driver:
862306a36Sopenharmony_ci * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "fc0011.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* Tuner registers */
1562306a36Sopenharmony_cienum {
1662306a36Sopenharmony_ci	FC11_REG_0,
1762306a36Sopenharmony_ci	FC11_REG_FA,		/* FA */
1862306a36Sopenharmony_ci	FC11_REG_FP,		/* FP */
1962306a36Sopenharmony_ci	FC11_REG_XINHI,		/* XIN high 8 bit */
2062306a36Sopenharmony_ci	FC11_REG_XINLO,		/* XIN low 8 bit */
2162306a36Sopenharmony_ci	FC11_REG_VCO,		/* VCO */
2262306a36Sopenharmony_ci	FC11_REG_VCOSEL,	/* VCO select */
2362306a36Sopenharmony_ci	FC11_REG_7,		/* Unknown tuner reg 7 */
2462306a36Sopenharmony_ci	FC11_REG_8,		/* Unknown tuner reg 8 */
2562306a36Sopenharmony_ci	FC11_REG_9,
2662306a36Sopenharmony_ci	FC11_REG_10,		/* Unknown tuner reg 10 */
2762306a36Sopenharmony_ci	FC11_REG_11,		/* Unknown tuner reg 11 */
2862306a36Sopenharmony_ci	FC11_REG_12,
2962306a36Sopenharmony_ci	FC11_REG_RCCAL,		/* RC calibrate */
3062306a36Sopenharmony_ci	FC11_REG_VCOCAL,	/* VCO calibrate */
3162306a36Sopenharmony_ci	FC11_REG_15,
3262306a36Sopenharmony_ci	FC11_REG_16,		/* Unknown tuner reg 16 */
3362306a36Sopenharmony_ci	FC11_REG_17,
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	FC11_NR_REGS,		/* Number of registers */
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cienum FC11_REG_VCOSEL_bits {
3962306a36Sopenharmony_ci	FC11_VCOSEL_2		= 0x08, /* VCO select 2 */
4062306a36Sopenharmony_ci	FC11_VCOSEL_1		= 0x10, /* VCO select 1 */
4162306a36Sopenharmony_ci	FC11_VCOSEL_CLKOUT	= 0x20, /* Fix clock out */
4262306a36Sopenharmony_ci	FC11_VCOSEL_BW7M	= 0x40, /* 7MHz bw */
4362306a36Sopenharmony_ci	FC11_VCOSEL_BW6M	= 0x80, /* 6MHz bw */
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cienum FC11_REG_RCCAL_bits {
4762306a36Sopenharmony_ci	FC11_RCCAL_FORCE	= 0x10, /* force */
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cienum FC11_REG_VCOCAL_bits {
5162306a36Sopenharmony_ci	FC11_VCOCAL_RUN		= 0,	/* VCO calibration run */
5262306a36Sopenharmony_ci	FC11_VCOCAL_VALUEMASK	= 0x3F,	/* VCO calibration value mask */
5362306a36Sopenharmony_ci	FC11_VCOCAL_OK		= 0x40,	/* VCO calibration Ok */
5462306a36Sopenharmony_ci	FC11_VCOCAL_RESET	= 0x80, /* VCO calibration reset */
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistruct fc0011_priv {
5962306a36Sopenharmony_ci	struct i2c_adapter *i2c;
6062306a36Sopenharmony_ci	u8 addr;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	u32 frequency;
6362306a36Sopenharmony_ci	u32 bandwidth;
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	u8 buf[2] = { reg, val };
7062306a36Sopenharmony_ci	struct i2c_msg msg = { .addr = priv->addr,
7162306a36Sopenharmony_ci		.flags = 0, .buf = buf, .len = 2 };
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
7462306a36Sopenharmony_ci		dev_err(&priv->i2c->dev,
7562306a36Sopenharmony_ci			"I2C write reg failed, reg: %02x, val: %02x\n",
7662306a36Sopenharmony_ci			reg, val);
7762306a36Sopenharmony_ci		return -EIO;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	u8 dummy;
8662306a36Sopenharmony_ci	struct i2c_msg msg[2] = {
8762306a36Sopenharmony_ci		{ .addr = priv->addr,
8862306a36Sopenharmony_ci		  .flags = 0, .buf = &reg, .len = 1 },
8962306a36Sopenharmony_ci		{ .addr = priv->addr,
9062306a36Sopenharmony_ci		  .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 },
9162306a36Sopenharmony_ci	};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
9462306a36Sopenharmony_ci		dev_err(&priv->i2c->dev,
9562306a36Sopenharmony_ci			"I2C read failed, reg: %02x\n", reg);
9662306a36Sopenharmony_ci		return -EIO;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void fc0011_release(struct dvb_frontend *fe)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	kfree(fe->tuner_priv);
10562306a36Sopenharmony_ci	fe->tuner_priv = NULL;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int fc0011_init(struct dvb_frontend *fe)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct fc0011_priv *priv = fe->tuner_priv;
11162306a36Sopenharmony_ci	int err;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (WARN_ON(!fe->callback))
11462306a36Sopenharmony_ci		return -EINVAL;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
11762306a36Sopenharmony_ci			   FC0011_FE_CALLBACK_POWER, priv->addr);
11862306a36Sopenharmony_ci	if (err) {
11962306a36Sopenharmony_ci		dev_err(&priv->i2c->dev, "Power-on callback failed\n");
12062306a36Sopenharmony_ci		return err;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
12362306a36Sopenharmony_ci			   FC0011_FE_CALLBACK_RESET, priv->addr);
12462306a36Sopenharmony_ci	if (err) {
12562306a36Sopenharmony_ci		dev_err(&priv->i2c->dev, "Reset callback failed\n");
12662306a36Sopenharmony_ci		return err;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* Initiate VCO calibration */
13362306a36Sopenharmony_cistatic int fc0011_vcocal_trigger(struct fc0011_priv *priv)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	int err;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET);
13862306a36Sopenharmony_ci	if (err)
13962306a36Sopenharmony_ci		return err;
14062306a36Sopenharmony_ci	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
14162306a36Sopenharmony_ci	if (err)
14262306a36Sopenharmony_ci		return err;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* Read VCO calibration value */
14862306a36Sopenharmony_cistatic int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	int err;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
15362306a36Sopenharmony_ci	if (err)
15462306a36Sopenharmony_ci		return err;
15562306a36Sopenharmony_ci	usleep_range(10000, 20000);
15662306a36Sopenharmony_ci	err = fc0011_readreg(priv, FC11_REG_VCOCAL, value);
15762306a36Sopenharmony_ci	if (err)
15862306a36Sopenharmony_ci		return err;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int fc0011_set_params(struct dvb_frontend *fe)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
16662306a36Sopenharmony_ci	struct fc0011_priv *priv = fe->tuner_priv;
16762306a36Sopenharmony_ci	int err;
16862306a36Sopenharmony_ci	unsigned int i, vco_retries;
16962306a36Sopenharmony_ci	u32 freq = p->frequency / 1000;
17062306a36Sopenharmony_ci	u32 bandwidth = p->bandwidth_hz / 1000;
17162306a36Sopenharmony_ci	u32 fvco, xin, frac, xdiv, xdivr;
17262306a36Sopenharmony_ci	u8 fa, fp, vco_sel, vco_cal;
17362306a36Sopenharmony_ci	u8 regs[FC11_NR_REGS] = { };
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	regs[FC11_REG_7] = 0x0F;
17662306a36Sopenharmony_ci	regs[FC11_REG_8] = 0x3E;
17762306a36Sopenharmony_ci	regs[FC11_REG_10] = 0xB8;
17862306a36Sopenharmony_ci	regs[FC11_REG_11] = 0x80;
17962306a36Sopenharmony_ci	regs[FC11_REG_RCCAL] = 0x04;
18062306a36Sopenharmony_ci	err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
18162306a36Sopenharmony_ci	err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
18262306a36Sopenharmony_ci	err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
18362306a36Sopenharmony_ci	err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
18462306a36Sopenharmony_ci	err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
18562306a36Sopenharmony_ci	if (err)
18662306a36Sopenharmony_ci		return -EIO;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Set VCO freq and VCO div */
18962306a36Sopenharmony_ci	if (freq < 54000) {
19062306a36Sopenharmony_ci		fvco = freq * 64;
19162306a36Sopenharmony_ci		regs[FC11_REG_VCO] = 0x82;
19262306a36Sopenharmony_ci	} else if (freq < 108000) {
19362306a36Sopenharmony_ci		fvco = freq * 32;
19462306a36Sopenharmony_ci		regs[FC11_REG_VCO] = 0x42;
19562306a36Sopenharmony_ci	} else if (freq < 216000) {
19662306a36Sopenharmony_ci		fvco = freq * 16;
19762306a36Sopenharmony_ci		regs[FC11_REG_VCO] = 0x22;
19862306a36Sopenharmony_ci	} else if (freq < 432000) {
19962306a36Sopenharmony_ci		fvco = freq * 8;
20062306a36Sopenharmony_ci		regs[FC11_REG_VCO] = 0x12;
20162306a36Sopenharmony_ci	} else {
20262306a36Sopenharmony_ci		fvco = freq * 4;
20362306a36Sopenharmony_ci		regs[FC11_REG_VCO] = 0x0A;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Calc XIN. The PLL reference frequency is 18 MHz. */
20762306a36Sopenharmony_ci	xdiv = fvco / 18000;
20862306a36Sopenharmony_ci	WARN_ON(xdiv > 0xFF);
20962306a36Sopenharmony_ci	frac = fvco - xdiv * 18000;
21062306a36Sopenharmony_ci	frac = (frac << 15) / 18000;
21162306a36Sopenharmony_ci	if (frac >= 16384)
21262306a36Sopenharmony_ci		frac += 32786;
21362306a36Sopenharmony_ci	if (!frac)
21462306a36Sopenharmony_ci		xin = 0;
21562306a36Sopenharmony_ci	else
21662306a36Sopenharmony_ci		xin = clamp_t(u32, frac, 512, 65024);
21762306a36Sopenharmony_ci	regs[FC11_REG_XINHI] = xin >> 8;
21862306a36Sopenharmony_ci	regs[FC11_REG_XINLO] = xin;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Calc FP and FA */
22162306a36Sopenharmony_ci	xdivr = xdiv;
22262306a36Sopenharmony_ci	if (fvco - xdiv * 18000 >= 9000)
22362306a36Sopenharmony_ci		xdivr += 1; /* round */
22462306a36Sopenharmony_ci	fp = xdivr / 8;
22562306a36Sopenharmony_ci	fa = xdivr - fp * 8;
22662306a36Sopenharmony_ci	if (fa < 2) {
22762306a36Sopenharmony_ci		fp -= 1;
22862306a36Sopenharmony_ci		fa += 8;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	if (fp > 0x1F) {
23162306a36Sopenharmony_ci		fp = 0x1F;
23262306a36Sopenharmony_ci		fa = 0xF;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci	if (fa >= fp) {
23562306a36Sopenharmony_ci		dev_warn(&priv->i2c->dev,
23662306a36Sopenharmony_ci			 "fa %02X >= fp %02X, but trying to continue\n",
23762306a36Sopenharmony_ci			 (unsigned int)(u8)fa, (unsigned int)(u8)fp);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci	regs[FC11_REG_FA] = fa;
24062306a36Sopenharmony_ci	regs[FC11_REG_FP] = fp;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* Select bandwidth */
24362306a36Sopenharmony_ci	switch (bandwidth) {
24462306a36Sopenharmony_ci	case 8000:
24562306a36Sopenharmony_ci		break;
24662306a36Sopenharmony_ci	case 7000:
24762306a36Sopenharmony_ci		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M;
24862306a36Sopenharmony_ci		break;
24962306a36Sopenharmony_ci	default:
25062306a36Sopenharmony_ci		dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. Using 6000 kHz.\n",
25162306a36Sopenharmony_ci			 bandwidth);
25262306a36Sopenharmony_ci		bandwidth = 6000;
25362306a36Sopenharmony_ci		fallthrough;
25462306a36Sopenharmony_ci	case 6000:
25562306a36Sopenharmony_ci		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M;
25662306a36Sopenharmony_ci		break;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Pre VCO select */
26062306a36Sopenharmony_ci	if (fvco < 2320000) {
26162306a36Sopenharmony_ci		vco_sel = 0;
26262306a36Sopenharmony_ci		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
26362306a36Sopenharmony_ci	} else if (fvco < 3080000) {
26462306a36Sopenharmony_ci		vco_sel = 1;
26562306a36Sopenharmony_ci		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
26662306a36Sopenharmony_ci		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
26762306a36Sopenharmony_ci	} else {
26862306a36Sopenharmony_ci		vco_sel = 2;
26962306a36Sopenharmony_ci		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
27062306a36Sopenharmony_ci		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Fix for low freqs */
27462306a36Sopenharmony_ci	if (freq < 45000) {
27562306a36Sopenharmony_ci		regs[FC11_REG_FA] = 0x6;
27662306a36Sopenharmony_ci		regs[FC11_REG_FP] = 0x11;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Clock out fix */
28062306a36Sopenharmony_ci	regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Write the cached registers */
28362306a36Sopenharmony_ci	for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) {
28462306a36Sopenharmony_ci		err = fc0011_writereg(priv, i, regs[i]);
28562306a36Sopenharmony_ci		if (err)
28662306a36Sopenharmony_ci			return err;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* VCO calibration */
29062306a36Sopenharmony_ci	err = fc0011_vcocal_trigger(priv);
29162306a36Sopenharmony_ci	if (err)
29262306a36Sopenharmony_ci		return err;
29362306a36Sopenharmony_ci	err = fc0011_vcocal_read(priv, &vco_cal);
29462306a36Sopenharmony_ci	if (err)
29562306a36Sopenharmony_ci		return err;
29662306a36Sopenharmony_ci	vco_retries = 0;
29762306a36Sopenharmony_ci	while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) {
29862306a36Sopenharmony_ci		/* Reset the tuner and try again */
29962306a36Sopenharmony_ci		err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
30062306a36Sopenharmony_ci				   FC0011_FE_CALLBACK_RESET, priv->addr);
30162306a36Sopenharmony_ci		if (err) {
30262306a36Sopenharmony_ci			dev_err(&priv->i2c->dev, "Failed to reset tuner\n");
30362306a36Sopenharmony_ci			return err;
30462306a36Sopenharmony_ci		}
30562306a36Sopenharmony_ci		/* Reinit tuner config */
30662306a36Sopenharmony_ci		err = 0;
30762306a36Sopenharmony_ci		for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++)
30862306a36Sopenharmony_ci			err |= fc0011_writereg(priv, i, regs[i]);
30962306a36Sopenharmony_ci		err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
31062306a36Sopenharmony_ci		err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
31162306a36Sopenharmony_ci		err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
31262306a36Sopenharmony_ci		err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
31362306a36Sopenharmony_ci		err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
31462306a36Sopenharmony_ci		if (err)
31562306a36Sopenharmony_ci			return -EIO;
31662306a36Sopenharmony_ci		/* VCO calibration */
31762306a36Sopenharmony_ci		err = fc0011_vcocal_trigger(priv);
31862306a36Sopenharmony_ci		if (err)
31962306a36Sopenharmony_ci			return err;
32062306a36Sopenharmony_ci		err = fc0011_vcocal_read(priv, &vco_cal);
32162306a36Sopenharmony_ci		if (err)
32262306a36Sopenharmony_ci			return err;
32362306a36Sopenharmony_ci		vco_retries++;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	if (!(vco_cal & FC11_VCOCAL_OK)) {
32662306a36Sopenharmony_ci		dev_err(&priv->i2c->dev,
32762306a36Sopenharmony_ci			"Failed to read VCO calibration value (got %02X)\n",
32862306a36Sopenharmony_ci			(unsigned int)vco_cal);
32962306a36Sopenharmony_ci		return -EIO;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci	vco_cal &= FC11_VCOCAL_VALUEMASK;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	switch (vco_sel) {
33462306a36Sopenharmony_ci	default:
33562306a36Sopenharmony_ci		WARN_ON(1);
33662306a36Sopenharmony_ci		return -EINVAL;
33762306a36Sopenharmony_ci	case 0:
33862306a36Sopenharmony_ci		if (vco_cal < 8) {
33962306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
34062306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
34162306a36Sopenharmony_ci			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
34262306a36Sopenharmony_ci					      regs[FC11_REG_VCOSEL]);
34362306a36Sopenharmony_ci			if (err)
34462306a36Sopenharmony_ci				return err;
34562306a36Sopenharmony_ci			err = fc0011_vcocal_trigger(priv);
34662306a36Sopenharmony_ci			if (err)
34762306a36Sopenharmony_ci				return err;
34862306a36Sopenharmony_ci		} else {
34962306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
35062306a36Sopenharmony_ci			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
35162306a36Sopenharmony_ci					      regs[FC11_REG_VCOSEL]);
35262306a36Sopenharmony_ci			if (err)
35362306a36Sopenharmony_ci				return err;
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci		break;
35662306a36Sopenharmony_ci	case 1:
35762306a36Sopenharmony_ci		if (vco_cal < 5) {
35862306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
35962306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
36062306a36Sopenharmony_ci			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
36162306a36Sopenharmony_ci					      regs[FC11_REG_VCOSEL]);
36262306a36Sopenharmony_ci			if (err)
36362306a36Sopenharmony_ci				return err;
36462306a36Sopenharmony_ci			err = fc0011_vcocal_trigger(priv);
36562306a36Sopenharmony_ci			if (err)
36662306a36Sopenharmony_ci				return err;
36762306a36Sopenharmony_ci		} else if (vco_cal <= 48) {
36862306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
36962306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
37062306a36Sopenharmony_ci			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
37162306a36Sopenharmony_ci					      regs[FC11_REG_VCOSEL]);
37262306a36Sopenharmony_ci			if (err)
37362306a36Sopenharmony_ci				return err;
37462306a36Sopenharmony_ci		} else {
37562306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
37662306a36Sopenharmony_ci			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
37762306a36Sopenharmony_ci					      regs[FC11_REG_VCOSEL]);
37862306a36Sopenharmony_ci			if (err)
37962306a36Sopenharmony_ci				return err;
38062306a36Sopenharmony_ci			err = fc0011_vcocal_trigger(priv);
38162306a36Sopenharmony_ci			if (err)
38262306a36Sopenharmony_ci				return err;
38362306a36Sopenharmony_ci		}
38462306a36Sopenharmony_ci		break;
38562306a36Sopenharmony_ci	case 2:
38662306a36Sopenharmony_ci		if (vco_cal > 53) {
38762306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
38862306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
38962306a36Sopenharmony_ci			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
39062306a36Sopenharmony_ci					      regs[FC11_REG_VCOSEL]);
39162306a36Sopenharmony_ci			if (err)
39262306a36Sopenharmony_ci				return err;
39362306a36Sopenharmony_ci			err = fc0011_vcocal_trigger(priv);
39462306a36Sopenharmony_ci			if (err)
39562306a36Sopenharmony_ci				return err;
39662306a36Sopenharmony_ci		} else {
39762306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
39862306a36Sopenharmony_ci			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
39962306a36Sopenharmony_ci			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
40062306a36Sopenharmony_ci					      regs[FC11_REG_VCOSEL]);
40162306a36Sopenharmony_ci			if (err)
40262306a36Sopenharmony_ci				return err;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci	err = fc0011_vcocal_read(priv, NULL);
40762306a36Sopenharmony_ci	if (err)
40862306a36Sopenharmony_ci		return err;
40962306a36Sopenharmony_ci	usleep_range(10000, 50000);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	err = fc0011_readreg(priv, FC11_REG_RCCAL, &regs[FC11_REG_RCCAL]);
41262306a36Sopenharmony_ci	if (err)
41362306a36Sopenharmony_ci		return err;
41462306a36Sopenharmony_ci	regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE;
41562306a36Sopenharmony_ci	err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
41662306a36Sopenharmony_ci	if (err)
41762306a36Sopenharmony_ci		return err;
41862306a36Sopenharmony_ci	regs[FC11_REG_16] = 0xB;
41962306a36Sopenharmony_ci	err = fc0011_writereg(priv, FC11_REG_16, regs[FC11_REG_16]);
42062306a36Sopenharmony_ci	if (err)
42162306a36Sopenharmony_ci		return err;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	dev_dbg(&priv->i2c->dev, "Tuned to fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X vcocal=%02X(%u) bw=%u\n",
42462306a36Sopenharmony_ci		(unsigned int)regs[FC11_REG_FA],
42562306a36Sopenharmony_ci		(unsigned int)regs[FC11_REG_FP],
42662306a36Sopenharmony_ci		(unsigned int)regs[FC11_REG_XINHI],
42762306a36Sopenharmony_ci		(unsigned int)regs[FC11_REG_XINLO],
42862306a36Sopenharmony_ci		(unsigned int)regs[FC11_REG_VCO],
42962306a36Sopenharmony_ci		(unsigned int)regs[FC11_REG_VCOSEL],
43062306a36Sopenharmony_ci		(unsigned int)vco_cal, vco_retries,
43162306a36Sopenharmony_ci		(unsigned int)bandwidth);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	priv->frequency = p->frequency;
43462306a36Sopenharmony_ci	priv->bandwidth = p->bandwidth_hz;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return 0;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct fc0011_priv *priv = fe->tuner_priv;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	*frequency = priv->frequency;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	*frequency = 0;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct fc0011_priv *priv = fe->tuner_priv;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	*bandwidth = priv->bandwidth;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic const struct dvb_tuner_ops fc0011_tuner_ops = {
46562306a36Sopenharmony_ci	.info = {
46662306a36Sopenharmony_ci		.name		  = "Fitipower FC0011",
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		.frequency_min_hz =   45 * MHz,
46962306a36Sopenharmony_ci		.frequency_max_hz = 1000 * MHz,
47062306a36Sopenharmony_ci	},
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	.release		= fc0011_release,
47362306a36Sopenharmony_ci	.init			= fc0011_init,
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	.set_params		= fc0011_set_params,
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	.get_frequency		= fc0011_get_frequency,
47862306a36Sopenharmony_ci	.get_if_frequency	= fc0011_get_if_frequency,
47962306a36Sopenharmony_ci	.get_bandwidth		= fc0011_get_bandwidth,
48062306a36Sopenharmony_ci};
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistruct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
48362306a36Sopenharmony_ci				   struct i2c_adapter *i2c,
48462306a36Sopenharmony_ci				   const struct fc0011_config *config)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct fc0011_priv *priv;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
48962306a36Sopenharmony_ci	if (!priv)
49062306a36Sopenharmony_ci		return NULL;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	priv->i2c = i2c;
49362306a36Sopenharmony_ci	priv->addr = config->i2c_address;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	fe->tuner_priv = priv;
49662306a36Sopenharmony_ci	fe->ops.tuner_ops = fc0011_tuner_ops;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n");
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return fe;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fc0011_attach);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ciMODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
50562306a36Sopenharmony_ciMODULE_AUTHOR("Michael Buesch <m@bues.ch>");
50662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
507