* [REVIEW PATCHv12 2/6] [media] dvb: the core logic to handle the DVBv5 QoS properties
2013-01-22 16:44 [REVIEW PATCHv12 1/6] [media] dvb: Add DVBv5 statistics properties Mauro Carvalho Chehab
@ 2013-01-22 16:44 ` Mauro Carvalho Chehab
2013-01-22 16:44 ` [REVIEW PATCHv12 3/6] [media] mb86a20s: calculate statistics at .read_status() Mauro Carvalho Chehab
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Mauro Carvalho Chehab @ 2013-01-22 16:44 UTC (permalink / raw)
Cc: Mauro Carvalho Chehab, Linux Media Mailing List
Add the logic to poll, reset counters and report the QoS stats
to the end user.
The idea is that the core will periodically poll the frontend for
the stats. The frontend may return -EBUSY, if the previous collect
didn't finish, or it may fill the cached data.
The value returned to the end user is always the cached data.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
v12: identical to v11.
drivers/media/dvb-core/dvb_frontend.c | 27 +++++++++++++++++++++++++++
drivers/media/dvb-core/dvb_frontend.h | 8 ++++++++
2 files changed, 35 insertions(+)
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index dd35fa9..66be7f7 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -1053,6 +1053,14 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0),
_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0),
_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0),
+
+ /* Statistics API */
+ _DTV_CMD(DTV_STAT_SIGNAL_STRENGTH, 0, 0),
+ _DTV_CMD(DTV_STAT_CNR, 0, 0),
+ _DTV_CMD(DTV_STAT_BIT_ERROR_COUNT, 0, 0),
+ _DTV_CMD(DTV_STAT_TOTAL_BITS_COUNT, 0, 0),
+ _DTV_CMD(DTV_STAT_ERROR_BLOCK_COUNT, 0, 0),
+ _DTV_CMD(DTV_STAT_TOTAL_BLOCKS_COUNT, 0, 0),
};
static void dtv_property_dump(struct dvb_frontend *fe, struct dtv_property *tvp)
@@ -1443,6 +1451,25 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
tvp->u.data = c->lna;
break;
+ /* Fill quality measures */
+ case DTV_STAT_SIGNAL_STRENGTH:
+ tvp->u.st = c->strength;
+ break;
+ case DTV_STAT_CNR:
+ tvp->u.st = c->cnr;
+ break;
+ case DTV_STAT_BIT_ERROR_COUNT:
+ tvp->u.st = c->bit_error;
+ break;
+ case DTV_STAT_TOTAL_BITS_COUNT:
+ tvp->u.st = c->bit_count;
+ break;
+ case DTV_STAT_ERROR_BLOCK_COUNT:
+ tvp->u.st = c->block_error;
+ break;
+ case DTV_STAT_TOTAL_BLOCKS_COUNT:
+ tvp->u.st = c->block_count;
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h
index 97112cd..47952c5 100644
--- a/drivers/media/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb-core/dvb_frontend.h
@@ -393,6 +393,14 @@ struct dtv_frontend_properties {
u8 atscmh_sccc_code_mode_d;
u32 lna;
+
+ /* statistics data */
+ struct dtv_fe_stats strength;
+ struct dtv_fe_stats cnr;
+ struct dtv_fe_stats bit_error;
+ struct dtv_fe_stats bit_count;
+ struct dtv_fe_stats block_error;
+ struct dtv_fe_stats block_count;
};
struct dvb_frontend {
--
1.7.11.7
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [REVIEW PATCHv12 3/6] [media] mb86a20s: calculate statistics at .read_status()
2013-01-22 16:44 [REVIEW PATCHv12 1/6] [media] dvb: Add DVBv5 statistics properties Mauro Carvalho Chehab
2013-01-22 16:44 ` [REVIEW PATCHv12 2/6] [media] dvb: the core logic to handle the DVBv5 QoS properties Mauro Carvalho Chehab
@ 2013-01-22 16:44 ` Mauro Carvalho Chehab
2013-01-22 16:44 ` [REVIEW PATCHv12 4/6] [media] mb86a20s: add BER measurement Mauro Carvalho Chehab
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Mauro Carvalho Chehab @ 2013-01-22 16:44 UTC (permalink / raw)
Cc: Mauro Carvalho Chehab, Linux Media Mailing List
Instead of providing separate callbacks to read the several FE
stats properties, the better seems to use just one method that will:
- Read lock status;
- Read signal strength;
- if locked, get TMCC data;
- if locked, get DVB statistics.
As the DVB frontend thread will call this read_status callback
on every 3 seconds, and userspace can even call it earlier,
all stats data and layers layout will be updated together if
available, with is a good thing.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
v12: minor changes:
- Folded with other patches to make easier to read/understand;
- BER code moved to a separate patch;
- Rebased to be applied after mb86a20s fix patches, that were
independent from the status changes and got merged at media-tree
from a separate patch series.
drivers/media/dvb-frontends/mb86a20s.c | 262 ++++++++++++++++++++++++++++-----
1 file changed, 223 insertions(+), 39 deletions(-)
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index c52ae2e..b307cb3 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -80,17 +80,26 @@ static struct regdata mb86a20s_init[] = {
{ 0x04, 0x13 }, { 0x05, 0xff },
{ 0x04, 0x15 }, { 0x05, 0x4e },
{ 0x04, 0x16 }, { 0x05, 0x20 },
- { 0x52, 0x01 },
- { 0x50, 0xa7 }, { 0x51, 0xff },
+
+ /*
+ * On this demod, when the bit count reaches the count below,
+ * it collects the bit error count. The bit counters are initialized
+ * to 65535 here. This warrants that all of them will be quickly
+ * calculated when device gets locked. As TMCC is parsed, the values
+ * can be adjusted later in the driver's code.
+ */
+ { 0x52, 0x01 }, /* Turn on BER before Viterbi */
+ { 0x50, 0xa7 }, { 0x51, 0x00 },
{ 0x50, 0xa8 }, { 0x51, 0xff },
{ 0x50, 0xa9 }, { 0x51, 0xff },
- { 0x50, 0xaa }, { 0x51, 0xff },
+ { 0x50, 0xaa }, { 0x51, 0x00 },
{ 0x50, 0xab }, { 0x51, 0xff },
{ 0x50, 0xac }, { 0x51, 0xff },
- { 0x50, 0xad }, { 0x51, 0xff },
+ { 0x50, 0xad }, { 0x51, 0x00 },
{ 0x50, 0xae }, { 0x51, 0xff },
{ 0x50, 0xaf }, { 0x51, 0xff },
- { 0x5e, 0x07 },
+
+ { 0x5e, 0x00 }, /* Turn off BER after Viterbi */
{ 0x50, 0xdc }, { 0x51, 0x01 },
{ 0x50, 0xdd }, { 0x51, 0xf4 },
{ 0x50, 0xde }, { 0x51, 0x01 },
@@ -105,8 +114,8 @@ static struct regdata mb86a20s_init[] = {
{ 0x50, 0xb6 }, { 0x51, 0xff },
{ 0x50, 0xb7 }, { 0x51, 0xff },
{ 0x50, 0x50 }, { 0x51, 0x02 },
- { 0x50, 0x51 }, { 0x51, 0x04 },
- { 0x45, 0x04 },
+ { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */
+ { 0x45, 0x04 }, /* CN symbol 4 */
{ 0x48, 0x04 },
{ 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */
{ 0x50, 0xd6 }, { 0x51, 0x1f },
@@ -163,12 +172,23 @@ static struct regdata mb86a20s_reset_reception[] = {
{ 0x08, 0x00 },
};
+static struct regdata mb86a20s_vber_reset[] = {
+ { 0x53, 0x00 }, /* VBER Counter reset */
+ { 0x53, 0x07 },
+};
+
+static struct regdata mb86a20s_per_reset[] = {
+ { 0x50, 0xb1 }, /* PER Counter reset */
+ { 0x51, 0x07 },
+ { 0x51, 0x00 },
+};
+
/*
* I2C read/write functions and macros
*/
static int mb86a20s_i2c_writereg(struct mb86a20s_state *state,
- u8 i2c_addr, int reg, int data)
+ u8 i2c_addr, u8 reg, u8 data)
{
u8 buf[] = { reg, data };
struct i2c_msg msg = {
@@ -230,6 +250,12 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state,
mb86a20s_i2c_writeregdata(state, state->config->demod_address, \
regdata, ARRAY_SIZE(regdata))
+/*
+ * Ancillary internal routines (likely compiled inlined)
+ *
+ * The functions below assume that gateway lock has already obtained
+ */
+
static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct mb86a20s_state *state = fe->demodulator_priv;
@@ -262,42 +288,49 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status)
return 0;
}
-static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+static int mb86a20s_read_signal_strength(struct dvb_frontend *fe)
{
struct mb86a20s_state *state = fe->demodulator_priv;
+ int rc;
unsigned rf_max, rf_min, rf;
- u8 val;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
/* Does a binary search to get RF strength */
rf_max = 0xfff;
rf_min = 0;
do {
rf = (rf_max + rf_min) / 2;
- mb86a20s_writereg(state, 0x04, 0x1f);
- mb86a20s_writereg(state, 0x05, rf >> 8);
- mb86a20s_writereg(state, 0x04, 0x20);
- mb86a20s_writereg(state, 0x04, rf);
+ rc = mb86a20s_writereg(state, 0x04, 0x1f);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x05, rf >> 8);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x04, 0x20);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x04, rf);
+ if (rc < 0)
+ return rc;
- val = mb86a20s_readreg(state, 0x02);
- if (val & 0x08)
+ rc = mb86a20s_readreg(state, 0x02);
+ if (rc < 0)
+ return rc;
+ if (rc & 0x08)
rf_min = (rf_max + rf_min) / 2;
else
rf_max = (rf_max + rf_min) / 2;
if (rf_max - rf_min < 4) {
- *strength = (((rf_max + rf_min) / 2) * 65535) / 4095;
+ rf = (rf_max + rf_min) / 2;
+
+ /* Rescale it from 2^12 (4096) to 2^16 */
+ rf <<= (16 - 12);
dev_dbg(&state->i2c->dev,
"%s: signal strength = %d (%d < RF=%d < %d)\n",
__func__, rf, rf_min, rf >> 4, rf_max);
- break;
+ return rf;
}
} while (1);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
return 0;
}
@@ -462,9 +495,6 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe)
/* Reset frontend cache to default values */
mb86a20s_reset_frontend_cache(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
/* Check for partial reception */
rc = mb86a20s_writereg(state, 0x6d, 0x85);
if (rc < 0)
@@ -550,15 +580,119 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe)
break;
}
}
+ return 0;
noperlayer_error:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
+ /* per-layer info is incomplete; discard all per-layer */
+ c->isdbt_layer_enabled = 0;
+
+ return rc;
+}
+
+static int mb86a20s_reset_counters(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc, val;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ /* Reset the counters, if the channel changed */
+ if (state->last_frequency != c->frequency) {
+ memset(&c->strength, 0, sizeof(c->strength));
+ memset(&c->cnr, 0, sizeof(c->cnr));
+ memset(&c->bit_error, 0, sizeof(c->bit_error));
+ memset(&c->bit_count, 0, sizeof(c->bit_count));
+ memset(&c->block_error, 0, sizeof(c->block_error));
+ memset(&c->block_count, 0, sizeof(c->block_count));
+
+ state->last_frequency = c->frequency;
+ }
+
+ /* Clear status for most stats */
+
+ /* BER counter reset */
+ rc = mb86a20s_writeregdata(state, mb86a20s_vber_reset);
+ if (rc < 0)
+ goto err;
+
+ /* MER, PER counter reset */
+ rc = mb86a20s_writeregdata(state, mb86a20s_per_reset);
+ if (rc < 0)
+ goto err;
+
+ /* CNR counter reset */
+ rc = mb86a20s_readreg(state, 0x45);
+ if (rc < 0)
+ goto err;
+ val = rc;
+ rc = mb86a20s_writereg(state, 0x45, val | 0x10);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
+ if (rc < 0)
+ goto err;
+
+ /* MER counter reset */
+ rc = mb86a20s_writereg(state, 0x50, 0x50);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ goto err;
+ val = rc;
+ rc = mb86a20s_writereg(state, 0x51, val | 0x01);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x51, val & 0x06);
+ if (rc < 0)
+ goto err;
+
+err:
return rc;
+}
+
+static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int i;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+ /* Fill the length of each status counter */
+
+ /* Only global stats */
+ c->strength.len = 1;
+
+ /* Per-layer stats - 3 layers + global */
+ c->cnr.len = 4;
+ c->bit_error.len = 4;
+ c->bit_count.len = 4;
+ c->block_error.len = 4;
+ c->block_count.len = 4;
+
+ /* Signal is always available */
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = 0;
+
+ /* Put all of them at FE_SCALE_NOT_AVAILABLE */
+ for (i = 0; i < 4; i++) {
+ c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ }
}
+
+/*
+ * The functions below are called via DVB callbacks, so they need to
+ * properly use the I2C gate control
+ */
+
static int mb86a20s_initfe(struct dvb_frontend *fe)
{
struct mb86a20s_state *state = fe->demodulator_priv;
@@ -637,30 +771,80 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception);
+ mb86a20s_reset_counters(fe);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
return rc;
}
-static int mb86a20s_read_status_gate(struct dvb_frontend *fe,
- fe_status_t *status)
+static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
+ fe_status_t *status)
{
- int ret;
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc;
- *status = 0;
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
- ret = mb86a20s_read_status(fe, status);
+ /* Get lock */
+ rc = mb86a20s_read_status(fe, status);
+ if (!(*status & FE_HAS_LOCK)) {
+ mb86a20s_stats_not_ready(fe);
+ mb86a20s_reset_frontend_cache(fe);
+ }
+ if (rc < 0)
+ goto error;
+
+ /* Get signal strength */
+ rc = mb86a20s_read_signal_strength(fe);
+ if (rc < 0) {
+ mb86a20s_stats_not_ready(fe);
+ mb86a20s_reset_frontend_cache(fe);
+ goto error;
+ }
+ /* Fill signal strength */
+ c->strength.stat[0].uvalue = rc;
+
+ if (*status & FE_HAS_LOCK) {
+ /* Get TMCC info*/
+ rc = mb86a20s_get_frontend(fe);
+ if (rc < 0)
+ goto error;
+ }
+
+ mb86a20s_stats_not_ready(fe);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
+error:
+ return rc;
+}
+
+static int mb86a20s_read_signal_strength_from_cache(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+
+ *strength = c->strength.stat[0].uvalue;
- return ret;
+ return 0;
}
+static int mb86a20s_get_frontend_dummy(struct dvb_frontend *fe)
+{
+ /*
+ * get_frontend is now handled together with other stats
+ * retrival, when read_status() is called, as some statistics
+ * will depend on the layers detection.
+ */
+ return 0;
+};
+
static int mb86a20s_tune(struct dvb_frontend *fe,
bool re_tune,
unsigned int mode_flags,
@@ -676,7 +860,7 @@ static int mb86a20s_tune(struct dvb_frontend *fe,
rc = mb86a20s_set_frontend(fe);
if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
- mb86a20s_read_status_gate(fe, status);
+ mb86a20s_read_status_and_stats(fe, status);
return rc;
}
@@ -759,9 +943,9 @@ static struct dvb_frontend_ops mb86a20s_ops = {
.init = mb86a20s_initfe,
.set_frontend = mb86a20s_set_frontend,
- .get_frontend = mb86a20s_get_frontend,
- .read_status = mb86a20s_read_status_gate,
- .read_signal_strength = mb86a20s_read_signal_strength,
+ .get_frontend = mb86a20s_get_frontend_dummy,
+ .read_status = mb86a20s_read_status_and_stats,
+ .read_signal_strength = mb86a20s_read_signal_strength_from_cache,
.tune = mb86a20s_tune,
};
--
1.7.11.7
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [REVIEW PATCHv12 4/6] [media] mb86a20s: add BER measurement
2013-01-22 16:44 [REVIEW PATCHv12 1/6] [media] dvb: Add DVBv5 statistics properties Mauro Carvalho Chehab
2013-01-22 16:44 ` [REVIEW PATCHv12 2/6] [media] dvb: the core logic to handle the DVBv5 QoS properties Mauro Carvalho Chehab
2013-01-22 16:44 ` [REVIEW PATCHv12 3/6] [media] mb86a20s: calculate statistics at .read_status() Mauro Carvalho Chehab
@ 2013-01-22 16:44 ` Mauro Carvalho Chehab
2013-01-22 16:44 ` [REVIEW PATCHv12 5/6] [media] mb86a20s: improve bit error count for BER Mauro Carvalho Chehab
2013-01-22 16:44 ` [REVIEW PATCHv12 6/6] [media] mb86a20s: add CNR measurement Mauro Carvalho Chehab
4 siblings, 0 replies; 6+ messages in thread
From: Mauro Carvalho Chehab @ 2013-01-22 16:44 UTC (permalink / raw)
Cc: Mauro Carvalho Chehab, Linux Media Mailing List
Add the methods to read bit error/bit count measurements from
mb86a20s. On ISDB-T devices, those reads are done per layer.
However, as userspace applications may not be aware of that,
add a global measure that will sum the bit errors and bit
counts for each layer, storing them into a global value.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
v12: minor changes:
- Rebased on the top of the previous patch;
- BER function was splitted on this patch and merged with a few
fixup patches.
drivers/media/dvb-frontends/mb86a20s.c | 181 ++++++++++++++++++++++++++++++++-
1 file changed, 178 insertions(+), 3 deletions(-)
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index b307cb3..0017ecb 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -649,10 +649,95 @@ static int mb86a20s_reset_counters(struct dvb_frontend *fe)
if (rc < 0)
goto err;
+ goto ok;
err:
+ dev_err(&state->i2c->dev,
+ "%s: Can't reset FE statistics (error %d).\n",
+ __func__, rc);
+ok:
return rc;
}
+static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe,
+ unsigned layer,
+ u32 *error, u32 *count)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ int rc;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (layer >= 3)
+ return -EINVAL;
+
+ /* Check if the BER measures are already available */
+ rc = mb86a20s_readreg(state, 0x54);
+ if (rc < 0)
+ return rc;
+
+ /* Check if data is available for that layer */
+ if (!(rc & (1 << layer))) {
+ dev_dbg(&state->i2c->dev,
+ "%s: BER for layer %c is not available yet.\n",
+ __func__, 'A' + layer);
+ return -EBUSY;
+ }
+
+ /* Read Bit Error Count */
+ rc = mb86a20s_readreg(state, 0x55 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error = rc << 16;
+ rc = mb86a20s_readreg(state, 0x56 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error |= rc << 8;
+ rc = mb86a20s_readreg(state, 0x57 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error |= rc;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: bit error before Viterbi for layer %c: %d.\n",
+ __func__, 'A' + layer, *error);
+
+ /* Read Bit Count */
+ rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *count = rc << 16;
+ rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *count |= rc << 8;
+ rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *count |= rc;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: bit count before Viterbi for layer %c: %d.\n",
+ __func__, 'A' + layer, *count);
+
+
+ /* Reset counter to collect new data */
+ rc = mb86a20s_writereg(state, 0x53, 0x07 & ~(1 << layer));
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x53, 0x07);
+
+ return 0;
+}
+
static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
{
struct mb86a20s_state *state = fe->demodulator_priv;
@@ -687,6 +772,72 @@ static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
}
}
+static int mb86a20s_get_stats(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc = 0, i;
+ u32 bit_error = 0, bit_count = 0;
+ u32 t_bit_error = 0, t_bit_count = 0;
+ int active_layers = 0, ber_layers = 0;
+
+ /* Get per-layer stats */
+ for (i = 0; i < 3; i++) {
+ if (c->isdbt_layer_enabled & (1 << i)) {
+ /* Layer is active and has rc segments */
+ active_layers++;
+
+ /* Read per-layer BER */
+ /* Handle BER before vterbi */
+ rc = mb86a20s_get_ber_before_vterbi(fe, i,
+ &bit_error,
+ &bit_count);
+ if (rc >= 0) {
+ c->bit_error.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->bit_error.stat[1 + i].uvalue += bit_error;
+ c->bit_count.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->bit_count.stat[1 + i].uvalue += bit_count;
+ } else if (rc != -EBUSY) {
+ /*
+ * If an I/O error happened,
+ * measures are now unavailable
+ */
+ c->bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_err(&state->i2c->dev,
+ "%s: Can't get BER for layer %c (error %d).\n",
+ __func__, 'A' + i, rc);
+ }
+
+ if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE)
+ ber_layers++;
+
+ /* Update total BER */
+ t_bit_error += c->bit_error.stat[1 + i].uvalue;
+ t_bit_count += c->bit_count.stat[1 + i].uvalue;
+ }
+ }
+
+ /*
+ * Start showing global count if at least one error count is
+ * available.
+ */
+ if (ber_layers) {
+ /*
+ * At least one per-layer BER measure was read. We can now
+ * calculate the total BER
+ *
+ * Total Bit Error/Count is calculated as the sum of the
+ * bit errors on all active layers.
+ */
+ c->bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->bit_error.stat[0].uvalue = t_bit_error;
+ c->bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->bit_count.stat[0].uvalue = t_bit_count;
+ }
+
+ return rc;
+}
/*
* The functions below are called via DVB callbacks, so they need to
@@ -796,14 +947,21 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
mb86a20s_stats_not_ready(fe);
mb86a20s_reset_frontend_cache(fe);
}
- if (rc < 0)
+ if (rc < 0) {
+ dev_err(&state->i2c->dev,
+ "%s: Can't read frontend lock status\n", __func__);
goto error;
+ }
/* Get signal strength */
rc = mb86a20s_read_signal_strength(fe);
if (rc < 0) {
+ dev_err(&state->i2c->dev,
+ "%s: Can't reset VBER registers.\n", __func__);
mb86a20s_stats_not_ready(fe);
mb86a20s_reset_frontend_cache(fe);
+
+ rc = 0; /* Status is OK */
goto error;
}
/* Fill signal strength */
@@ -812,15 +970,32 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
if (*status & FE_HAS_LOCK) {
/* Get TMCC info*/
rc = mb86a20s_get_frontend(fe);
- if (rc < 0)
+ if (rc < 0) {
+ dev_err(&state->i2c->dev,
+ "%s: Can't get FE TMCC data.\n", __func__);
+ rc = 0; /* Status is OK */
+ goto error;
+ }
+
+ /* Get statistics */
+ rc = mb86a20s_get_stats(fe);
+ if (rc < 0 && rc != -EBUSY) {
+ dev_err(&state->i2c->dev,
+ "%s: Can't get FE statistics.\n", __func__);
+ rc = 0;
goto error;
+ }
+ rc = 0; /* Don't return EBUSY to userspace */
}
+ goto ok;
+error:
mb86a20s_stats_not_ready(fe);
+ok:
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
-error:
+
return rc;
}
--
1.7.11.7
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [REVIEW PATCHv12 5/6] [media] mb86a20s: improve bit error count for BER
2013-01-22 16:44 [REVIEW PATCHv12 1/6] [media] dvb: Add DVBv5 statistics properties Mauro Carvalho Chehab
` (2 preceding siblings ...)
2013-01-22 16:44 ` [REVIEW PATCHv12 4/6] [media] mb86a20s: add BER measurement Mauro Carvalho Chehab
@ 2013-01-22 16:44 ` Mauro Carvalho Chehab
2013-01-22 16:44 ` [REVIEW PATCHv12 6/6] [media] mb86a20s: add CNR measurement Mauro Carvalho Chehab
4 siblings, 0 replies; 6+ messages in thread
From: Mauro Carvalho Chehab @ 2013-01-22 16:44 UTC (permalink / raw)
Cc: Mauro Carvalho Chehab, Linux Media Mailing List
Do a better job on setting the bit error counters, in order to
have all layer measures to happen in a little less than one
second.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
v12: some improvements:
- Add a macro that defines the desired time for the bit count registers;
- Don't divide the counter by two anymore;
- avoid counter registers underflow/overflow.
drivers/media/dvb-frontends/mb86a20s.c | 161 ++++++++++++++++++++++++++++++++-
1 file changed, 158 insertions(+), 3 deletions(-)
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index 0017ecb..4440df6 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -27,9 +27,12 @@ MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
struct mb86a20s_state {
struct i2c_adapter *i2c;
const struct mb86a20s_config *config;
+ u32 last_frequency;
struct dvb_frontend frontend;
+ u32 estimated_rate[3];
+
bool need_init;
};
@@ -38,6 +41,8 @@ struct regdata {
u8 data;
};
+#define BER_SAMPLING_RATE 1 /* Seconds */
+
/*
* Initialization sequence: Use whatevere default values that PV SBTVD
* does on its initialisation, obtained via USB snoop
@@ -86,7 +91,7 @@ static struct regdata mb86a20s_init[] = {
* it collects the bit error count. The bit counters are initialized
* to 65535 here. This warrants that all of them will be quickly
* calculated when device gets locked. As TMCC is parsed, the values
- * can be adjusted later in the driver's code.
+ * will be adjusted later in the driver's code.
*/
{ 0x52, 0x01 }, /* Turn on BER before Viterbi */
{ 0x50, 0xa7 }, { 0x51, 0x00 },
@@ -484,6 +489,113 @@ static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe)
c->isdbt_sb_segment_count = 0;
}
+/*
+ * Estimates the bit rate using the per-segment bit rate given by
+ * ABNT/NBR 15601 spec (table 4).
+ */
+static u32 isdbt_rate[3][5][4] = {
+ { /* DQPSK/QPSK */
+ { 280850, 312060, 330420, 340430 }, /* 1/2 */
+ { 374470, 416080, 440560, 453910 }, /* 2/3 */
+ { 421280, 468090, 495630, 510650 }, /* 3/4 */
+ { 468090, 520100, 550700, 567390 }, /* 5/6 */
+ { 491500, 546110, 578230, 595760 }, /* 7/8 */
+ }, { /* QAM16 */
+ { 561710, 624130, 660840, 680870 }, /* 1/2 */
+ { 748950, 832170, 881120, 907820 }, /* 2/3 */
+ { 842570, 936190, 991260, 1021300 }, /* 3/4 */
+ { 936190, 1040210, 1101400, 1134780 }, /* 5/6 */
+ { 983000, 1092220, 1156470, 1191520 }, /* 7/8 */
+ }, { /* QAM64 */
+ { 842570, 936190, 991260, 1021300 }, /* 1/2 */
+ { 1123430, 1248260, 1321680, 1361740 }, /* 2/3 */
+ { 1263860, 1404290, 1486900, 1531950 }, /* 3/4 */
+ { 1404290, 1560320, 1652110, 1702170 }, /* 5/6 */
+ { 1474500, 1638340, 1734710, 1787280 }, /* 7/8 */
+ }
+};
+
+static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer,
+ u32 modulation, u32 fec, u32 interleaving,
+ u32 segment)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ u32 rate;
+ int m, f, i;
+
+ /*
+ * If modulation/fec/interleaving is not detected, the default is
+ * to consider the lowest bit rate, to avoid taking too long time
+ * to get BER.
+ */
+ switch (modulation) {
+ case DQPSK:
+ case QPSK:
+ default:
+ m = 0;
+ break;
+ case QAM_16:
+ m = 1;
+ break;
+ case QAM_64:
+ m = 2;
+ break;
+ }
+
+ switch (fec) {
+ default:
+ case FEC_1_2:
+ case FEC_AUTO:
+ f = 0;
+ break;
+ case FEC_2_3:
+ f = 1;
+ break;
+ case FEC_3_4:
+ f = 2;
+ break;
+ case FEC_5_6:
+ f = 3;
+ break;
+ case FEC_7_8:
+ f = 4;
+ break;
+ }
+
+ switch (interleaving) {
+ default:
+ case GUARD_INTERVAL_1_4:
+ i = 0;
+ break;
+ case GUARD_INTERVAL_1_8:
+ i = 1;
+ break;
+ case GUARD_INTERVAL_1_16:
+ i = 2;
+ break;
+ case GUARD_INTERVAL_1_32:
+ i = 3;
+ break;
+ }
+
+ /* Samples BER at BER_SAMPLING_RATE seconds */
+ rate = isdbt_rate[m][f][i] * segment * BER_SAMPLING_RATE;
+
+ /* Avoids sampling too quickly or to overflow the register */
+ if (rate < 256)
+ rate = 256;
+ else if (rate > (1 << 24) - 1)
+ rate = (1 << 24) - 1;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n",
+ __func__, 'A' + layer, segment * isdbt_rate[m][f][i]/1000,
+ rate, rate);
+
+ state->estimated_rate[i] = rate;
+}
+
+
static int mb86a20s_get_frontend(struct dvb_frontend *fe)
{
struct mb86a20s_state *state = fe->demodulator_priv;
@@ -513,10 +625,11 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe)
rc = mb86a20s_get_segment_count(state, i);
if (rc < 0)
goto noperlayer_error;
- if (rc >= 0 && rc < 14)
+ if (rc >= 0 && rc < 14) {
c->layer[i].segment_count = rc;
- else {
+ } else {
c->layer[i].segment_count = 0;
+ state->estimated_rate[i] = 0;
continue;
}
c->isdbt_layer_enabled |= 1 << i;
@@ -538,6 +651,10 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe)
dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n",
__func__, rc);
c->layer[i].interleaving = rc;
+ mb86a20s_layer_bitrate(fe, i, c->layer[i].modulation,
+ c->layer[i].fec,
+ c->layer[i].interleaving,
+ c->layer[i].segment_count);
}
rc = mb86a20s_writereg(state, 0x6d, 0x84);
@@ -729,6 +846,42 @@ static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe,
__func__, 'A' + layer, *count);
+ /*
+ * As we get TMCC data from the frontend, we can better estimate the
+ * BER bit counters, in order to do the BER measure during a longer
+ * time. Use those data, if available, to update the bit count
+ * measure.
+ */
+
+ if (state->estimated_rate[layer]
+ && state->estimated_rate[layer] != *count) {
+ dev_dbg(&state->i2c->dev,
+ "%s: updating layer %c counter to %d.\n",
+ __func__, 'A' + layer, state->estimated_rate[layer]);
+ rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51,
+ state->estimated_rate[layer] >> 16);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51,
+ state->estimated_rate[layer] >> 8);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51,
+ state->estimated_rate[layer]);
+ if (rc < 0)
+ return rc;
+ }
+
+
/* Reset counter to collect new data */
rc = mb86a20s_writereg(state, 0x53, 0x07 & ~(1 << layer));
if (rc < 0)
@@ -921,8 +1074,10 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe)
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
+
rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception);
mb86a20s_reset_counters(fe);
+
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
--
1.7.11.7
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [REVIEW PATCHv12 6/6] [media] mb86a20s: add CNR measurement
2013-01-22 16:44 [REVIEW PATCHv12 1/6] [media] dvb: Add DVBv5 statistics properties Mauro Carvalho Chehab
` (3 preceding siblings ...)
2013-01-22 16:44 ` [REVIEW PATCHv12 5/6] [media] mb86a20s: improve bit error count for BER Mauro Carvalho Chehab
@ 2013-01-22 16:44 ` Mauro Carvalho Chehab
4 siblings, 0 replies; 6+ messages in thread
From: Mauro Carvalho Chehab @ 2013-01-22 16:44 UTC (permalink / raw)
Cc: Mauro Carvalho Chehab, Linux Media Mailing List
Add Signal/Noise ratio measurement. On this device, a global measure
is taken by the demod. It also provides per-layer CNR measurements,
based on Modulation Error measures (MER).
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
v12: patch rebased and cleaned.
drivers/media/dvb-frontends/mb86a20s.c | 336 ++++++++++++++++++++++++++++++++-
1 file changed, 334 insertions(+), 2 deletions(-)
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index 4440df6..56a027c 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -118,10 +118,12 @@ static struct regdata mb86a20s_init[] = {
{ 0x50, 0xb5 }, { 0x51, 0xff },
{ 0x50, 0xb6 }, { 0x51, 0xff },
{ 0x50, 0xb7 }, { 0x51, 0xff },
- { 0x50, 0x50 }, { 0x51, 0x02 },
+
+ { 0x50, 0x50 }, { 0x51, 0x02 }, /* MER manual mode */
{ 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */
{ 0x45, 0x04 }, /* CN symbol 4 */
- { 0x48, 0x04 },
+ { 0x48, 0x04 }, /* CN manual mode */
+
{ 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */
{ 0x50, 0xd6 }, { 0x51, 0x1f },
{ 0x50, 0xd2 }, { 0x51, 0x03 },
@@ -891,6 +893,330 @@ static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe,
return 0;
}
+struct linear_segments {
+ unsigned x, y;
+};
+
+/*
+ * All tables below return a dB/1000 measurement
+ */
+
+static struct linear_segments cnr_to_db_table[] = {
+ { 19648, 0},
+ { 18187, 1000},
+ { 16534, 2000},
+ { 14823, 3000},
+ { 13161, 4000},
+ { 11622, 5000},
+ { 10279, 6000},
+ { 9089, 7000},
+ { 8042, 8000},
+ { 7137, 9000},
+ { 6342, 10000},
+ { 5641, 11000},
+ { 5030, 12000},
+ { 4474, 13000},
+ { 3988, 14000},
+ { 3556, 15000},
+ { 3180, 16000},
+ { 2841, 17000},
+ { 2541, 18000},
+ { 2276, 19000},
+ { 2038, 20000},
+ { 1800, 21000},
+ { 1625, 22000},
+ { 1462, 23000},
+ { 1324, 24000},
+ { 1175, 25000},
+ { 1063, 26000},
+ { 980, 27000},
+ { 907, 28000},
+ { 840, 29000},
+ { 788, 30000},
+};
+
+static struct linear_segments cnr_64qam_table[] = {
+ { 3922688, 0},
+ { 3920384, 1000},
+ { 3902720, 2000},
+ { 3894784, 3000},
+ { 3882496, 4000},
+ { 3872768, 5000},
+ { 3858944, 6000},
+ { 3851520, 7000},
+ { 3838976, 8000},
+ { 3829248, 9000},
+ { 3818240, 10000},
+ { 3806976, 11000},
+ { 3791872, 12000},
+ { 3767040, 13000},
+ { 3720960, 14000},
+ { 3637504, 15000},
+ { 3498496, 16000},
+ { 3296000, 17000},
+ { 3031040, 18000},
+ { 2715392, 19000},
+ { 2362624, 20000},
+ { 1963264, 21000},
+ { 1649664, 22000},
+ { 1366784, 23000},
+ { 1120768, 24000},
+ { 890880, 25000},
+ { 723456, 26000},
+ { 612096, 27000},
+ { 518912, 28000},
+ { 448256, 29000},
+ { 388864, 30000},
+};
+
+static struct linear_segments cnr_16qam_table[] = {
+ { 5314816, 0},
+ { 5219072, 1000},
+ { 5118720, 2000},
+ { 4998912, 3000},
+ { 4875520, 4000},
+ { 4736000, 5000},
+ { 4604160, 6000},
+ { 4458752, 7000},
+ { 4300288, 8000},
+ { 4092928, 9000},
+ { 3836160, 10000},
+ { 3521024, 11000},
+ { 3155968, 12000},
+ { 2756864, 13000},
+ { 2347008, 14000},
+ { 1955072, 15000},
+ { 1593600, 16000},
+ { 1297920, 17000},
+ { 1043968, 18000},
+ { 839680, 19000},
+ { 672256, 20000},
+ { 523008, 21000},
+ { 424704, 22000},
+ { 345088, 23000},
+ { 280064, 24000},
+ { 221440, 25000},
+ { 179712, 26000},
+ { 151040, 27000},
+ { 128512, 28000},
+ { 110080, 29000},
+ { 95744, 30000},
+};
+
+struct linear_segments cnr_qpsk_table[] = {
+ { 2834176, 0},
+ { 2683648, 1000},
+ { 2536960, 2000},
+ { 2391808, 3000},
+ { 2133248, 4000},
+ { 1906176, 5000},
+ { 1666560, 6000},
+ { 1422080, 7000},
+ { 1189632, 8000},
+ { 976384, 9000},
+ { 790272, 10000},
+ { 633344, 11000},
+ { 505600, 12000},
+ { 402944, 13000},
+ { 320768, 14000},
+ { 255488, 15000},
+ { 204032, 16000},
+ { 163072, 17000},
+ { 130304, 18000},
+ { 105216, 19000},
+ { 83456, 20000},
+ { 65024, 21000},
+ { 52480, 22000},
+ { 42752, 23000},
+ { 34560, 24000},
+ { 27136, 25000},
+ { 22016, 26000},
+ { 18432, 27000},
+ { 15616, 28000},
+ { 13312, 29000},
+ { 11520, 30000},
+};
+
+static u32 interpolate_value(u32 value, struct linear_segments *segments,
+ unsigned len)
+{
+ u64 tmp64;
+ u32 dx, dy;
+ int i, ret;
+
+ if (value >= segments[0].x)
+ return segments[0].y;
+ if (value < segments[len-1].x)
+ return segments[len-1].y;
+
+ for (i = 1; i < len - 1; i++) {
+ /* If value is identical, no need to interpolate */
+ if (value == segments[i].x)
+ return segments[i].y;
+ if (value > segments[i].x)
+ break;
+ }
+
+ /* Linear interpolation between the two (x,y) points */
+ dy = segments[i].y - segments[i - 1].y;
+ dx = segments[i - 1].x - segments[i].x;
+ tmp64 = value - segments[i].x;
+ tmp64 *= dy;
+ do_div(tmp64, dx);
+ ret = segments[i].y - tmp64;
+
+ return ret;
+}
+
+static int mb86a20s_get_main_CNR(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 cnr_linear, cnr;
+ int rc, val;
+
+ /* Check if CNR is available */
+ rc = mb86a20s_readreg(state, 0x45);
+ if (rc < 0)
+ return rc;
+
+ if (!(rc & 0x40)) {
+ dev_info(&state->i2c->dev, "%s: CNR is not available yet.\n",
+ __func__);
+ return -EBUSY;
+ }
+ val = rc;
+
+ rc = mb86a20s_readreg(state, 0x46);
+ if (rc < 0)
+ return rc;
+ cnr_linear = rc << 8;
+
+ rc = mb86a20s_readreg(state, 0x46);
+ if (rc < 0)
+ return rc;
+ cnr_linear |= rc;
+
+ cnr = interpolate_value(cnr_linear,
+ cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table));
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = cnr;
+
+ dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n",
+ __func__, cnr / 1000, cnr % 1000, cnr_linear);
+
+ /* CNR counter reset */
+ rc = mb86a20s_writereg(state, 0x45, val | 0x10);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
+
+ return rc;
+}
+
+static int mb86a20s_get_per_layer_CNR(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 mer, cnr;
+ int rc, val, i;
+ struct linear_segments *segs;
+ unsigned segs_len;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ /* Check if the measures are already available */
+ rc = mb86a20s_writereg(state, 0x50, 0x5b);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+
+ /* Check if data is available */
+ if (!(rc & 0x01)) {
+ dev_info(&state->i2c->dev,
+ "%s: MER measures aren't available yet.\n", __func__);
+ return -EBUSY;
+ }
+
+ /* Read all layers */
+ for (i = 0; i < 3; i++) {
+ if (!(c->isdbt_layer_enabled & (1 << i))) {
+ c->cnr.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
+ continue;
+ }
+
+ rc = mb86a20s_writereg(state, 0x50, 0x52 + i * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ mer = rc << 16;
+ rc = mb86a20s_writereg(state, 0x50, 0x53 + i * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ mer |= rc << 8;
+ rc = mb86a20s_writereg(state, 0x50, 0x54 + i * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ mer |= rc;
+
+ switch (c->layer[i].modulation) {
+ case DQPSK:
+ case QPSK:
+ segs = cnr_qpsk_table;
+ segs_len = ARRAY_SIZE(cnr_qpsk_table);
+ break;
+ case QAM_16:
+ segs = cnr_16qam_table;
+ segs_len = ARRAY_SIZE(cnr_16qam_table);
+ break;
+ default:
+ case QAM_64:
+ segs = cnr_64qam_table;
+ segs_len = ARRAY_SIZE(cnr_64qam_table);
+ break;
+ }
+ cnr = interpolate_value(mer, segs, segs_len);
+
+ c->cnr.stat[1 + i].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[1 + i].svalue = cnr;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: CNR for layer %c is %d.%03d dB (MER = %d).\n",
+ __func__, 'A' + i, cnr / 1000, cnr % 1000, mer);
+
+ }
+
+ /* Start a new MER measurement */
+ /* MER counter reset */
+ rc = mb86a20s_writereg(state, 0x50, 0x50);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ val = rc;
+
+ rc = mb86a20s_writereg(state, 0x51, val | 0x01);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, val & 0x06);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
{
struct mb86a20s_state *state = fe->demodulator_priv;
@@ -934,7 +1260,13 @@ static int mb86a20s_get_stats(struct dvb_frontend *fe)
u32 t_bit_error = 0, t_bit_count = 0;
int active_layers = 0, ber_layers = 0;
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ mb86a20s_get_main_CNR(fe);
+
/* Get per-layer stats */
+ mb86a20s_get_per_layer_CNR(fe);
+
for (i = 0; i < 3; i++) {
if (c->isdbt_layer_enabled & (1 << i)) {
/* Layer is active and has rc segments */
--
1.7.11.7
^ permalink raw reply related [flat|nested] 6+ messages in thread