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