mirror of
https://github.com/torvalds/linux.git
synced 2024-12-15 15:41:58 +00:00
[media] mb86a20s: add CNR measurement
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). Reviewed-by: Antti Palosaari <crope@iki.fi> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
d01a8ee37a
commit
25188bd0e6
@ -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_pre_bit_error = 0, t_pre_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 */
|
||||
|
Loading…
Reference in New Issue
Block a user