174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab 	STV6110(A) Silicon tuner driver
49a0bf528SMauro Carvalho Chehab 
59a0bf528SMauro Carvalho Chehab 	Copyright (C) Manu Abraham <abraham.manu@gmail.com>
69a0bf528SMauro Carvalho Chehab 
79a0bf528SMauro Carvalho Chehab 	Copyright (C) ST Microelectronics
89a0bf528SMauro Carvalho Chehab 
99a0bf528SMauro Carvalho Chehab */
109a0bf528SMauro Carvalho Chehab 
119a0bf528SMauro Carvalho Chehab #include <linux/init.h>
129a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
139a0bf528SMauro Carvalho Chehab #include <linux/module.h>
149a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
159a0bf528SMauro Carvalho Chehab #include <linux/string.h>
169a0bf528SMauro Carvalho Chehab 
17fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
189a0bf528SMauro Carvalho Chehab 
199a0bf528SMauro Carvalho Chehab #include "stv6110x_reg.h"
209a0bf528SMauro Carvalho Chehab #include "stv6110x.h"
219a0bf528SMauro Carvalho Chehab #include "stv6110x_priv.h"
229a0bf528SMauro Carvalho Chehab 
238393796dSMauro Carvalho Chehab /* Max transfer size done by I2C transfer functions */
248393796dSMauro Carvalho Chehab #define MAX_XFER_SIZE  64
258393796dSMauro Carvalho Chehab 
269a0bf528SMauro Carvalho Chehab static unsigned int verbose;
279a0bf528SMauro Carvalho Chehab module_param(verbose, int, 0644);
289a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(verbose, "Set Verbosity level");
299a0bf528SMauro Carvalho Chehab 
stv6110x_read_reg(struct stv6110x_state * stv6110x,u8 reg,u8 * data)309a0bf528SMauro Carvalho Chehab static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data)
319a0bf528SMauro Carvalho Chehab {
329a0bf528SMauro Carvalho Chehab 	int ret;
339a0bf528SMauro Carvalho Chehab 	const struct stv6110x_config *config = stv6110x->config;
349a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
359a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
369a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
379a0bf528SMauro Carvalho Chehab 		{ .addr = config->addr, .flags = 0,	   .buf = b0, .len = 1 },
389a0bf528SMauro Carvalho Chehab 		{ .addr = config->addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }
399a0bf528SMauro Carvalho Chehab 	};
409a0bf528SMauro Carvalho Chehab 
419a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(stv6110x->i2c, msg, 2);
429a0bf528SMauro Carvalho Chehab 	if (ret != 2) {
439a0bf528SMauro Carvalho Chehab 		dprintk(FE_ERROR, 1, "I/O Error");
449a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
459a0bf528SMauro Carvalho Chehab 	}
469a0bf528SMauro Carvalho Chehab 	*data = b1[0];
479a0bf528SMauro Carvalho Chehab 
489a0bf528SMauro Carvalho Chehab 	return 0;
499a0bf528SMauro Carvalho Chehab }
509a0bf528SMauro Carvalho Chehab 
stv6110x_write_regs(struct stv6110x_state * stv6110x,int start,u8 data[],int len)519a0bf528SMauro Carvalho Chehab static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 data[], int len)
529a0bf528SMauro Carvalho Chehab {
539a0bf528SMauro Carvalho Chehab 	int ret;
549a0bf528SMauro Carvalho Chehab 	const struct stv6110x_config *config = stv6110x->config;
558393796dSMauro Carvalho Chehab 	u8 buf[MAX_XFER_SIZE];
568393796dSMauro Carvalho Chehab 
579a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
589a0bf528SMauro Carvalho Chehab 		.addr = config->addr,
599a0bf528SMauro Carvalho Chehab 		.flags = 0,
609a0bf528SMauro Carvalho Chehab 		.buf = buf,
619a0bf528SMauro Carvalho Chehab 		.len = len + 1
629a0bf528SMauro Carvalho Chehab 	};
639a0bf528SMauro Carvalho Chehab 
648393796dSMauro Carvalho Chehab 	if (1 + len > sizeof(buf)) {
658393796dSMauro Carvalho Chehab 		printk(KERN_WARNING
668393796dSMauro Carvalho Chehab 		       "%s: i2c wr: len=%d is too big!\n",
678393796dSMauro Carvalho Chehab 		       KBUILD_MODNAME, len);
688393796dSMauro Carvalho Chehab 		return -EINVAL;
698393796dSMauro Carvalho Chehab 	}
708393796dSMauro Carvalho Chehab 
719a0bf528SMauro Carvalho Chehab 	if (start + len > 8)
729a0bf528SMauro Carvalho Chehab 		return -EINVAL;
739a0bf528SMauro Carvalho Chehab 
749a0bf528SMauro Carvalho Chehab 	buf[0] = start;
759a0bf528SMauro Carvalho Chehab 	memcpy(&buf[1], data, len);
769a0bf528SMauro Carvalho Chehab 
779a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(stv6110x->i2c, &msg, 1);
789a0bf528SMauro Carvalho Chehab 	if (ret != 1) {
799a0bf528SMauro Carvalho Chehab 		dprintk(FE_ERROR, 1, "I/O Error");
809a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
819a0bf528SMauro Carvalho Chehab 	}
829a0bf528SMauro Carvalho Chehab 
839a0bf528SMauro Carvalho Chehab 	return 0;
849a0bf528SMauro Carvalho Chehab }
859a0bf528SMauro Carvalho Chehab 
stv6110x_write_reg(struct stv6110x_state * stv6110x,u8 reg,u8 data)869a0bf528SMauro Carvalho Chehab static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data)
879a0bf528SMauro Carvalho Chehab {
883cd890dbSArnd Bergmann 	u8 tmp = data; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */
893cd890dbSArnd Bergmann 
903cd890dbSArnd Bergmann 	return stv6110x_write_regs(stv6110x, reg, &tmp, 1);
919a0bf528SMauro Carvalho Chehab }
929a0bf528SMauro Carvalho Chehab 
stv6110x_init(struct dvb_frontend * fe)939a0bf528SMauro Carvalho Chehab static int stv6110x_init(struct dvb_frontend *fe)
949a0bf528SMauro Carvalho Chehab {
959a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
969a0bf528SMauro Carvalho Chehab 	int ret;
979a0bf528SMauro Carvalho Chehab 
989a0bf528SMauro Carvalho Chehab 	ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs,
999a0bf528SMauro Carvalho Chehab 				  ARRAY_SIZE(stv6110x->regs));
1009a0bf528SMauro Carvalho Chehab 	if (ret < 0) {
1019a0bf528SMauro Carvalho Chehab 		dprintk(FE_ERROR, 1, "Initialization failed");
1029a0bf528SMauro Carvalho Chehab 		return -1;
1039a0bf528SMauro Carvalho Chehab 	}
1049a0bf528SMauro Carvalho Chehab 
1059a0bf528SMauro Carvalho Chehab 	return 0;
1069a0bf528SMauro Carvalho Chehab }
1079a0bf528SMauro Carvalho Chehab 
stv6110x_set_frequency(struct dvb_frontend * fe,u32 frequency)1089a0bf528SMauro Carvalho Chehab static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency)
1099a0bf528SMauro Carvalho Chehab {
1109a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
1119a0bf528SMauro Carvalho Chehab 	u32 rDiv, divider;
1129a0bf528SMauro Carvalho Chehab 	s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000;
1139a0bf528SMauro Carvalho Chehab 	u8 i;
1149a0bf528SMauro Carvalho Chehab 
1159a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16));
1169a0bf528SMauro Carvalho Chehab 
1179a0bf528SMauro Carvalho Chehab 	if (frequency <= 1023000) {
1189a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1);
1199a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0);
1209a0bf528SMauro Carvalho Chehab 		pVal = 40;
1219a0bf528SMauro Carvalho Chehab 	} else if (frequency <= 1300000) {
1229a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1);
1239a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1);
1249a0bf528SMauro Carvalho Chehab 		pVal = 40;
1259a0bf528SMauro Carvalho Chehab 	} else if (frequency <= 2046000) {
1269a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0);
1279a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0);
1289a0bf528SMauro Carvalho Chehab 		pVal = 20;
1299a0bf528SMauro Carvalho Chehab 	} else {
1309a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0);
1319a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1);
1329a0bf528SMauro Carvalho Chehab 		pVal = 20;
1339a0bf528SMauro Carvalho Chehab 	}
1349a0bf528SMauro Carvalho Chehab 
1359a0bf528SMauro Carvalho Chehab 	for (rDiv = 0; rDiv <= 3; rDiv++) {
1369a0bf528SMauro Carvalho Chehab 		pCalc = (REFCLOCK_kHz / 100) / R_DIV(rDiv);
1379a0bf528SMauro Carvalho Chehab 
1389a0bf528SMauro Carvalho Chehab 		if ((abs((s32)(pCalc - pVal))) < (abs((s32)(pCalcOpt - pVal))))
1399a0bf528SMauro Carvalho Chehab 			rDivOpt = rDiv;
1409a0bf528SMauro Carvalho Chehab 
1419a0bf528SMauro Carvalho Chehab 		pCalcOpt = (REFCLOCK_kHz / 100) / R_DIV(rDivOpt);
1429a0bf528SMauro Carvalho Chehab 	}
1439a0bf528SMauro Carvalho Chehab 
1449a0bf528SMauro Carvalho Chehab 	divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz;
1459a0bf528SMauro Carvalho Chehab 	divider = (divider + 5) / 10;
1469a0bf528SMauro Carvalho Chehab 
1479a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_R_DIV, rDivOpt);
1489a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_N_DIV_11_8, MSB(divider));
1499a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG0], TNG0_N_DIV_7_0, LSB(divider));
1509a0bf528SMauro Carvalho Chehab 
1519a0bf528SMauro Carvalho Chehab 	/* VCO Auto calibration */
1529a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALVCO_STRT, 1);
1539a0bf528SMauro Carvalho Chehab 
1549a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]);
1559a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_TNG1, stv6110x->regs[STV6110x_TNG1]);
1569a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_TNG0, stv6110x->regs[STV6110x_TNG0]);
1579a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]);
1589a0bf528SMauro Carvalho Chehab 
1599a0bf528SMauro Carvalho Chehab 	for (i = 0; i < TRIALS; i++) {
1609a0bf528SMauro Carvalho Chehab 		stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]);
1619a0bf528SMauro Carvalho Chehab 		if (!STV6110x_GETFIELD(STAT1_CALVCO_STRT, stv6110x->regs[STV6110x_STAT1]))
1629a0bf528SMauro Carvalho Chehab 				break;
1639a0bf528SMauro Carvalho Chehab 		msleep(1);
1649a0bf528SMauro Carvalho Chehab 	}
1659a0bf528SMauro Carvalho Chehab 
1669a0bf528SMauro Carvalho Chehab 	return 0;
1679a0bf528SMauro Carvalho Chehab }
1689a0bf528SMauro Carvalho Chehab 
stv6110x_get_frequency(struct dvb_frontend * fe,u32 * frequency)1699a0bf528SMauro Carvalho Chehab static int stv6110x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
1709a0bf528SMauro Carvalho Chehab {
1719a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
1729a0bf528SMauro Carvalho Chehab 
1739a0bf528SMauro Carvalho Chehab 	stv6110x_read_reg(stv6110x, STV6110x_TNG1, &stv6110x->regs[STV6110x_TNG1]);
1749a0bf528SMauro Carvalho Chehab 	stv6110x_read_reg(stv6110x, STV6110x_TNG0, &stv6110x->regs[STV6110x_TNG0]);
1759a0bf528SMauro Carvalho Chehab 
1769a0bf528SMauro Carvalho Chehab 	*frequency = (MAKEWORD16(STV6110x_GETFIELD(TNG1_N_DIV_11_8, stv6110x->regs[STV6110x_TNG1]),
1779a0bf528SMauro Carvalho Chehab 				 STV6110x_GETFIELD(TNG0_N_DIV_7_0, stv6110x->regs[STV6110x_TNG0]))) * REFCLOCK_kHz;
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab 	*frequency /= (1 << (STV6110x_GETFIELD(TNG1_R_DIV, stv6110x->regs[STV6110x_TNG1]) +
1809a0bf528SMauro Carvalho Chehab 			     STV6110x_GETFIELD(TNG1_DIV4SEL, stv6110x->regs[STV6110x_TNG1])));
1819a0bf528SMauro Carvalho Chehab 
1829a0bf528SMauro Carvalho Chehab 	*frequency >>= 2;
1839a0bf528SMauro Carvalho Chehab 
1849a0bf528SMauro Carvalho Chehab 	return 0;
1859a0bf528SMauro Carvalho Chehab }
1869a0bf528SMauro Carvalho Chehab 
stv6110x_set_bandwidth(struct dvb_frontend * fe,u32 bandwidth)1879a0bf528SMauro Carvalho Chehab static int stv6110x_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
1889a0bf528SMauro Carvalho Chehab {
1899a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
1909a0bf528SMauro Carvalho Chehab 	u32 halfbw;
1919a0bf528SMauro Carvalho Chehab 	u8 i;
1929a0bf528SMauro Carvalho Chehab 
1939a0bf528SMauro Carvalho Chehab 	halfbw = bandwidth >> 1;
1949a0bf528SMauro Carvalho Chehab 
1959a0bf528SMauro Carvalho Chehab 	if (halfbw > 36000000)
1969a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 31); /* LPF */
1979a0bf528SMauro Carvalho Chehab 	else if (halfbw < 5000000)
1989a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 0); /* LPF */
1999a0bf528SMauro Carvalho Chehab 	else
2009a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, ((halfbw / 1000000) - 5)); /* LPF */
2019a0bf528SMauro Carvalho Chehab 
2029a0bf528SMauro Carvalho Chehab 
2039a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x0); /* cal. clk activated */
2049a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALRC_STRT, 0x1); /* LPF auto cal */
2059a0bf528SMauro Carvalho Chehab 
2069a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]);
2079a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]);
2089a0bf528SMauro Carvalho Chehab 
2099a0bf528SMauro Carvalho Chehab 	for (i = 0; i < TRIALS; i++) {
2109a0bf528SMauro Carvalho Chehab 		stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]);
2119a0bf528SMauro Carvalho Chehab 		if (!STV6110x_GETFIELD(STAT1_CALRC_STRT, stv6110x->regs[STV6110x_STAT1]))
2129a0bf528SMauro Carvalho Chehab 			break;
2139a0bf528SMauro Carvalho Chehab 		msleep(1);
2149a0bf528SMauro Carvalho Chehab 	}
2159a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x1); /* cal. done */
2169a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]);
2179a0bf528SMauro Carvalho Chehab 
2189a0bf528SMauro Carvalho Chehab 	return 0;
2199a0bf528SMauro Carvalho Chehab }
2209a0bf528SMauro Carvalho Chehab 
stv6110x_get_bandwidth(struct dvb_frontend * fe,u32 * bandwidth)2219a0bf528SMauro Carvalho Chehab static int stv6110x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
2229a0bf528SMauro Carvalho Chehab {
2239a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
2249a0bf528SMauro Carvalho Chehab 
2259a0bf528SMauro Carvalho Chehab 	stv6110x_read_reg(stv6110x, STV6110x_CTRL3, &stv6110x->regs[STV6110x_CTRL3]);
2269a0bf528SMauro Carvalho Chehab 	*bandwidth = (STV6110x_GETFIELD(CTRL3_CF, stv6110x->regs[STV6110x_CTRL3]) + 5) * 2000000;
2279a0bf528SMauro Carvalho Chehab 
2289a0bf528SMauro Carvalho Chehab 	return 0;
2299a0bf528SMauro Carvalho Chehab }
2309a0bf528SMauro Carvalho Chehab 
stv6110x_set_refclock(struct dvb_frontend * fe,u32 refclock)2319a0bf528SMauro Carvalho Chehab static int stv6110x_set_refclock(struct dvb_frontend *fe, u32 refclock)
2329a0bf528SMauro Carvalho Chehab {
2339a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
2349a0bf528SMauro Carvalho Chehab 
2359a0bf528SMauro Carvalho Chehab 	/* setup divider */
2369a0bf528SMauro Carvalho Chehab 	switch (refclock) {
2379a0bf528SMauro Carvalho Chehab 	default:
2389a0bf528SMauro Carvalho Chehab 	case 1:
2399a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0);
2409a0bf528SMauro Carvalho Chehab 		break;
2419a0bf528SMauro Carvalho Chehab 	case 2:
2429a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1);
2439a0bf528SMauro Carvalho Chehab 		break;
2449a0bf528SMauro Carvalho Chehab 	case 4:
2459a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2);
2469a0bf528SMauro Carvalho Chehab 		break;
2479a0bf528SMauro Carvalho Chehab 	case 8:
2489a0bf528SMauro Carvalho Chehab 	case 0:
2499a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3);
2509a0bf528SMauro Carvalho Chehab 		break;
2519a0bf528SMauro Carvalho Chehab 	}
2529a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]);
2539a0bf528SMauro Carvalho Chehab 
2549a0bf528SMauro Carvalho Chehab 	return 0;
2559a0bf528SMauro Carvalho Chehab }
2569a0bf528SMauro Carvalho Chehab 
stv6110x_get_bbgain(struct dvb_frontend * fe,u32 * gain)2579a0bf528SMauro Carvalho Chehab static int stv6110x_get_bbgain(struct dvb_frontend *fe, u32 *gain)
2589a0bf528SMauro Carvalho Chehab {
2599a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
2609a0bf528SMauro Carvalho Chehab 
2619a0bf528SMauro Carvalho Chehab 	stv6110x_read_reg(stv6110x, STV6110x_CTRL2, &stv6110x->regs[STV6110x_CTRL2]);
2629a0bf528SMauro Carvalho Chehab 	*gain = 2 * STV6110x_GETFIELD(CTRL2_BBGAIN, stv6110x->regs[STV6110x_CTRL2]);
2639a0bf528SMauro Carvalho Chehab 
2649a0bf528SMauro Carvalho Chehab 	return 0;
2659a0bf528SMauro Carvalho Chehab }
2669a0bf528SMauro Carvalho Chehab 
stv6110x_set_bbgain(struct dvb_frontend * fe,u32 gain)2679a0bf528SMauro Carvalho Chehab static int stv6110x_set_bbgain(struct dvb_frontend *fe, u32 gain)
2689a0bf528SMauro Carvalho Chehab {
2699a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
2709a0bf528SMauro Carvalho Chehab 
2719a0bf528SMauro Carvalho Chehab 	STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_BBGAIN, gain / 2);
2729a0bf528SMauro Carvalho Chehab 	stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]);
2739a0bf528SMauro Carvalho Chehab 
2749a0bf528SMauro Carvalho Chehab 	return 0;
2759a0bf528SMauro Carvalho Chehab }
2769a0bf528SMauro Carvalho Chehab 
stv6110x_set_mode(struct dvb_frontend * fe,enum tuner_mode mode)2779a0bf528SMauro Carvalho Chehab static int stv6110x_set_mode(struct dvb_frontend *fe, enum tuner_mode mode)
2789a0bf528SMauro Carvalho Chehab {
2799a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
2809a0bf528SMauro Carvalho Chehab 	int ret;
2819a0bf528SMauro Carvalho Chehab 
2829a0bf528SMauro Carvalho Chehab 	switch (mode) {
2839a0bf528SMauro Carvalho Chehab 	case TUNER_SLEEP:
2849a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 0);
2859a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 0);
2869a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 0);
2879a0bf528SMauro Carvalho Chehab 		break;
2889a0bf528SMauro Carvalho Chehab 
2899a0bf528SMauro Carvalho Chehab 	case TUNER_WAKE:
2909a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 1);
2919a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 1);
2929a0bf528SMauro Carvalho Chehab 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 1);
2939a0bf528SMauro Carvalho Chehab 		break;
2949a0bf528SMauro Carvalho Chehab 	}
2959a0bf528SMauro Carvalho Chehab 
2969a0bf528SMauro Carvalho Chehab 	ret = stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]);
2979a0bf528SMauro Carvalho Chehab 	if (ret < 0) {
2989a0bf528SMauro Carvalho Chehab 		dprintk(FE_ERROR, 1, "I/O Error");
2999a0bf528SMauro Carvalho Chehab 		return -EIO;
3009a0bf528SMauro Carvalho Chehab 	}
3019a0bf528SMauro Carvalho Chehab 
3029a0bf528SMauro Carvalho Chehab 	return 0;
3039a0bf528SMauro Carvalho Chehab }
3049a0bf528SMauro Carvalho Chehab 
stv6110x_sleep(struct dvb_frontend * fe)3059a0bf528SMauro Carvalho Chehab static int stv6110x_sleep(struct dvb_frontend *fe)
3069a0bf528SMauro Carvalho Chehab {
3079a0bf528SMauro Carvalho Chehab 	if (fe->tuner_priv)
3089a0bf528SMauro Carvalho Chehab 		return stv6110x_set_mode(fe, TUNER_SLEEP);
3099a0bf528SMauro Carvalho Chehab 
3109a0bf528SMauro Carvalho Chehab 	return 0;
3119a0bf528SMauro Carvalho Chehab }
3129a0bf528SMauro Carvalho Chehab 
stv6110x_get_status(struct dvb_frontend * fe,u32 * status)3139a0bf528SMauro Carvalho Chehab static int stv6110x_get_status(struct dvb_frontend *fe, u32 *status)
3149a0bf528SMauro Carvalho Chehab {
3159a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
3169a0bf528SMauro Carvalho Chehab 
3179a0bf528SMauro Carvalho Chehab 	stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]);
3189a0bf528SMauro Carvalho Chehab 
3199a0bf528SMauro Carvalho Chehab 	if (STV6110x_GETFIELD(STAT1_LOCK, stv6110x->regs[STV6110x_STAT1]))
3209a0bf528SMauro Carvalho Chehab 		*status = TUNER_PHASELOCKED;
3219a0bf528SMauro Carvalho Chehab 	else
3229a0bf528SMauro Carvalho Chehab 		*status = 0;
3239a0bf528SMauro Carvalho Chehab 
3249a0bf528SMauro Carvalho Chehab 	return 0;
3259a0bf528SMauro Carvalho Chehab }
3269a0bf528SMauro Carvalho Chehab 
3279a0bf528SMauro Carvalho Chehab 
stv6110x_release(struct dvb_frontend * fe)328f2709c20SMauro Carvalho Chehab static void stv6110x_release(struct dvb_frontend *fe)
329f2709c20SMauro Carvalho Chehab {
330f2709c20SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x = fe->tuner_priv;
331f2709c20SMauro Carvalho Chehab 
332f2709c20SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
333f2709c20SMauro Carvalho Chehab 	kfree(stv6110x);
334f2709c20SMauro Carvalho Chehab }
335f2709c20SMauro Carvalho Chehab 
st6110x_init_regs(struct stv6110x_state * stv6110x)3363c8f4cd2STobias Klausmann static void st6110x_init_regs(struct stv6110x_state *stv6110x)
3373c8f4cd2STobias Klausmann {
3383c8f4cd2STobias Klausmann 	u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e};
3393c8f4cd2STobias Klausmann 
3403c8f4cd2STobias Klausmann 	memcpy(stv6110x->regs, default_regs, 8);
3413c8f4cd2STobias Klausmann }
3423c8f4cd2STobias Klausmann 
stv6110x_setup_divider(struct stv6110x_state * stv6110x)3433c8f4cd2STobias Klausmann static void stv6110x_setup_divider(struct stv6110x_state *stv6110x)
3443c8f4cd2STobias Klausmann {
3453c8f4cd2STobias Klausmann 	switch (stv6110x->config->clk_div) {
3463c8f4cd2STobias Klausmann 	default:
3473c8f4cd2STobias Klausmann 	case 1:
3483c8f4cd2STobias Klausmann 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2],
3493c8f4cd2STobias Klausmann 				  CTRL2_CO_DIV,
3503c8f4cd2STobias Klausmann 				  0);
3513c8f4cd2STobias Klausmann 		break;
3523c8f4cd2STobias Klausmann 	case 2:
3533c8f4cd2STobias Klausmann 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2],
3543c8f4cd2STobias Klausmann 				  CTRL2_CO_DIV,
3553c8f4cd2STobias Klausmann 				  1);
3563c8f4cd2STobias Klausmann 		break;
3573c8f4cd2STobias Klausmann 	case 4:
3583c8f4cd2STobias Klausmann 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2],
3593c8f4cd2STobias Klausmann 				  CTRL2_CO_DIV,
3603c8f4cd2STobias Klausmann 				  2);
3613c8f4cd2STobias Klausmann 		break;
3623c8f4cd2STobias Klausmann 	case 8:
3633c8f4cd2STobias Klausmann 	case 0:
3643c8f4cd2STobias Klausmann 		STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2],
3653c8f4cd2STobias Klausmann 				  CTRL2_CO_DIV,
3663c8f4cd2STobias Klausmann 				  3);
3673c8f4cd2STobias Klausmann 		break;
3683c8f4cd2STobias Klausmann 	}
3693c8f4cd2STobias Klausmann }
3703c8f4cd2STobias Klausmann 
37114c4bf3cSJulia Lawall static const struct dvb_tuner_ops stv6110x_ops = {
3729a0bf528SMauro Carvalho Chehab 	.info = {
3739a0bf528SMauro Carvalho Chehab 		.name		  = "STV6110(A) Silicon Tuner",
374a3f90c75SMauro Carvalho Chehab 		.frequency_min_hz =  950 * MHz,
375a3f90c75SMauro Carvalho Chehab 		.frequency_max_hz = 2150 * MHz,
3769a0bf528SMauro Carvalho Chehab 	},
377f2709c20SMauro Carvalho Chehab 	.release		= stv6110x_release
3789a0bf528SMauro Carvalho Chehab };
3799a0bf528SMauro Carvalho Chehab 
3803c8f4cd2STobias Klausmann static struct stv6110x_devctl stv6110x_ctl = {
3819a0bf528SMauro Carvalho Chehab 	.tuner_init		= stv6110x_init,
3829a0bf528SMauro Carvalho Chehab 	.tuner_sleep		= stv6110x_sleep,
3839a0bf528SMauro Carvalho Chehab 	.tuner_set_mode		= stv6110x_set_mode,
3849a0bf528SMauro Carvalho Chehab 	.tuner_set_frequency	= stv6110x_set_frequency,
3859a0bf528SMauro Carvalho Chehab 	.tuner_get_frequency	= stv6110x_get_frequency,
3869a0bf528SMauro Carvalho Chehab 	.tuner_set_bandwidth	= stv6110x_set_bandwidth,
3879a0bf528SMauro Carvalho Chehab 	.tuner_get_bandwidth	= stv6110x_get_bandwidth,
3889a0bf528SMauro Carvalho Chehab 	.tuner_set_bbgain	= stv6110x_set_bbgain,
3899a0bf528SMauro Carvalho Chehab 	.tuner_get_bbgain	= stv6110x_get_bbgain,
3909a0bf528SMauro Carvalho Chehab 	.tuner_set_refclk	= stv6110x_set_refclock,
3919a0bf528SMauro Carvalho Chehab 	.tuner_get_status	= stv6110x_get_status,
3929a0bf528SMauro Carvalho Chehab };
3939a0bf528SMauro Carvalho Chehab 
stv6110x_set_frontend_opts(struct stv6110x_state * stv6110x)3943c8f4cd2STobias Klausmann static void stv6110x_set_frontend_opts(struct stv6110x_state *stv6110x)
3953c8f4cd2STobias Klausmann {
3963c8f4cd2STobias Klausmann 	stv6110x->frontend->tuner_priv		= stv6110x;
3973c8f4cd2STobias Klausmann 	stv6110x->frontend->ops.tuner_ops	= stv6110x_ops;
3983c8f4cd2STobias Klausmann }
3993c8f4cd2STobias Klausmann 
stv6110x_get_devctl(struct i2c_client * client)4003c8f4cd2STobias Klausmann static struct stv6110x_devctl *stv6110x_get_devctl(struct i2c_client *client)
4013c8f4cd2STobias Klausmann {
4023c8f4cd2STobias Klausmann 	struct stv6110x_state *stv6110x = i2c_get_clientdata(client);
4033c8f4cd2STobias Klausmann 
4043c8f4cd2STobias Klausmann 	dev_dbg(&client->dev, "\n");
4053c8f4cd2STobias Klausmann 
4063c8f4cd2STobias Klausmann 	return stv6110x->devctl;
4073c8f4cd2STobias Klausmann }
4083c8f4cd2STobias Klausmann 
stv6110x_probe(struct i2c_client * client)409ce087f31SUwe Kleine-König static int stv6110x_probe(struct i2c_client *client)
4103c8f4cd2STobias Klausmann {
4113c8f4cd2STobias Klausmann 	struct stv6110x_config *config = client->dev.platform_data;
4123c8f4cd2STobias Klausmann 
4133c8f4cd2STobias Klausmann 	struct stv6110x_state *stv6110x;
4143c8f4cd2STobias Klausmann 
4153c8f4cd2STobias Klausmann 	stv6110x = kzalloc(sizeof(*stv6110x), GFP_KERNEL);
4163c8f4cd2STobias Klausmann 	if (!stv6110x)
4173c8f4cd2STobias Klausmann 		return -ENOMEM;
4183c8f4cd2STobias Klausmann 
4193c8f4cd2STobias Klausmann 	stv6110x->frontend	= config->frontend;
4203c8f4cd2STobias Klausmann 	stv6110x->i2c		= client->adapter;
4213c8f4cd2STobias Klausmann 	stv6110x->config	= config;
4223c8f4cd2STobias Klausmann 	stv6110x->devctl	= &stv6110x_ctl;
4233c8f4cd2STobias Klausmann 
4243c8f4cd2STobias Klausmann 	st6110x_init_regs(stv6110x);
4253c8f4cd2STobias Klausmann 	stv6110x_setup_divider(stv6110x);
4263c8f4cd2STobias Klausmann 	stv6110x_set_frontend_opts(stv6110x);
4273c8f4cd2STobias Klausmann 
4283c8f4cd2STobias Klausmann 	dev_info(&stv6110x->i2c->dev, "Probed STV6110x\n");
4293c8f4cd2STobias Klausmann 
4303c8f4cd2STobias Klausmann 	i2c_set_clientdata(client, stv6110x);
4313c8f4cd2STobias Klausmann 
4323c8f4cd2STobias Klausmann 	/* setup callbacks */
4333c8f4cd2STobias Klausmann 	config->get_devctl = stv6110x_get_devctl;
4343c8f4cd2STobias Klausmann 
4353c8f4cd2STobias Klausmann 	return 0;
4363c8f4cd2STobias Klausmann }
4373c8f4cd2STobias Klausmann 
stv6110x_remove(struct i2c_client * client)438ed5c2f5fSUwe Kleine-König static void stv6110x_remove(struct i2c_client *client)
4393c8f4cd2STobias Klausmann {
4403c8f4cd2STobias Klausmann 	struct stv6110x_state *stv6110x = i2c_get_clientdata(client);
4413c8f4cd2STobias Klausmann 
4423c8f4cd2STobias Klausmann 	stv6110x_release(stv6110x->frontend);
4433c8f4cd2STobias Klausmann }
4443c8f4cd2STobias Klausmann 
stv6110x_attach(struct dvb_frontend * fe,const struct stv6110x_config * config,struct i2c_adapter * i2c)445242c5033SJulia Lawall const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
4469a0bf528SMauro Carvalho Chehab 					const struct stv6110x_config *config,
4479a0bf528SMauro Carvalho Chehab 					struct i2c_adapter *i2c)
4489a0bf528SMauro Carvalho Chehab {
4499a0bf528SMauro Carvalho Chehab 	struct stv6110x_state *stv6110x;
4509a0bf528SMauro Carvalho Chehab 
4513c8f4cd2STobias Klausmann 	stv6110x = kzalloc(sizeof(*stv6110x), GFP_KERNEL);
4529a0bf528SMauro Carvalho Chehab 	if (!stv6110x)
4539a0bf528SMauro Carvalho Chehab 		return NULL;
4549a0bf528SMauro Carvalho Chehab 
4553c8f4cd2STobias Klausmann 	stv6110x->frontend	= fe;
4569a0bf528SMauro Carvalho Chehab 	stv6110x->i2c		= i2c;
4579a0bf528SMauro Carvalho Chehab 	stv6110x->config	= config;
4589a0bf528SMauro Carvalho Chehab 	stv6110x->devctl	= &stv6110x_ctl;
4599a0bf528SMauro Carvalho Chehab 
4603c8f4cd2STobias Klausmann 	st6110x_init_regs(stv6110x);
4613c8f4cd2STobias Klausmann 	stv6110x_setup_divider(stv6110x);
4623c8f4cd2STobias Klausmann 	stv6110x_set_frontend_opts(stv6110x);
4639a0bf528SMauro Carvalho Chehab 
4649a0bf528SMauro Carvalho Chehab 	fe->tuner_priv		= stv6110x;
4659a0bf528SMauro Carvalho Chehab 	fe->ops.tuner_ops	= stv6110x_ops;
4669a0bf528SMauro Carvalho Chehab 
4673c8f4cd2STobias Klausmann 	dev_info(&stv6110x->i2c->dev, "Attaching STV6110x\n");
4689a0bf528SMauro Carvalho Chehab 	return stv6110x->devctl;
4699a0bf528SMauro Carvalho Chehab }
470*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(stv6110x_attach);
4719a0bf528SMauro Carvalho Chehab 
4723c8f4cd2STobias Klausmann static const struct i2c_device_id stv6110x_id_table[] = {
4733c8f4cd2STobias Klausmann 	{"stv6110x", 0},
4743c8f4cd2STobias Klausmann 	{}
4753c8f4cd2STobias Klausmann };
4763c8f4cd2STobias Klausmann MODULE_DEVICE_TABLE(i2c, stv6110x_id_table);
4773c8f4cd2STobias Klausmann 
4783c8f4cd2STobias Klausmann static struct i2c_driver stv6110x_driver = {
4793c8f4cd2STobias Klausmann 	.driver = {
4803c8f4cd2STobias Klausmann 		.name	= "stv6110x",
4813c8f4cd2STobias Klausmann 		.suppress_bind_attrs = true,
4823c8f4cd2STobias Klausmann 	},
483aaeb31c0SUwe Kleine-König 	.probe		= stv6110x_probe,
4843c8f4cd2STobias Klausmann 	.remove		= stv6110x_remove,
4853c8f4cd2STobias Klausmann 	.id_table	= stv6110x_id_table,
4863c8f4cd2STobias Klausmann };
4873c8f4cd2STobias Klausmann 
4883c8f4cd2STobias Klausmann module_i2c_driver(stv6110x_driver);
4893c8f4cd2STobias Klausmann 
4909a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Manu Abraham");
4919a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("STV6110x Silicon tuner");
4929a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
493