diff options
Diffstat (limited to 'drivers/media/dvb/frontends')
35 files changed, 3836 insertions, 209 deletions
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 32e08e35152..4a2d2e6c91a 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -236,6 +236,13 @@ config DVB_MB86A16 A DVB-S/DSS Direct Conversion reveiver. Say Y when you want to support this frontend. +config DVB_TDA10071 + tristate "NXP TDA10071" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + Say Y when you want to support this frontend. + comment "DVB-T (terrestrial) frontends" depends on DVB_CORE @@ -600,6 +607,16 @@ config DVB_LNBP21 help An SEC control chips. +config DVB_LNBP22 + tristate "LNBP22 SEC controllers" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + LNB power supply and control voltage + regulator chip with step-up converter + and I2C interface. + Say Y when you want to support this chip. + config DVB_ISL6405 tristate "ISL6405 SEC controller" depends on DVB_CORE && I2C @@ -621,6 +638,11 @@ config DVB_ISL6423 help A SEC controller chip from Intersil +config DVB_A8293 + tristate "Allegro A8293" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + config DVB_LGS8GL5 tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" depends on DVB_CORE && I2C @@ -661,6 +683,14 @@ config DVB_IX2505V help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_IT913X_FE + tristate "it913x frontend and it9137 tuner" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T tuner module. + Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 6a6ba053ead..f639f678155 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -2,8 +2,8 @@ # Makefile for the kernel DVB frontend device drivers. # -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -EXTRA_CFLAGS += -Idrivers/media/common/tuners/ +ccflags-y += -Idrivers/media/dvb/dvb-core/ +ccflags-y += -Idrivers/media/common/tuners/ stb0899-objs = stb0899_drv.o stb0899_algo.o stv0900-objs = stv0900_core.o stv0900_sw.o @@ -52,6 +52,7 @@ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o +obj-$(CONFIG_DVB_LNBP22) += lnbp22.o obj-$(CONFIG_DVB_ISL6405) += isl6405.o obj-$(CONFIG_DVB_ISL6421) += isl6421.o obj-$(CONFIG_DVB_TDA10086) += tda10086.o @@ -91,4 +92,7 @@ obj-$(CONFIG_DVB_STV0367) += stv0367.o obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o obj-$(CONFIG_DVB_DRXK) += drxk.o obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o +obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o +obj-$(CONFIG_DVB_A8293) += a8293.o +obj-$(CONFIG_DVB_TDA10071) += tda10071.o diff --git a/drivers/media/dvb/frontends/a8293.c b/drivers/media/dvb/frontends/a8293.c new file mode 100644 index 00000000000..bb56497e940 --- /dev/null +++ b/drivers/media/dvb/frontends/a8293.c @@ -0,0 +1,184 @@ +/* + * Allegro A8293 SEC driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "dvb_frontend.h" +#include "a8293.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define LOG_PREFIX "a8293" + +#undef dbg +#define dbg(f, arg...) \ + if (debug) \ + printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + + +struct a8293_priv { + struct i2c_adapter *i2c; + const struct a8293_config *cfg; + u8 reg[2]; +}; + +static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd) +{ + int ret; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .len = len, + .buf = val, + } + }; + + if (rd) + msg[0].flags = I2C_M_RD; + else + msg[0].flags = 0; + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + warn("i2c failed=%d rd=%d", ret, rd); + ret = -EREMOTEIO; + } + + return ret; +} + +static int a8293_wr(struct a8293_priv *priv, u8 *val, int len) +{ + return a8293_i2c(priv, val, len, 0); +} + +static int a8293_rd(struct a8293_priv *priv, u8 *val, int len) +{ + return a8293_i2c(priv, val, len, 1); +} + +static int a8293_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t fe_sec_voltage) +{ + struct a8293_priv *priv = fe->sec_priv; + int ret; + + dbg("%s: fe_sec_voltage=%d", __func__, fe_sec_voltage); + + switch (fe_sec_voltage) { + case SEC_VOLTAGE_OFF: + /* ENB=0 */ + priv->reg[0] = 0x10; + break; + case SEC_VOLTAGE_13: + /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/ + priv->reg[0] = 0x31; + break; + case SEC_VOLTAGE_18: + /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/ + priv->reg[0] = 0x38; + break; + default: + ret = -EINVAL; + goto err; + }; + + ret = a8293_wr(priv, &priv->reg[0], 1); + if (ret) + goto err; + + return ret; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static void a8293_release_sec(struct dvb_frontend *fe) +{ + dbg("%s:", __func__); + + a8293_set_voltage(fe, SEC_VOLTAGE_OFF); + + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct a8293_config *cfg) +{ + int ret; + struct a8293_priv *priv = NULL; + u8 buf[2]; + + /* allocate memory for the internal priv */ + priv = kzalloc(sizeof(struct a8293_priv), GFP_KERNEL); + if (priv == NULL) { + ret = -ENOMEM; + goto err; + } + + /* setup the priv */ + priv->i2c = i2c; + priv->cfg = cfg; + fe->sec_priv = priv; + + /* check if the SEC is there */ + ret = a8293_rd(priv, buf, 2); + if (ret) + goto err; + + /* ENB=0 */ + priv->reg[0] = 0x10; + ret = a8293_wr(priv, &priv->reg[1], 1); + if (ret) + goto err; + + /* TMODE=0, TGATE=1 */ + priv->reg[1] = 0x82; + ret = a8293_wr(priv, &priv->reg[1], 1); + if (ret) + goto err; + + info("Allegro A8293 SEC attached."); + + fe->ops.release_sec = a8293_release_sec; + + /* override frontend ops */ + fe->ops.set_voltage = a8293_set_voltage; + + return fe; +err: + dbg("%s: failed=%d", __func__, ret); + kfree(priv); + return NULL; +} +EXPORT_SYMBOL(a8293_attach); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Allegro A8293 SEC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/a8293.h b/drivers/media/dvb/frontends/a8293.h new file mode 100644 index 00000000000..ed29e5504f7 --- /dev/null +++ b/drivers/media/dvb/frontends/a8293.h @@ -0,0 +1,41 @@ +/* + * Allegro A8293 SEC driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef A8293_H +#define A8293_H + +struct a8293_config { + u8 i2c_addr; +}; + +#if defined(CONFIG_DVB_A8293) || \ + (defined(CONFIG_DVB_A8293_MODULE) && defined(MODULE)) +extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct a8293_config *cfg); +#else +static inline struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct a8293_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* A8293_H */ diff --git a/drivers/media/dvb/frontends/cxd2820r.h b/drivers/media/dvb/frontends/cxd2820r.h index 2906582dc94..03cab7b547f 100644 --- a/drivers/media/dvb/frontends/cxd2820r.h +++ b/drivers/media/dvb/frontends/cxd2820r.h @@ -93,9 +93,6 @@ extern struct dvb_frontend *cxd2820r_attach( struct i2c_adapter *i2c, struct dvb_frontend *fe ); -extern struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter( - struct dvb_frontend *fe -); #else static inline struct dvb_frontend *cxd2820r_attach( const struct cxd2820r_config *config, @@ -106,12 +103,6 @@ static inline struct dvb_frontend *cxd2820r_attach( printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -static inline struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter( - struct dvb_frontend *fe -) -{ - return NULL; -} #endif diff --git a/drivers/media/dvb/frontends/cxd2820r_c.c b/drivers/media/dvb/frontends/cxd2820r_c.c index 3c07d400731..b85f5011e34 100644 --- a/drivers/media/dvb/frontends/cxd2820r_c.c +++ b/drivers/media/dvb/frontends/cxd2820r_c.c @@ -335,4 +335,3 @@ int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe, return 0; } - diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index d416e85589e..036480f967b 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -727,72 +727,22 @@ static void cxd2820r_release(struct dvb_frontend *fe) struct cxd2820r_priv *priv = fe->demodulator_priv; dbg("%s", __func__); - if (fe->ops.info.type == FE_OFDM) { - i2c_del_adapter(&priv->tuner_i2c_adapter); + if (fe->ops.info.type == FE_OFDM) kfree(priv); - } return; } -static u32 cxd2820r_tuner_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C; -} - -static int cxd2820r_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msg[], int num) -{ - struct cxd2820r_priv *priv = i2c_get_adapdata(i2c_adap); - int ret; - u8 *obuf = kmalloc(msg[0].len + 2, GFP_KERNEL); - struct i2c_msg msg2[2] = { - { - .addr = priv->cfg.i2c_address, - .flags = 0, - .len = msg[0].len + 2, - .buf = obuf, - }, { - .addr = priv->cfg.i2c_address, - .flags = I2C_M_RD, - .len = msg[1].len, - .buf = msg[1].buf, - } - }; - - if (!obuf) - return -ENOMEM; - - obuf[0] = 0x09; - obuf[1] = (msg[0].addr << 1); - if (num == 2) { /* I2C read */ - obuf[1] = (msg[0].addr << 1) | I2C_M_RD; /* I2C RD flag */ - msg2[0].len = msg[0].len + 2 - 1; /* '-1' maybe HW bug ? */ - } - memcpy(&obuf[2], msg[0].buf, msg[0].len); - - ret = i2c_transfer(priv->i2c, msg2, num); - if (ret < 0) - warn("tuner i2c failed ret:%d", ret); - - kfree(obuf); - - return ret; -} - -static struct i2c_algorithm cxd2820r_tuner_i2c_algo = { - .master_xfer = cxd2820r_tuner_i2c_xfer, - .functionality = cxd2820r_tuner_i2c_func, -}; - -struct i2c_adapter *cxd2820r_get_tuner_i2c_adapter(struct dvb_frontend *fe) +static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { struct cxd2820r_priv *priv = fe->demodulator_priv; - return &priv->tuner_i2c_adapter; + dbg("%s: %d", __func__, enable); + + /* Bit 0 of reg 0xdb in bank 0x00 controls I2C repeater */ + return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1); } -EXPORT_SYMBOL(cxd2820r_get_tuner_i2c_adapter); -static struct dvb_frontend_ops cxd2820r_ops[2]; +static const struct dvb_frontend_ops cxd2820r_ops[2]; struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, struct i2c_adapter *i2c, struct dvb_frontend *fe) @@ -831,18 +781,6 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, priv->fe[0].demodulator_priv = priv; priv->fe[1].demodulator_priv = priv; - /* create tuner i2c adapter */ - strlcpy(priv->tuner_i2c_adapter.name, - "CXD2820R tuner I2C adapter", - sizeof(priv->tuner_i2c_adapter.name)); - priv->tuner_i2c_adapter.algo = &cxd2820r_tuner_i2c_algo; - priv->tuner_i2c_adapter.algo_data = NULL; - i2c_set_adapdata(&priv->tuner_i2c_adapter, priv); - if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) { - err("tuner I2C bus could not be initialized"); - goto error; - } - return &priv->fe[0]; } else { @@ -858,7 +796,7 @@ error: } EXPORT_SYMBOL(cxd2820r_attach); -static struct dvb_frontend_ops cxd2820r_ops[2] = { +static const struct dvb_frontend_ops cxd2820r_ops[2] = { { /* DVB-T/T2 */ .info = { @@ -883,6 +821,7 @@ static struct dvb_frontend_ops cxd2820r_ops[2] = { .sleep = cxd2820r_sleep, .get_tune_settings = cxd2820r_get_tune_settings, + .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl, .get_frontend = cxd2820r_get_frontend, @@ -911,6 +850,7 @@ static struct dvb_frontend_ops cxd2820r_ops[2] = { .sleep = cxd2820r_sleep, .get_tune_settings = cxd2820r_get_tune_settings, + .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl, .set_frontend = cxd2820r_set_frontend, .get_frontend = cxd2820r_get_frontend, diff --git a/drivers/media/dvb/frontends/cxd2820r_priv.h b/drivers/media/dvb/frontends/cxd2820r_priv.h index 0c0ebc9d5c4..95539134efd 100644 --- a/drivers/media/dvb/frontends/cxd2820r_priv.h +++ b/drivers/media/dvb/frontends/cxd2820r_priv.h @@ -50,7 +50,6 @@ struct cxd2820r_priv { struct i2c_adapter *i2c; struct dvb_frontend fe[2]; struct cxd2820r_config cfg; - struct i2c_adapter tuner_i2c_adapter; struct mutex fe_lock; /* FE lock */ int active_fe:2; /* FE lock, -1=NONE, 0=DVB-T/T2, 1=DVB-C */ diff --git a/drivers/media/dvb/frontends/cxd2820r_t.c b/drivers/media/dvb/frontends/cxd2820r_t.c index 6582564c930..a04f9c81010 100644 --- a/drivers/media/dvb/frontends/cxd2820r_t.c +++ b/drivers/media/dvb/frontends/cxd2820r_t.c @@ -446,4 +446,3 @@ int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, return 0; } - diff --git a/drivers/media/dvb/frontends/cxd2820r_t2.c b/drivers/media/dvb/frontends/cxd2820r_t2.c index c47b35c8acf..6548588309f 100644 --- a/drivers/media/dvb/frontends/cxd2820r_t2.c +++ b/drivers/media/dvb/frontends/cxd2820r_t2.c @@ -420,4 +420,3 @@ int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, return 0; } - diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c index 1d47d4da7d4..dc1cb17a6ea 100644 --- a/drivers/media/dvb/frontends/dib0070.c +++ b/drivers/media/dvb/frontends/dib0070.c @@ -27,6 +27,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/mutex.h> #include "dvb_frontend.h" @@ -78,10 +79,18 @@ struct dib0070_state { struct i2c_msg msg[2]; u8 i2c_write_buffer[3]; u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; }; -static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg) +static u16 dib0070_read_reg(struct dib0070_state *state, u8 reg) { + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + state->i2c_write_buffer[0] = reg; memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); @@ -96,13 +105,23 @@ static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg) if (i2c_transfer(state->i2c, state->msg, 2) != 2) { printk(KERN_WARNING "DiB0070 I2C read failed\n"); - return 0; - } - return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + ret = 0; + } else + ret = (state->i2c_read_buffer[0] << 8) + | state->i2c_read_buffer[1]; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; } static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) { + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } state->i2c_write_buffer[0] = reg; state->i2c_write_buffer[1] = val >> 8; state->i2c_write_buffer[2] = val & 0xff; @@ -115,9 +134,12 @@ static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) if (i2c_transfer(state->i2c, state->msg, 1) != 1) { printk(KERN_WARNING "DiB0070 I2C write failed\n"); - return -EREMOTEIO; - } - return 0; + ret = -EREMOTEIO; + } else + ret = 0; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; } #define HARD_RESET(state) do { \ @@ -734,6 +756,7 @@ struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter state->cfg = cfg; state->i2c = i2c; state->fe = fe; + mutex_init(&state->i2c_buffer_lock); fe->tuner_priv = state; if (dib0070_reset(fe) != 0) diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c index c9c935ae41e..b174d1c7858 100644 --- a/drivers/media/dvb/frontends/dib0090.c +++ b/drivers/media/dvb/frontends/dib0090.c @@ -27,6 +27,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/mutex.h> #include "dvb_frontend.h" @@ -196,6 +197,7 @@ struct dib0090_state { struct i2c_msg msg[2]; u8 i2c_write_buffer[3]; u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; }; struct dib0090_fw_state { @@ -208,10 +210,18 @@ struct dib0090_fw_state { struct i2c_msg msg; u8 i2c_write_buffer[2]; u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; }; static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) { + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + state->i2c_write_buffer[0] = reg; memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); @@ -226,14 +236,24 @@ static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) if (i2c_transfer(state->i2c, state->msg, 2) != 2) { printk(KERN_WARNING "DiB0090 I2C read failed\n"); - return 0; - } + ret = 0; + } else + ret = (state->i2c_read_buffer[0] << 8) + | state->i2c_read_buffer[1]; - return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + mutex_unlock(&state->i2c_buffer_lock); + return ret; } static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val) { + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + state->i2c_write_buffer[0] = reg & 0xff; state->i2c_write_buffer[1] = val >> 8; state->i2c_write_buffer[2] = val & 0xff; @@ -246,13 +266,23 @@ static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val) if (i2c_transfer(state->i2c, state->msg, 1) != 1) { printk(KERN_WARNING "DiB0090 I2C write failed\n"); - return -EREMOTEIO; - } - return 0; + ret = -EREMOTEIO; + } else + ret = 0; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; } static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg) { + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + state->i2c_write_buffer[0] = reg; memset(&state->msg, 0, sizeof(struct i2c_msg)); @@ -262,13 +292,24 @@ static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg) state->msg.len = 2; if (i2c_transfer(state->i2c, &state->msg, 1) != 1) { printk(KERN_WARNING "DiB0090 I2C read failed\n"); - return 0; - } - return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + ret = 0; + } else + ret = (state->i2c_read_buffer[0] << 8) + | state->i2c_read_buffer[1]; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; } static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val) { + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + state->i2c_write_buffer[0] = val >> 8; state->i2c_write_buffer[1] = val & 0xff; @@ -279,9 +320,12 @@ static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val) state->msg.len = 2; if (i2c_transfer(state->i2c, &state->msg, 1) != 1) { printk(KERN_WARNING "DiB0090 I2C write failed\n"); - return -EREMOTEIO; - } - return 0; + ret = -EREMOTEIO; + } else + ret = 0; + + mutex_unlock(&state->i2c_buffer_lock); + return ret; } #define HARD_RESET(state) do { if (cfg->reset) { if (cfg->sleep) cfg->sleep(fe, 0); msleep(10); cfg->reset(fe, 1); msleep(10); cfg->reset(fe, 0); msleep(10); } } while (0) @@ -2440,6 +2484,7 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte st->config = config; st->i2c = i2c; st->fe = fe; + mutex_init(&st->i2c_buffer_lock); fe->tuner_priv = st; if (config->wbd == NULL) @@ -2471,6 +2516,7 @@ struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_ada st->config = config; st->i2c = i2c; st->fe = fe; + mutex_init(&st->i2c_buffer_lock); fe->tuner_priv = st; if (dib0090_fw_reset_digital(fe, st->config) != 0) diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c index 79cb1c20df2..dbb76d75c93 100644 --- a/drivers/media/dvb/frontends/dib7000m.c +++ b/drivers/media/dvb/frontends/dib7000m.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/mutex.h> #include "dvb_frontend.h" @@ -55,6 +56,7 @@ struct dib7000m_state { struct i2c_msg msg[2]; u8 i2c_write_buffer[4]; u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; }; enum dib7000m_power_mode { @@ -69,6 +71,13 @@ enum dib7000m_power_mode { static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) { + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + state->i2c_write_buffer[0] = (reg >> 8) | 0x80; state->i2c_write_buffer[1] = reg & 0xff; @@ -85,11 +94,21 @@ static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2) dprintk("i2c read error on %d",reg); - return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + mutex_unlock(&state->i2c_buffer_lock); + + return ret; } static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) { + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + state->i2c_write_buffer[0] = (reg >> 8) & 0xff; state->i2c_write_buffer[1] = reg & 0xff; state->i2c_write_buffer[2] = (val >> 8) & 0xff; @@ -101,7 +120,10 @@ static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) state->msg[0].buf = state->i2c_write_buffer; state->msg[0].len = 4; - return i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0; + ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? + -EREMOTEIO : 0); + mutex_unlock(&state->i2c_buffer_lock); + return ret; } static void dib7000m_write_tab(struct dib7000m_state *state, u16 *buf) { @@ -1385,6 +1407,7 @@ struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, demod = &st->demod; demod->demodulator_priv = st; memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops)); + mutex_init(&st->i2c_buffer_lock); st->timf_default = cfg->bw->timf; diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index a64a538ba36..ce8534ff142 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/mutex.h> #include "dvb_math.h" #include "dvb_frontend.h" @@ -68,6 +69,7 @@ struct dib7000p_state { struct i2c_msg msg[2]; u8 i2c_write_buffer[4]; u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; }; enum dib7000p_power_mode { @@ -81,6 +83,13 @@ static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff); static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) { + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + state->i2c_write_buffer[0] = reg >> 8; state->i2c_write_buffer[1] = reg & 0xff; @@ -97,11 +106,20 @@ static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2) dprintk("i2c read error on %d", reg); - return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + mutex_unlock(&state->i2c_buffer_lock); + return ret; } static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) { + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + state->i2c_write_buffer[0] = (reg >> 8) & 0xff; state->i2c_write_buffer[1] = reg & 0xff; state->i2c_write_buffer[2] = (val >> 8) & 0xff; @@ -113,7 +131,10 @@ static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) state->msg[0].buf = state->i2c_write_buffer; state->msg[0].len = 4; - return i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0; + ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? + -EREMOTEIO : 0); + mutex_unlock(&state->i2c_buffer_lock); + return ret; } static void dib7000p_write_tab(struct dib7000p_state *state, u16 * buf) @@ -1577,8 +1598,8 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap) return -ENOMEM; rx = kzalloc(2*sizeof(u8), GFP_KERNEL); if (!rx) { - goto rx_memory_error; ret = -ENOMEM; + goto rx_memory_error; } msg[0].buf = tx; @@ -1646,6 +1667,7 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau return -ENOMEM; dpst->i2c_adap = i2c; + mutex_init(&dpst->i2c_buffer_lock); for (k = no_of_demods - 1; k >= 0; k--) { dpst->cfg = cfg[k]; @@ -2324,6 +2346,7 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, demod = &st->demod; demod->demodulator_priv = st; memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); + mutex_init(&st->i2c_buffer_lock); dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */ @@ -2333,8 +2356,9 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, st->version = dib7000p_read_word(st, 897); /* FIXME: make sure the dev.parent field is initialized, or else - request_firmware() will hit an OOPS (this should be moved somewhere - more common) */ + request_firmware() will hit an OOPS (this should be moved somewhere + more common) */ + st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; /* FIXME: make sure the dev.parent field is initialized, or else request_firmware() will hit an OOPS (this should be moved somewhere diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 7d2ea112ae2..fe284d5292f 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -10,6 +10,8 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/mutex.h> + #include "dvb_math.h" #include "dvb_frontend.h" @@ -37,6 +39,7 @@ struct i2c_device { u8 addr; u8 *i2c_write_buffer; u8 *i2c_read_buffer; + struct mutex *i2c_buffer_lock; }; struct dib8000_state { @@ -77,6 +80,7 @@ struct dib8000_state { struct i2c_msg msg[2]; u8 i2c_write_buffer[4]; u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; }; enum dib8000_power_mode { @@ -86,24 +90,39 @@ enum dib8000_power_mode { static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg) { + u16 ret; struct i2c_msg msg[2] = { - {.addr = i2c->addr >> 1, .flags = 0, - .buf = i2c->i2c_write_buffer, .len = 2}, - {.addr = i2c->addr >> 1, .flags = I2C_M_RD, - .buf = i2c->i2c_read_buffer, .len = 2}, + {.addr = i2c->addr >> 1, .flags = 0, .len = 2}, + {.addr = i2c->addr >> 1, .flags = I2C_M_RD, .len = 2}, }; + if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + + msg[0].buf = i2c->i2c_write_buffer; msg[0].buf[0] = reg >> 8; msg[0].buf[1] = reg & 0xff; + msg[1].buf = i2c->i2c_read_buffer; if (i2c_transfer(i2c->adap, msg, 2) != 2) dprintk("i2c read error on %d", reg); - return (msg[1].buf[0] << 8) | msg[1].buf[1]; + ret = (msg[1].buf[0] << 8) | msg[1].buf[1]; + mutex_unlock(i2c->i2c_buffer_lock); + return ret; } static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) { + u16 ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + state->i2c_write_buffer[0] = reg >> 8; state->i2c_write_buffer[1] = reg & 0xff; @@ -120,7 +139,10 @@ static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) if (i2c_transfer(state->i2c.adap, state->msg, 2) != 2) dprintk("i2c read error on %d", reg); - return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; + mutex_unlock(&state->i2c_buffer_lock); + + return ret; } static u32 dib8000_read32(struct dib8000_state *state, u16 reg) @@ -135,22 +157,35 @@ static u32 dib8000_read32(struct dib8000_state *state, u16 reg) static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) { - struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0, - .buf = i2c->i2c_write_buffer, .len = 4}; + struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0, .len = 4}; int ret = 0; + if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + + msg.buf = i2c->i2c_write_buffer; msg.buf[0] = (reg >> 8) & 0xff; msg.buf[1] = reg & 0xff; msg.buf[2] = (val >> 8) & 0xff; msg.buf[3] = val & 0xff; ret = i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0; + mutex_unlock(i2c->i2c_buffer_lock); return ret; } static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val) { + int ret; + + if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + state->i2c_write_buffer[0] = (reg >> 8) & 0xff; state->i2c_write_buffer[1] = reg & 0xff; state->i2c_write_buffer[2] = (val >> 8) & 0xff; @@ -162,7 +197,11 @@ static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val) state->msg[0].buf = state->i2c_write_buffer; state->msg[0].len = 4; - return i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ? -EREMOTEIO : 0; + ret = (i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ? + -EREMOTEIO : 0); + mutex_unlock(&state->i2c_buffer_lock); + + return ret; } static const s16 coeff_2k_sb_1seg_dqpsk[8] = { @@ -2434,8 +2473,15 @@ int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 defau if (!client.i2c_read_buffer) { dprintk("%s: not enough memory", __func__); ret = -ENOMEM; - goto error_memory; + goto error_memory_read; + } + client.i2c_buffer_lock = kzalloc(sizeof(struct mutex), GFP_KERNEL); + if (!client.i2c_buffer_lock) { + dprintk("%s: not enough memory", __func__); + ret = -ENOMEM; + goto error_memory_lock; } + mutex_init(client.i2c_buffer_lock); for (k = no_of_demods - 1; k >= 0; k--) { /* designated i2c address */ @@ -2476,8 +2522,10 @@ int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 defau } error: + kfree(client.i2c_buffer_lock); +error_memory_lock: kfree(client.i2c_read_buffer); -error_memory: +error_memory_read: kfree(client.i2c_write_buffer); return ret; @@ -2581,6 +2629,8 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s state->i2c.addr = i2c_addr; state->i2c.i2c_write_buffer = state->i2c_write_buffer; state->i2c.i2c_read_buffer = state->i2c_read_buffer; + mutex_init(&state->i2c_buffer_lock); + state->i2c.i2c_buffer_lock = &state->i2c_buffer_lock; state->gpio_val = cfg->gpio_val; state->gpio_dir = cfg->gpio_dir; diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c index a0855883b5c..660f80661ed 100644 --- a/drivers/media/dvb/frontends/dib9000.c +++ b/drivers/media/dvb/frontends/dib9000.c @@ -38,6 +38,15 @@ struct i2c_device { #define DibInitLock(lock) mutex_init(lock) #define DibFreeLock(lock) +struct dib9000_pid_ctrl { +#define DIB9000_PID_FILTER_CTRL 0 +#define DIB9000_PID_FILTER 1 + u8 cmd; + u8 id; + u16 pid; + u8 onoff; +}; + struct dib9000_state { struct i2c_device i2c; @@ -99,6 +108,10 @@ struct dib9000_state { struct i2c_msg msg[2]; u8 i2c_write_buffer[255]; u8 i2c_read_buffer[255]; + DIB_LOCK demod_lock; + u8 get_frontend_internal; + struct dib9000_pid_ctrl pid_ctrl[10]; + s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */ }; static const u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1167,8 +1180,8 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe, struct dvb_frontend_p DibAcquireLock(&state->platform.risc.mem_mbx_lock); if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - goto error; ret = -EIO; + goto error; } dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION, @@ -1743,19 +1756,56 @@ EXPORT_SYMBOL(dib9000_set_gpio); int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) { struct dib9000_state *state = fe->demodulator_priv; - u16 val = dib9000_read_word(state, 294 + 1) & 0xffef; + u16 val; + int ret; + + if ((state->pid_ctrl_index != -2) && (state->pid_ctrl_index < 9)) { + /* postpone the pid filtering cmd */ + dprintk("pid filter cmd postpone"); + state->pid_ctrl_index++; + state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL; + state->pid_ctrl[state->pid_ctrl_index].onoff = onoff; + return 0; + } + + DibAcquireLock(&state->demod_lock); + + val = dib9000_read_word(state, 294 + 1) & 0xffef; val |= (onoff & 0x1) << 4; dprintk("PID filter enabled %d", onoff); - return dib9000_write_word(state, 294 + 1, val); + ret = dib9000_write_word(state, 294 + 1, val); + DibReleaseLock(&state->demod_lock); + return ret; + } EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl); int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) { struct dib9000_state *state = fe->demodulator_priv; + int ret; + + if (state->pid_ctrl_index != -2) { + /* postpone the pid filtering cmd */ + dprintk("pid filter postpone"); + if (state->pid_ctrl_index < 9) { + state->pid_ctrl_index++; + state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER; + state->pid_ctrl[state->pid_ctrl_index].id = id; + state->pid_ctrl[state->pid_ctrl_index].pid = pid; + state->pid_ctrl[state->pid_ctrl_index].onoff = onoff; + } else + dprintk("can not add any more pid ctrl cmd"); + return 0; + } + + DibAcquireLock(&state->demod_lock); dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); - return dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0); + ret = dib9000_write_word(state, 300 + 1 + id, + onoff ? (1 << 13) | pid : 0); + DibReleaseLock(&state->demod_lock); + return ret; } EXPORT_SYMBOL(dib9000_fw_pid_filter); @@ -1778,6 +1828,7 @@ static void dib9000_release(struct dvb_frontend *demod) DibFreeLock(&state->platform.risc.mbx_lock); DibFreeLock(&state->platform.risc.mem_lock); DibFreeLock(&state->platform.risc.mem_mbx_lock); + DibFreeLock(&state->demod_lock); dibx000_exit_i2c_master(&st->i2c_master); i2c_del_adapter(&st->tuner_adap); @@ -1795,14 +1846,19 @@ static int dib9000_sleep(struct dvb_frontend *fe) { struct dib9000_state *state = fe->demodulator_priv; u8 index_frontend; - int ret; + int ret = 0; + DibAcquireLock(&state->demod_lock); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]); if (ret < 0) - return ret; + goto error; } - return dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0); + ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0); + +error: + DibReleaseLock(&state->demod_lock); + return ret; } static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) @@ -1816,7 +1872,10 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par struct dib9000_state *state = fe->demodulator_priv; u8 index_frontend, sub_index_frontend; fe_status_t stat; - int ret; + int ret = 0; + + if (state->get_frontend_internal == 0) + DibAcquireLock(&state->demod_lock); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); @@ -1846,14 +1905,15 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par state->fe[index_frontend]->dtv_property_cache.rolloff; } } - return 0; + ret = 0; + goto return_value; } } /* get the channel from master chip */ ret = dib9000_fw_get_channel(fe, fep); if (ret != 0) - return ret; + goto return_value; /* synchronize the cache with the other frontends */ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { @@ -1866,8 +1926,12 @@ static int dib9000_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_par state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP; state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff; } + ret = 0; - return 0; +return_value: + if (state->get_frontend_internal == 0) + DibReleaseLock(&state->demod_lock); + return ret; } static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) @@ -1912,6 +1976,10 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par dprintk("dib9000: must specify bandwidth "); return 0; } + + state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */ + DibAcquireLock(&state->demod_lock); + fe->dtv_property_cache.delivery_system = SYS_DVBT; /* set the master status */ @@ -1974,13 +2042,18 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par /* check the tune result */ if (exit_condition == 1) { /* tune failed */ dprintk("tune failed"); + DibReleaseLock(&state->demod_lock); + /* tune failed; put all the pid filtering cmd to junk */ + state->pid_ctrl_index = -1; return 0; } dprintk("tune success on frontend%i", index_frontend_success); /* synchronize all the channel cache */ + state->get_frontend_internal = 1; dib9000_get_frontend(state->fe[0], fep); + state->get_frontend_internal = 0; /* retune the other frontends with the found channel */ channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; @@ -2025,6 +2098,28 @@ static int dib9000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par /* turn off the diversity for the last frontend */ dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0); + DibReleaseLock(&state->demod_lock); + if (state->pid_ctrl_index >= 0) { + u8 index_pid_filter_cmd; + u8 pid_ctrl_index = state->pid_ctrl_index; + + state->pid_ctrl_index = -2; + for (index_pid_filter_cmd = 0; + index_pid_filter_cmd <= pid_ctrl_index; + index_pid_filter_cmd++) { + if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER_CTRL) + dib9000_fw_pid_filter_ctrl(state->fe[0], + state->pid_ctrl[index_pid_filter_cmd].onoff); + else if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER) + dib9000_fw_pid_filter(state->fe[0], + state->pid_ctrl[index_pid_filter_cmd].id, + state->pid_ctrl[index_pid_filter_cmd].pid, + state->pid_ctrl[index_pid_filter_cmd].onoff); + } + } + /* do not postpone any more the pid filtering */ + state->pid_ctrl_index = -2; + return 0; } @@ -2041,6 +2136,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) u8 index_frontend; u16 lock = 0, lock_slave = 0; + DibAcquireLock(&state->demod_lock); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) lock_slave |= dib9000_read_lock(state->fe[index_frontend]); @@ -2059,6 +2155,8 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) if ((lock & 0x0008) || (lock_slave & 0x0008)) *stat |= FE_HAS_LOCK; + DibReleaseLock(&state->demod_lock); + return 0; } @@ -2066,10 +2164,15 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) { struct dib9000_state *state = fe->demodulator_priv; u16 *c; + int ret = 0; + DibAcquireLock(&state->demod_lock); DibAcquireLock(&state->platform.risc.mem_mbx_lock); - if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) - return -EIO; + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + DibReleaseLock(&state->platform.risc.mem_mbx_lock); + ret = -EIO; + goto error; + } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, state->i2c_read_buffer, 16 * 2); DibReleaseLock(&state->platform.risc.mem_mbx_lock); @@ -2077,7 +2180,10 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) c = (u16 *)state->i2c_read_buffer; *ber = c[10] << 16 | c[11]; - return 0; + +error: + DibReleaseLock(&state->demod_lock); + return ret; } static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) @@ -2086,7 +2192,9 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) u8 index_frontend; u16 *c = (u16 *)state->i2c_read_buffer; u16 val; + int ret = 0; + DibAcquireLock(&state->demod_lock); *strength = 0; for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val); @@ -2097,8 +2205,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) } DibAcquireLock(&state->platform.risc.mem_mbx_lock); - if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) - return -EIO; + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + ret = -EIO; + goto error; + } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); DibReleaseLock(&state->platform.risc.mem_mbx_lock); @@ -2107,7 +2217,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) *strength = 65535; else *strength += val; - return 0; + +error: + DibReleaseLock(&state->demod_lock); + return ret; } static u32 dib9000_get_snr(struct dvb_frontend *fe) @@ -2151,6 +2264,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) u8 index_frontend; u32 snr_master; + DibAcquireLock(&state->demod_lock); snr_master = dib9000_get_snr(fe); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) snr_master += dib9000_get_snr(state->fe[index_frontend]); @@ -2161,6 +2275,8 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) } else *snr = 0; + DibReleaseLock(&state->demod_lock); + return 0; } @@ -2168,15 +2284,22 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) { struct dib9000_state *state = fe->demodulator_priv; u16 *c = (u16 *)state->i2c_read_buffer; + int ret = 0; + DibAcquireLock(&state->demod_lock); DibAcquireLock(&state->platform.risc.mem_mbx_lock); - if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) - return -EIO; + if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { + ret = -EIO; + goto error; + } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); DibReleaseLock(&state->platform.risc.mem_mbx_lock); *unc = c[12]; - return 0; + +error: + DibReleaseLock(&state->demod_lock); + return ret; } int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr) @@ -2322,6 +2445,10 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c DibInitLock(&st->platform.risc.mbx_lock); DibInitLock(&st->platform.risc.mem_lock); DibInitLock(&st->platform.risc.mem_mbx_lock); + DibInitLock(&st->demod_lock); + st->get_frontend_internal = 0; + + st->pid_ctrl_index = -2; st->fe[0] = fe; fe->demodulator_priv = st; diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index dc5d17a6757..43be7238311 100644 --- a/drivers/media/dvb/frontends/dibx000_common.c +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -1,4 +1,6 @@ #include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/module.h> #include "dibx000_common.h" @@ -10,6 +12,13 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) { + int ret; + + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + mst->i2c_write_buffer[0] = (reg >> 8) & 0xff; mst->i2c_write_buffer[1] = reg & 0xff; mst->i2c_write_buffer[2] = (val >> 8) & 0xff; @@ -21,11 +30,21 @@ static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) mst->msg[0].buf = mst->i2c_write_buffer; mst->msg[0].len = 4; - return i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0; + ret = i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0; + mutex_unlock(&mst->i2c_buffer_lock); + + return ret; } static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg) { + u16 ret; + + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return 0; + } + mst->i2c_write_buffer[0] = reg >> 8; mst->i2c_write_buffer[1] = reg & 0xff; @@ -42,7 +61,10 @@ static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg) if (i2c_transfer(mst->i2c_adap, mst->msg, 2) != 2) dprintk("i2c read error on %d", reg); - return (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1]; + ret = (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1]; + mutex_unlock(&mst->i2c_buffer_lock); + + return ret; } static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst) @@ -257,6 +279,7 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) { struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + int ret; if (num > 32) { dprintk("%s: too much I2C message to be transmitted (%i).\ @@ -264,10 +287,15 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap, return -ENOMEM; } - memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); - dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_6_7); + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + + memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); + /* open the gate */ dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1); mst->msg[0].addr = mst->i2c_addr; @@ -282,7 +310,11 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap, mst->msg[num + 1].buf = &mst->i2c_write_buffer[4]; mst->msg[num + 1].len = 4; - return i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? num : -EIO; + ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? + num : -EIO); + + mutex_unlock(&mst->i2c_buffer_lock); + return ret; } static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = { @@ -294,6 +326,7 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) { struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); + int ret; if (num > 32) { dprintk("%s: too much I2C message to be transmitted (%i).\ @@ -301,10 +334,14 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, return -ENOMEM; } - memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); - dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); + /* open the gate */ dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1); mst->msg[0].addr = mst->i2c_addr; @@ -319,7 +356,10 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, mst->msg[num + 1].buf = &mst->i2c_write_buffer[4]; mst->msg[num + 1].len = 4; - return i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? num : -EIO; + ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? + num : -EIO); + mutex_unlock(&mst->i2c_buffer_lock); + return ret; } static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { @@ -390,8 +430,18 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap, int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr) { - u8 tx[4]; - struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 }; + int ret; + + mutex_init(&mst->i2c_buffer_lock); + if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { + dprintk("could not acquire lock"); + return -EINVAL; + } + memset(mst->msg, 0, sizeof(struct i2c_msg)); + mst->msg[0].addr = i2c_addr >> 1; + mst->msg[0].flags = 0; + mst->msg[0].buf = mst->i2c_write_buffer; + mst->msg[0].len = 4; mst->device_rev = device_rev; mst->i2c_adap = i2c_adap; @@ -431,9 +481,12 @@ int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, "DiBX000: could not initialize the master i2c_adapter\n"); /* initialize the i2c-master by closing the gate */ - dibx000_i2c_gate_ctrl(mst, tx, 0, 0); + dibx000_i2c_gate_ctrl(mst, mst->i2c_write_buffer, 0, 0); + + ret = (i2c_transfer(i2c_adap, mst->msg, 1) == 1); + mutex_unlock(&mst->i2c_buffer_lock); - return i2c_transfer(i2c_adap, &m, 1) == 1; + return ret; } EXPORT_SYMBOL(dibx000_init_i2c_master); diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h index f031165c045..5e011474be4 100644 --- a/drivers/media/dvb/frontends/dibx000_common.h +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -33,6 +33,7 @@ struct dibx000_i2c_master { struct i2c_msg msg[34]; u8 i2c_write_buffer[8]; u8 i2c_read_buffer[2]; + struct mutex i2c_buffer_lock; }; extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb/frontends/drxd_hard.c index 2238bf0be95..88e46f4cdbb 100644 --- a/drivers/media/dvb/frontends/drxd_hard.c +++ b/drivers/media/dvb/frontends/drxd_hard.c @@ -889,10 +889,15 @@ static int ReadIFAgc(struct drxd_state *state, u32 * pValue) u32 R2 = state->if_agc_cfg.R2; u32 R3 = state->if_agc_cfg.R3; - u32 Vmax = (3300 * R2) / (R1 + R2); - u32 Rpar = (R2 * R3) / (R3 + R2); - u32 Vmin = (3300 * Rpar) / (R1 + Rpar); - u32 Vout = Vmin + ((Vmax - Vmin) * Value) / 1024; + u32 Vmax, Rpar, Vmin, Vout; + + if (R2 == 0 && (R1 == 0 || R3 == 0)) + return 0; + + Vmax = (3300 * R2) / (R1 + R2); + Rpar = (R2 * R3) / (R3 + R2); + Vmin = (3300 * Rpar) / (R1 + Rpar); + Vout = Vmin + ((Vmax - Vmin) * Value) / 1024; *pValue = Vout; } @@ -926,16 +931,15 @@ static int DownloadMicrocode(struct drxd_state *state, const u8 *pMCImage, u32 Length) { u8 *pSrc; - u16 Flags; u32 Address; u16 nBlocks; u16 BlockSize; - u16 BlockCRC; u32 offset = 0; int i, status = 0; pSrc = (u8 *) pMCImage; - Flags = (pSrc[0] << 8) | pSrc[1]; + /* We're not using Flags */ + /* Flags = (pSrc[0] << 8) | pSrc[1]; */ pSrc += sizeof(u16); offset += sizeof(u16); nBlocks = (pSrc[0] << 8) | pSrc[1]; @@ -952,11 +956,13 @@ static int DownloadMicrocode(struct drxd_state *state, pSrc += sizeof(u16); offset += sizeof(u16); - Flags = (pSrc[0] << 8) | pSrc[1]; + /* We're not using Flags */ + /* u16 Flags = (pSrc[0] << 8) | pSrc[1]; */ pSrc += sizeof(u16); offset += sizeof(u16); - BlockCRC = (pSrc[0] << 8) | pSrc[1]; + /* We're not using BlockCRC */ + /* u16 BlockCRC = (pSrc[0] << 8) | pSrc[1]; */ pSrc += sizeof(u16); offset += sizeof(u16); diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c index 41b083820da..f6431ef827d 100644 --- a/drivers/media/dvb/frontends/drxk_hard.c +++ b/drivers/media/dvb/frontends/drxk_hard.c @@ -6211,6 +6211,14 @@ static int drxk_set_parameters(struct dvb_frontend *fe, u32 IF; dprintk(1, "\n"); + + if (!fe->ops.tuner_ops.get_if_frequency) { + printk(KERN_ERR + "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n"); + return -EINVAL; + } + + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (fe->ops.tuner_ops.set_params) @@ -6218,7 +6226,7 @@ static int drxk_set_parameters(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); state->param = *p; - fe->ops.tuner_ops.get_frequency(fe, &IF); + fe->ops.tuner_ops.get_if_frequency(fe, &IF); Start(state, 0, IF); /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */ diff --git a/drivers/media/dvb/frontends/it913x-fe-priv.h b/drivers/media/dvb/frontends/it913x-fe-priv.h new file mode 100644 index 00000000000..1c6fb4b6625 --- /dev/null +++ b/drivers/media/dvb/frontends/it913x-fe-priv.h @@ -0,0 +1,336 @@ + +struct it913xset { u32 pro; + u32 address; + u8 reg[15]; + u8 count; +}; + +struct adctable { u32 adcFrequency; + u32 bandwidth; + u32 coeff_1_2048; + u32 coeff_1_4096; + u32 coeff_1_8191; + u32 coeff_1_8192; + u32 coeff_1_8193; + u32 coeff_2_2k; + u32 coeff_2_4k; + u32 coeff_2_8k; + u16 bfsfcw_fftinx_ratio; + u16 fftinx_bfsfcw_ratio; +}; + +/* clock and coeff tables only table 3 is used with IT9137*/ +/* TODO other tables relate AF9035 may be removed */ +static struct adctable tab1[] = { + { 20156250, BANDWIDTH_6_MHZ, + 0x02b8ba6e, 0x015c5d37, 0x00ae340d, 0x00ae2e9b, 0x00ae292a, + 0x015c5d37, 0x00ae2e9b, 0x0057174e, 0x02f1, 0x015c }, + { 20156250, BANDWIDTH_7_MHZ, + 0x032cd980, 0x01966cc0, 0x00cb3cba, 0x00cb3660, 0x00cb3007, + 0x01966cc0, 0x00cb3660, 0x00659b30, 0x0285, 0x0196 }, + { 20156250, BANDWIDTH_8_MHZ, + 0x03a0f893, 0x01d07c49, 0x00e84567, 0x00e83e25, 0x00e836e3, + 0x01d07c49, 0x00e83e25, 0x00741f12, 0x0234, 0x01d0 }, + { 20156250, BANDWIDTH_5_MHZ, + 0x02449b5c, 0x01224dae, 0x00912b60, 0x009126d7, 0x0091224e, + 0x01224dae, 0x009126d7, 0x0048936b, 0x0387, 0x0122 } +}; + +static struct adctable tab2[] = { + { 20187500, BANDWIDTH_6_MHZ, + 0x02b7a654, 0x015bd32a, 0x00adef04, 0x00ade995, 0x00ade426, + 0x015bd32a, 0x00ade995, 0x0056f4ca, 0x02f2, 0x015c }, + { 20187500, BANDWIDTH_7_MHZ, + 0x032b9761, 0x0195cbb1, 0x00caec30, 0x00cae5d8, 0x00cadf81, + 0x0195cbb1, 0x00cae5d8, 0x006572ec, 0x0286, 0x0196 }, + { 20187500, BANDWIDTH_8_MHZ, + 0x039f886f, 0x01cfc438, 0x00e7e95b, 0x00e7e21c, 0x00e7dadd, + 0x01cfc438, 0x00e7e21c, 0x0073f10e, 0x0235, 0x01d0 }, + { 20187500, BANDWIDTH_5_MHZ, + 0x0243b546, 0x0121daa3, 0x0090f1d9, 0x0090ed51, 0x0090e8ca, + 0x0121daa3, 0x0090ed51, 0x004876a9, 0x0388, 0x0122 } + +}; + +static struct adctable tab3[] = { + { 20250000, BANDWIDTH_6_MHZ, + 0x02b580ad, 0x015ac057, 0x00ad6597, 0x00ad602b, 0x00ad5ac1, + 0x015ac057, 0x00ad602b, 0x0056b016, 0x02f4, 0x015b }, + { 20250000, BANDWIDTH_7_MHZ, + 0x03291620, 0x01948b10, 0x00ca4bda, 0x00ca4588, 0x00ca3f36, + 0x01948b10, 0x00ca4588, 0x006522c4, 0x0288, 0x0195 }, + { 20250000, BANDWIDTH_8_MHZ, + 0x039cab92, 0x01ce55c9, 0x00e7321e, 0x00e72ae4, 0x00e723ab, + 0x01ce55c9, 0x00e72ae4, 0x00739572, 0x0237, 0x01ce }, + { 20250000, BANDWIDTH_5_MHZ, + 0x0241eb3b, 0x0120f59e, 0x00907f53, 0x00907acf, 0x0090764b, + 0x0120f59e, 0x00907acf, 0x00483d67, 0x038b, 0x0121 } + +}; + +static struct adctable tab4[] = { + { 20583333, BANDWIDTH_6_MHZ, + 0x02aa4598, 0x015522cc, 0x00aa96bb, 0x00aa9166, 0x00aa8c12, + 0x015522cc, 0x00aa9166, 0x005548b3, 0x0300, 0x0155 }, + { 20583333, BANDWIDTH_7_MHZ, + 0x031bfbdc, 0x018dfdee, 0x00c7052f, 0x00c6fef7, 0x00c6f8bf, + 0x018dfdee, 0x00c6fef7, 0x00637f7b, 0x0293, 0x018e }, + { 20583333, BANDWIDTH_8_MHZ, + 0x038db21f, 0x01c6d910, 0x00e373a3, 0x00e36c88, 0x00e3656d, + 0x01c6d910, 0x00e36c88, 0x0071b644, 0x0240, 0x01c7 }, + { 20583333, BANDWIDTH_5_MHZ, + 0x02388f54, 0x011c47aa, 0x008e2846, 0x008e23d5, 0x008e1f64, + 0x011c47aa, 0x008e23d5, 0x004711ea, 0x039a, 0x011c } + +}; + +static struct adctable tab5[] = { + { 20416667, BANDWIDTH_6_MHZ, + 0x02afd765, 0x0157ebb3, 0x00abfb39, 0x00abf5d9, 0x00abf07a, + 0x0157ebb3, 0x00abf5d9, 0x0055faed, 0x02fa, 0x0158 }, + { 20416667, BANDWIDTH_7_MHZ, + 0x03227b4b, 0x01913da6, 0x00c8a518, 0x00c89ed3, 0x00c8988e, + 0x01913da6, 0x00c89ed3, 0x00644f69, 0x028d, 0x0191 }, + { 20416667, BANDWIDTH_8_MHZ, + 0x03951f32, 0x01ca8f99, 0x00e54ef7, 0x00e547cc, 0x00e540a2, + 0x01ca8f99, 0x00e547cc, 0x0072a3e6, 0x023c, 0x01cb }, + { 20416667, BANDWIDTH_5_MHZ, + 0x023d337f, 0x011e99c0, 0x008f515a, 0x008f4ce0, 0x008f4865, + 0x011e99c0, 0x008f4ce0, 0x0047a670, 0x0393, 0x011f } + +}; + +static struct adctable tab6[] = { + { 20480000, BANDWIDTH_6_MHZ, + 0x02adb6db, 0x0156db6e, 0x00ab7312, 0x00ab6db7, 0x00ab685c, + 0x0156db6e, 0x00ab6db7, 0x0055b6db, 0x02fd, 0x0157 }, + { 20480000, BANDWIDTH_7_MHZ, + 0x03200000, 0x01900000, 0x00c80640, 0x00c80000, 0x00c7f9c0, + 0x01900000, 0x00c80000, 0x00640000, 0x028f, 0x0190 }, + { 20480000, BANDWIDTH_8_MHZ, + 0x03924925, 0x01c92492, 0x00e4996e, 0x00e49249, 0x00e48b25, + 0x01c92492, 0x00e49249, 0x00724925, 0x023d, 0x01c9 }, + { 20480000, BANDWIDTH_5_MHZ, + 0x023b6db7, 0x011db6db, 0x008edfe5, 0x008edb6e, 0x008ed6f7, + 0x011db6db, 0x008edb6e, 0x00476db7, 0x0396, 0x011e } +}; + +static struct adctable tab7[] = { + { 20500000, BANDWIDTH_6_MHZ, + 0x02ad0b99, 0x015685cc, 0x00ab4840, 0x00ab42e6, 0x00ab3d8c, + 0x015685cc, 0x00ab42e6, 0x0055a173, 0x02fd, 0x0157 }, + { 20500000, BANDWIDTH_7_MHZ, + 0x031f3832, 0x018f9c19, 0x00c7d44b, 0x00c7ce0c, 0x00c7c7ce, + 0x018f9c19, 0x00c7ce0c, 0x0063e706, 0x0290, 0x0190 }, + { 20500000, BANDWIDTH_8_MHZ, + 0x039164cb, 0x01c8b266, 0x00e46056, 0x00e45933, 0x00e45210, + 0x01c8b266, 0x00e45933, 0x00722c99, 0x023e, 0x01c9 }, + { 20500000, BANDWIDTH_5_MHZ, + 0x023adeff, 0x011d6f80, 0x008ebc36, 0x008eb7c0, 0x008eb34a, + 0x011d6f80, 0x008eb7c0, 0x00475be0, 0x0396, 0x011d } + +}; + +static struct adctable tab8[] = { + { 20625000, BANDWIDTH_6_MHZ, + 0x02a8e4bd, 0x0154725e, 0x00aa3e81, 0x00aa392f, 0x00aa33de, + 0x0154725e, 0x00aa392f, 0x00551c98, 0x0302, 0x0154 }, + { 20625000, BANDWIDTH_7_MHZ, + 0x031a6032, 0x018d3019, 0x00c69e41, 0x00c6980c, 0x00c691d8, + 0x018d3019, 0x00c6980c, 0x00634c06, 0x0294, 0x018d }, + { 20625000, BANDWIDTH_8_MHZ, + 0x038bdba6, 0x01c5edd3, 0x00e2fe02, 0x00e2f6ea, 0x00e2efd2, + 0x01c5edd3, 0x00e2f6ea, 0x00717b75, 0x0242, 0x01c6 }, + { 20625000, BANDWIDTH_5_MHZ, + 0x02376948, 0x011bb4a4, 0x008ddec1, 0x008dda52, 0x008dd5e3, + 0x011bb4a4, 0x008dda52, 0x0046ed29, 0x039c, 0x011c } + +}; + +struct table { + u32 xtal; + struct adctable *table; +}; + +static struct table fe_clockTable[] = { + {12000000, tab3}, /* FPGA */ + {16384000, tab6}, /* 16.38MHz */ + {20480000, tab6}, /* 20.48MHz */ + {36000000, tab3}, /* 36.00MHz */ + {30000000, tab1}, /* 30.00MHz */ + {26000000, tab4}, /* 26.00MHz */ + {28000000, tab5}, /* 28.00MHz */ + {32000000, tab7}, /* 32.00MHz */ + {34000000, tab2}, /* 34.00MHz */ + {24000000, tab1}, /* 24.00MHz */ + {22000000, tab8}, /* 22.00MHz */ + {12000000, tab3} /* 12.00MHz */ +}; + +/* fe get */ +fe_code_rate_t fe_code[] = { + FEC_1_2, + FEC_2_3, + FEC_3_4, + FEC_5_6, + FEC_7_8, + FEC_NONE, +}; + +fe_guard_interval_t fe_gi[] = { + GUARD_INTERVAL_1_32, + GUARD_INTERVAL_1_16, + GUARD_INTERVAL_1_8, + GUARD_INTERVAL_1_4, +}; + +fe_hierarchy_t fe_hi[] = { + HIERARCHY_NONE, + HIERARCHY_1, + HIERARCHY_2, + HIERARCHY_4, +}; + +fe_transmit_mode_t fe_mode[] = { + TRANSMISSION_MODE_2K, + TRANSMISSION_MODE_8K, + TRANSMISSION_MODE_4K, +}; + +fe_modulation_t fe_con[] = { + QPSK, + QAM_16, + QAM_64, +}; + +/* Standard demodulator functions */ +static struct it913xset set_solo_fe[] = { + {PRO_LINK, DVBT_INTEN, {0x04}, 0x01}, + {PRO_LINK, DVBT_ENABLE, {0x05}, 0x01}, + {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01}, + {PRO_LINK, HOSTB_MPEG_SER_MODE, {0x00}, 0x01}, + {PRO_LINK, HOSTB_MPEG_PAR_MODE, {0x00}, 0x01}, + {PRO_DMOD, DCA_UPPER_CHIP, {0x00}, 0x01}, + {PRO_LINK, HOSTB_DCA_UPPER, {0x00}, 0x01}, + {PRO_DMOD, DCA_LOWER_CHIP, {0x00}, 0x01}, + {PRO_LINK, HOSTB_DCA_LOWER, {0x00}, 0x01}, + {PRO_DMOD, DCA_PLATCH, {0x00}, 0x01}, + {PRO_DMOD, DCA_FPGA_LATCH, {0x00}, 0x01}, + {PRO_DMOD, DCA_STAND_ALONE, {0x01}, 0x01}, + {PRO_DMOD, DCA_ENABLE, {0x00}, 0x01}, + {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01}, + {PRO_DMOD, BFS_FCW, {0x00, 0x00, 0x00}, 0x03}, + {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ +}; + + +static struct it913xset init_1[] = { + {PRO_LINK, LOCK3_OUT, {0x01}, 0x01}, + {PRO_LINK, PADMISCDRSR, {0x01}, 0x01}, + {PRO_LINK, PADMISCDR2, {0x00}, 0x01}, + {PRO_LINK, PADMISCDR4, {0x00}, 0x01}, /* Power up */ + {PRO_LINK, PADMISCDR8, {0x00}, 0x01}, + {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +}; + +/* ---------IT9137 0x38 tuner init---------- */ +static struct it913xset it9137_set[] = { + {PRO_DMOD, 0x0043, {0x00}, 0x01}, + {PRO_DMOD, 0x0046, {0x38}, 0x01}, + {PRO_DMOD, 0x0051, {0x01}, 0x01}, + {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, + {PRO_DMOD, 0x0068, {0x0a}, 0x01}, + {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, + {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05}, + {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02}, + {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc8, 0xb8, + 0xd0, 0xc3, 0x01 }, 0x0a}, + {PRO_DMOD, 0x008e, {0x01}, 0x01}, + {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, + {PRO_DMOD, 0x0099, {0x01}, 0x01}, + {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, + {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, + {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, + {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, + {PRO_DMOD, 0x00b0, {0x01}, 0x01}, + {PRO_DMOD, 0x00b3, {0x02, 0x32}, 0x02}, + {PRO_DMOD, 0x00b6, {0x14}, 0x01}, + {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03}, + {PRO_DMOD, 0x00c4, {0x00}, 0x01}, + {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, + {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03}, + {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03}, + {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, + {PRO_DMOD, 0x00fc, { 0x02, 0x02, 0x02, 0x09, 0x50, 0x7b, 0x77, + 0x00, 0x02, 0xc8, 0x05, 0x7b }, 0x0c}, + {PRO_DMOD, 0x0109, {0x02}, 0x01}, + {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02}, + {PRO_DMOD, 0x011a, {0xc8, 0x7b, 0xbc, 0xa0}, 0x04}, + {PRO_DMOD, 0x0122, {0x02, 0x18, 0xc3}, 0x03}, + {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02}, + {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, + {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05}, + {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc8}, 0x04}, + {PRO_DMOD, 0xf130, {0x04}, 0x01}, + {PRO_DMOD, 0xf132, {0x04}, 0x01}, + {PRO_DMOD, 0xf144, {0x1a}, 0x01}, + {PRO_DMOD, 0xf146, {0x00}, 0x01}, + {PRO_DMOD, 0xf14a, {0x01}, 0x01}, + {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, + {PRO_DMOD, 0xf14f, {0x04}, 0x01}, + {PRO_DMOD, 0xf158, {0x7f}, 0x01}, + {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, + {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, + {PRO_DMOD, 0xf163, {0x05}, 0x01}, + {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, + {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, + {PRO_DMOD, 0xf183, {0x01}, 0x01}, + {PRO_DMOD, 0xf19d, {0x40}, 0x01}, + {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, + {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, + {PRO_DMOD, 0xf204, {0x10}, 0x01}, + {PRO_DMOD, 0xf214, {0x00}, 0x01}, + {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, + {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, + {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, + {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, + {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, + {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, + {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02}, + {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, + {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, + {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, + {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, + 0x1f }, 0x08}, + {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, + {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, + {PRO_DMOD, 0xf78b, {0x01}, 0x01}, + {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, + {PRO_DMOD, 0xf905, {0x01}, 0x01}, + {PRO_DMOD, 0xfb06, {0x03}, 0x01}, + {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, + {PRO_LINK, GPIOH5_EN, {0x01}, 0x01}, + {PRO_LINK, GPIOH5_ON, {0x01}, 0x01}, + {PRO_LINK, GPIOH5_O, {0x00}, 0x01}, + {PRO_LINK, GPIOH5_O, {0x01}, 0x01}, + {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ +}; + +static struct it913xset it9137_tuner_off[] = { + {PRO_DMOD, 0xfba8, {0x01}, 0x01}, /* Tuner Clock Off */ + {PRO_DMOD, 0xec40, {0x00}, 0x01}, /* Power Down Tuner */ + {PRO_DMOD, 0xec02, {0x3f, 0x1f, 0x3f, 0x3f}, 0x04}, + {PRO_DMOD, 0xec3f, {0x01}, 0x01}, + {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ +}; + +static struct it913xset set_it9137_template[] = { + {PRO_DMOD, 0xee06, {0x00}, 0x01}, + {PRO_DMOD, 0xec56, {0x00}, 0x01}, + {PRO_DMOD, 0xec4c, {0x00}, 0x01}, + {PRO_DMOD, 0xec4d, {0x00}, 0x01}, + {PRO_DMOD, 0xec4e, {0x00}, 0x01}, + {PRO_DMOD, 0xec4f, {0x00}, 0x01}, + {PRO_DMOD, 0xec50, {0x00}, 0x01}, + {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ +}; diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c new file mode 100644 index 00000000000..d4bd24eb470 --- /dev/null +++ b/drivers/media/dvb/frontends/it913x-fe.c @@ -0,0 +1,839 @@ +/* + * Driver for it913x-fe Frontend + * + * with support for on chip it9137 integral tuner + * + * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) + * IT9137 Copyright (C) ITE Tech Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "dvb_frontend.h" +#include "it913x-fe.h" +#include "it913x-fe-priv.h" + +static int it913x_debug; + +module_param_named(debug, it913x_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +#define dprintk(level, args...) do { \ + if (level & it913x_debug) \ + printk(KERN_DEBUG "it913x-fe: " args); \ +} while (0) + +#define deb_info(args...) dprintk(0x01, args) +#define debug_data_snipet(level, name, p) \ + dprintk(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ + *p, *(p+1), *(p+2), *(p+3), *(p+4), \ + *(p+5), *(p+6), *(p+7)); + +struct it913x_fe_state { + struct dvb_frontend frontend; + struct i2c_adapter *i2c_adap; + u8 i2c_addr; + u32 frequency; + u8 adf; + u32 crystalFrequency; + u32 adcFrequency; + u8 tuner_type; + struct adctable *table; + fe_status_t it913x_status; + u16 tun_xtal; + u8 tun_fdiv; + u8 tun_clk_mode; + u32 tun_fn_min; +}; + +static int it913x_read_reg(struct it913x_fe_state *state, + u32 reg, u8 *data, u8 count) +{ + int ret; + u8 pro = PRO_DMOD; /* All reads from demodulator */ + u8 b[4]; + struct i2c_msg msg[2] = { + { .addr = state->i2c_addr + (pro << 1), .flags = 0, + .buf = b, .len = sizeof(b) }, + { .addr = state->i2c_addr + (pro << 1), .flags = I2C_M_RD, + .buf = data, .len = count } + }; + b[0] = (u8) reg >> 24; + b[1] = (u8)(reg >> 16) & 0xff; + b[2] = (u8)(reg >> 8) & 0xff; + b[3] = (u8) reg & 0xff; + + ret = i2c_transfer(state->i2c_adap, msg, 2); + + return ret; +} + +static int it913x_read_reg_u8(struct it913x_fe_state *state, u32 reg) +{ + int ret; + u8 b[1]; + ret = it913x_read_reg(state, reg, &b[0], sizeof(b)); + return (ret < 0) ? -ENODEV : b[0]; +} + +static int it913x_write(struct it913x_fe_state *state, + u8 pro, u32 reg, u8 buf[], u8 count) +{ + u8 b[256]; + struct i2c_msg msg[1] = { + { .addr = state->i2c_addr + (pro << 1), .flags = 0, + .buf = b, .len = count + 4 } + }; + int ret; + + b[0] = (u8) reg >> 24; + b[1] = (u8)(reg >> 16) & 0xff; + b[2] = (u8)(reg >> 8) & 0xff; + b[3] = (u8) reg & 0xff; + memcpy(&b[4], buf, count); + + ret = i2c_transfer(state->i2c_adap, msg, 1); + + if (ret < 0) + return -EIO; + + return 0; +} + +static int it913x_write_reg(struct it913x_fe_state *state, + u8 pro, u32 reg, u32 data) +{ + int ret; + u8 b[4]; + u8 s; + + b[0] = data >> 24; + b[1] = (data >> 16) & 0xff; + b[2] = (data >> 8) & 0xff; + b[3] = data & 0xff; + /* expand write as needed */ + if (data < 0x100) + s = 3; + else if (data < 0x1000) + s = 2; + else if (data < 0x100000) + s = 1; + else + s = 0; + + ret = it913x_write(state, pro, reg, &b[s], sizeof(b) - s); + + return ret; +} + +static int it913x_fe_script_loader(struct it913x_fe_state *state, + struct it913xset *loadscript) +{ + int ret, i; + if (loadscript == NULL) + return -EINVAL; + + for (i = 0; i < 1000; ++i) { + if (loadscript[i].pro == 0xff) + break; + ret = it913x_write(state, loadscript[i].pro, + loadscript[i].address, + loadscript[i].reg, loadscript[i].count); + if (ret < 0) + return -ENODEV; + } + return 0; +} + +static int it913x_init_tuner(struct it913x_fe_state *state) +{ + int ret, i, reg; + u8 val, nv_val; + u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2}; + u8 b[2]; + + reg = it913x_read_reg_u8(state, 0xec86); + switch (reg) { + case 0: + state->tun_clk_mode = reg; + state->tun_xtal = 2000; + state->tun_fdiv = 3; + val = 16; + break; + case -ENODEV: + return -ENODEV; + case 1: + default: + state->tun_clk_mode = reg; + state->tun_xtal = 640; + state->tun_fdiv = 1; + val = 6; + break; + } + + reg = it913x_read_reg_u8(state, 0xed03); + + if (reg < 0) + return -ENODEV; + else if (reg < sizeof(nv)) + nv_val = nv[reg]; + else + nv_val = 2; + + for (i = 0; i < 50; i++) { + ret = it913x_read_reg(state, 0xed23, &b[0], sizeof(b)); + reg = (b[1] << 8) + b[0]; + if (reg > 0) + break; + if (ret < 0) + return -ENODEV; + udelay(2000); + } + state->tun_fn_min = state->tun_xtal * reg; + state->tun_fn_min /= (state->tun_fdiv * nv_val); + deb_info("Tuner fn_min %d", state->tun_fn_min); + + for (i = 0; i < 50; i++) { + reg = it913x_read_reg_u8(state, 0xec82); + if (reg > 0) + break; + if (reg < 0) + return -ENODEV; + udelay(2000); + } + + return it913x_write_reg(state, PRO_DMOD, 0xed81, val); +} + +static int it9137_set_tuner(struct it913x_fe_state *state, + enum fe_bandwidth bandwidth, u32 frequency_m) +{ + struct it913xset *set_tuner = set_it9137_template; + int ret, reg; + u32 frequency = frequency_m / 1000; + u32 freq, temp_f, tmp; + u16 iqik_m_cal; + u16 n_div; + u8 n; + u8 l_band; + u8 lna_band; + u8 bw; + + deb_info("Tuner Frequency %d Bandwidth %d", frequency, bandwidth); + + if (frequency >= 51000 && frequency <= 440000) { + l_band = 0; + lna_band = 0; + } else if (frequency > 440000 && frequency <= 484000) { + l_band = 1; + lna_band = 1; + } else if (frequency > 484000 && frequency <= 533000) { + l_band = 1; + lna_band = 2; + } else if (frequency > 533000 && frequency <= 587000) { + l_band = 1; + lna_band = 3; + } else if (frequency > 587000 && frequency <= 645000) { + l_band = 1; + lna_band = 4; + } else if (frequency > 645000 && frequency <= 710000) { + l_band = 1; + lna_band = 5; + } else if (frequency > 710000 && frequency <= 782000) { + l_band = 1; + lna_band = 6; + } else if (frequency > 782000 && frequency <= 860000) { + l_band = 1; + lna_band = 7; + } else if (frequency > 1450000 && frequency <= 1492000) { + l_band = 1; + lna_band = 0; + } else if (frequency > 1660000 && frequency <= 1685000) { + l_band = 1; + lna_band = 1; + } else + return -EINVAL; + set_tuner[0].reg[0] = lna_band; + + if (bandwidth == BANDWIDTH_5_MHZ) + bw = 0; + else if (bandwidth == BANDWIDTH_6_MHZ) + bw = 2; + else if (bandwidth == BANDWIDTH_7_MHZ) + bw = 4; + else if (bandwidth == BANDWIDTH_8_MHZ) + bw = 6; + else + bw = 6; + + set_tuner[1].reg[0] = bw; + set_tuner[2].reg[0] = 0xa0 | (l_band << 3); + + if (frequency > 53000 && frequency <= 74000) { + n_div = 48; + n = 0; + } else if (frequency > 74000 && frequency <= 111000) { + n_div = 32; + n = 1; + } else if (frequency > 111000 && frequency <= 148000) { + n_div = 24; + n = 2; + } else if (frequency > 148000 && frequency <= 222000) { + n_div = 16; + n = 3; + } else if (frequency > 222000 && frequency <= 296000) { + n_div = 12; + n = 4; + } else if (frequency > 296000 && frequency <= 445000) { + n_div = 8; + n = 5; + } else if (frequency > 445000 && frequency <= state->tun_fn_min) { + n_div = 6; + n = 6; + } else if (frequency > state->tun_fn_min && frequency <= 950000) { + n_div = 4; + n = 7; + } else if (frequency > 1450000 && frequency <= 1680000) { + n_div = 2; + n = 0; + } else + return -EINVAL; + + reg = it913x_read_reg_u8(state, 0xed81); + iqik_m_cal = (u16)reg * n_div; + + if (reg < 0x20) { + if (state->tun_clk_mode == 0) + iqik_m_cal = (iqik_m_cal * 9) >> 5; + else + iqik_m_cal >>= 1; + } else { + iqik_m_cal = 0x40 - iqik_m_cal; + if (state->tun_clk_mode == 0) + iqik_m_cal = ~((iqik_m_cal * 9) >> 5); + else + iqik_m_cal = ~(iqik_m_cal >> 1); + } + + temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv; + freq = temp_f / state->tun_xtal; + tmp = freq * state->tun_xtal; + + if ((temp_f - tmp) >= (state->tun_xtal >> 1)) + freq++; + + freq += (u32) n << 13; + /* Frequency OMEGA_IQIK_M_CAL_MID*/ + temp_f = freq + (u32)iqik_m_cal; + + set_tuner[3].reg[0] = temp_f & 0xff; + set_tuner[4].reg[0] = (temp_f >> 8) & 0xff; + + deb_info("High Frequency = %04x", temp_f); + + /* Lower frequency */ + set_tuner[5].reg[0] = freq & 0xff; + set_tuner[6].reg[0] = (freq >> 8) & 0xff; + + deb_info("low Frequency = %04x", freq); + + ret = it913x_fe_script_loader(state, set_tuner); + + return (ret < 0) ? -ENODEV : 0; +} + +static int it913x_fe_select_bw(struct it913x_fe_state *state, + enum fe_bandwidth bandwidth, u32 adcFrequency) +{ + int ret, i; + u8 buffer[256]; + u32 coeff[8]; + u16 bfsfcw_fftinx_ratio; + u16 fftinx_bfsfcw_ratio; + u8 count; + u8 bw; + u8 adcmultiplier; + + deb_info("Bandwidth %d Adc %d", bandwidth, adcFrequency); + + if (bandwidth == BANDWIDTH_5_MHZ) + bw = 3; + else if (bandwidth == BANDWIDTH_6_MHZ) + bw = 0; + else if (bandwidth == BANDWIDTH_7_MHZ) + bw = 1; + else if (bandwidth == BANDWIDTH_8_MHZ) + bw = 2; + else + bw = 2; + + ret = it913x_write_reg(state, PRO_DMOD, REG_BW, bw); + + if (state->table == NULL) + return -EINVAL; + + /* In write order */ + coeff[0] = state->table[bw].coeff_1_2048; + coeff[1] = state->table[bw].coeff_2_2k; + coeff[2] = state->table[bw].coeff_1_8191; + coeff[3] = state->table[bw].coeff_1_8192; + coeff[4] = state->table[bw].coeff_1_8193; + coeff[5] = state->table[bw].coeff_2_8k; + coeff[6] = state->table[bw].coeff_1_4096; + coeff[7] = state->table[bw].coeff_2_4k; + bfsfcw_fftinx_ratio = state->table[bw].bfsfcw_fftinx_ratio; + fftinx_bfsfcw_ratio = state->table[bw].fftinx_bfsfcw_ratio; + + /* ADC multiplier */ + ret = it913x_read_reg_u8(state, ADC_X_2); + if (ret < 0) + return -EINVAL; + + adcmultiplier = ret; + + count = 0; + + /* Build Buffer for COEFF Registers */ + for (i = 0; i < 8; i++) { + if (adcmultiplier == 1) + coeff[i] /= 2; + buffer[count++] = (coeff[i] >> 24) & 0x3; + buffer[count++] = (coeff[i] >> 16) & 0xff; + buffer[count++] = (coeff[i] >> 8) & 0xff; + buffer[count++] = coeff[i] & 0xff; + } + + /* bfsfcw_fftinx_ratio register 0x21-0x22 */ + buffer[count++] = bfsfcw_fftinx_ratio & 0xff; + buffer[count++] = (bfsfcw_fftinx_ratio >> 8) & 0xff; + /* fftinx_bfsfcw_ratio register 0x23-0x24 */ + buffer[count++] = fftinx_bfsfcw_ratio & 0xff; + buffer[count++] = (fftinx_bfsfcw_ratio >> 8) & 0xff; + /* start at COEFF_1_2048 and write through to fftinx_bfsfcw_ratio*/ + ret = it913x_write(state, PRO_DMOD, COEFF_1_2048, buffer, count); + + for (i = 0; i < 42; i += 8) + debug_data_snipet(0x1, "Buffer", &buffer[i]); + + return ret; +} + + + +static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct it913x_fe_state *state = fe->demodulator_priv; + int ret, i; + fe_status_t old_status = state->it913x_status; + *status = 0; + + if (state->it913x_status == 0) { + ret = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS); + if (ret == 0x1) { + *status |= FE_HAS_SIGNAL; + for (i = 0; i < 40; i++) { + ret = it913x_read_reg_u8(state, MP2IF_SYNC_LK); + if (ret == 0x1) + break; + msleep(25); + } + if (ret == 0x1) + *status |= FE_HAS_CARRIER + | FE_HAS_VITERBI + | FE_HAS_SYNC; + state->it913x_status = *status; + } + } + + if (state->it913x_status & FE_HAS_SYNC) { + ret = it913x_read_reg_u8(state, TPSD_LOCK); + if (ret == 0x1) + *status |= FE_HAS_LOCK + | state->it913x_status; + else + state->it913x_status = 0; + if (old_status != state->it913x_status) + ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, ret); + } + + return 0; +} + +static int it913x_fe_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct it913x_fe_state *state = fe->demodulator_priv; + int ret = it913x_read_reg_u8(state, SIGNAL_LEVEL); + /*SIGNAL_LEVEL always returns 100%! so using FE_HAS_SIGNAL as switch*/ + if (state->it913x_status & FE_HAS_SIGNAL) + ret = (ret * 0xff) / 0x64; + else + ret = 0x0; + ret |= ret << 0x8; + *strength = ret; + return 0; +} + +static int it913x_fe_read_snr(struct dvb_frontend *fe, u16* snr) +{ + struct it913x_fe_state *state = fe->demodulator_priv; + int ret = it913x_read_reg_u8(state, SIGNAL_QUALITY); + ret = (ret * 0xff) / 0x64; + ret |= (ret << 0x8); + *snr = ~ret; + return 0; +} + +static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + return 0; +} + +static int it913x_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int it913x_fe_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct it913x_fe_state *state = fe->demodulator_priv; + int ret; + u8 reg[8]; + + ret = it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg)); + + if (reg[3] < 3) + p->u.ofdm.constellation = fe_con[reg[3]]; + + if (reg[0] < 3) + p->u.ofdm.transmission_mode = fe_mode[reg[0]]; + + if (reg[1] < 4) + p->u.ofdm.guard_interval = fe_gi[reg[1]]; + + if (reg[2] < 4) + p->u.ofdm.hierarchy_information = fe_hi[reg[2]]; + + p->u.ofdm.code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE; + p->u.ofdm.code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE; + + return 0; +} + +static int it913x_fe_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct it913x_fe_state *state = fe->demodulator_priv; + int ret, i; + u8 empty_ch, last_ch; + + state->it913x_status = 0; + + /* Set bw*/ + ret = it913x_fe_select_bw(state, p->u.ofdm.bandwidth, + state->adcFrequency); + + /* Training Mode Off */ + ret = it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0); + + /* Clear Empty Channel */ + ret = it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0); + + /* Clear bits */ + ret = it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0); + /* LED on */ + ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); + /* Select Band*/ + if ((p->frequency >= 51000000) && (p->frequency <= 230000000)) + i = 0; + else if ((p->frequency >= 350000000) && (p->frequency <= 900000000)) + i = 1; + else if ((p->frequency >= 1450000000) && (p->frequency <= 1680000000)) + i = 2; + else + return -EOPNOTSUPP; + + ret = it913x_write_reg(state, PRO_DMOD, FREE_BAND, i); + + deb_info("Frontend Set Tuner Type %02x", state->tuner_type); + switch (state->tuner_type) { + case IT9137: /* Tuner type 0x38 */ + ret = it9137_set_tuner(state, + p->u.ofdm.bandwidth, p->frequency); + break; + default: + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, p); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + break; + } + /* LED off */ + ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); + /* Trigger ofsm */ + ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); + last_ch = 2; + for (i = 0; i < 40; ++i) { + empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS); + if (last_ch == 1 && empty_ch == 1) + break; + if (last_ch == 2 && empty_ch == 2) + return 0; + last_ch = empty_ch; + msleep(25); + } + for (i = 0; i < 40; ++i) { + if (it913x_read_reg_u8(state, D_TPSD_LOCK) == 1) + break; + msleep(25); + } + + state->frequency = p->frequency; + return 0; +} + +static int it913x_fe_suspend(struct it913x_fe_state *state) +{ + int ret, i; + u8 b; + + ret = it913x_write_reg(state, PRO_DMOD, SUSPEND_FLAG, 0x1); + + ret |= it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); + + for (i = 0; i < 128; i++) { + ret = it913x_read_reg(state, SUSPEND_FLAG, &b, 1); + if (ret < 0) + return -ENODEV; + if (b == 0) + break; + + } + + ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x8); + /* Turn LED off */ + ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); + + ret |= it913x_fe_script_loader(state, it9137_tuner_off); + + return (ret < 0) ? -ENODEV : 0; +} + +/* Power sequence */ +/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */ +/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */ + +static int it913x_fe_sleep(struct dvb_frontend *fe) +{ + struct it913x_fe_state *state = fe->demodulator_priv; + return it913x_fe_suspend(state); +} + +static u32 compute_div(u32 a, u32 b, u32 x) +{ + u32 res = 0; + u32 c = 0; + u32 i = 0; + + if (a > b) { + c = a / b; + a = a - c * b; + } + + for (i = 0; i < x; i++) { + if (a >= b) { + res += 1; + a -= b; + } + a <<= 1; + res <<= 1; + } + + res = (c << x) + res; + + return res; +} + +static int it913x_fe_start(struct it913x_fe_state *state) +{ + struct it913xset *set_fe; + struct it913xset *set_mode; + int ret; + u8 adf = (state->adf & 0xf); + u32 adc, xtal; + u8 b[4]; + + ret = it913x_init_tuner(state); + + if (adf < 12) { + state->crystalFrequency = fe_clockTable[adf].xtal ; + state->table = fe_clockTable[adf].table; + state->adcFrequency = state->table->adcFrequency; + + adc = compute_div(state->adcFrequency, 1000000ul, 19ul); + xtal = compute_div(state->crystalFrequency, 1000000ul, 19ul); + + } else + return -EINVAL; + + deb_info("Xtal Freq :%d Adc Freq :%d Adc %08x Xtal %08x", + state->crystalFrequency, state->adcFrequency, adc, xtal); + + /* Set LED indicator on GPIOH3 */ + ret = it913x_write_reg(state, PRO_LINK, GPIOH3_EN, 0x1); + ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_ON, 0x1); + ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); + + ret |= it913x_write_reg(state, PRO_LINK, 0xf641, state->tuner_type); + ret |= it913x_write_reg(state, PRO_DMOD, 0xf5ca, 0x01); + ret |= it913x_write_reg(state, PRO_DMOD, 0xf715, 0x01); + + b[0] = xtal & 0xff; + b[1] = (xtal >> 8) & 0xff; + b[2] = (xtal >> 16) & 0xff; + b[3] = (xtal >> 24); + ret |= it913x_write(state, PRO_DMOD, XTAL_CLK, b , 4); + + b[0] = adc & 0xff; + b[1] = (adc >> 8) & 0xff; + b[2] = (adc >> 16) & 0xff; + ret |= it913x_write(state, PRO_DMOD, ADC_FREQ, b, 3); + + switch (state->tuner_type) { + case IT9137: /* Tuner type 0x38 */ + set_fe = it9137_set; + break; + default: + return -EINVAL; + } + + /* set the demod */ + ret = it913x_fe_script_loader(state, set_fe); + /* Always solo frontend */ + set_mode = set_solo_fe; + ret |= it913x_fe_script_loader(state, set_mode); + + ret |= it913x_fe_suspend(state); + return 0; +} + +static int it913x_fe_init(struct dvb_frontend *fe) +{ + struct it913x_fe_state *state = fe->demodulator_priv; + int ret = 0; + /* Power Up Tuner - common all versions */ + ret = it913x_write_reg(state, PRO_DMOD, 0xec40, 0x1); + + ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x0); + + ret |= it913x_fe_script_loader(state, init_1); + + switch (state->tuner_type) { + case IT9137: + ret |= it913x_write_reg(state, PRO_DMOD, 0xfba8, 0x0); + break; + default: + return -EINVAL; + } + + return (ret < 0) ? -ENODEV : 0; +} + +static void it913x_fe_release(struct dvb_frontend *fe) +{ + struct it913x_fe_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops it913x_fe_ofdm_ops; + +struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap, + u8 i2c_addr, u8 adf, u8 type) +{ + struct it913x_fe_state *state = NULL; + int ret; + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct it913x_fe_state), GFP_KERNEL); + if (state == NULL) + goto error; + + state->i2c_adap = i2c_adap; + state->i2c_addr = i2c_addr; + state->adf = adf; + state->tuner_type = type; + + ret = it913x_fe_start(state); + if (ret < 0) + goto error; + + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &it913x_fe_ofdm_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(it913x_fe_attach); + +static struct dvb_frontend_ops it913x_fe_ofdm_ops = { + + .info = { + .name = "it913x-fe DVB-T", + .type = FE_OFDM, + .frequency_min = 51000000, + .frequency_max = 1680000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = it913x_fe_release, + + .init = it913x_fe_init, + .sleep = it913x_fe_sleep, + + .set_frontend = it913x_fe_set_frontend, + .get_frontend = it913x_fe_get_frontend, + + .read_status = it913x_fe_read_status, + .read_signal_strength = it913x_fe_read_signal_strength, + .read_snr = it913x_fe_read_snr, + .read_ber = it913x_fe_read_ber, + .read_ucblocks = it913x_fe_read_ucblocks, +}; + +MODULE_DESCRIPTION("it913x Frontend and it9137 tuner"); +MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); +MODULE_VERSION("1.07"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/it913x-fe.h b/drivers/media/dvb/frontends/it913x-fe.h new file mode 100644 index 00000000000..9d97f32e690 --- /dev/null +++ b/drivers/media/dvb/frontends/it913x-fe.h @@ -0,0 +1,196 @@ +/* + * Driver for it913x Frontend + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= + */ + +#ifndef IT913X_FE_H +#define IT913X_FE_H + +#include <linux/dvb/frontend.h> +#include "dvb_frontend.h" +#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \ +defined(MODULE)) +extern struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap, + u8 i2c_addr, u8 adf, u8 type); +#else +static inline struct dvb_frontend *it913x_fe_attach( + struct i2c_adapter *i2c_adap, u8 i2c_addr, u8 adf, u8 type) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_IT913X_FE */ +#define I2C_BASE_ADDR 0x10 +#define DEV_0 0x0 +#define DEV_1 0x10 +#define PRO_LINK 0x0 +#define PRO_DMOD 0x1 +#define DEV_0_DMOD (PRO_DMOD << 0x7) +#define DEV_1_DMOD (DEV_0_DMOD | DEV_1) +#define CHIP2_I2C_ADDR 0x3a + +#define AFE_MEM0 0xfb24 + +#define MP2_SW_RST 0xf99d +#define MP2IF2_SW_RST 0xf9a4 + +#define PADODPU 0xd827 +#define THIRDODPU 0xd828 +#define AGC_O_D 0xd829 + +#define EP0_TX_EN 0xdd11 +#define EP0_TX_NAK 0xdd13 +#define EP4_TX_LEN_LSB 0xdd88 +#define EP4_TX_LEN_MSB 0xdd89 +#define EP4_MAX_PKT 0xdd0c +#define EP5_TX_LEN_LSB 0xdd8a +#define EP5_TX_LEN_MSB 0xdd8b +#define EP5_MAX_PKT 0xdd0d + +#define IO_MUX_POWER_CLK 0xd800 +#define CLK_O_EN 0xd81a +#define I2C_CLK 0xf103 +#define I2C_CLK_100 0x7 +#define I2C_CLK_400 0x1a + +#define D_TPSD_LOCK 0xf5a9 +#define MP2IF2_EN 0xf9a3 +#define MP2IF_SERIAL 0xf985 +#define TSIS_ENABLE 0xf9cd +#define MP2IF2_HALF_PSB 0xf9a5 +#define MP2IF_STOP_EN 0xf9b5 +#define MPEG_FULL_SPEED 0xf990 +#define TOP_HOSTB_SER_MODE 0xd91c + +#define PID_RST 0xf992 +#define PID_EN 0xf993 +#define PID_INX_EN 0xf994 +#define PID_INX 0xf995 +#define PID_LSB 0xf996 +#define PID_MSB 0xf997 + +#define MP2IF_MPEG_PAR_MODE 0xf986 +#define DCA_UPPER_CHIP 0xf731 +#define DCA_LOWER_CHIP 0xf732 +#define DCA_PLATCH 0xf730 +#define DCA_FPGA_LATCH 0xf778 +#define DCA_STAND_ALONE 0xf73c +#define DCA_ENABLE 0xf776 + +#define DVBT_INTEN 0xf41f +#define DVBT_ENABLE 0xf41a +#define HOSTB_DCA_LOWER 0xd91f +#define HOSTB_MPEG_PAR_MODE 0xd91b +#define HOSTB_MPEG_SER_MODE 0xd91c +#define HOSTB_MPEG_SER_DO7 0xd91d +#define HOSTB_DCA_UPPER 0xd91e +#define PADMISCDR2 0xd830 +#define PADMISCDR4 0xd831 +#define PADMISCDR8 0xd832 +#define PADMISCDRSR 0xd833 +#define LOCK3_OUT 0xd8fd + +#define GPIOH1_O 0xd8af +#define GPIOH1_EN 0xd8b0 +#define GPIOH1_ON 0xd8b1 +#define GPIOH3_O 0xd8b3 +#define GPIOH3_EN 0xd8b4 +#define GPIOH3_ON 0xd8b5 +#define GPIOH5_O 0xd8bb +#define GPIOH5_EN 0xd8bc +#define GPIOH5_ON 0xd8bd + +#define AFE_MEM0 0xfb24 + +#define REG_TPSD_TX_MODE 0xf900 +#define REG_TPSD_GI 0xf901 +#define REG_TPSD_HIER 0xf902 +#define REG_TPSD_CONST 0xf903 +#define REG_BW 0xf904 +#define REG_PRIV 0xf905 +#define REG_TPSD_HP_CODE 0xf906 +#define REG_TPSD_LP_CODE 0xf907 + +#define MP2IF_SYNC_LK 0xf999 +#define ADC_FREQ 0xf1cd + +#define TRIGGER_OFSM 0x0000 +/* COEFF Registers start at 0x0001 to 0x0020 */ +#define COEFF_1_2048 0x0001 +#define XTAL_CLK 0x0025 +#define BFS_FCW 0x0029 +#define TPSD_LOCK 0x003c +#define TRAINING_MODE 0x0040 +#define ADC_X_2 0x0045 +#define TUNER_ID 0x0046 +#define EMPTY_CHANNEL_STATUS 0x0047 +#define SIGNAL_LEVEL 0x0048 +#define SIGNAL_QUALITY 0x0049 +#define EST_SIGNAL_LEVEL 0x004a +#define FREE_BAND 0x004b +#define SUSPEND_FLAG 0x004c +/* Build in tuners */ +#define IT9137 0x38 + +enum { + CMD_DEMOD_READ = 0, + CMD_DEMOD_WRITE, + CMD_TUNER_READ, + CMD_TUNER_WRITE, + CMD_REG_EEPROM_READ, + CMD_REG_EEPROM_WRITE, + CMD_DATA_READ, + CMD_VAR_READ = 8, + CMD_VAR_WRITE, + CMD_PLATFORM_GET, + CMD_PLATFORM_SET, + CMD_IP_CACHE, + CMD_IP_ADD, + CMD_IP_REMOVE, + CMD_PID_ADD, + CMD_PID_REMOVE, + CMD_SIPSI_GET, + CMD_SIPSI_MPE_RESET, + CMD_H_PID_ADD = 0x15, + CMD_H_PID_REMOVE, + CMD_ABORT, + CMD_IR_GET, + CMD_IR_SET, + CMD_FW_DOWNLOAD = 0x21, + CMD_QUERYINFO, + CMD_BOOT, + CMD_FW_DOWNLOAD_BEGIN, + CMD_FW_DOWNLOAD_END, + CMD_RUN_CODE, + CMD_SCATTER_READ = 0x28, + CMD_SCATTER_WRITE, + CMD_GENERIC_READ, + CMD_GENERIC_WRITE +}; + +enum { + READ_LONG, + WRITE_LONG, + READ_SHORT, + WRITE_SHORT, + READ_DATA, + WRITE_DATA, + WRITE_CMD, +}; + +#endif /* IT913X_FE_H */ diff --git a/drivers/media/dvb/frontends/lnbp22.c b/drivers/media/dvb/frontends/lnbp22.c new file mode 100644 index 00000000000..84ad0390a4a --- /dev/null +++ b/drivers/media/dvb/frontends/lnbp22.c @@ -0,0 +1,148 @@ +/* + * lnbp22.h - driver for lnb supply and control ic lnbp22 + * + * Copyright (C) 2006 Dominik Kuhlen + * Based on lnbp21 driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org + */ +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include "dvb_frontend.h" +#include "lnbp22.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + + +#define dprintk(lvl, arg...) if (debug >= (lvl)) printk(arg) + +struct lnbp22 { + u8 config[4]; + struct i2c_adapter *i2c; +}; + +static int lnbp22_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv; + struct i2c_msg msg = { + .addr = 0x08, + .flags = 0, + .buf = (char *)&lnbp22->config, + .len = sizeof(lnbp22->config), + }; + + dprintk(1, "%s: %d (18V=%d 13V=%d)\n", __func__, voltage, + SEC_VOLTAGE_18, SEC_VOLTAGE_13); + + lnbp22->config[3] = 0x60; /* Power down */ + switch (voltage) { + case SEC_VOLTAGE_OFF: + break; + case SEC_VOLTAGE_13: + lnbp22->config[3] |= LNBP22_EN; + break; + case SEC_VOLTAGE_18: + lnbp22->config[3] |= (LNBP22_EN | LNBP22_VSEL); + break; + default: + return -EINVAL; + }; + + dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]); + return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static int lnbp22_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +{ + struct lnbp22 *lnbp22 = (struct lnbp22 *) fe->sec_priv; + struct i2c_msg msg = { + .addr = 0x08, + .flags = 0, + .buf = (char *)&lnbp22->config, + .len = sizeof(lnbp22->config), + }; + + dprintk(1, "%s: %d\n", __func__, (int)arg); + if (arg) + lnbp22->config[3] |= LNBP22_LLC; + else + lnbp22->config[3] &= ~LNBP22_LLC; + + return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static void lnbp22_release(struct dvb_frontend *fe) +{ + dprintk(1, "%s\n", __func__); + /* LNBP power off */ + lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF); + + /* free data */ + kfree(fe->sec_priv); + fe->sec_priv = NULL; +} + +struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c) +{ + struct lnbp22 *lnbp22 = kmalloc(sizeof(struct lnbp22), GFP_KERNEL); + if (!lnbp22) + return NULL; + + /* default configuration */ + lnbp22->config[0] = 0x00; /* ? */ + lnbp22->config[1] = 0x28; /* ? */ + lnbp22->config[2] = 0x48; /* ? */ + lnbp22->config[3] = 0x60; /* Power down */ + lnbp22->i2c = i2c; + fe->sec_priv = lnbp22; + + /* detect if it is present or not */ + if (lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF)) { + dprintk(0, "%s LNBP22 not found\n", __func__); + kfree(lnbp22); + fe->sec_priv = NULL; + return NULL; + } + + /* install release callback */ + fe->ops.release_sec = lnbp22_release; + + /* override frontend ops */ + fe->ops.set_voltage = lnbp22_set_voltage; + fe->ops.enable_high_lnb_voltage = lnbp22_enable_high_lnb_voltage; + + return fe; +} +EXPORT_SYMBOL(lnbp22_attach); + +MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22"); +MODULE_AUTHOR("Dominik Kuhlen"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/lnbp22.h b/drivers/media/dvb/frontends/lnbp22.h new file mode 100644 index 00000000000..63e2dec7e68 --- /dev/null +++ b/drivers/media/dvb/frontends/lnbp22.h @@ -0,0 +1,57 @@ +/* + * lnbp22.h - driver for lnb supply and control ic lnbp22 + * + * Copyright (C) 2006 Dominik Kuhlen + * Based on lnbp21.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org + */ + +#ifndef _LNBP22_H +#define _LNBP22_H + +/* Enable */ +#define LNBP22_EN 0x10 +/* Voltage selection */ +#define LNBP22_VSEL 0x02 +/* Plus 1 Volt Bit */ +#define LNBP22_LLC 0x01 + +#include <linux/dvb/frontend.h> + +#if defined(CONFIG_DVB_LNBP22) || \ + (defined(CONFIG_DVB_LNBP22_MODULE) && defined(MODULE)) +/* + * override_set and override_clear control which system register bits (above) + * to always set & clear + */ +extern struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LNBP22 */ + +#endif /* _LNBP22_H */ diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c index d70eee00f33..117a56926dc 100644 --- a/drivers/media/dvb/frontends/stb0899_algo.c +++ b/drivers/media/dvb/frontends/stb0899_algo.c @@ -358,6 +358,9 @@ static enum stb0899_status stb0899_check_data(struct stb0899_state *state) else dataTime = 500; + /* clear previous failed END_LOOPVIT */ + stb0899_read_reg(state, STB0899_VSTATUS); + stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */ while (1) { /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */ diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c index 37a222d9ddb..8408ef877b4 100644 --- a/drivers/media/dvb/frontends/stb0899_drv.c +++ b/drivers/media/dvb/frontends/stb0899_drv.c @@ -706,7 +706,7 @@ static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_ma stb0899_write_reg(state, STB0899_DISCNTRL1, reg); for (i = 0; i < cmd->msg_len; i++) { /* wait for FIFO empty */ - if (stb0899_wait_diseqc_fifo_empty(state, 10) < 0) + if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0) return -ETIMEDOUT; stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]); @@ -1426,9 +1426,9 @@ static void stb0899_set_iterations(struct stb0899_state *state) if (iter_scale > config->ldpc_max_iter) iter_scale = config->ldpc_max_iter; - reg = STB0899_READ_S2REG(STB0899_S2DEMOD, MAX_ITER); + reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER); STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale); - stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg); + stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg); } static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c index 8e0cfadba68..0aa3962ff18 100644 --- a/drivers/media/dvb/frontends/stv0288.c +++ b/drivers/media/dvb/frontends/stv0288.c @@ -127,6 +127,11 @@ static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate) if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; + stv0288_writeregI(state, 0x22, 0); + stv0288_writeregI(state, 0x23, 0); + stv0288_writeregI(state, 0x2b, 0xff); + stv0288_writeregI(state, 0x2c, 0xf7); + temp = (unsigned int)srate / 1000; temp = temp * 32768; @@ -461,6 +466,7 @@ static int stv0288_set_frontend(struct dvb_frontend *fe, char tm; unsigned char tda[3]; + u8 reg, time_out = 0; dprintk("%s : FE_SET_FRONTEND\n", __func__); @@ -488,22 +494,29 @@ static int stv0288_set_frontend(struct dvb_frontend *fe, /* Carrier lock control register */ stv0288_writeregI(state, 0x15, 0xc5); - tda[0] = 0x2b; /* CFRM */ tda[2] = 0x0; /* CFRL */ - for (tm = -6; tm < 7;) { + for (tm = -9; tm < 7;) { /* Viterbi status */ - if (stv0288_readreg(state, 0x24) & 0x8) - break; - - tda[2] += 40; - if (tda[2] < 40) + reg = stv0288_readreg(state, 0x24); + if (reg & 0x8) + break; + if (reg & 0x80) { + time_out++; + if (time_out > 10) + break; + tda[2] += 40; + if (tda[2] < 40) + tm++; + } else { tm++; + tda[2] = 0; + time_out = 0; + } tda[1] = (unsigned char)tm; stv0288_writeregI(state, 0x2b, tda[1]); stv0288_writeregI(state, 0x2c, tda[2]); udelay(30); } - state->tuner_frequency = c->frequency; state->fec_inner = FEC_AUTO; state->symbol_rate = c->symbol_rate; diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 52d8712411e..ebda41936b9 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -3463,9 +3463,15 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct stv090x_state *state = fe->demodulator_priv; - u32 reg; + u32 reg, dstatus; u8 search_state; + *status = 0; + + dstatus = STV090x_READ_DEMOD(state, DSTATUS); + if (STV090x_GETFIELD_Px(dstatus, CAR_LOCK_FIELD)) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + reg = STV090x_READ_DEMOD(state, DMDSTATE); search_state = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); @@ -3474,41 +3480,30 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) case 1: /* first PLH detected */ default: dprintk(FE_DEBUG, 1, "Status: Unlocked (Searching ..)"); - *status = 0; break; case 2: /* DVB-S2 mode */ dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2"); - reg = STV090x_READ_DEMOD(state, DSTATUS); - if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) { + if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) { reg = STV090x_READ_DEMOD(state, PDELSTATUS1); if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) { + *status |= FE_HAS_VITERBI; reg = STV090x_READ_DEMOD(state, TSSTATUS); - if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) { - *status = FE_HAS_SIGNAL | - FE_HAS_CARRIER | - FE_HAS_VITERBI | - FE_HAS_SYNC | - FE_HAS_LOCK; - } + if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; } } break; case 3: /* DVB-S1/legacy mode */ dprintk(FE_DEBUG, 1, "Delivery system: DVB-S"); - reg = STV090x_READ_DEMOD(state, DSTATUS); - if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) { + if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) { reg = STV090x_READ_DEMOD(state, VSTATUSVIT); if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) { + *status |= FE_HAS_VITERBI; reg = STV090x_READ_DEMOD(state, TSSTATUS); - if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) { - *status = FE_HAS_SIGNAL | - FE_HAS_CARRIER | - FE_HAS_VITERBI | - FE_HAS_SYNC | - FE_HAS_LOCK; - } + if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; } } break; diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c index 93f6a75c238..7f105946a43 100644 --- a/drivers/media/dvb/frontends/tda10048.c +++ b/drivers/media/dvb/frontends/tda10048.c @@ -206,15 +206,16 @@ static struct init_tab { static struct pll_tab { u32 clk_freq_khz; u32 if_freq_khz; - u8 m, n, p; } pll_tab[] = { - { TDA10048_CLK_4000, TDA10048_IF_36130, 10, 0, 0 }, - { TDA10048_CLK_16000, TDA10048_IF_3300, 10, 3, 0 }, - { TDA10048_CLK_16000, TDA10048_IF_3500, 10, 3, 0 }, - { TDA10048_CLK_16000, TDA10048_IF_3800, 10, 3, 0 }, - { TDA10048_CLK_16000, TDA10048_IF_4000, 10, 3, 0 }, - { TDA10048_CLK_16000, TDA10048_IF_4300, 10, 3, 0 }, - { TDA10048_CLK_16000, TDA10048_IF_36130, 10, 3, 0 }, + { TDA10048_CLK_4000, TDA10048_IF_36130 }, + { TDA10048_CLK_16000, TDA10048_IF_3300 }, + { TDA10048_CLK_16000, TDA10048_IF_3500 }, + { TDA10048_CLK_16000, TDA10048_IF_3800 }, + { TDA10048_CLK_16000, TDA10048_IF_4000 }, + { TDA10048_CLK_16000, TDA10048_IF_4300 }, + { TDA10048_CLK_16000, TDA10048_IF_4500 }, + { TDA10048_CLK_16000, TDA10048_IF_5000 }, + { TDA10048_CLK_16000, TDA10048_IF_36130 }, }; static int tda10048_writereg(struct tda10048_state *state, u8 reg, u8 data) @@ -460,9 +461,6 @@ static int tda10048_set_if(struct dvb_frontend *fe, enum fe_bandwidth bw) state->freq_if_hz = pll_tab[i].if_freq_khz * 1000; state->xtal_hz = pll_tab[i].clk_freq_khz * 1000; - state->pll_mfactor = pll_tab[i].m; - state->pll_nfactor = pll_tab[i].n; - state->pll_pfactor = pll_tab[i].p; break; } } @@ -781,6 +779,10 @@ static int tda10048_init(struct dvb_frontend *fe) dprintk(1, "%s()\n", __func__); + /* PLL */ + init_tab[4].data = (u8)(state->pll_mfactor); + init_tab[5].data = (u8)(state->pll_nfactor) | 0x40; + /* Apply register defaults */ for (i = 0; i < ARRAY_SIZE(init_tab); i++) tda10048_writereg(state, init_tab[i].reg, init_tab[i].data); @@ -1123,7 +1125,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, /* setup the state and clone the config */ memcpy(&state->config, config, sizeof(*config)); state->i2c = i2c; - state->fwloaded = 0; + state->fwloaded = config->no_firmware; state->bandwidth = BANDWIDTH_8_MHZ; /* check if the demod is present */ @@ -1135,6 +1137,17 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, sizeof(struct dvb_frontend_ops)); state->frontend.demodulator_priv = state; + /* set pll */ + if (config->set_pll) { + state->pll_mfactor = config->pll_m; + state->pll_nfactor = config->pll_n; + state->pll_pfactor = config->pll_p; + } else { + state->pll_mfactor = 10; + state->pll_nfactor = 3; + state->pll_pfactor = 0; + } + /* Establish any defaults the the user didn't pass */ tda10048_establish_defaults(&state->frontend); diff --git a/drivers/media/dvb/frontends/tda10048.h b/drivers/media/dvb/frontends/tda10048.h index 8828ceaf74b..fb2ef5ac948 100644 --- a/drivers/media/dvb/frontends/tda10048.h +++ b/drivers/media/dvb/frontends/tda10048.h @@ -51,6 +51,7 @@ struct tda10048_config { #define TDA10048_IF_4300 4300 #define TDA10048_IF_4500 4500 #define TDA10048_IF_4750 4750 +#define TDA10048_IF_5000 5000 #define TDA10048_IF_36130 36130 u16 dtv6_if_freq_khz; u16 dtv7_if_freq_khz; @@ -62,6 +63,13 @@ struct tda10048_config { /* Disable I2C gate access */ u8 disable_gate_access; + + bool no_firmware; + + bool set_pll; + u8 pll_m; + u8 pll_p; + u8 pll_n; }; #if defined(CONFIG_DVB_TDA10048) || \ diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c new file mode 100644 index 00000000000..0c37434d19e --- /dev/null +++ b/drivers/media/dvb/frontends/tda10071.c @@ -0,0 +1,1269 @@ +/* + * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "tda10071_priv.h" + +int tda10071_debug; +module_param_named(debug, tda10071_debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +static struct dvb_frontend_ops tda10071_ops; + +/* write multiple registers */ +static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, + int len) +{ + int ret; + u8 buf[len+1]; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg.i2c_address, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + buf[0] = reg; + memcpy(&buf[1], val, len); + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len); + ret = -EREMOTEIO; + } + return ret; +} + +/* read multiple registers */ +static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, + int len) +{ + int ret; + u8 buf[len]; + struct i2c_msg msg[2] = { + { + .addr = priv->cfg.i2c_address, + .flags = 0, + .len = 1, + .buf = ®, + }, { + .addr = priv->cfg.i2c_address, + .flags = I2C_M_RD, + .len = sizeof(buf), + .buf = buf, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret == 2) { + memcpy(val, buf, len); + ret = 0; + } else { + warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len); + ret = -EREMOTEIO; + } + return ret; +} + +/* write single register */ +static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val) +{ + return tda10071_wr_regs(priv, reg, &val, 1); +} + +/* read single register */ +static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val) +{ + return tda10071_rd_regs(priv, reg, val, 1); +} + +/* write single register with mask */ +int tda10071_wr_reg_mask(struct tda10071_priv *priv, u8 reg, u8 val, u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = tda10071_rd_regs(priv, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return tda10071_wr_regs(priv, reg, &val, 1); +} + +/* read single register with mask */ +int tda10071_rd_reg_mask(struct tda10071_priv *priv, u8 reg, u8 *val, u8 mask) +{ + int ret, i; + u8 tmp; + + ret = tda10071_rd_regs(priv, reg, &tmp, 1); + if (ret) + return ret; + + tmp &= mask; + + /* find position of the first bit */ + for (i = 0; i < 8; i++) { + if ((mask >> i) & 0x01) + break; + } + *val = tmp >> i; + + return 0; +} + +/* execute firmware command */ +static int tda10071_cmd_execute(struct tda10071_priv *priv, + struct tda10071_cmd *cmd) +{ + int ret, i; + u8 tmp; + + if (!priv->warm) { + ret = -EFAULT; + goto error; + } + + /* write cmd and args for firmware */ + ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len); + if (ret) + goto error; + + /* start cmd execution */ + ret = tda10071_wr_reg(priv, 0x1f, 1); + if (ret) + goto error; + + /* wait cmd execution terminate */ + for (i = 1000, tmp = 1; i && tmp; i--) { + ret = tda10071_rd_reg(priv, 0x1f, &tmp); + if (ret) + goto error; + + usleep_range(200, 5000); + } + + dbg("%s: loop=%d", __func__, i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto error; + } + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_set_tone(struct dvb_frontend *fe, + fe_sec_tone_mode_t fe_sec_tone_mode) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret; + u8 tone; + + if (!priv->warm) { + ret = -EFAULT; + goto error; + } + + dbg("%s: tone_mode=%d", __func__, fe_sec_tone_mode); + + switch (fe_sec_tone_mode) { + case SEC_TONE_ON: + tone = 1; + break; + case SEC_TONE_OFF: + tone = 0; + break; + default: + dbg("%s: invalid fe_sec_tone_mode", __func__); + ret = -EINVAL; + goto error; + } + + cmd.args[0x00] = CMD_LNB_PCB_CONFIG; + cmd.args[0x01] = 0; + cmd.args[0x02] = 0x00; + cmd.args[0x03] = 0x00; + cmd.args[0x04] = tone; + cmd.len = 0x05; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t fe_sec_voltage) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret; + u8 voltage; + + if (!priv->warm) { + ret = -EFAULT; + goto error; + } + + dbg("%s: voltage=%d", __func__, fe_sec_voltage); + + switch (fe_sec_voltage) { + case SEC_VOLTAGE_13: + voltage = 0; + break; + case SEC_VOLTAGE_18: + voltage = 1; + break; + case SEC_VOLTAGE_OFF: + voltage = 0; + break; + default: + dbg("%s: invalid fe_sec_voltage", __func__); + ret = -EINVAL; + goto error; + }; + + cmd.args[0x00] = CMD_LNB_SET_DC_LEVEL; + cmd.args[0x01] = 0; + cmd.args[0x02] = voltage; + cmd.len = 0x03; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *diseqc_cmd) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret, i; + u8 tmp; + + if (!priv->warm) { + ret = -EFAULT; + goto error; + } + + dbg("%s: msg_len=%d", __func__, diseqc_cmd->msg_len); + + if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 16) { + ret = -EINVAL; + goto error; + } + + /* wait LNB TX */ + for (i = 500, tmp = 0; i && !tmp; i--) { + ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); + if (ret) + goto error; + + usleep_range(10000, 20000); + } + + dbg("%s: loop=%d", __func__, i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto error; + } + + ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); + if (ret) + goto error; + + cmd.args[0x00] = CMD_LNB_SEND_DISEQC; + cmd.args[0x01] = 0; + cmd.args[0x02] = 0; + cmd.args[0x03] = 0; + cmd.args[0x04] = 2; + cmd.args[0x05] = 0; + cmd.args[0x06] = diseqc_cmd->msg_len; + memcpy(&cmd.args[0x07], diseqc_cmd->msg, diseqc_cmd->msg_len); + cmd.len = 0x07 + diseqc_cmd->msg_len; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, + struct dvb_diseqc_slave_reply *reply) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret, i; + u8 tmp; + + if (!priv->warm) { + ret = -EFAULT; + goto error; + } + + dbg("%s:", __func__); + + /* wait LNB RX */ + for (i = 500, tmp = 0; i && !tmp; i--) { + ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02); + if (ret) + goto error; + + usleep_range(10000, 20000); + } + + dbg("%s: loop=%d", __func__, i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto error; + } + + /* reply len */ + ret = tda10071_rd_reg(priv, 0x46, &tmp); + if (ret) + goto error; + + reply->msg_len = tmp & 0x1f; /* [4:0] */; + if (reply->msg_len > sizeof(reply->msg)) + reply->msg_len = sizeof(reply->msg); /* truncate API max */ + + /* read reply */ + cmd.args[0x00] = CMD_LNB_UPDATE_REPLY; + cmd.args[0x01] = 0; + cmd.len = 0x02; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len); + if (ret) + goto error; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t fe_sec_mini_cmd) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret, i; + u8 tmp, burst; + + if (!priv->warm) { + ret = -EFAULT; + goto error; + } + + dbg("%s: fe_sec_mini_cmd=%d", __func__, fe_sec_mini_cmd); + + switch (fe_sec_mini_cmd) { + case SEC_MINI_A: + burst = 0; + break; + case SEC_MINI_B: + burst = 1; + break; + default: + dbg("%s: invalid fe_sec_mini_cmd", __func__); + ret = -EINVAL; + goto error; + } + + /* wait LNB TX */ + for (i = 500, tmp = 0; i && !tmp; i--) { + ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); + if (ret) + goto error; + + usleep_range(10000, 20000); + } + + dbg("%s: loop=%d", __func__, i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto error; + } + + ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); + if (ret) + goto error; + + cmd.args[0x00] = CMD_LNB_SEND_TONEBURST; + cmd.args[0x01] = 0; + cmd.args[0x02] = burst; + cmd.len = 0x03; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + int ret; + u8 tmp; + + *status = 0; + + if (!priv->warm) { + ret = 0; + goto error; + } + + ret = tda10071_rd_reg(priv, 0x39, &tmp); + if (ret) + goto error; + + if (tmp & 0x01) /* tuner PLL */ + *status |= FE_HAS_SIGNAL; + if (tmp & 0x02) /* demod PLL */ + *status |= FE_HAS_CARRIER; + if (tmp & 0x04) /* viterbi or LDPC*/ + *status |= FE_HAS_VITERBI; + if (tmp & 0x08) /* RS or BCH */ + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + + priv->fe_status = *status; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + int ret; + u8 buf[2]; + + if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + *snr = 0; + ret = 0; + goto error; + } + + ret = tda10071_rd_regs(priv, 0x3a, buf, 2); + if (ret) + goto error; + + /* Es/No dBx10 */ + *snr = buf[0] << 8 | buf[1]; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret; + u8 tmp; + + if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + *strength = 0; + ret = 0; + goto error; + } + + cmd.args[0x00] = CMD_GET_AGCACC; + cmd.args[0x01] = 0; + cmd.len = 0x02; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + /* input power estimate dBm */ + ret = tda10071_rd_reg(priv, 0x50, &tmp); + if (ret) + goto error; + + if (tmp < 181) + tmp = 181; /* -75 dBm */ + else if (tmp > 236) + tmp = 236; /* -20 dBm */ + + /* scale value to 0x0000-0xffff */ + *strength = (tmp-181) * 0xffff / (236-181); + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret, i, len; + u8 tmp, reg, buf[8]; + + if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + *ber = priv->ber = 0; + ret = 0; + goto error; + } + + switch (priv->delivery_system) { + case SYS_DVBS: + reg = 0x4c; + len = 8; + i = 1; + break; + case SYS_DVBS2: + reg = 0x4d; + len = 4; + i = 0; + break; + default: + *ber = priv->ber = 0; + return 0; + } + + ret = tda10071_rd_reg(priv, reg, &tmp); + if (ret) + goto error; + + if (priv->meas_count[i] == tmp) { + dbg("%s: meas not ready=%02x", __func__, tmp); + *ber = priv->ber; + return 0; + } else { + priv->meas_count[i] = tmp; + } + + cmd.args[0x00] = CMD_BER_UPDATE_COUNTERS; + cmd.args[0x01] = 0; + cmd.args[0x02] = i; + cmd.len = 0x03; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + ret = tda10071_rd_regs(priv, cmd.len, buf, len); + if (ret) + goto error; + + if (priv->delivery_system == SYS_DVBS) { + *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + priv->ucb += (buf[4] << 8) | buf[5]; + } else { + *ber = (buf[0] << 8) | buf[1]; + } + priv->ber = *ber; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + int ret = 0; + + if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + *ucblocks = 0; + goto error; + } + + /* UCB is updated when BER is read. Assume BER is read anyway. */ + + *ucblocks = priv->ucb; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u8 mode, rolloff, pilot, inversion, div; + + dbg("%s: delivery_system=%d modulation=%d frequency=%d " \ + "symbol_rate=%d inversion=%d pilot=%d rolloff=%d", __func__, + c->delivery_system, c->modulation, c->frequency, + c->symbol_rate, c->inversion, c->pilot, c->rolloff); + + priv->delivery_system = SYS_UNDEFINED; + + if (!priv->warm) { + ret = -EFAULT; + goto error; + } + + switch (c->inversion) { + case INVERSION_OFF: + inversion = 1; + break; + case INVERSION_ON: + inversion = 0; + break; + case INVERSION_AUTO: + /* 2 = auto; try first on then off + * 3 = auto; try first off then on */ + inversion = 3; + break; + default: + dbg("%s: invalid inversion", __func__); + ret = -EINVAL; + goto error; + } + + switch (c->delivery_system) { + case SYS_DVBS: + rolloff = 0; + pilot = 2; + break; + case SYS_DVBS2: + switch (c->rolloff) { + case ROLLOFF_20: + rolloff = 2; + break; + case ROLLOFF_25: + rolloff = 1; + break; + case ROLLOFF_35: + rolloff = 0; + break; + case ROLLOFF_AUTO: + default: + dbg("%s: invalid rolloff", __func__); + ret = -EINVAL; + goto error; + } + + switch (c->pilot) { + case PILOT_OFF: + pilot = 0; + break; + case PILOT_ON: + pilot = 1; + break; + case PILOT_AUTO: + pilot = 2; + break; + default: + dbg("%s: invalid pilot", __func__); + ret = -EINVAL; + goto error; + } + break; + default: + dbg("%s: invalid delivery_system", __func__); + ret = -EINVAL; + goto error; + } + + for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { + if (c->delivery_system == TDA10071_MODCOD[i].delivery_system && + c->modulation == TDA10071_MODCOD[i].modulation && + c->fec_inner == TDA10071_MODCOD[i].fec) { + mode = TDA10071_MODCOD[i].val; + dbg("%s: mode found=%02x", __func__, mode); + break; + } + } + + if (mode == 0xff) { + dbg("%s: invalid parameter combination", __func__); + ret = -EINVAL; + goto error; + } + + if (c->symbol_rate <= 5000000) + div = 14; + else + div = 4; + + ret = tda10071_wr_reg(priv, 0x81, div); + if (ret) + goto error; + + ret = tda10071_wr_reg(priv, 0xe3, div); + if (ret) + goto error; + + cmd.args[0x00] = CMD_CHANGE_CHANNEL; + cmd.args[0x01] = 0; + cmd.args[0x02] = mode; + cmd.args[0x03] = (c->frequency >> 16) & 0xff; + cmd.args[0x04] = (c->frequency >> 8) & 0xff; + cmd.args[0x05] = (c->frequency >> 0) & 0xff; + cmd.args[0x06] = ((c->symbol_rate / 1000) >> 8) & 0xff; + cmd.args[0x07] = ((c->symbol_rate / 1000) >> 0) & 0xff; + cmd.args[0x08] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff; + cmd.args[0x09] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff; + cmd.args[0x0a] = rolloff; + cmd.args[0x0b] = inversion; + cmd.args[0x0c] = pilot; + cmd.args[0x0d] = 0x00; + cmd.args[0x0e] = 0x00; + cmd.len = 0x0f; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + priv->delivery_system = c->delivery_system; + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u8 buf[5], tmp; + + if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { + ret = -EFAULT; + goto error; + } + + ret = tda10071_rd_regs(priv, 0x30, buf, 5); + if (ret) + goto error; + + tmp = buf[0] & 0x3f; + for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { + if (tmp == TDA10071_MODCOD[i].val) { + c->modulation = TDA10071_MODCOD[i].modulation; + c->fec_inner = TDA10071_MODCOD[i].fec; + c->delivery_system = TDA10071_MODCOD[i].delivery_system; + } + } + + switch ((buf[1] >> 0) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + } + + switch ((buf[1] >> 7) & 0x01) { + case 0: + c->pilot = PILOT_OFF; + break; + case 1: + c->pilot = PILOT_ON; + break; + } + + c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0); + + ret = tda10071_rd_regs(priv, 0x52, buf, 3); + if (ret) + goto error; + + c->symbol_rate = (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0); + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_init(struct dvb_frontend *fe) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret, i, len, remaining, fw_size; + const struct firmware *fw; + u8 *fw_file = TDA10071_DEFAULT_FIRMWARE; + u8 tmp, buf[4]; + struct tda10071_reg_val_mask tab[] = { + { 0xcd, 0x00, 0x07 }, + { 0x80, 0x00, 0x02 }, + { 0xcd, 0x00, 0xc0 }, + { 0xce, 0x00, 0x1b }, + { 0x9d, 0x00, 0x01 }, + { 0x9d, 0x00, 0x02 }, + { 0x9e, 0x00, 0x01 }, + { 0x87, 0x00, 0x80 }, + { 0xce, 0x00, 0x08 }, + { 0xce, 0x00, 0x10 }, + }; + struct tda10071_reg_val_mask tab2[] = { + { 0xf1, 0x70, 0xff }, + { 0x88, priv->cfg.pll_multiplier, 0x3f }, + { 0x89, 0x00, 0x10 }, + { 0x89, 0x10, 0x10 }, + { 0xc0, 0x01, 0x01 }, + { 0xc0, 0x00, 0x01 }, + { 0xe0, 0xff, 0xff }, + { 0xe0, 0x00, 0xff }, + { 0x96, 0x1e, 0x7e }, + { 0x8b, 0x08, 0x08 }, + { 0x8b, 0x00, 0x08 }, + { 0x8f, 0x1a, 0x7e }, + { 0x8c, 0x68, 0xff }, + { 0x8d, 0x08, 0xff }, + { 0x8e, 0x4c, 0xff }, + { 0x8f, 0x01, 0x01 }, + { 0x8b, 0x04, 0x04 }, + { 0x8b, 0x00, 0x04 }, + { 0x87, 0x05, 0x07 }, + { 0x80, 0x00, 0x20 }, + { 0xc8, 0x01, 0xff }, + { 0xb4, 0x47, 0xff }, + { 0xb5, 0x9c, 0xff }, + { 0xb6, 0x7d, 0xff }, + { 0xba, 0x00, 0x03 }, + { 0xb7, 0x47, 0xff }, + { 0xb8, 0x9c, 0xff }, + { 0xb9, 0x7d, 0xff }, + { 0xba, 0x00, 0x0c }, + { 0xc8, 0x00, 0xff }, + { 0xcd, 0x00, 0x04 }, + { 0xcd, 0x00, 0x20 }, + { 0xe8, 0x02, 0xff }, + { 0xcf, 0x20, 0xff }, + { 0x9b, 0xd7, 0xff }, + { 0x9a, 0x01, 0x03 }, + { 0xa8, 0x05, 0x0f }, + { 0xa8, 0x65, 0xf0 }, + { 0xa6, 0xa0, 0xf0 }, + { 0x9d, 0x50, 0xfc }, + { 0x9e, 0x20, 0xe0 }, + { 0xa3, 0x1c, 0x7c }, + { 0xd5, 0x03, 0x03 }, + }; + + /* firmware status */ + ret = tda10071_rd_reg(priv, 0x51, &tmp); + if (ret) + goto error; + + if (!tmp) { + /* warm state - wake up device from sleep */ + priv->warm = 1; + + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = tda10071_wr_reg_mask(priv, tab[i].reg, + tab[i].val, tab[i].mask); + if (ret) + goto error; + } + + cmd.args[0x00] = CMD_SET_SLEEP_MODE; + cmd.args[0x01] = 0; + cmd.args[0x02] = 0; + cmd.len = 0x03; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + } else { + /* cold state - try to download firmware */ + priv->warm = 0; + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); + if (ret) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more" \ + " details on firmware-problems. (%d)", + fw_file, ret); + goto error; + } + + /* init */ + for (i = 0; i < ARRAY_SIZE(tab2); i++) { + ret = tda10071_wr_reg_mask(priv, tab2[i].reg, + tab2[i].val, tab2[i].mask); + if (ret) + goto error_release_firmware; + } + + /* download firmware */ + ret = tda10071_wr_reg(priv, 0xe0, 0x7f); + if (ret) + goto error_release_firmware; + + ret = tda10071_wr_reg(priv, 0xf7, 0x81); + if (ret) + goto error_release_firmware; + + ret = tda10071_wr_reg(priv, 0xf8, 0x00); + if (ret) + goto error_release_firmware; + + ret = tda10071_wr_reg(priv, 0xf9, 0x00); + if (ret) + goto error_release_firmware; + + info("found a '%s' in cold state, will try to load a firmware", + tda10071_ops.info.name); + + info("downloading firmware from file '%s'", fw_file); + + /* do not download last byte */ + fw_size = fw->size - 1; + + for (remaining = fw_size; remaining > 0; + remaining -= (priv->cfg.i2c_wr_max - 1)) { + len = remaining; + if (len > (priv->cfg.i2c_wr_max - 1)) + len = (priv->cfg.i2c_wr_max - 1); + + ret = tda10071_wr_regs(priv, 0xfa, + (u8 *) &fw->data[fw_size - remaining], len); + if (ret) { + err("firmware download failed=%d", ret); + if (ret) + goto error_release_firmware; + } + } + release_firmware(fw); + + ret = tda10071_wr_reg(priv, 0xf7, 0x0c); + if (ret) + goto error; + + ret = tda10071_wr_reg(priv, 0xe0, 0x00); + if (ret) + goto error; + + /* wait firmware start */ + msleep(250); + + /* firmware status */ + ret = tda10071_rd_reg(priv, 0x51, &tmp); + if (ret) + goto error; + + if (tmp) { + info("firmware did not run"); + ret = -EFAULT; + goto error; + } else { + priv->warm = 1; + } + + cmd.args[0x00] = CMD_GET_FW_VERSION; + cmd.len = 0x01; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + ret = tda10071_rd_regs(priv, cmd.len, buf, 4); + if (ret) + goto error; + + info("firmware version %d.%d.%d.%d", + buf[0], buf[1], buf[2], buf[3]); + info("found a '%s' in warm state.", tda10071_ops.info.name); + + ret = tda10071_rd_regs(priv, 0x81, buf, 2); + if (ret) + goto error; + + cmd.args[0x00] = CMD_DEMOD_INIT; + cmd.args[0x01] = ((priv->cfg.xtal / 1000) >> 8) & 0xff; + cmd.args[0x02] = ((priv->cfg.xtal / 1000) >> 0) & 0xff; + cmd.args[0x03] = buf[0]; + cmd.args[0x04] = buf[1]; + cmd.args[0x05] = priv->cfg.pll_multiplier; + cmd.args[0x06] = priv->cfg.spec_inv; + cmd.args[0x07] = 0x00; + cmd.len = 0x08; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + cmd.args[0x00] = CMD_TUNER_INIT; + cmd.args[0x01] = 0x00; + cmd.args[0x02] = 0x00; + cmd.args[0x03] = 0x00; + cmd.args[0x04] = 0x00; + cmd.args[0x05] = 0x14; + cmd.args[0x06] = 0x00; + cmd.args[0x07] = 0x03; + cmd.args[0x08] = 0x02; + cmd.args[0x09] = 0x02; + cmd.args[0x0a] = 0x00; + cmd.args[0x0b] = 0x00; + cmd.args[0x0c] = 0x00; + cmd.args[0x0d] = 0x00; + cmd.args[0x0e] = 0x00; + cmd.len = 0x0f; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + cmd.args[0x00] = CMD_MPEG_CONFIG; + cmd.args[0x01] = 0; + cmd.args[0x02] = priv->cfg.ts_mode; + cmd.args[0x03] = 0x00; + cmd.args[0x04] = 0x04; + cmd.args[0x05] = 0x00; + cmd.len = 0x06; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01); + if (ret) + goto error; + + cmd.args[0x00] = CMD_LNB_CONFIG; + cmd.args[0x01] = 0; + cmd.args[0x02] = 150; + cmd.args[0x03] = 3; + cmd.args[0x04] = 22; + cmd.args[0x05] = 1; + cmd.args[0x06] = 1; + cmd.args[0x07] = 30; + cmd.args[0x08] = 30; + cmd.args[0x09] = 30; + cmd.args[0x0a] = 30; + cmd.len = 0x0b; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + cmd.args[0x00] = CMD_BER_CONTROL; + cmd.args[0x01] = 0; + cmd.args[0x02] = 14; + cmd.args[0x03] = 14; + cmd.len = 0x04; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + } + + return ret; +error_release_firmware: + release_firmware(fw); +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_sleep(struct dvb_frontend *fe) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + struct tda10071_cmd cmd; + int ret, i; + struct tda10071_reg_val_mask tab[] = { + { 0xcd, 0x07, 0x07 }, + { 0x80, 0x02, 0x02 }, + { 0xcd, 0xc0, 0xc0 }, + { 0xce, 0x1b, 0x1b }, + { 0x9d, 0x01, 0x01 }, + { 0x9d, 0x02, 0x02 }, + { 0x9e, 0x01, 0x01 }, + { 0x87, 0x80, 0x80 }, + { 0xce, 0x08, 0x08 }, + { 0xce, 0x10, 0x10 }, + }; + + if (!priv->warm) { + ret = -EFAULT; + goto error; + } + + cmd.args[0x00] = CMD_SET_SLEEP_MODE; + cmd.args[0x01] = 0; + cmd.args[0x02] = 1; + cmd.len = 0x03; + ret = tda10071_cmd_execute(priv, &cmd); + if (ret) + goto error; + + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret) + goto error; + } + + return ret; +error: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + +static int tda10071_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 8000; + s->step_size = 0; + s->max_drift = 0; + + return 0; +} + +static void tda10071_release(struct dvb_frontend *fe) +{ + struct tda10071_priv *priv = fe->demodulator_priv; + kfree(priv); +} + +struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct tda10071_priv *priv = NULL; + u8 tmp; + + /* allocate memory for the internal priv */ + priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL); + if (priv == NULL) { + ret = -ENOMEM; + goto error; + } + + /* setup the priv */ + priv->i2c = i2c; + memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); + + /* chip ID */ + ret = tda10071_rd_reg(priv, 0xff, &tmp); + if (ret || tmp != 0x0f) + goto error; + + /* chip type */ + ret = tda10071_rd_reg(priv, 0xdd, &tmp); + if (ret || tmp != 0x00) + goto error; + + /* chip version */ + ret = tda10071_rd_reg(priv, 0xfe, &tmp); + if (ret || tmp != 0x01) + goto error; + + /* create dvb_frontend */ + memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops)); + priv->fe.demodulator_priv = priv; + + return &priv->fe; +error: + dbg("%s: failed=%d", __func__, ret); + kfree(priv); + return NULL; +} +EXPORT_SYMBOL(tda10071_attach); + +static struct dvb_frontend_ops tda10071_ops = { + .info = { + .name = "NXP TDA10071", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_8_9 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_RECOVER | + FE_CAN_2G_MODULATION + }, + + .release = tda10071_release, + + .get_tune_settings = tda10071_get_tune_settings, + + .init = tda10071_init, + .sleep = tda10071_sleep, + + .set_frontend = tda10071_set_frontend, + .get_frontend = tda10071_get_frontend, + + .read_status = tda10071_read_status, + .read_snr = tda10071_read_snr, + .read_signal_strength = tda10071_read_signal_strength, + .read_ber = tda10071_read_ber, + .read_ucblocks = tda10071_read_ucblocks, + + .diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd, + .diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply, + .diseqc_send_burst = tda10071_diseqc_send_burst, + + .set_tone = tda10071_set_tone, + .set_voltage = tda10071_set_voltage, +}; + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/tda10071.h b/drivers/media/dvb/frontends/tda10071.h new file mode 100644 index 00000000000..21163c4b555 --- /dev/null +++ b/drivers/media/dvb/frontends/tda10071.h @@ -0,0 +1,81 @@ +/* + * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TDA10071_H +#define TDA10071_H + +#include <linux/dvb/frontend.h> + +struct tda10071_config { + /* Demodulator I2C address. + * Default: none, must set + * Values: 0x55, + */ + u8 i2c_address; + + /* Max bytes I2C provider can write at once. + * Note: Buffer is taken from the stack currently! + * Default: none, must set + * Values: + */ + u16 i2c_wr_max; + + /* TS output mode. + * Default: TDA10071_TS_SERIAL + * Values: + */ +#define TDA10071_TS_SERIAL 0 +#define TDA10071_TS_PARALLEL 1 + u8 ts_mode; + + /* Input spectrum inversion. + * Default: 0 + * Values: 0, 1 + */ + bool spec_inv; + + /* Xtal frequency Hz + * Default: none, must set + * Values: + */ + u32 xtal; + + /* PLL multiplier. + * Default: none, must set + * Values: + */ + u8 pll_multiplier; +}; + + +#if defined(CONFIG_DVB_TDA10071) || \ + (defined(CONFIG_DVB_TDA10071_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tda10071_attach( + const struct tda10071_config *config, struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *tda10071_attach( + const struct tda10071_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* TDA10071_H */ diff --git a/drivers/media/dvb/frontends/tda10071_priv.h b/drivers/media/dvb/frontends/tda10071_priv.h new file mode 100644 index 00000000000..93c5e6317f0 --- /dev/null +++ b/drivers/media/dvb/frontends/tda10071_priv.h @@ -0,0 +1,122 @@ +/* + * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver + * + * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TDA10071_PRIV +#define TDA10071_PRIV + +#include "dvb_frontend.h" +#include "tda10071.h" +#include <linux/firmware.h> + +#define LOG_PREFIX "tda10071" + +#undef dbg +#define dbg(f, arg...) \ + if (tda10071_debug) \ + printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +struct tda10071_priv { + struct i2c_adapter *i2c; + struct dvb_frontend fe; + struct tda10071_config cfg; + + u8 meas_count[2]; + u32 ber; + u32 ucb; + fe_status_t fe_status; + fe_delivery_system_t delivery_system; + bool warm; /* FW running */ +}; + +static struct tda10071_modcod { + fe_delivery_system_t delivery_system; + fe_modulation_t modulation; + fe_code_rate_t fec; + u8 val; +} TDA10071_MODCOD[] = { + /* NBC-QPSK */ + { SYS_DVBS2, QPSK, FEC_AUTO, 0x00 }, + { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, + { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, + { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, + { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, + { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, + { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, + { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, + { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, + /* 8PSK */ + { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, + { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, + { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, + { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, + { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, + { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, + /* QPSK */ + { SYS_DVBS, QPSK, FEC_AUTO, 0x2d }, + { SYS_DVBS, QPSK, FEC_1_2, 0x2e }, + { SYS_DVBS, QPSK, FEC_2_3, 0x2f }, + { SYS_DVBS, QPSK, FEC_3_4, 0x30 }, + { SYS_DVBS, QPSK, FEC_5_6, 0x31 }, + { SYS_DVBS, QPSK, FEC_7_8, 0x32 }, +}; + +struct tda10071_reg_val_mask { + u8 reg; + u8 val; + u8 mask; +}; + +/* firmware filename */ +#define TDA10071_DEFAULT_FIRMWARE "dvb-fe-tda10071.fw" + +/* firmware commands */ +#define CMD_DEMOD_INIT 0x10 +#define CMD_CHANGE_CHANNEL 0x11 +#define CMD_MPEG_CONFIG 0x13 +#define CMD_TUNER_INIT 0x15 +#define CMD_GET_AGCACC 0x1a + +#define CMD_LNB_CONFIG 0x20 +#define CMD_LNB_SEND_DISEQC 0x21 +#define CMD_LNB_SET_DC_LEVEL 0x22 +#define CMD_LNB_PCB_CONFIG 0x23 +#define CMD_LNB_SEND_TONEBURST 0x24 +#define CMD_LNB_UPDATE_REPLY 0x25 + +#define CMD_GET_FW_VERSION 0x35 +#define CMD_SET_SLEEP_MODE 0x36 +#define CMD_BER_CONTROL 0x3e +#define CMD_BER_UPDATE_COUNTERS 0x3f + +/* firmare command struct */ +#define TDA10071_ARGLEN 0x1e +struct tda10071_cmd { + u8 args[TDA10071_ARGLEN]; + u8 len; +}; + + +#endif /* TDA10071_PRIV */ diff --git a/drivers/media/dvb/frontends/tda18271c2dd.c b/drivers/media/dvb/frontends/tda18271c2dd.c index 0384e8da4f5..1b1bf200c55 100644 --- a/drivers/media/dvb/frontends/tda18271c2dd.c +++ b/drivers/media/dvb/frontends/tda18271c2dd.c @@ -1195,7 +1195,7 @@ static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc) } #endif -static int get_frequency(struct dvb_frontend *fe, u32 *frequency) +static int get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { struct tda_state *state = fe->tuner_priv; @@ -1222,7 +1222,7 @@ static struct dvb_tuner_ops tuner_ops = { .sleep = sleep, .set_params = set_params, .release = release, - .get_frequency = get_frequency, + .get_if_frequency = get_if_frequency, .get_bandwidth = get_bandwidth, }; |