18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci	STB6100 Silicon Tuner
48c2ecf20Sopenharmony_ci	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci	Copyright (C) ST Microelectronics
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci*/
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
178c2ecf20Sopenharmony_ci#include "stb6100.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic unsigned int verbose;
208c2ecf20Sopenharmony_cimodule_param(verbose, int, 0644);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* Max transfer size done by I2C transfer functions */
238c2ecf20Sopenharmony_ci#define MAX_XFER_SIZE  64
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define FE_ERROR		0
268c2ecf20Sopenharmony_ci#define FE_NOTICE		1
278c2ecf20Sopenharmony_ci#define FE_INFO			2
288c2ecf20Sopenharmony_ci#define FE_DEBUG		3
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define dprintk(x, y, z, format, arg...) do {						\
318c2ecf20Sopenharmony_ci	if (z) {									\
328c2ecf20Sopenharmony_ci		if	((x > FE_ERROR) && (x > y))					\
338c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: " format "\n", __func__ , ##arg);		\
348c2ecf20Sopenharmony_ci		else if	((x > FE_NOTICE) && (x > y))					\
358c2ecf20Sopenharmony_ci			printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg);	\
368c2ecf20Sopenharmony_ci		else if ((x > FE_INFO) && (x > y))					\
378c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: " format "\n", __func__ , ##arg);		\
388c2ecf20Sopenharmony_ci		else if ((x > FE_DEBUG) && (x > y))					\
398c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg);	\
408c2ecf20Sopenharmony_ci	} else {									\
418c2ecf20Sopenharmony_ci		if (x > y)								\
428c2ecf20Sopenharmony_ci			printk(format, ##arg);						\
438c2ecf20Sopenharmony_ci	}										\
448c2ecf20Sopenharmony_ci} while (0)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct stb6100_lkup {
478c2ecf20Sopenharmony_ci	u32 val_low;
488c2ecf20Sopenharmony_ci	u32 val_high;
498c2ecf20Sopenharmony_ci	u8   reg;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void stb6100_release(struct dvb_frontend *fe);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct stb6100_lkup lkup[] = {
558c2ecf20Sopenharmony_ci	{       0,  950000, 0x0a },
568c2ecf20Sopenharmony_ci	{  950000, 1000000, 0x0a },
578c2ecf20Sopenharmony_ci	{ 1000000, 1075000, 0x0c },
588c2ecf20Sopenharmony_ci	{ 1075000, 1200000, 0x00 },
598c2ecf20Sopenharmony_ci	{ 1200000, 1300000, 0x01 },
608c2ecf20Sopenharmony_ci	{ 1300000, 1370000, 0x02 },
618c2ecf20Sopenharmony_ci	{ 1370000, 1470000, 0x04 },
628c2ecf20Sopenharmony_ci	{ 1470000, 1530000, 0x05 },
638c2ecf20Sopenharmony_ci	{ 1530000, 1650000, 0x06 },
648c2ecf20Sopenharmony_ci	{ 1650000, 1800000, 0x08 },
658c2ecf20Sopenharmony_ci	{ 1800000, 1950000, 0x0a },
668c2ecf20Sopenharmony_ci	{ 1950000, 2150000, 0x0c },
678c2ecf20Sopenharmony_ci	{ 2150000, 9999999, 0x0c },
688c2ecf20Sopenharmony_ci	{       0,       0, 0x00 }
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* Register names for easy debugging.	*/
728c2ecf20Sopenharmony_cistatic const char *stb6100_regnames[] = {
738c2ecf20Sopenharmony_ci	[STB6100_LD]		= "LD",
748c2ecf20Sopenharmony_ci	[STB6100_VCO]		= "VCO",
758c2ecf20Sopenharmony_ci	[STB6100_NI]		= "NI",
768c2ecf20Sopenharmony_ci	[STB6100_NF_LSB]	= "NF",
778c2ecf20Sopenharmony_ci	[STB6100_K]		= "K",
788c2ecf20Sopenharmony_ci	[STB6100_G]		= "G",
798c2ecf20Sopenharmony_ci	[STB6100_F]		= "F",
808c2ecf20Sopenharmony_ci	[STB6100_DLB]		= "DLB",
818c2ecf20Sopenharmony_ci	[STB6100_TEST1]		= "TEST1",
828c2ecf20Sopenharmony_ci	[STB6100_FCCK]		= "FCCK",
838c2ecf20Sopenharmony_ci	[STB6100_LPEN]		= "LPEN",
848c2ecf20Sopenharmony_ci	[STB6100_TEST3]		= "TEST3",
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Template for normalisation, i.e. setting unused or undocumented
888c2ecf20Sopenharmony_ci * bits as required according to the documentation.
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistruct stb6100_regmask {
918c2ecf20Sopenharmony_ci	u8 mask;
928c2ecf20Sopenharmony_ci	u8 set;
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic const struct stb6100_regmask stb6100_template[] = {
968c2ecf20Sopenharmony_ci	[STB6100_LD]		= { 0xff, 0x00 },
978c2ecf20Sopenharmony_ci	[STB6100_VCO]		= { 0xff, 0x00 },
988c2ecf20Sopenharmony_ci	[STB6100_NI]		= { 0xff, 0x00 },
998c2ecf20Sopenharmony_ci	[STB6100_NF_LSB]	= { 0xff, 0x00 },
1008c2ecf20Sopenharmony_ci	[STB6100_K]		= { 0xc7, 0x38 },
1018c2ecf20Sopenharmony_ci	[STB6100_G]		= { 0xef, 0x10 },
1028c2ecf20Sopenharmony_ci	[STB6100_F]		= { 0x1f, 0xc0 },
1038c2ecf20Sopenharmony_ci	[STB6100_DLB]		= { 0x38, 0xc4 },
1048c2ecf20Sopenharmony_ci	[STB6100_TEST1]		= { 0x00, 0x8f },
1058c2ecf20Sopenharmony_ci	[STB6100_FCCK]		= { 0x40, 0x0d },
1068c2ecf20Sopenharmony_ci	[STB6100_LPEN]		= { 0xf0, 0x0b },
1078c2ecf20Sopenharmony_ci	[STB6100_TEST3]		= { 0x00, 0xde },
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/*
1118c2ecf20Sopenharmony_ci * Currently unused. Some boards might need it in the future
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic inline void stb6100_normalise_regs(u8 regs[])
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	int i;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	for (i = 0; i < STB6100_NUMREGS; i++)
1188c2ecf20Sopenharmony_ci		regs[i] = (regs[i] & stb6100_template[i].mask) | stb6100_template[i].set;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int stb6100_read_regs(struct stb6100_state *state, u8 regs[])
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int rc;
1248c2ecf20Sopenharmony_ci	struct i2c_msg msg = {
1258c2ecf20Sopenharmony_ci		.addr	= state->config->tuner_address,
1268c2ecf20Sopenharmony_ci		.flags	= I2C_M_RD,
1278c2ecf20Sopenharmony_ci		.buf	= regs,
1288c2ecf20Sopenharmony_ci		.len	= STB6100_NUMREGS
1298c2ecf20Sopenharmony_ci	};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	rc = i2c_transfer(state->i2c, &msg, 1);
1328c2ecf20Sopenharmony_ci	if (unlikely(rc != 1)) {
1338c2ecf20Sopenharmony_ci		dprintk(verbose, FE_ERROR, 1, "Read (0x%x) err, rc=[%d]",
1348c2ecf20Sopenharmony_ci			state->config->tuner_address, rc);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		return -EREMOTEIO;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci	if (unlikely(verbose > FE_DEBUG)) {
1398c2ecf20Sopenharmony_ci		int i;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		dprintk(verbose, FE_DEBUG, 1, "    Read from 0x%02x", state->config->tuner_address);
1428c2ecf20Sopenharmony_ci		for (i = 0; i < STB6100_NUMREGS; i++)
1438c2ecf20Sopenharmony_ci			dprintk(verbose, FE_DEBUG, 1, "        %s: 0x%02x", stb6100_regnames[i], regs[i]);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int stb6100_read_reg(struct stb6100_state *state, u8 reg)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	u8 regs[STB6100_NUMREGS];
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	struct i2c_msg msg = {
1538c2ecf20Sopenharmony_ci		.addr	= state->config->tuner_address + reg,
1548c2ecf20Sopenharmony_ci		.flags	= I2C_M_RD,
1558c2ecf20Sopenharmony_ci		.buf	= regs,
1568c2ecf20Sopenharmony_ci		.len	= 1
1578c2ecf20Sopenharmony_ci	};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	i2c_transfer(state->i2c, &msg, 1);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (unlikely(reg >= STB6100_NUMREGS)) {
1628c2ecf20Sopenharmony_ci		dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg);
1638c2ecf20Sopenharmony_ci		return -EINVAL;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	if (unlikely(verbose > FE_DEBUG)) {
1668c2ecf20Sopenharmony_ci		dprintk(verbose, FE_DEBUG, 1, "    Read from 0x%02x", state->config->tuner_address);
1678c2ecf20Sopenharmony_ci		dprintk(verbose, FE_DEBUG, 1, "        %s: 0x%02x", stb6100_regnames[reg], regs[0]);
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return (unsigned int)regs[0];
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int start, int len)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	int rc;
1768c2ecf20Sopenharmony_ci	u8 cmdbuf[MAX_XFER_SIZE];
1778c2ecf20Sopenharmony_ci	struct i2c_msg msg = {
1788c2ecf20Sopenharmony_ci		.addr	= state->config->tuner_address,
1798c2ecf20Sopenharmony_ci		.flags	= 0,
1808c2ecf20Sopenharmony_ci		.buf	= cmdbuf,
1818c2ecf20Sopenharmony_ci		.len	= len + 1
1828c2ecf20Sopenharmony_ci	};
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (1 + len > sizeof(cmdbuf)) {
1858c2ecf20Sopenharmony_ci		printk(KERN_WARNING
1868c2ecf20Sopenharmony_ci		       "%s: i2c wr: len=%d is too big!\n",
1878c2ecf20Sopenharmony_ci		       KBUILD_MODNAME, len);
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (unlikely(start < 1 || start + len > STB6100_NUMREGS)) {
1928c2ecf20Sopenharmony_ci		dprintk(verbose, FE_ERROR, 1, "Invalid register range %d:%d",
1938c2ecf20Sopenharmony_ci			start, len);
1948c2ecf20Sopenharmony_ci		return -EINVAL;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	memcpy(&cmdbuf[1], buf, len);
1978c2ecf20Sopenharmony_ci	cmdbuf[0] = start;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (unlikely(verbose > FE_DEBUG)) {
2008c2ecf20Sopenharmony_ci		int i;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		dprintk(verbose, FE_DEBUG, 1, "    Write @ 0x%02x: [%d:%d]", state->config->tuner_address, start, len);
2038c2ecf20Sopenharmony_ci		for (i = 0; i < len; i++)
2048c2ecf20Sopenharmony_ci			dprintk(verbose, FE_DEBUG, 1, "        %s: 0x%02x", stb6100_regnames[start + i], buf[i]);
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	rc = i2c_transfer(state->i2c, &msg, 1);
2078c2ecf20Sopenharmony_ci	if (unlikely(rc != 1)) {
2088c2ecf20Sopenharmony_ci		dprintk(verbose, FE_ERROR, 1, "(0x%x) write err [%d:%d], rc=[%d]",
2098c2ecf20Sopenharmony_ci			(unsigned int)state->config->tuner_address, start, len,	rc);
2108c2ecf20Sopenharmony_ci		return -EREMOTEIO;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci	return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int stb6100_write_reg(struct stb6100_state *state, u8 reg, u8 data)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	u8 tmp = data; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (unlikely(reg >= STB6100_NUMREGS)) {
2208c2ecf20Sopenharmony_ci		dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg);
2218c2ecf20Sopenharmony_ci		return -EREMOTEIO;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	tmp = (tmp & stb6100_template[reg].mask) | stb6100_template[reg].set;
2248c2ecf20Sopenharmony_ci	return stb6100_write_reg_range(state, &tmp, reg, 1);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int stb6100_get_status(struct dvb_frontend *fe, u32 *status)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	int rc;
2318c2ecf20Sopenharmony_ci	struct stb6100_state *state = fe->tuner_priv;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	rc = stb6100_read_reg(state, STB6100_LD);
2348c2ecf20Sopenharmony_ci	if (rc < 0) {
2358c2ecf20Sopenharmony_ci		dprintk(verbose, FE_ERROR, 1, "%s failed", __func__);
2368c2ecf20Sopenharmony_ci		return rc;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci	return (rc & STB6100_LD_LOCK) ? TUNER_STATUS_LOCKED : 0;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	int rc;
2448c2ecf20Sopenharmony_ci	u8 f;
2458c2ecf20Sopenharmony_ci	u32 bw;
2468c2ecf20Sopenharmony_ci	struct stb6100_state *state = fe->tuner_priv;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	rc = stb6100_read_reg(state, STB6100_F);
2498c2ecf20Sopenharmony_ci	if (rc < 0)
2508c2ecf20Sopenharmony_ci		return rc;
2518c2ecf20Sopenharmony_ci	f = rc & STB6100_F_F;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	bw = (f + 5) * 2000;	/* x2 for ZIF	*/
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	*bandwidth = state->bandwidth = bw * 1000;
2568c2ecf20Sopenharmony_ci	dprintk(verbose, FE_DEBUG, 1, "bandwidth = %u Hz", state->bandwidth);
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	u32 tmp;
2638c2ecf20Sopenharmony_ci	int rc;
2648c2ecf20Sopenharmony_ci	struct stb6100_state *state = fe->tuner_priv;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	dprintk(verbose, FE_DEBUG, 1, "set bandwidth to %u Hz", bandwidth);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	bandwidth /= 2; /* ZIF */
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (bandwidth >= 36000000)	/* F[4:0] BW/2 max =31+5=36 mhz for F=31	*/
2718c2ecf20Sopenharmony_ci		tmp = 31;
2728c2ecf20Sopenharmony_ci	else if (bandwidth <= 5000000)	/* bw/2 min = 5Mhz for F=0			*/
2738c2ecf20Sopenharmony_ci		tmp = 0;
2748c2ecf20Sopenharmony_ci	else				/* if 5 < bw/2 < 36				*/
2758c2ecf20Sopenharmony_ci		tmp = (bandwidth + 500000) / 1000000 - 5;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* Turn on LPF bandwidth setting clock control,
2788c2ecf20Sopenharmony_ci	 * set bandwidth, wait 10ms, turn off.
2798c2ecf20Sopenharmony_ci	 */
2808c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d | STB6100_FCCK_FCCK);
2818c2ecf20Sopenharmony_ci	if (rc < 0)
2828c2ecf20Sopenharmony_ci		return rc;
2838c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_F, 0xc0 | tmp);
2848c2ecf20Sopenharmony_ci	if (rc < 0)
2858c2ecf20Sopenharmony_ci		return rc;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	msleep(5);  /*  This is dangerous as another (related) thread may start */
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d);
2908c2ecf20Sopenharmony_ci	if (rc < 0)
2918c2ecf20Sopenharmony_ci		return rc;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	msleep(10);  /*  This is dangerous as another (related) thread may start */
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	int rc;
3018c2ecf20Sopenharmony_ci	u32 nint, nfrac, fvco;
3028c2ecf20Sopenharmony_ci	int psd2, odiv;
3038c2ecf20Sopenharmony_ci	struct stb6100_state *state = fe->tuner_priv;
3048c2ecf20Sopenharmony_ci	u8 regs[STB6100_NUMREGS];
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	rc = stb6100_read_regs(state, regs);
3078c2ecf20Sopenharmony_ci	if (rc < 0)
3088c2ecf20Sopenharmony_ci		return rc;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	odiv = (regs[STB6100_VCO] & STB6100_VCO_ODIV) >> STB6100_VCO_ODIV_SHIFT;
3118c2ecf20Sopenharmony_ci	psd2 = (regs[STB6100_K] & STB6100_K_PSD2) >> STB6100_K_PSD2_SHIFT;
3128c2ecf20Sopenharmony_ci	nint = regs[STB6100_NI];
3138c2ecf20Sopenharmony_ci	nfrac = ((regs[STB6100_K] & STB6100_K_NF_MSB) << 8) | regs[STB6100_NF_LSB];
3148c2ecf20Sopenharmony_ci	fvco = (nfrac * state->reference >> (9 - psd2)) + (nint * state->reference << psd2);
3158c2ecf20Sopenharmony_ci	*frequency = state->frequency = fvco >> (odiv + 1);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	dprintk(verbose, FE_DEBUG, 1,
3188c2ecf20Sopenharmony_ci		"frequency = %u kHz, odiv = %u, psd2 = %u, fxtal = %u kHz, fvco = %u kHz, N(I) = %u, N(F) = %u",
3198c2ecf20Sopenharmony_ci		state->frequency, odiv, psd2, state->reference,	fvco, nint, nfrac);
3208c2ecf20Sopenharmony_ci	return 0;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	int rc;
3278c2ecf20Sopenharmony_ci	const struct stb6100_lkup *ptr;
3288c2ecf20Sopenharmony_ci	struct stb6100_state *state = fe->tuner_priv;
3298c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	u32 srate = 0, fvco, nint, nfrac;
3328c2ecf20Sopenharmony_ci	u8 regs[STB6100_NUMREGS];
3338c2ecf20Sopenharmony_ci	u8 g, psd2, odiv;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	dprintk(verbose, FE_DEBUG, 1, "Version 2010-8-14 13:51");
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (fe->ops.get_frontend) {
3388c2ecf20Sopenharmony_ci		dprintk(verbose, FE_DEBUG, 1, "Get frontend parameters");
3398c2ecf20Sopenharmony_ci		fe->ops.get_frontend(fe, p);
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci	srate = p->symbol_rate;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Set up tuner cleanly, LPF calibration on */
3448c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_FCCK, 0x4d | STB6100_FCCK_FCCK);
3458c2ecf20Sopenharmony_ci	if (rc < 0)
3468c2ecf20Sopenharmony_ci		return rc;  /* allow LPF calibration */
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* PLL Loop disabled, bias on, VCO on, synth on */
3498c2ecf20Sopenharmony_ci	regs[STB6100_LPEN] = 0xeb;
3508c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN]);
3518c2ecf20Sopenharmony_ci	if (rc < 0)
3528c2ecf20Sopenharmony_ci		return rc;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/* Program the registers with their data values */
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* VCO divide ratio (LO divide ratio, VCO prescaler enable).	*/
3578c2ecf20Sopenharmony_ci	if (frequency <= 1075000)
3588c2ecf20Sopenharmony_ci		odiv = 1;
3598c2ecf20Sopenharmony_ci	else
3608c2ecf20Sopenharmony_ci		odiv = 0;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* VCO enabled, search clock off as per LL3.7, 3.4.1 */
3638c2ecf20Sopenharmony_ci	regs[STB6100_VCO] = 0xe0 | (odiv << STB6100_VCO_ODIV_SHIFT);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* OSM	*/
3668c2ecf20Sopenharmony_ci	for (ptr = lkup;
3678c2ecf20Sopenharmony_ci	     (ptr->val_high != 0) && !CHKRANGE(frequency, ptr->val_low, ptr->val_high);
3688c2ecf20Sopenharmony_ci	     ptr++);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (ptr->val_high == 0) {
3718c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: frequency out of range: %u kHz\n", __func__, frequency);
3728c2ecf20Sopenharmony_ci		return -EINVAL;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci	regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_OSM) | ptr->reg;
3758c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]);
3768c2ecf20Sopenharmony_ci	if (rc < 0)
3778c2ecf20Sopenharmony_ci		return rc;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if ((frequency > 1075000) && (frequency <= 1325000))
3808c2ecf20Sopenharmony_ci		psd2 = 0;
3818c2ecf20Sopenharmony_ci	else
3828c2ecf20Sopenharmony_ci		psd2 = 1;
3838c2ecf20Sopenharmony_ci	/* F(VCO) = F(LO) * (ODIV == 0 ? 2 : 4)			*/
3848c2ecf20Sopenharmony_ci	fvco = frequency << (1 + odiv);
3858c2ecf20Sopenharmony_ci	/* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1)))	*/
3868c2ecf20Sopenharmony_ci	nint = fvco / (state->reference << psd2);
3878c2ecf20Sopenharmony_ci	/* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9	*/
3888c2ecf20Sopenharmony_ci	nfrac = DIV_ROUND_CLOSEST((fvco - (nint * state->reference << psd2))
3898c2ecf20Sopenharmony_ci					 << (9 - psd2), state->reference);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* NI */
3928c2ecf20Sopenharmony_ci	regs[STB6100_NI] = nint;
3938c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_NI, regs[STB6100_NI]);
3948c2ecf20Sopenharmony_ci	if (rc < 0)
3958c2ecf20Sopenharmony_ci		return rc;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* NF */
3988c2ecf20Sopenharmony_ci	regs[STB6100_NF_LSB] = nfrac;
3998c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_NF_LSB, regs[STB6100_NF_LSB]);
4008c2ecf20Sopenharmony_ci	if (rc < 0)
4018c2ecf20Sopenharmony_ci		return rc;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* K */
4048c2ecf20Sopenharmony_ci	regs[STB6100_K] = (0x38 & ~STB6100_K_PSD2) | (psd2 << STB6100_K_PSD2_SHIFT);
4058c2ecf20Sopenharmony_ci	regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_NF_MSB) | ((nfrac >> 8) & STB6100_K_NF_MSB);
4068c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_K, regs[STB6100_K]);
4078c2ecf20Sopenharmony_ci	if (rc < 0)
4088c2ecf20Sopenharmony_ci		return rc;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* G Baseband gain. */
4118c2ecf20Sopenharmony_ci	if (srate >= 15000000)
4128c2ecf20Sopenharmony_ci		g = 9;  /*  +4 dB */
4138c2ecf20Sopenharmony_ci	else if (srate >= 5000000)
4148c2ecf20Sopenharmony_ci		g = 11; /*  +8 dB */
4158c2ecf20Sopenharmony_ci	else
4168c2ecf20Sopenharmony_ci		g = 14; /* +14 dB */
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	regs[STB6100_G] = (0x10 & ~STB6100_G_G) | g;
4198c2ecf20Sopenharmony_ci	regs[STB6100_G] &= ~STB6100_G_GCT; /* mask GCT */
4208c2ecf20Sopenharmony_ci	regs[STB6100_G] |= (1 << 5); /* 2Vp-p Mode */
4218c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_G, regs[STB6100_G]);
4228c2ecf20Sopenharmony_ci	if (rc < 0)
4238c2ecf20Sopenharmony_ci		return rc;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* F we don't write as it is set up in BW set */
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* DLB set DC servo loop BW to 160Hz (LLA 3.8 / 2.1) */
4288c2ecf20Sopenharmony_ci	regs[STB6100_DLB] = 0xcc;
4298c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_DLB, regs[STB6100_DLB]);
4308c2ecf20Sopenharmony_ci	if (rc < 0)
4318c2ecf20Sopenharmony_ci		return rc;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	dprintk(verbose, FE_DEBUG, 1,
4348c2ecf20Sopenharmony_ci		"frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u",
4358c2ecf20Sopenharmony_ci		frequency, srate, (unsigned int)g, (unsigned int)odiv,
4368c2ecf20Sopenharmony_ci		(unsigned int)psd2, state->reference,
4378c2ecf20Sopenharmony_ci		ptr->reg, fvco, nint, nfrac);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Set up the test registers */
4408c2ecf20Sopenharmony_ci	regs[STB6100_TEST1] = 0x8f;
4418c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_TEST1, regs[STB6100_TEST1]);
4428c2ecf20Sopenharmony_ci	if (rc < 0)
4438c2ecf20Sopenharmony_ci		return rc;
4448c2ecf20Sopenharmony_ci	regs[STB6100_TEST3] = 0xde;
4458c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_TEST3, regs[STB6100_TEST3]);
4468c2ecf20Sopenharmony_ci	if (rc < 0)
4478c2ecf20Sopenharmony_ci		return rc;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/* Bring up tuner according to LLA 3.7 3.4.1, step 2 */
4508c2ecf20Sopenharmony_ci	regs[STB6100_LPEN] = 0xfb; /* PLL Loop enabled, bias on, VCO on, synth on */
4518c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN]);
4528c2ecf20Sopenharmony_ci	if (rc < 0)
4538c2ecf20Sopenharmony_ci		return rc;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	msleep(2);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Bring up tuner according to LLA 3.7 3.4.1, step 3 */
4588c2ecf20Sopenharmony_ci	regs[STB6100_VCO] &= ~STB6100_VCO_OCK;		/* VCO fast search		*/
4598c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]);
4608c2ecf20Sopenharmony_ci	if (rc < 0)
4618c2ecf20Sopenharmony_ci		return rc;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	msleep(10);  /*  This is dangerous as another (related) thread may start */ /* wait for LO to lock */
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	regs[STB6100_VCO] &= ~STB6100_VCO_OSCH;		/* vco search disabled		*/
4668c2ecf20Sopenharmony_ci	regs[STB6100_VCO] |= STB6100_VCO_OCK;		/* search clock off		*/
4678c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]);
4688c2ecf20Sopenharmony_ci	if (rc < 0)
4698c2ecf20Sopenharmony_ci		return rc;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d);
4728c2ecf20Sopenharmony_ci	if (rc < 0)
4738c2ecf20Sopenharmony_ci		return rc;  /* Stop LPF calibration */
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	msleep(10);  /*  This is dangerous as another (related) thread may start */
4768c2ecf20Sopenharmony_ci		     /* wait for stabilisation, (should not be necessary)		*/
4778c2ecf20Sopenharmony_ci	return 0;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic int stb6100_sleep(struct dvb_frontend *fe)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	/* TODO: power down	*/
4838c2ecf20Sopenharmony_ci	return 0;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic int stb6100_init(struct dvb_frontend *fe)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	struct stb6100_state *state = fe->tuner_priv;
4898c2ecf20Sopenharmony_ci	int refclk = 27000000; /* Hz */
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/*
4928c2ecf20Sopenharmony_ci	 * iqsense = 1
4938c2ecf20Sopenharmony_ci	 * tunerstep = 125000
4948c2ecf20Sopenharmony_ci	 */
4958c2ecf20Sopenharmony_ci	state->bandwidth        = 36000000;		/* Hz	*/
4968c2ecf20Sopenharmony_ci	state->reference	= refclk / 1000;	/* kHz	*/
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	/* Set default bandwidth. Modified, PN 13-May-10	*/
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic int stb6100_set_params(struct dvb_frontend *fe)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (c->frequency > 0)
5078c2ecf20Sopenharmony_ci		stb6100_set_frequency(fe, c->frequency);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (c->bandwidth_hz > 0)
5108c2ecf20Sopenharmony_ci		stb6100_set_bandwidth(fe, c->bandwidth_hz);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	return 0;
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops stb6100_ops = {
5168c2ecf20Sopenharmony_ci	.info = {
5178c2ecf20Sopenharmony_ci		.name			= "STB6100 Silicon Tuner",
5188c2ecf20Sopenharmony_ci		.frequency_min_hz	=  950 * MHz,
5198c2ecf20Sopenharmony_ci		.frequency_max_hz	= 2150 * MHz,
5208c2ecf20Sopenharmony_ci	},
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	.init		= stb6100_init,
5238c2ecf20Sopenharmony_ci	.sleep          = stb6100_sleep,
5248c2ecf20Sopenharmony_ci	.get_status	= stb6100_get_status,
5258c2ecf20Sopenharmony_ci	.set_params	= stb6100_set_params,
5268c2ecf20Sopenharmony_ci	.get_frequency  = stb6100_get_frequency,
5278c2ecf20Sopenharmony_ci	.get_bandwidth  = stb6100_get_bandwidth,
5288c2ecf20Sopenharmony_ci	.release	= stb6100_release
5298c2ecf20Sopenharmony_ci};
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistruct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
5328c2ecf20Sopenharmony_ci				    const struct stb6100_config *config,
5338c2ecf20Sopenharmony_ci				    struct i2c_adapter *i2c)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct stb6100_state *state = NULL;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	state = kzalloc(sizeof (struct stb6100_state), GFP_KERNEL);
5388c2ecf20Sopenharmony_ci	if (!state)
5398c2ecf20Sopenharmony_ci		return NULL;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	state->config		= config;
5428c2ecf20Sopenharmony_ci	state->i2c		= i2c;
5438c2ecf20Sopenharmony_ci	state->frontend		= fe;
5448c2ecf20Sopenharmony_ci	state->reference	= config->refclock / 1000; /* kHz */
5458c2ecf20Sopenharmony_ci	fe->tuner_priv		= state;
5468c2ecf20Sopenharmony_ci	fe->ops.tuner_ops	= stb6100_ops;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	printk("%s: Attaching STB6100 \n", __func__);
5498c2ecf20Sopenharmony_ci	return fe;
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic void stb6100_release(struct dvb_frontend *fe)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct stb6100_state *state = fe->tuner_priv;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
5578c2ecf20Sopenharmony_ci	kfree(state);
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stb6100_attach);
5618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(verbose, "Set Verbosity level");
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manu Abraham");
5648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STB6100 Silicon tuner");
5658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
566