[media] Sony CXD2820R DVB-T/T2/C demodulator driver
It is very first DVB-T2 Linux driver! Signed-off-by: Antti Palosaari <crope@iki.fi> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Родитель
bc022694d7
Коммит
27cfc85e3d
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1,886 @@
|
|||
/*
|
||||
* 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).");
|
||||
|
||||
/* TODO: temporary hack, will be removed later when there is app support */
|
||||
unsigned int cxd2820r_dvbt2_freq[5];
|
||||
int cxd2820r_dvbt2_count;
|
||||
module_param_array_named(dvbt2_freq, cxd2820r_dvbt2_freq, int,
|
||||
&cxd2820r_dvbt2_count, 0644);
|
||||
MODULE_PARM_DESC(dvbt2_freq, "RF frequencies forced to DVB-T2 (unit Hz)");
|
||||
|
||||
/* 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 */
|
||||
static 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 */
|
||||
static 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 */
|
||||
static int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val)
|
||||
{
|
||||
return cxd2820r_wr_regs(priv, reg, &val, 1);
|
||||
}
|
||||
|
||||
/* read single register */
|
||||
static 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 */
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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 */
|
||||
static u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor)
|
||||
{
|
||||
return div_u64(dividend + (divisor / 2), divisor);
|
||||
}
|
||||
|
||||
/* TODO: ... */
|
||||
#include "cxd2820r_t.c"
|
||||
#include "cxd2820r_c.c"
|
||||
#include "cxd2820r_t2.c"
|
||||
|
||||
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, i;
|
||||
unsigned int rf1, rf2;
|
||||
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;
|
||||
|
||||
/* TODO: hack! This will be removed later when there is better
|
||||
* app support for DVB-T2... */
|
||||
|
||||
/* Hz => MHz */
|
||||
rf1 = DIV_ROUND_CLOSEST(fe->dtv_property_cache.frequency,
|
||||
1000000);
|
||||
for (i = 0; i < cxd2820r_dvbt2_count; i++) {
|
||||
if (cxd2820r_dvbt2_freq[i] > 100000000) {
|
||||
/* Hz => MHz */
|
||||
rf2 = DIV_ROUND_CLOSEST(cxd2820r_dvbt2_freq[i],
|
||||
1000000);
|
||||
} else if (cxd2820r_dvbt2_freq[i] > 100000) {
|
||||
/* kHz => MHz */
|
||||
rf2 = DIV_ROUND_CLOSEST(cxd2820r_dvbt2_freq[i],
|
||||
1000);
|
||||
} else {
|
||||
rf2 = cxd2820r_dvbt2_freq[i];
|
||||
}
|
||||
|
||||
dbg("%s: rf1=%d rf2=%d", __func__, rf1, rf2);
|
||||
|
||||
if (rf1 == rf2) {
|
||||
dbg("%s: forcing DVB-T2, frequency=%d",
|
||||
__func__, fe->dtv_property_cache.frequency);
|
||||
fe->dtv_property_cache.delivery_system =
|
||||
SYS_DVBT2;
|
||||
}
|
||||
}
|
||||
|
||||
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 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,
|
||||
|
||||
.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,
|
||||
},
|
||||
{
|
||||
/* 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");
|
|
@ -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 */
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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 */
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
Загрузка…
Ссылка в новой задаче