Commit 27cfc85e authored by Antti Palosaari's avatar Antti Palosaari Committed by Mauro Carvalho Chehab

[media] Sony CXD2820R DVB-T/T2/C demodulator driver

It is very first DVB-T2 Linux driver!
Signed-off-by: default avatarAntti Palosaari <crope@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent bc022694
......@@ -383,6 +383,13 @@ config DVB_STV0367
help
A DVB-T/C tuner module. 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
......
......@@ -86,3 +86,5 @@ obj-$(CONFIG_DVB_MB86A16) += mb86a16.o
obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o
obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
obj-$(CONFIG_DVB_STV0367) += stv0367.o
obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
This diff is collapsed.
/*
* 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 */
/*
* 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.
*/
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks)
{
*ucblocks = 0;
/* no way to read ? */
return 0;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
/*
* 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 "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;
};
#endif /* CXD2820R_PRIV_H */
/*
* 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.
*/
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks)
{
*ucblocks = 0;
/* no way to read ? */
return 0;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
/*
* 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.
*/
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks)
{
*ucblocks = 0;
/* no way to read ? */
return 0;
}
static 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;
}
static 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment