Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
- index 9ad4454..88a5b3e 100644
- --- a/drivers/media/common/tuners/tda18271-fe.c
- +++ b/drivers/media/common/tuners/tda18271-fe.c
- @@ -975,6 +975,10 @@ static int tda18271_set_params(struct dvb_frontend *fe,
- tda_warn("bandwidth not set!\n");
- return -EINVAL;
- }
- + } else if (fe->ops.info.type == FE_QAM) {
- + /* DVB-C */
- + map = &std_map->qam_8;
- + bw = 8000000;
- } else {
- tda_warn("modulation type not supported!\n");
- return -EINVAL;
- diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
- index b8519ba..71ca237 100644
- --- a/drivers/media/dvb/frontends/Kconfig
- +++ b/drivers/media/dvb/frontends/Kconfig
- @@ -370,6 +370,13 @@ config DVB_EC100
- help
- Say Y when you want to support this frontend.
- +config DVB_CXD2820R
- + tristate "Sony CXD2820R"
- + depends on DVB_CORE && I2C
- + default m if DVB_FE_CUSTOMISE
- + help
- + Say Y when you want to support this frontend.
- +
- comment "DVB-C (cable) frontends"
- depends on DVB_CORE
- diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
- index b1d9525..c9195d0 100644
- --- a/drivers/media/dvb/frontends/Makefile
- +++ b/drivers/media/dvb/frontends/Makefile
- @@ -8,6 +8,7 @@ EXTRA_CFLAGS += -Idrivers/media/common/tuners/
- stb0899-objs = stb0899_drv.o stb0899_algo.o
- stv0900-objs = stv0900_core.o stv0900_sw.o
- au8522-objs = au8522_dig.o au8522_decoder.o
- +cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o
- obj-$(CONFIG_DVB_PLL) += dvb-pll.o
- obj-$(CONFIG_DVB_STV0299) += stv0299.o
- @@ -83,3 +84,4 @@ obj-$(CONFIG_DVB_DS3000) += ds3000.o
- obj-$(CONFIG_DVB_MB86A16) += mb86a16.o
- obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o
- obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
- +obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
- diff --git a/drivers/media/dvb/frontends/cxd2820r.h b/drivers/media/dvb/frontends/cxd2820r.h
- new file mode 100644
- index 0000000..ad17845
- --- /dev/null
- +++ b/drivers/media/dvb/frontends/cxd2820r.h
- @@ -0,0 +1,118 @@
- +/*
- + * Sony CXD2820R demodulator driver
- + *
- + * Copyright (C) 2010 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 CXD2820R_H
- +#define CXD2820R_H
- +
- +#include <linux/dvb/frontend.h>
- +
- +#define CXD2820R_GPIO_D (0 << 0) /* disable */
- +#define CXD2820R_GPIO_E (1 << 0) /* enable */
- +#define CXD2820R_GPIO_O (0 << 1) /* output */
- +#define CXD2820R_GPIO_I (1 << 1) /* input */
- +#define CXD2820R_GPIO_L (0 << 2) /* output low */
- +#define CXD2820R_GPIO_H (1 << 2) /* output high */
- +
- +#define CXD2820R_TS_SERIAL 0x08
- +#define CXD2820R_TS_SERIAL_MSB 0x28
- +#define CXD2820R_TS_PARALLEL 0x30
- +#define CXD2820R_TS_PARALLEL_MSB 0x70
- +
- +struct cxd2820r_config {
- + /* Demodulator I2C address.
- + * Driver determines DVB-C slave I2C address automatically from master
- + * address.
- + * Default: none, must set
- + * Values: 0x6c, 0x6d
- + */
- + u8 i2c_address;
- +
- + /* TS output mode.
- + * Default: none, must set.
- + * Values:
- + */
- + u8 ts_mode;
- +
- + /* IF AGC polarity.
- + * Default: 0
- + * Values: 0, 1
- + */
- + int if_agc_polarity:1;
- +
- + /* Spectrum inversion.
- + * Default: 0
- + * Values: 0, 1
- + */
- + int spec_inv:1;
- +
- + /* IFs for all used modes.
- + * Default: none, must set
- + * Values: <kHz>
- + */
- + u16 if_dvbt_6;
- + u16 if_dvbt_7;
- + u16 if_dvbt_8;
- + u16 if_dvbt2_5;
- + u16 if_dvbt2_6;
- + u16 if_dvbt2_7;
- + u16 if_dvbt2_8;
- + u16 if_dvbc;
- +
- + /* GPIOs for all used modes.
- + * Default: none, disabled
- + * Values: <see above>
- + */
- + u8 gpio_dvbt[3];
- + u8 gpio_dvbt2[3];
- + u8 gpio_dvbc[3];
- +};
- +
- +
- +#if defined(CONFIG_DVB_CXD2820R) || \
- + (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE))
- +extern struct dvb_frontend *cxd2820r_attach(
- + const struct cxd2820r_config *config,
- + 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,
- + struct i2c_adapter *i2c,
- + struct dvb_frontend *fe
- +)
- +{
- + 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
- +
- +#endif /* CXD2820R_H */
- diff --git a/drivers/media/dvb/frontends/cxd2820r_c.c b/drivers/media/dvb/frontends/cxd2820r_c.c
- new file mode 100644
- index 0000000..3c07d40
- --- /dev/null
- +++ b/drivers/media/dvb/frontends/cxd2820r_c.c
- @@ -0,0 +1,338 @@
- +/*
- + * Sony CXD2820R demodulator driver
- + *
- + * Copyright (C) 2010 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 "cxd2820r_priv.h"
- +
- +int cxd2820r_set_frontend_c(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *params)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- + int ret, i;
- + u8 buf[2];
- + u16 if_ctl;
- + u64 num;
- + struct reg_val_mask tab[] = {
- + { 0x00080, 0x01, 0xff },
- + { 0x00081, 0x05, 0xff },
- + { 0x00085, 0x07, 0xff },
- + { 0x00088, 0x01, 0xff },
- +
- + { 0x00082, 0x20, 0x60 },
- + { 0x1016a, 0x48, 0xff },
- + { 0x100a5, 0x00, 0x01 },
- + { 0x10020, 0x06, 0x07 },
- + { 0x10059, 0x50, 0xff },
- + { 0x10087, 0x0c, 0x3c },
- + { 0x1008b, 0x07, 0xff },
- + { 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 },
- + { 0x10070, priv->cfg.ts_mode, 0xff },
- + };
- +
- + dbg("%s: RF=%d SR=%d", __func__, c->frequency, c->symbol_rate);
- +
- + /* update GPIOs */
- + ret = cxd2820r_gpio(fe);
- + if (ret)
- + goto error;
- +
- + /* program tuner */
- + if (fe->ops.tuner_ops.set_params)
- + fe->ops.tuner_ops.set_params(fe, params);
- +
- + if (priv->delivery_system != SYS_DVBC_ANNEX_AC) {
- + for (i = 0; i < ARRAY_SIZE(tab); i++) {
- + ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
- + tab[i].val, tab[i].mask);
- + if (ret)
- + goto error;
- + }
- + }
- +
- + priv->delivery_system = SYS_DVBC_ANNEX_AC;
- + priv->ber_running = 0; /* tune stops BER counter */
- +
- + num = priv->cfg.if_dvbc;
- + num *= 0x4000;
- + if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
- + buf[0] = (if_ctl >> 8) & 0x3f;
- + buf[1] = (if_ctl >> 0) & 0xff;
- +
- + ret = cxd2820r_wr_regs(priv, 0x10042, buf, 2);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
- + if (ret)
- + goto error;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_get_frontend_c(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- + int ret;
- + u8 buf[2];
- +
- + ret = cxd2820r_rd_regs(priv, 0x1001a, buf, 2);
- + if (ret)
- + goto error;
- +
- + c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]);
- +
- + ret = cxd2820r_rd_reg(priv, 0x10019, &buf[0]);
- + if (ret)
- + goto error;
- +
- + switch ((buf[0] >> 0) & 0x03) {
- + case 0:
- + c->modulation = QAM_16;
- + break;
- + case 1:
- + c->modulation = QAM_32;
- + break;
- + case 2:
- + c->modulation = QAM_64;
- + break;
- + case 3:
- + c->modulation = QAM_128;
- + break;
- + case 4:
- + c->modulation = QAM_256;
- + break;
- + }
- +
- + switch ((buf[0] >> 7) & 0x01) {
- + case 0:
- + c->inversion = INVERSION_OFF;
- + break;
- + case 1:
- + c->inversion = INVERSION_ON;
- + break;
- + }
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[3], start_ber = 0;
- + *ber = 0;
- +
- + if (priv->ber_running) {
- + ret = cxd2820r_rd_regs(priv, 0x10076, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) {
- + *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0];
- + start_ber = 1;
- + }
- + } else {
- + priv->ber_running = 1;
- + start_ber = 1;
- + }
- +
- + if (start_ber) {
- + /* (re)start BER */
- + ret = cxd2820r_wr_reg(priv, 0x10079, 0x01);
- + if (ret)
- + goto error;
- + }
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe,
- + u16 *strength)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[2];
- + u16 tmp;
- +
- + ret = cxd2820r_rd_regs(priv, 0x10049, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + tmp = (buf[0] & 0x03) << 8 | buf[1];
- + tmp = (~tmp & 0x03ff);
- +
- + if (tmp == 512)
- + /* ~no signal */
- + tmp = 0;
- + else if (tmp > 350)
- + tmp = 350;
- +
- + /* scale value to 0x0000-0xffff */
- + *strength = tmp * 0xffff / (350-0);
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 tmp;
- + unsigned int A, B;
- + /* report SNR in dB * 10 */
- +
- + ret = cxd2820r_rd_reg(priv, 0x10019, &tmp);
- + if (ret)
- + goto error;
- +
- + if (((tmp >> 0) & 0x03) % 2) {
- + A = 875;
- + B = 650;
- + } else {
- + A = 950;
- + B = 760;
- + }
- +
- + ret = cxd2820r_rd_reg(priv, 0x1004d, &tmp);
- + if (ret)
- + goto error;
- +
- + #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */
- + if (tmp)
- + *snr = A * (intlog2(B / tmp) >> 5) / (CXD2820R_LOG2_E_24 >> 5)
- + / 10;
- + else
- + *snr = 0;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks)
- +{
- + *ucblocks = 0;
- + /* no way to read ? */
- + return 0;
- +}
- +
- +int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[2];
- + *status = 0;
- +
- + ret = cxd2820r_rd_regs(priv, 0x10088, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + if (((buf[0] >> 0) & 0x01) == 1) {
- + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- + FE_HAS_VITERBI | FE_HAS_SYNC;
- +
- + if (((buf[1] >> 3) & 0x01) == 1) {
- + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- + }
- + }
- +
- + dbg("%s: lock=%02x %02x", __func__, buf[0], buf[1]);
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_init_c(struct dvb_frontend *fe)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- +
- + ret = cxd2820r_wr_reg(priv, 0x00085, 0x07);
- + if (ret)
- + goto error;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_sleep_c(struct dvb_frontend *fe)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret, i;
- + struct reg_val_mask tab[] = {
- + { 0x000ff, 0x1f, 0xff },
- + { 0x00085, 0x00, 0xff },
- + { 0x00088, 0x01, 0xff },
- + { 0x00081, 0x00, 0xff },
- + { 0x00080, 0x00, 0xff },
- + };
- +
- + dbg("%s", __func__);
- +
- + priv->delivery_system = SYS_UNDEFINED;
- +
- + for (i = 0; i < ARRAY_SIZE(tab); i++) {
- + ret = cxd2820r_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;
- +}
- +
- +int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
- + struct dvb_frontend_tune_settings *s)
- +{
- + s->min_delay_ms = 500;
- + s->step_size = 0; /* no zigzag */
- + s->max_drift = 0;
- +
- + return 0;
- +}
- +
- diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c
- new file mode 100644
- index 0000000..e900c4c
- --- /dev/null
- +++ b/drivers/media/dvb/frontends/cxd2820r_core.c
- @@ -0,0 +1,914 @@
- +/*
- + * Sony CXD2820R demodulator driver
- + *
- + * Copyright (C) 2010 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 "cxd2820r_priv.h"
- +
- +int cxd2820r_debug;
- +module_param_named(debug, cxd2820r_debug, int, 0644);
- +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
- +
- +/* write multiple registers */
- +static int cxd2820r_wr_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
- + u8 *val, int len)
- +{
- + int ret;
- + u8 buf[len+1];
- + struct i2c_msg msg[1] = {
- + {
- + .addr = i2c,
- + .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 ret:%d reg:%02x len:%d", ret, reg, len);
- + ret = -EREMOTEIO;
- + }
- + return ret;
- +}
- +
- +/* read multiple registers */
- +static int cxd2820r_rd_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
- + u8 *val, int len)
- +{
- + int ret;
- + u8 buf[len];
- + struct i2c_msg msg[2] = {
- + {
- + .addr = i2c,
- + .flags = 0,
- + .len = 1,
- + .buf = ®,
- + }, {
- + .addr = i2c,
- + .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 ret:%d reg:%02x len:%d", ret, reg, len);
- + ret = -EREMOTEIO;
- + }
- +
- + return ret;
- +}
- +
- +/* write multiple registers */
- +int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- + int len)
- +{
- + int ret;
- + u8 i2c_addr;
- + u8 reg = (reginfo >> 0) & 0xff;
- + u8 bank = (reginfo >> 8) & 0xff;
- + u8 i2c = (reginfo >> 16) & 0x01;
- +
- + /* select I2C */
- + if (i2c)
- + i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */
- + else
- + i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */
- +
- + /* switch bank if needed */
- + if (bank != priv->bank[i2c]) {
- + ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1);
- + if (ret)
- + return ret;
- + priv->bank[i2c] = bank;
- + }
- + return cxd2820r_wr_regs_i2c(priv, i2c_addr, reg, val, len);
- +}
- +
- +/* read multiple registers */
- +int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- + int len)
- +{
- + int ret;
- + u8 i2c_addr;
- + u8 reg = (reginfo >> 0) & 0xff;
- + u8 bank = (reginfo >> 8) & 0xff;
- + u8 i2c = (reginfo >> 16) & 0x01;
- +
- + /* select I2C */
- + if (i2c)
- + i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */
- + else
- + i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */
- +
- + /* switch bank if needed */
- + if (bank != priv->bank[i2c]) {
- + ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1);
- + if (ret)
- + return ret;
- + priv->bank[i2c] = bank;
- + }
- + return cxd2820r_rd_regs_i2c(priv, i2c_addr, reg, val, len);
- +}
- +
- +/* write single register */
- +int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val)
- +{
- + return cxd2820r_wr_regs(priv, reg, &val, 1);
- +}
- +
- +/* read single register */
- +int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val)
- +{
- + return cxd2820r_rd_regs(priv, reg, val, 1);
- +}
- +
- +/* write single register with mask */
- +int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val,
- + u8 mask)
- +{
- + int ret;
- + u8 tmp;
- +
- + /* no need for read if whole reg is written */
- + if (mask != 0xff) {
- + ret = cxd2820r_rd_reg(priv, reg, &tmp);
- + if (ret)
- + return ret;
- +
- + val &= mask;
- + tmp &= ~mask;
- + val |= tmp;
- + }
- +
- + return cxd2820r_wr_reg(priv, reg, val);
- +}
- +
- +int cxd2820r_gpio(struct dvb_frontend *fe)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret, i;
- + u8 *gpio, tmp0, tmp1;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + gpio = priv->cfg.gpio_dvbt;
- + break;
- + case SYS_DVBT2:
- + gpio = priv->cfg.gpio_dvbt2;
- + break;
- + case SYS_DVBC_ANNEX_AC:
- + gpio = priv->cfg.gpio_dvbc;
- + break;
- + default:
- + ret = -EINVAL;
- + goto error;
- + }
- +
- + /* update GPIOs only when needed */
- + if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio)))
- + return 0;
- +
- + tmp0 = 0x00;
- + tmp1 = 0x00;
- + for (i = 0; i < sizeof(priv->gpio); i++) {
- + /* enable / disable */
- + if (gpio[i] & CXD2820R_GPIO_E)
- + tmp0 |= (2 << 6) >> (2 * i);
- + else
- + tmp0 |= (1 << 6) >> (2 * i);
- +
- + /* input / output */
- + if (gpio[i] & CXD2820R_GPIO_I)
- + tmp1 |= (1 << (3 + i));
- + else
- + tmp1 |= (0 << (3 + i));
- +
- + /* high / low */
- + if (gpio[i] & CXD2820R_GPIO_H)
- + tmp1 |= (1 << (0 + i));
- + else
- + tmp1 |= (0 << (0 + i));
- +
- + dbg("%s: GPIO i=%d %02x %02x", __func__, i, tmp0, tmp1);
- + }
- +
- + dbg("%s: wr gpio=%02x %02x", __func__, tmp0, tmp1);
- +
- + /* write bits [7:2] */
- + ret = cxd2820r_wr_reg_mask(priv, 0x00089, tmp0, 0xfc);
- + if (ret)
- + goto error;
- +
- + /* write bits [5:0] */
- + ret = cxd2820r_wr_reg_mask(priv, 0x0008e, tmp1, 0x3f);
- + if (ret)
- + goto error;
- +
- + memcpy(priv->gpio, gpio, sizeof(priv->gpio));
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +/* lock FE */
- +static int cxd2820r_lock(struct cxd2820r_priv *priv, int active_fe)
- +{
- + int ret = 0;
- + dbg("%s: active_fe=%d", __func__, active_fe);
- +
- + mutex_lock(&priv->fe_lock);
- +
- + /* -1=NONE, 0=DVB-T/T2, 1=DVB-C */
- + if (priv->active_fe == active_fe)
- + ;
- + else if (priv->active_fe == -1)
- + priv->active_fe = active_fe;
- + else
- + ret = -EBUSY;
- +
- + mutex_unlock(&priv->fe_lock);
- +
- + return ret;
- +}
- +
- +/* unlock FE */
- +static void cxd2820r_unlock(struct cxd2820r_priv *priv, int active_fe)
- +{
- + dbg("%s: active_fe=%d", __func__, active_fe);
- +
- + mutex_lock(&priv->fe_lock);
- +
- + /* -1=NONE, 0=DVB-T/T2, 1=DVB-C */
- + if (priv->active_fe == active_fe)
- + priv->active_fe = -1;
- +
- + mutex_unlock(&priv->fe_lock);
- +
- + return;
- +}
- +
- +/* 64 bit div with round closest, like DIV_ROUND_CLOSEST but 64 bit */
- +u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor)
- +{
- + return div_u64(dividend + (divisor / 2), divisor);
- +}
- +
- +static int cxd2820r_set_frontend(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (priv->delivery_system) {
- + case SYS_UNDEFINED:
- + if (c->delivery_system == SYS_DVBT) {
- + /* SLEEP => DVB-T */
- + ret = cxd2820r_set_frontend_t(fe, p);
- + } else {
- + /* SLEEP => DVB-T2 */
- + ret = cxd2820r_set_frontend_t2(fe, p);
- + }
- + break;
- + case SYS_DVBT:
- + if (c->delivery_system == SYS_DVBT) {
- + /* DVB-T => DVB-T */
- + ret = cxd2820r_set_frontend_t(fe, p);
- + } else if (c->delivery_system == SYS_DVBT2) {
- + /* DVB-T => DVB-T2 */
- + ret = cxd2820r_sleep_t(fe);
- + ret = cxd2820r_set_frontend_t2(fe, p);
- + }
- + break;
- + case SYS_DVBT2:
- + if (c->delivery_system == SYS_DVBT2) {
- + /* DVB-T2 => DVB-T2 */
- + ret = cxd2820r_set_frontend_t2(fe, p);
- + } else if (c->delivery_system == SYS_DVBT) {
- + /* DVB-T2 => DVB-T */
- + ret = cxd2820r_sleep_t2(fe);
- + ret = cxd2820r_set_frontend_t(fe, p);
- + }
- + break;
- + default:
- + dbg("%s: error state=%d", __func__,
- + priv->delivery_system);
- + ret = -EINVAL;
- + }
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_set_frontend_c(fe, p);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_read_status(struct dvb_frontend *fe, fe_status_t *status)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + ret = cxd2820r_read_status_t(fe, status);
- + break;
- + case SYS_DVBT2:
- + ret = cxd2820r_read_status_t2(fe, status);
- + break;
- + default:
- + ret = -EINVAL;
- + }
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_read_status_c(fe, status);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_get_frontend(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + ret = cxd2820r_get_frontend_t(fe, p);
- + break;
- + case SYS_DVBT2:
- + ret = cxd2820r_get_frontend_t2(fe, p);
- + break;
- + default:
- + ret = -EINVAL;
- + }
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_get_frontend_c(fe, p);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_read_ber(struct dvb_frontend *fe, u32 *ber)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + ret = cxd2820r_read_ber_t(fe, ber);
- + break;
- + case SYS_DVBT2:
- + ret = cxd2820r_read_ber_t2(fe, ber);
- + break;
- + default:
- + ret = -EINVAL;
- + }
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_read_ber_c(fe, ber);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + ret = cxd2820r_read_signal_strength_t(fe, strength);
- + break;
- + case SYS_DVBT2:
- + ret = cxd2820r_read_signal_strength_t2(fe, strength);
- + break;
- + default:
- + ret = -EINVAL;
- + }
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_read_signal_strength_c(fe, strength);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_read_snr(struct dvb_frontend *fe, u16 *snr)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + ret = cxd2820r_read_snr_t(fe, snr);
- + break;
- + case SYS_DVBT2:
- + ret = cxd2820r_read_snr_t2(fe, snr);
- + break;
- + default:
- + ret = -EINVAL;
- + }
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_read_snr_c(fe, snr);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + ret = cxd2820r_read_ucblocks_t(fe, ucblocks);
- + break;
- + case SYS_DVBT2:
- + ret = cxd2820r_read_ucblocks_t2(fe, ucblocks);
- + break;
- + default:
- + ret = -EINVAL;
- + }
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_read_ucblocks_c(fe, ucblocks);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_init(struct dvb_frontend *fe)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + priv->delivery_system = SYS_UNDEFINED;
- + /* delivery system is unknown at that (init) phase */
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_init_t(fe);
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_init_c(fe);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_sleep(struct dvb_frontend *fe)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + ret = cxd2820r_sleep_t(fe);
- + break;
- + case SYS_DVBT2:
- + ret = cxd2820r_sleep_t2(fe);
- + break;
- + default:
- + ret = -EINVAL;
- + }
- +
- + cxd2820r_unlock(priv, 0);
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_sleep_c(fe);
- +
- + cxd2820r_unlock(priv, 1);
- + }
- +
- + return ret;
- +}
- +
- +static int cxd2820r_get_tune_settings(struct dvb_frontend *fe,
- + struct dvb_frontend_tune_settings *s)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + if (fe->ops.info.type == FE_OFDM) {
- + /* DVB-T/T2 */
- + ret = cxd2820r_lock(priv, 0);
- + if (ret)
- + return ret;
- +
- + switch (fe->dtv_property_cache.delivery_system) {
- + case SYS_DVBT:
- + ret = cxd2820r_get_tune_settings_t(fe, s);
- + break;
- + case SYS_DVBT2:
- + ret = cxd2820r_get_tune_settings_t2(fe, s);
- + break;
- + default:
- + ret = -EINVAL;
- + }
- + } else {
- + /* DVB-C */
- + ret = cxd2820r_lock(priv, 1);
- + if (ret)
- + return ret;
- +
- + ret = cxd2820r_get_tune_settings_c(fe, s);
- + }
- +
- + return ret;
- +}
- +
- +static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- + int ret, i;
- + fe_status_t status = 0;
- + dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- +
- + /* switch between DVB-T and DVB-T2 when tune fails */
- + if (priv->last_tune_failed) {
- + if (priv->delivery_system == SYS_DVBT)
- + c->delivery_system = SYS_DVBT2;
- + else
- + c->delivery_system = SYS_DVBT;
- + }
- +
- + /* set frontend */
- + ret = cxd2820r_set_frontend(fe, p);
- + if (ret)
- + goto error;
- +
- +
- + /* frontend lock wait loop count */
- + switch (priv->delivery_system) {
- + case SYS_DVBT:
- + i = 20;
- + break;
- + case SYS_DVBT2:
- + i = 40;
- + break;
- + case SYS_UNDEFINED:
- + default:
- + i = 0;
- + break;
- + }
- +
- + /* wait frontend lock */
- + for (; i > 0; i--) {
- + dbg("%s: LOOP=%d", __func__, i);
- + msleep(50);
- + ret = cxd2820r_read_status(fe, &status);
- + if (ret)
- + goto error;
- +
- + if (status & FE_HAS_SIGNAL)
- + break;
- + }
- +
- + /* check if we have a valid signal */
- + if (status) {
- + priv->last_tune_failed = 0;
- + return DVBFE_ALGO_SEARCH_SUCCESS;
- + } else {
- + priv->last_tune_failed = 1;
- + return DVBFE_ALGO_SEARCH_AGAIN;
- + }
- +
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return DVBFE_ALGO_SEARCH_ERROR;
- +}
- +
- +static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe)
- +{
- + return DVBFE_ALGO_CUSTOM;
- +}
- +
- +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);
- + 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);
- + u8 obuf[msg[0].len + 2];
- + struct i2c_msg msg2[2] = {
- + {
- + .addr = priv->cfg.i2c_address,
- + .flags = 0,
- + .len = sizeof(obuf),
- + .buf = obuf,
- + }, {
- + .addr = priv->cfg.i2c_address,
- + .flags = I2C_M_RD,
- + .len = msg[1].len,
- + .buf = msg[1].buf,
- + }
- + };
- +
- + 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 = sizeof(obuf) - 1; /* maybe HW bug ? */
- + }
- + memcpy(&obuf[2], msg[0].buf, msg[0].len);
- +
- + return i2c_transfer(priv->i2c, msg2, num);
- +}
- +
- +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)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + return &priv->tuner_i2c_adapter;
- +}
- +EXPORT_SYMBOL(cxd2820r_get_tuner_i2c_adapter);
- +
- +static 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)
- +{
- + int ret;
- + struct cxd2820r_priv *priv = NULL;
- + u8 tmp;
- +
- + if (fe == NULL) {
- + /* FE0 */
- + /* allocate memory for the internal priv */
- + priv = kzalloc(sizeof(struct cxd2820r_priv), GFP_KERNEL);
- + if (priv == NULL)
- + goto error;
- +
- + /* setup the priv */
- + priv->i2c = i2c;
- + memcpy(&priv->cfg, cfg, sizeof(struct cxd2820r_config));
- + mutex_init(&priv->fe_lock);
- +
- + priv->active_fe = -1; /* NONE */
- +
- + /* check if the demod is there */
- + priv->bank[0] = priv->bank[1] = 0xff;
- + ret = cxd2820r_rd_reg(priv, 0x000fd, &tmp);
- + dbg("%s: chip id=%02x", __func__, tmp);
- + if (ret || tmp != 0xe1)
- + goto error;
- +
- + /* create frontends */
- + memcpy(&priv->fe[0].ops, &cxd2820r_ops[0],
- + sizeof(struct dvb_frontend_ops));
- + memcpy(&priv->fe[1].ops, &cxd2820r_ops[1],
- + sizeof(struct dvb_frontend_ops));
- +
- + 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 {
- + /* FE1: FE0 given as pointer, just return FE1 we have
- + * already created */
- + priv = fe->demodulator_priv;
- + return &priv->fe[1];
- + }
- +
- +error:
- + kfree(priv);
- + return NULL;
- +}
- +EXPORT_SYMBOL(cxd2820r_attach);
- +
- +static struct dvb_frontend_ops cxd2820r_ops[2] = {
- + {
- + /* DVB-T/T2 */
- + .info = {
- + .name = "Sony CXD2820R (DVB-T/T2)",
- + .type = FE_OFDM,
- + .caps =
- + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
- + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 |
- + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
- + FE_CAN_QPSK | 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 |
- + FE_CAN_MUTE_TS |
- + FE_CAN_2G_MODULATION
- + },
- +
- + .release = cxd2820r_release,
- + .init = cxd2820r_init,
- + .sleep = cxd2820r_sleep,
- +
- + .get_tune_settings = cxd2820r_get_tune_settings,
- +
- + .get_frontend = cxd2820r_get_frontend,
- +
- + .get_frontend_algo = cxd2820r_get_frontend_algo,
- + .search = cxd2820r_search,
- +
- + .read_status = cxd2820r_read_status,
- + .read_snr = cxd2820r_read_snr,
- + .read_ber = cxd2820r_read_ber,
- + .read_ucblocks = cxd2820r_read_ucblocks,
- + .read_signal_strength = cxd2820r_read_signal_strength,
- + },
- + {
- + /* DVB-C */
- + .info = {
- + .name = "Sony CXD2820R (DVB-C)",
- + .type = FE_QAM,
- + .caps =
- + FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
- + FE_CAN_QAM_128 | FE_CAN_QAM_256 |
- + FE_CAN_FEC_AUTO
- + },
- +
- + .release = cxd2820r_release,
- + .init = cxd2820r_init,
- + .sleep = cxd2820r_sleep,
- +
- + .get_tune_settings = cxd2820r_get_tune_settings,
- +
- + .set_frontend = cxd2820r_set_frontend,
- + .get_frontend = cxd2820r_get_frontend,
- +
- + .read_status = cxd2820r_read_status,
- + .read_snr = cxd2820r_read_snr,
- + .read_ber = cxd2820r_read_ber,
- + .read_ucblocks = cxd2820r_read_ucblocks,
- + .read_signal_strength = cxd2820r_read_signal_strength,
- + },
- +};
- +
- +
- +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
- +MODULE_DESCRIPTION("Sony CXD2820R demodulator driver");
- +MODULE_LICENSE("GPL");
- diff --git a/drivers/media/dvb/frontends/cxd2820r_priv.h b/drivers/media/dvb/frontends/cxd2820r_priv.h
- new file mode 100644
- index 0000000..d4e2e0b
- --- /dev/null
- +++ b/drivers/media/dvb/frontends/cxd2820r_priv.h
- @@ -0,0 +1,178 @@
- +/*
- + * Sony CXD2820R demodulator driver
- + *
- + * Copyright (C) 2010 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 CXD2820R_PRIV_H
- +#define CXD2820R_PRIV_H
- +
- +#include <linux/dvb/version.h>
- +#include "dvb_frontend.h"
- +#include "dvb_math.h"
- +#include "cxd2820r.h"
- +
- +#define LOG_PREFIX "cxd2820r"
- +
- +#undef dbg
- +#define dbg(f, arg...) \
- + if (cxd2820r_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)
- +
- +/*
- + * FIXME: These are totally wrong and must be added properly to the API.
- + * Only temporary solution in order to get driver compile.
- + */
- +#define SYS_DVBT2 SYS_DAB
- +#define TRANSMISSION_MODE_1K 0
- +#define TRANSMISSION_MODE_16K 0
- +#define TRANSMISSION_MODE_32K 0
- +#define GUARD_INTERVAL_1_128 0
- +#define GUARD_INTERVAL_19_128 0
- +#define GUARD_INTERVAL_19_256 0
- +
- +struct reg_val_mask {
- + u32 reg;
- + u8 val;
- + u8 mask;
- +};
- +
- +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 */
- +
- + int ber_running:1;
- +
- + u8 bank[2];
- + u8 gpio[3];
- +
- + fe_delivery_system_t delivery_system;
- + int last_tune_failed:1; /* for switch between T and T2 tune */
- +};
- +
- +/* cxd2820r_core.c */
- +
- +extern int cxd2820r_debug;
- +
- +int cxd2820r_gpio(struct dvb_frontend *fe);
- +
- +int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val,
- + u8 mask);
- +
- +int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- + int len);
- +
- +u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor);
- +
- +int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- + int len);
- +
- +int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- + int len);
- +
- +int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val);
- +
- +int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val);
- +
- +/* cxd2820r_c.c */
- +
- +int cxd2820r_get_frontend_c(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p);
- +
- +int cxd2820r_set_frontend_c(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *params);
- +
- +int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status);
- +
- +int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber);
- +
- +int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, u16 *strength);
- +
- +int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr);
- +
- +int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks);
- +
- +int cxd2820r_init_c(struct dvb_frontend *fe);
- +
- +int cxd2820r_sleep_c(struct dvb_frontend *fe);
- +
- +int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
- + struct dvb_frontend_tune_settings *s);
- +
- +/* cxd2820r_t.c */
- +
- +int cxd2820r_get_frontend_t(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p);
- +
- +int cxd2820r_set_frontend_t(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *params);
- +
- +int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status);
- +
- +int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber);
- +
- +int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, u16 *strength);
- +
- +int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr);
- +
- +int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks);
- +
- +int cxd2820r_init_t(struct dvb_frontend *fe);
- +
- +int cxd2820r_sleep_t(struct dvb_frontend *fe);
- +
- +int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
- + struct dvb_frontend_tune_settings *s);
- +
- +/* cxd2820r_t2.c */
- +
- +int cxd2820r_get_frontend_t2(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p);
- +
- +int cxd2820r_set_frontend_t2(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *params);
- +
- +int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status);
- +
- +int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber);
- +
- +int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, u16 *strength);
- +
- +int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr);
- +
- +int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks);
- +
- +int cxd2820r_init_t2(struct dvb_frontend *fe);
- +
- +int cxd2820r_sleep_t2(struct dvb_frontend *fe);
- +
- +int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
- + struct dvb_frontend_tune_settings *s);
- +
- +#endif /* CXD2820R_PRIV_H */
- diff --git a/drivers/media/dvb/frontends/cxd2820r_t.c b/drivers/media/dvb/frontends/cxd2820r_t.c
- new file mode 100644
- index 0000000..6582564
- --- /dev/null
- +++ b/drivers/media/dvb/frontends/cxd2820r_t.c
- @@ -0,0 +1,449 @@
- +/*
- + * Sony CXD2820R demodulator driver
- + *
- + * Copyright (C) 2010 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 "cxd2820r_priv.h"
- +
- +int cxd2820r_set_frontend_t(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- + int ret, i;
- + u32 if_khz, if_ctl;
- + u64 num;
- + u8 buf[3], bw_param;
- + u8 bw_params1[][5] = {
- + { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */
- + { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */
- + { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */
- + };
- + u8 bw_params2[][2] = {
- + { 0x1f, 0xdc }, /* 6 MHz */
- + { 0x12, 0xf8 }, /* 7 MHz */
- + { 0x01, 0xe0 }, /* 8 MHz */
- + };
- + struct reg_val_mask tab[] = {
- + { 0x00080, 0x00, 0xff },
- + { 0x00081, 0x03, 0xff },
- + { 0x00085, 0x07, 0xff },
- + { 0x00088, 0x01, 0xff },
- +
- + { 0x00070, priv->cfg.ts_mode, 0xff },
- + { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 },
- + { 0x000a5, 0x00, 0x01 },
- + { 0x00082, 0x20, 0x60 },
- + { 0x000c2, 0xc3, 0xff },
- + { 0x0016a, 0x50, 0xff },
- + { 0x00427, 0x41, 0xff },
- + };
- +
- + dbg("%s: RF=%d BW=%d", __func__, c->frequency, c->bandwidth_hz);
- +
- + /* update GPIOs */
- + ret = cxd2820r_gpio(fe);
- + if (ret)
- + goto error;
- +
- + /* program tuner */
- + if (fe->ops.tuner_ops.set_params)
- + fe->ops.tuner_ops.set_params(fe, p);
- +
- + if (priv->delivery_system != SYS_DVBT) {
- + for (i = 0; i < ARRAY_SIZE(tab); i++) {
- + ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
- + tab[i].val, tab[i].mask);
- + if (ret)
- + goto error;
- + }
- + }
- +
- + priv->delivery_system = SYS_DVBT;
- + priv->ber_running = 0; /* tune stops BER counter */
- +
- + switch (c->bandwidth_hz) {
- + case 6000000:
- + if_khz = priv->cfg.if_dvbt_6;
- + i = 0;
- + bw_param = 2;
- + break;
- + case 7000000:
- + if_khz = priv->cfg.if_dvbt_7;
- + i = 1;
- + bw_param = 1;
- + break;
- + case 8000000:
- + if_khz = priv->cfg.if_dvbt_8;
- + i = 2;
- + bw_param = 0;
- + break;
- + default:
- + return -EINVAL;
- + }
- +
- + num = if_khz;
- + num *= 0x1000000;
- + if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
- + buf[0] = ((if_ctl >> 16) & 0xff);
- + buf[1] = ((if_ctl >> 8) & 0xff);
- + buf[2] = ((if_ctl >> 0) & 0xff);
- +
- + ret = cxd2820r_wr_regs(priv, 0x000b6, buf, 3);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_regs(priv, 0x0009f, bw_params1[i], 5);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_reg_mask(priv, 0x000d7, bw_param << 6, 0xc0);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_regs(priv, 0x000d9, bw_params2[i], 2);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
- + if (ret)
- + goto error;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_get_frontend_t(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- + int ret;
- + u8 buf[2];
- +
- + ret = cxd2820r_rd_regs(priv, 0x0002f, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + switch ((buf[0] >> 6) & 0x03) {
- + case 0:
- + c->modulation = QPSK;
- + break;
- + case 1:
- + c->modulation = QAM_16;
- + break;
- + case 2:
- + c->modulation = QAM_64;
- + break;
- + }
- +
- + switch ((buf[1] >> 1) & 0x03) {
- + case 0:
- + c->transmission_mode = TRANSMISSION_MODE_2K;
- + break;
- + case 1:
- + c->transmission_mode = TRANSMISSION_MODE_8K;
- + break;
- + }
- +
- + switch ((buf[1] >> 3) & 0x03) {
- + case 0:
- + c->guard_interval = GUARD_INTERVAL_1_32;
- + break;
- + case 1:
- + c->guard_interval = GUARD_INTERVAL_1_16;
- + break;
- + case 2:
- + c->guard_interval = GUARD_INTERVAL_1_8;
- + break;
- + case 3:
- + c->guard_interval = GUARD_INTERVAL_1_4;
- + break;
- + }
- +
- + switch ((buf[0] >> 3) & 0x07) {
- + case 0:
- + c->hierarchy = HIERARCHY_NONE;
- + break;
- + case 1:
- + c->hierarchy = HIERARCHY_1;
- + break;
- + case 2:
- + c->hierarchy = HIERARCHY_2;
- + break;
- + case 3:
- + c->hierarchy = HIERARCHY_4;
- + break;
- + }
- +
- + switch ((buf[0] >> 0) & 0x07) {
- + case 0:
- + c->code_rate_HP = FEC_1_2;
- + break;
- + case 1:
- + c->code_rate_HP = FEC_2_3;
- + break;
- + case 2:
- + c->code_rate_HP = FEC_3_4;
- + break;
- + case 3:
- + c->code_rate_HP = FEC_5_6;
- + break;
- + case 4:
- + c->code_rate_HP = FEC_7_8;
- + break;
- + }
- +
- + switch ((buf[1] >> 5) & 0x07) {
- + case 0:
- + c->code_rate_LP = FEC_1_2;
- + break;
- + case 1:
- + c->code_rate_LP = FEC_2_3;
- + break;
- + case 2:
- + c->code_rate_LP = FEC_3_4;
- + break;
- + case 3:
- + c->code_rate_LP = FEC_5_6;
- + break;
- + case 4:
- + c->code_rate_LP = FEC_7_8;
- + break;
- + }
- +
- + ret = cxd2820r_rd_reg(priv, 0x007c6, &buf[0]);
- + if (ret)
- + goto error;
- +
- + switch ((buf[0] >> 0) & 0x01) {
- + case 0:
- + c->inversion = INVERSION_OFF;
- + break;
- + case 1:
- + c->inversion = INVERSION_ON;
- + break;
- + }
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[3], start_ber = 0;
- + *ber = 0;
- +
- + if (priv->ber_running) {
- + ret = cxd2820r_rd_regs(priv, 0x00076, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) {
- + *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0];
- + start_ber = 1;
- + }
- + } else {
- + priv->ber_running = 1;
- + start_ber = 1;
- + }
- +
- + if (start_ber) {
- + /* (re)start BER */
- + ret = cxd2820r_wr_reg(priv, 0x00079, 0x01);
- + if (ret)
- + goto error;
- + }
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe,
- + u16 *strength)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[2];
- + u16 tmp;
- +
- + ret = cxd2820r_rd_regs(priv, 0x00026, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + tmp = (buf[0] & 0x0f) << 8 | buf[1];
- + tmp = ~tmp & 0x0fff;
- +
- + /* scale value to 0x0000-0xffff from 0x0000-0x0fff */
- + *strength = tmp * 0xffff / 0x0fff;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[2];
- + u16 tmp;
- + /* report SNR in dB * 10 */
- +
- + ret = cxd2820r_rd_regs(priv, 0x00028, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + tmp = (buf[0] & 0x1f) << 8 | buf[1];
- + #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */
- + if (tmp)
- + *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24)
- + / 100);
- + else
- + *snr = 0;
- +
- + dbg("%s: dBx10=%d val=%04x", __func__, *snr, tmp);
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks)
- +{
- + *ucblocks = 0;
- + /* no way to read ? */
- + return 0;
- +}
- +
- +int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[4];
- + *status = 0;
- +
- + ret = cxd2820r_rd_reg(priv, 0x00010, &buf[0]);
- + if (ret)
- + goto error;
- +
- + if ((buf[0] & 0x07) == 6) {
- + ret = cxd2820r_rd_reg(priv, 0x00073, &buf[1]);
- + if (ret)
- + goto error;
- +
- + if (((buf[1] >> 3) & 0x01) == 1) {
- + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- + } else {
- + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- + FE_HAS_VITERBI | FE_HAS_SYNC;
- + }
- + } else {
- + ret = cxd2820r_rd_reg(priv, 0x00014, &buf[2]);
- + if (ret)
- + goto error;
- +
- + if ((buf[2] & 0x0f) >= 4) {
- + ret = cxd2820r_rd_reg(priv, 0x00a14, &buf[3]);
- + if (ret)
- + goto error;
- +
- + if (((buf[3] >> 4) & 0x01) == 1)
- + *status |= FE_HAS_SIGNAL;
- + }
- + }
- +
- + dbg("%s: lock=%02x %02x %02x %02x", __func__,
- + buf[0], buf[1], buf[2], buf[3]);
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_init_t(struct dvb_frontend *fe)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- +
- + ret = cxd2820r_wr_reg(priv, 0x00085, 0x07);
- + if (ret)
- + goto error;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_sleep_t(struct dvb_frontend *fe)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret, i;
- + struct reg_val_mask tab[] = {
- + { 0x000ff, 0x1f, 0xff },
- + { 0x00085, 0x00, 0xff },
- + { 0x00088, 0x01, 0xff },
- + { 0x00081, 0x00, 0xff },
- + { 0x00080, 0x00, 0xff },
- + };
- +
- + dbg("%s", __func__);
- +
- + priv->delivery_system = SYS_UNDEFINED;
- +
- + for (i = 0; i < ARRAY_SIZE(tab); i++) {
- + ret = cxd2820r_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;
- +}
- +
- +int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
- + struct dvb_frontend_tune_settings *s)
- +{
- + s->min_delay_ms = 500;
- + s->step_size = fe->ops.info.frequency_stepsize * 2;
- + s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
- +
- + return 0;
- +}
- +
- diff --git a/drivers/media/dvb/frontends/cxd2820r_t2.c b/drivers/media/dvb/frontends/cxd2820r_t2.c
- new file mode 100644
- index 0000000..c47b35c
- --- /dev/null
- +++ b/drivers/media/dvb/frontends/cxd2820r_t2.c
- @@ -0,0 +1,423 @@
- +/*
- + * Sony CXD2820R demodulator driver
- + *
- + * Copyright (C) 2010 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 "cxd2820r_priv.h"
- +
- +int cxd2820r_set_frontend_t2(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *params)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- + int ret, i;
- + u32 if_khz, if_ctl;
- + u64 num;
- + u8 buf[3], bw_param;
- + u8 bw_params1[][5] = {
- + { 0x1c, 0xb3, 0x33, 0x33, 0x33 }, /* 5 MHz */
- + { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */
- + { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */
- + { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */
- + };
- + struct reg_val_mask tab[] = {
- + { 0x00080, 0x02, 0xff },
- + { 0x00081, 0x20, 0xff },
- + { 0x00085, 0x07, 0xff },
- + { 0x00088, 0x01, 0xff },
- + { 0x02069, 0x01, 0xff },
- +
- + { 0x0207f, 0x2a, 0xff },
- + { 0x02082, 0x0a, 0xff },
- + { 0x02083, 0x0a, 0xff },
- + { 0x020cb, priv->cfg.if_agc_polarity << 6, 0x40 },
- + { 0x02070, priv->cfg.ts_mode, 0xff },
- + { 0x020b5, priv->cfg.spec_inv << 4, 0x10 },
- + { 0x02567, 0x07, 0x0f },
- + { 0x02569, 0x03, 0x03 },
- + { 0x02595, 0x1a, 0xff },
- + { 0x02596, 0x50, 0xff },
- + { 0x02a8c, 0x00, 0xff },
- + { 0x02a8d, 0x34, 0xff },
- + { 0x02a45, 0x06, 0x07 },
- + { 0x03f10, 0x0d, 0xff },
- + { 0x03f11, 0x02, 0xff },
- + { 0x03f12, 0x01, 0xff },
- + { 0x03f23, 0x2c, 0xff },
- + { 0x03f51, 0x13, 0xff },
- + { 0x03f52, 0x01, 0xff },
- + { 0x03f53, 0x00, 0xff },
- + { 0x027e6, 0x14, 0xff },
- + { 0x02786, 0x02, 0x07 },
- + { 0x02787, 0x40, 0xe0 },
- + { 0x027ef, 0x10, 0x18 },
- + };
- +
- + dbg("%s: RF=%d BW=%d", __func__, c->frequency, c->bandwidth_hz);
- +
- + /* update GPIOs */
- + ret = cxd2820r_gpio(fe);
- + if (ret)
- + goto error;
- +
- + /* program tuner */
- + if (fe->ops.tuner_ops.set_params)
- + fe->ops.tuner_ops.set_params(fe, params);
- +
- + if (priv->delivery_system != SYS_DVBT2) {
- + for (i = 0; i < ARRAY_SIZE(tab); i++) {
- + ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
- + tab[i].val, tab[i].mask);
- + if (ret)
- + goto error;
- + }
- + }
- +
- + priv->delivery_system = SYS_DVBT2;
- +
- + switch (c->bandwidth_hz) {
- + case 5000000:
- + if_khz = priv->cfg.if_dvbt2_5;
- + i = 0;
- + bw_param = 3;
- + break;
- + case 6000000:
- + if_khz = priv->cfg.if_dvbt2_6;
- + i = 1;
- + bw_param = 2;
- + break;
- + case 7000000:
- + if_khz = priv->cfg.if_dvbt2_7;
- + i = 2;
- + bw_param = 1;
- + break;
- + case 8000000:
- + if_khz = priv->cfg.if_dvbt2_8;
- + i = 3;
- + bw_param = 0;
- + break;
- + default:
- + return -EINVAL;
- + }
- +
- + num = if_khz;
- + num *= 0x1000000;
- + if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
- + buf[0] = ((if_ctl >> 16) & 0xff);
- + buf[1] = ((if_ctl >> 8) & 0xff);
- + buf[2] = ((if_ctl >> 0) & 0xff);
- +
- + ret = cxd2820r_wr_regs(priv, 0x020b6, buf, 3);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_regs(priv, 0x0209f, bw_params1[i], 5);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_reg_mask(priv, 0x020d7, bw_param << 6, 0xc0);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
- + if (ret)
- + goto error;
- +
- + ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
- + if (ret)
- + goto error;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +
- +}
- +
- +int cxd2820r_get_frontend_t2(struct dvb_frontend *fe,
- + struct dvb_frontend_parameters *p)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- + int ret;
- + u8 buf[2];
- +
- + ret = cxd2820r_rd_regs(priv, 0x0205c, buf, 2);
- + if (ret)
- + goto error;
- +
- + switch ((buf[0] >> 0) & 0x07) {
- + case 0:
- + c->transmission_mode = TRANSMISSION_MODE_2K;
- + break;
- + case 1:
- + c->transmission_mode = TRANSMISSION_MODE_8K;
- + break;
- + case 2:
- + c->transmission_mode = TRANSMISSION_MODE_4K;
- + break;
- + case 3:
- + c->transmission_mode = TRANSMISSION_MODE_1K;
- + break;
- + case 4:
- + c->transmission_mode = TRANSMISSION_MODE_16K;
- + break;
- + case 5:
- + c->transmission_mode = TRANSMISSION_MODE_32K;
- + break;
- + }
- +
- + switch ((buf[1] >> 4) & 0x07) {
- + case 0:
- + c->guard_interval = GUARD_INTERVAL_1_32;
- + break;
- + case 1:
- + c->guard_interval = GUARD_INTERVAL_1_16;
- + break;
- + case 2:
- + c->guard_interval = GUARD_INTERVAL_1_8;
- + break;
- + case 3:
- + c->guard_interval = GUARD_INTERVAL_1_4;
- + break;
- + case 4:
- + c->guard_interval = GUARD_INTERVAL_1_128;
- + break;
- + case 5:
- + c->guard_interval = GUARD_INTERVAL_19_128;
- + break;
- + case 6:
- + c->guard_interval = GUARD_INTERVAL_19_256;
- + break;
- + }
- +
- + ret = cxd2820r_rd_regs(priv, 0x0225b, buf, 2);
- + if (ret)
- + goto error;
- +
- + switch ((buf[0] >> 0) & 0x07) {
- + case 0:
- + c->fec_inner = FEC_1_2;
- + break;
- + case 1:
- + c->fec_inner = FEC_3_5;
- + break;
- + case 2:
- + c->fec_inner = FEC_2_3;
- + break;
- + case 3:
- + c->fec_inner = FEC_3_4;
- + break;
- + case 4:
- + c->fec_inner = FEC_4_5;
- + break;
- + case 5:
- + c->fec_inner = FEC_5_6;
- + break;
- + }
- +
- + switch ((buf[1] >> 0) & 0x07) {
- + case 0:
- + c->modulation = QPSK;
- + break;
- + case 1:
- + c->modulation = QAM_16;
- + break;
- + case 2:
- + c->modulation = QAM_64;
- + break;
- + case 3:
- + c->modulation = QAM_256;
- + break;
- + }
- +
- + ret = cxd2820r_rd_reg(priv, 0x020b5, &buf[0]);
- + if (ret)
- + goto error;
- +
- + switch ((buf[0] >> 4) & 0x01) {
- + case 0:
- + c->inversion = INVERSION_OFF;
- + break;
- + case 1:
- + c->inversion = INVERSION_ON;
- + break;
- + }
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[1];
- + *status = 0;
- +
- + ret = cxd2820r_rd_reg(priv, 0x02010 , &buf[0]);
- + if (ret)
- + goto error;
- +
- + if ((buf[0] & 0x07) == 6) {
- + if (((buf[0] >> 5) & 0x01) == 1) {
- + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- + } else {
- + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- + FE_HAS_VITERBI | FE_HAS_SYNC;
- + }
- + }
- +
- + dbg("%s: lock=%02x", __func__, buf[0]);
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[4];
- + unsigned int errbits;
- + *ber = 0;
- + /* FIXME: correct calculation */
- +
- + ret = cxd2820r_rd_regs(priv, 0x02039, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + if ((buf[0] >> 4) & 0x01) {
- + errbits = (buf[0] & 0x0f) << 24 | buf[1] << 16 |
- + buf[2] << 8 | buf[3];
- +
- + if (errbits)
- + *ber = errbits * 64 / 16588800;
- + }
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe,
- + u16 *strength)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[2];
- + u16 tmp;
- +
- + ret = cxd2820r_rd_regs(priv, 0x02026, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + tmp = (buf[0] & 0x0f) << 8 | buf[1];
- + tmp = ~tmp & 0x0fff;
- +
- + /* scale value to 0x0000-0xffff from 0x0000-0x0fff */
- + *strength = tmp * 0xffff / 0x0fff;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret;
- + u8 buf[2];
- + u16 tmp;
- + /* report SNR in dB * 10 */
- +
- + ret = cxd2820r_rd_regs(priv, 0x02028, buf, sizeof(buf));
- + if (ret)
- + goto error;
- +
- + tmp = (buf[0] & 0x0f) << 8 | buf[1];
- + #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */
- + if (tmp)
- + *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24)
- + / 100);
- + else
- + *snr = 0;
- +
- + dbg("%s: dBx10=%d val=%04x", __func__, *snr, tmp);
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks)
- +{
- + *ucblocks = 0;
- + /* no way to read ? */
- + return 0;
- +}
- +
- +int cxd2820r_sleep_t2(struct dvb_frontend *fe)
- +{
- + struct cxd2820r_priv *priv = fe->demodulator_priv;
- + int ret, i;
- + struct reg_val_mask tab[] = {
- + { 0x000ff, 0x1f, 0xff },
- + { 0x00085, 0x00, 0xff },
- + { 0x00088, 0x01, 0xff },
- + { 0x02069, 0x00, 0xff },
- + { 0x00081, 0x00, 0xff },
- + { 0x00080, 0x00, 0xff },
- + };
- +
- + dbg("%s", __func__);
- +
- + for (i = 0; i < ARRAY_SIZE(tab); i++) {
- + ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val,
- + tab[i].mask);
- + if (ret)
- + goto error;
- + }
- +
- + priv->delivery_system = SYS_UNDEFINED;
- +
- + return ret;
- +error:
- + dbg("%s: failed:%d", __func__, ret);
- + return ret;
- +}
- +
- +int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
- + struct dvb_frontend_tune_settings *s)
- +{
- + s->min_delay_ms = 1500;
- + s->step_size = fe->ops.info.frequency_stepsize * 2;
- + s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
- +
- + return 0;
- +}
- +
- diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
- index 985100e..8ef78cd 100644
- --- a/drivers/media/video/em28xx/Kconfig
- +++ b/drivers/media/video/em28xx/Kconfig
- @@ -38,6 +38,7 @@ config VIDEO_EM28XX_DVB
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_TDA10023 if !DVB_FE_CUSTOMISE
- select DVB_S921 if !DVB_FE_CUSTOMISE
- + select DVB_CXD2820R if !DVB_FE_CUSTOMISE
- select VIDEOBUF_DVB
- ---help---
- This adds support for DVB cards based on the
- diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
- index 87f77a3..790f0c8 100644
- --- a/drivers/media/video/em28xx/em28xx-cards.c
- +++ b/drivers/media/video/em28xx/em28xx-cards.c
- @@ -282,6 +282,16 @@ static struct em28xx_reg_seq leadership_reset[] = {
- { -1, -1, -1, -1},
- };
- +/* 2013:024f PCTV Systems nanoStick T2 290e
- + * GPIO_6 - demod reset
- + * GPIO_7 - LED
- + */
- +static struct em28xx_reg_seq pctv_290e[] = {
- + {EM2874_R80_GPIO, 0x00, 0xff, 80},
- + {EM2874_R80_GPIO, 0x40, 0xff, 80}, /* GPIO_6 = 1 */
- + {EM2874_R80_GPIO, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */
- + {-1, -1, -1, -1},
- +};
- /*
- * Board definitions
- @@ -1749,6 +1759,17 @@ struct em28xx_board em28xx_boards[] = {
- .dvb_gpio = kworld_a340_digital,
- .tuner_gpio = default_tuner_gpio,
- },
- + /* 2013:024f PCTV Systems nanoStick T2 290e.
- + * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
- + [EM28174_BOARD_PCTV_290E] = {
- + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- + EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
- + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
- + .name = "PCTV Systems nanoStick T2 290e",
- + .tuner_type = TUNER_ABSENT,
- + .tuner_gpio = pctv_290e,
- + .has_dvb = 1,
- + },
- };
- const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
- @@ -1876,6 +1897,8 @@ struct usb_device_id em28xx_id_table[] = {
- .driver_info = EM2860_BOARD_GADMEI_UTV330 },
- { USB_DEVICE(0x1b80, 0xa340),
- .driver_info = EM2870_BOARD_KWORLD_A340 },
- + { USB_DEVICE(0x2013, 0x024f),
- + .driver_info = EM28174_BOARD_PCTV_290E },
- { },
- };
- MODULE_DEVICE_TABLE(usb, em28xx_id_table);
- @@ -2799,6 +2822,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
- dev->reg_gpio_num = EM2874_R80_GPIO;
- dev->wait_after_write = 0;
- break;
- + case CHIP_ID_EM28174:
- + em28xx_info("chip ID is em28174\n");
- + dev->reg_gpio_num = EM2874_R80_GPIO;
- + dev->wait_after_write = 0;
- + break;
- case CHIP_ID_EM2883:
- em28xx_info("chip ID is em2882/em2883\n");
- dev->wait_after_write = 0;
- diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
- index 44c63cb..e33f145 100644
- --- a/drivers/media/video/em28xx/em28xx-core.c
- +++ b/drivers/media/video/em28xx/em28xx-core.c
- @@ -489,7 +489,8 @@ int em28xx_audio_setup(struct em28xx *dev)
- int vid1, vid2, feat, cfg;
- u32 vid;
- - if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874) {
- + if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874
- + || dev->chip_id == CHIP_ID_EM28174) {
- /* Digital only device - don't load any alsa module */
- dev->audio_mode.has_audio = 0;
- dev->has_audio_class = 0;
- @@ -614,7 +615,7 @@ int em28xx_capture_start(struct em28xx *dev, int start)
- {
- int rc;
- - if (dev->chip_id == CHIP_ID_EM2874) {
- + if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
- /* The Transport Stream Enable Register moved in em2874 */
- if (!start) {
- rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
- @@ -1111,6 +1112,10 @@ int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev)
- /* FIXME - for now assume 564 like it was before, but the
- em2874 code should be added to return the proper value... */
- packet_size = 564;
- + } else if (dev->chip_id == CHIP_ID_EM28174) {
- + /* FIXME same as em2874. 564 was enough for 22 Mbit DVB-T
- + but too much for 44 Mbit DVB-C. */
- + packet_size = 752;
- } else {
- /* TS max packet size stored in bits 1-0 of R01 */
- chip_cfg2 = em28xx_read_reg(dev, EM28XX_R01_CHIPCFG2);
- diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
- index c7c04bf..b939f82 100644
- --- a/drivers/media/video/em28xx/em28xx-dvb.c
- +++ b/drivers/media/video/em28xx/em28xx-dvb.c
- @@ -38,6 +38,7 @@
- #include "tda1002x.h"
- #include "tda18271.h"
- #include "s921.h"
- +#include "cxd2820r.h"
- MODULE_DESCRIPTION("driver for em28xx based DVB cards");
- MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
- @@ -58,7 +59,7 @@ if (debug >= level) \
- #define EM28XX_DVB_MAX_PACKETS 64
- struct em28xx_dvb {
- - struct dvb_frontend *frontend;
- + struct dvb_frontend *fe[2];
- /* feed count management */
- struct mutex lock;
- @@ -332,6 +333,26 @@ static struct tda10023_config em28xx_tda10023_config = {
- .invert = 1,
- };
- +static struct cxd2820r_config em28xx_cxd2820r_config = {
- + .i2c_address = (0xd8 >> 1),
- + .ts_mode = CXD2820R_TS_SERIAL,
- + .if_dvbt_6 = 3300,
- + .if_dvbt_7 = 3500,
- + .if_dvbt_8 = 4000,
- + .if_dvbt2_6 = 3300,
- + .if_dvbt2_7 = 3500,
- + .if_dvbt2_8 = 4000,
- + .if_dvbc = 5000,
- +
- + /* enable LNA for DVB-T2 and DVB-C */
- + .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
- + .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
- +};
- +
- +static struct tda18271_config em28xx_cxd2820r_tda18271_config = {
- + .output_opt = TDA18271_OUTPUT_LT_OFF,
- +};
- +
- /* ------------------------------------------------------------------ */
- static int attach_xc3028(u8 addr, struct em28xx *dev)
- @@ -343,17 +364,17 @@ static int attach_xc3028(u8 addr, struct em28xx *dev)
- cfg.i2c_adap = &dev->i2c_adap;
- cfg.i2c_addr = addr;
- - if (!dev->dvb->frontend) {
- + if (!dev->dvb->fe[0]) {
- em28xx_errdev("/2: dvb frontend not attached. "
- "Can't attach xc3028\n");
- return -EINVAL;
- }
- - fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg);
- + fe = dvb_attach(xc2028_attach, dev->dvb->fe[0], &cfg);
- if (!fe) {
- em28xx_errdev("/2: xc3028 attach failed\n");
- - dvb_frontend_detach(dev->dvb->frontend);
- - dev->dvb->frontend = NULL;
- + dvb_frontend_detach(dev->dvb->fe[0]);
- + dev->dvb->fe[0] = NULL;
- return -EINVAL;
- }
- @@ -383,16 +404,28 @@ static int register_dvb(struct em28xx_dvb *dvb,
- }
- /* Ensure all frontends negotiate bus access */
- - dvb->frontend->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
- + dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
- + if (dvb->fe[1])
- + dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
- dvb->adapter.priv = dev;
- /* register frontend */
- - result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
- + result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
- dev->name, result);
- - goto fail_frontend;
- + goto fail_frontend0;
- + }
- +
- + /* register 2nd frontend */
- + if (dvb->fe[1]) {
- + result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]);
- + if (result < 0) {
- + printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n",
- + dev->name, result);
- + goto fail_frontend1;
- + }
- }
- /* register demux stuff */
- @@ -458,9 +491,14 @@ fail_fe_hw:
- fail_dmxdev:
- dvb_dmx_release(&dvb->demux);
- fail_dmx:
- - dvb_unregister_frontend(dvb->frontend);
- -fail_frontend:
- - dvb_frontend_detach(dvb->frontend);
- + if (dvb->fe[1])
- + dvb_unregister_frontend(dvb->fe[1]);
- + dvb_unregister_frontend(dvb->fe[0]);
- +fail_frontend1:
- + if (dvb->fe[1])
- + dvb_frontend_detach(dvb->fe[1]);
- +fail_frontend0:
- + dvb_frontend_detach(dvb->fe[0]);
- dvb_unregister_adapter(&dvb->adapter);
- fail_adapter:
- return result;
- @@ -473,12 +511,15 @@ static void unregister_dvb(struct em28xx_dvb *dvb)
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- dvb_dmxdev_release(&dvb->dmxdev);
- dvb_dmx_release(&dvb->demux);
- - dvb_unregister_frontend(dvb->frontend);
- - dvb_frontend_detach(dvb->frontend);
- + if (dvb->fe[1])
- + dvb_unregister_frontend(dvb->fe[1]);
- + dvb_unregister_frontend(dvb->fe[0]);
- + if (dvb->fe[1])
- + dvb_frontend_detach(dvb->fe[1]);
- + dvb_frontend_detach(dvb->fe[0]);
- dvb_unregister_adapter(&dvb->adapter);
- }
- -
- static int dvb_init(struct em28xx *dev)
- {
- int result = 0;
- @@ -497,16 +538,17 @@ static int dvb_init(struct em28xx *dev)
- return -ENOMEM;
- }
- dev->dvb = dvb;
- + dvb->fe[0] = dvb->fe[1] = NULL;
- mutex_lock(&dev->lock);
- em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
- /* init frontend */
- switch (dev->model) {
- case EM2874_LEADERSHIP_ISDBT:
- - dvb->frontend = dvb_attach(s921_attach,
- + dvb->fe[0] = dvb_attach(s921_attach,
- &sharp_isdbt, &dev->i2c_adap);
- - if (!dvb->frontend) {
- + if (!dvb->fe[0]) {
- result = -EINVAL;
- goto out_free;
- }
- @@ -516,7 +558,7 @@ static int dvb_init(struct em28xx *dev)
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
- case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
- case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
- - dvb->frontend = dvb_attach(lgdt330x_attach,
- + dvb->fe[0] = dvb_attach(lgdt330x_attach,
- &em2880_lgdt3303_dev,
- &dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
- @@ -525,7 +567,7 @@ static int dvb_init(struct em28xx *dev)
- }
- break;
- case EM2880_BOARD_KWORLD_DVB_310U:
- - dvb->frontend = dvb_attach(zl10353_attach,
- + dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_with_xc3028,
- &dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
- @@ -536,7 +578,7 @@ static int dvb_init(struct em28xx *dev)
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2882_BOARD_TERRATEC_HYBRID_XS:
- case EM2880_BOARD_EMPIRE_DUAL_TV:
- - dvb->frontend = dvb_attach(zl10353_attach,
- + dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_xc3028_no_i2c_gate,
- &dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
- @@ -549,13 +591,13 @@ static int dvb_init(struct em28xx *dev)
- case EM2881_BOARD_PINNACLE_HYBRID_PRO:
- case EM2882_BOARD_DIKOM_DK300:
- case EM2882_BOARD_KWORLD_VS_DVBT:
- - dvb->frontend = dvb_attach(zl10353_attach,
- + dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_xc3028_no_i2c_gate,
- &dev->i2c_adap);
- - if (dvb->frontend == NULL) {
- + if (dvb->fe[0] == NULL) {
- /* This board could have either a zl10353 or a mt352.
- If the chip id isn't for zl10353, try mt352 */
- - dvb->frontend = dvb_attach(mt352_attach,
- + dvb->fe[0] = dvb_attach(mt352_attach,
- &terratec_xs_mt352_cfg,
- &dev->i2c_adap);
- }
- @@ -567,7 +609,7 @@ static int dvb_init(struct em28xx *dev)
- break;
- case EM2883_BOARD_KWORLD_HYBRID_330U:
- case EM2882_BOARD_EVGA_INDTUBE:
- - dvb->frontend = dvb_attach(s5h1409_attach,
- + dvb->fe[0] = dvb_attach(s5h1409_attach,
- &em28xx_s5h1409_with_xc3028,
- &dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
- @@ -576,11 +618,11 @@ static int dvb_init(struct em28xx *dev)
- }
- break;
- case EM2882_BOARD_KWORLD_ATSC_315U:
- - dvb->frontend = dvb_attach(lgdt330x_attach,
- + dvb->fe[0] = dvb_attach(lgdt330x_attach,
- &em2880_lgdt3303_dev,
- &dev->i2c_adap);
- - if (dvb->frontend != NULL) {
- - if (!dvb_attach(simple_tuner_attach, dvb->frontend,
- + if (dvb->fe[0] != NULL) {
- + if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
- &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) {
- result = -EINVAL;
- goto out_free;
- @@ -602,11 +644,11 @@ static int dvb_init(struct em28xx *dev)
- #endif
- case EM2870_BOARD_REDDO_DVB_C_USB_BOX:
- /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */
- - dvb->frontend = dvb_attach(tda10023_attach,
- + dvb->fe[0] = dvb_attach(tda10023_attach,
- &em28xx_tda10023_config,
- &dev->i2c_adap, 0x48);
- - if (dvb->frontend) {
- - if (!dvb_attach(simple_tuner_attach, dvb->frontend,
- + if (dvb->fe[0]) {
- + if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
- &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) {
- result = -EINVAL;
- goto out_free;
- @@ -614,25 +656,53 @@ static int dvb_init(struct em28xx *dev)
- }
- break;
- case EM2870_BOARD_KWORLD_A340:
- - dvb->frontend = dvb_attach(lgdt3305_attach,
- + dvb->fe[0] = dvb_attach(lgdt3305_attach,
- &em2870_lgdt3304_dev,
- &dev->i2c_adap);
- - if (dvb->frontend != NULL)
- - dvb_attach(tda18271_attach, dvb->frontend, 0x60,
- + if (dvb->fe[0] != NULL)
- + dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
- &dev->i2c_adap, &kworld_a340_config);
- break;
- + case EM28174_BOARD_PCTV_290E:
- + /* MFE
- + * FE 0 = DVB-T/T2 + FE 1 = DVB-C, both sharing same tuner. */
- + /* FE 0 */
- + dvb->fe[0] = dvb_attach(cxd2820r_attach,
- + &em28xx_cxd2820r_config, &dev->i2c_adap, NULL);
- + if (dvb->fe[0]) {
- + struct i2c_adapter *i2c_tuner;
- + i2c_tuner = cxd2820r_get_tuner_i2c_adapter(dvb->fe[0]);
- + /* FE 0 attach tuner */
- + if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
- + i2c_tuner, &em28xx_cxd2820r_tda18271_config)) {
- + dvb_frontend_detach(dvb->fe[0]);
- + result = -EINVAL;
- + goto out_free;
- + }
- + /* FE 1. This dvb_attach() cannot fail. */
- + dvb->fe[1] = dvb_attach(cxd2820r_attach, NULL, NULL,
- + dvb->fe[0]);
- + dvb->fe[1]->id = 1;
- + /* FE 1 attach tuner */
- + if (!dvb_attach(tda18271_attach, dvb->fe[1], 0x60,
- + i2c_tuner, &em28xx_cxd2820r_tda18271_config)) {
- + dvb_frontend_detach(dvb->fe[1]);
- + /* leave FE 0 still active */
- + }
- + }
- + break;
- default:
- em28xx_errdev("/2: The frontend of your DVB/ATSC card"
- " isn't supported yet\n");
- break;
- }
- - if (NULL == dvb->frontend) {
- + if (NULL == dvb->fe[0]) {
- em28xx_errdev("/2: frontend initialization failed\n");
- result = -EINVAL;
- goto out_free;
- }
- /* define general-purpose callback pointer */
- - dvb->frontend->callback = em28xx_tuner_callback;
- + dvb->fe[0]->callback = em28xx_tuner_callback;
- /* register everything */
- result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
- diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
- index 71474d3..4739fc7 100644
- --- a/drivers/media/video/em28xx/em28xx-i2c.c
- +++ b/drivers/media/video/em28xx/em28xx-i2c.c
- @@ -332,7 +332,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
- struct em28xx_eeprom *em_eeprom = (void *)eedata;
- int i, err, size = len, block;
- - if (dev->chip_id == CHIP_ID_EM2874) {
- + if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
- /* Empia switched to a 16-bit addressable eeprom in newer
- devices. While we could certainly write a routine to read
- the eeprom, there is nothing of use in there that cannot be
- diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
- index 91e9055..e92a28e 100644
- --- a/drivers/media/video/em28xx/em28xx-reg.h
- +++ b/drivers/media/video/em28xx/em28xx-reg.h
- @@ -201,6 +201,7 @@ enum em28xx_chip_id {
- CHIP_ID_EM2870 = 35,
- CHIP_ID_EM2883 = 36,
- CHIP_ID_EM2874 = 65,
- + CHIP_ID_EM28174 = 113,
- };
- /*
- diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
- index 6f2795a..60f9c2a 100644
- --- a/drivers/media/video/em28xx/em28xx.h
- +++ b/drivers/media/video/em28xx/em28xx.h
- @@ -118,6 +118,7 @@
- #define EM2882_BOARD_DIKOM_DK300 75
- #define EM2870_BOARD_KWORLD_A340 76
- #define EM2874_LEADERSHIP_ISDBT 77
- +#define EM28174_BOARD_PCTV_290E 78
- /* Limits minimum and default number of buffers */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement