[media] cxd2841er: fix BER report via DVBv5 stats API

What userspace expects is to receive both bit_error and bit_count
counters. So, instead of doing the division at the Kernel,
return the counters for userspace to handle it the way it
wants.

Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
Mauro Carvalho Chehab 2016-07-01 11:03:15 -03:00
parent eca2d34b9d
commit 4216be14be

View File

@ -1330,11 +1330,10 @@ static int cxd2841er_read_packet_errors_i(
return 0;
}
static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv, u32 *ber)
static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv,
u32 *bit_error, u32 *bit_count)
{
u8 data[11];
u32 bit_error, bit_count;
u32 temp_q, temp_r;
/* Set SLV-T Bank : 0xA0 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
@ -1350,28 +1349,18 @@ static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv, u32 *ber)
*/
cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11);
if (data[0] & 0x01) {
bit_error = ((u32)(data[1] & 0x3F) << 16) |
((u32)(data[2] & 0xFF) << 8) |
(u32)(data[3] & 0xFF);
bit_count = ((u32)(data[8] & 0x3F) << 16) |
((u32)(data[9] & 0xFF) << 8) |
(u32)(data[10] & 0xFF);
/*
* BER = bitError / bitCount
* = (bitError * 10^7) / bitCount
* = ((bitError * 625 * 125 * 128) / bitCount
*/
if ((bit_count == 0) || (bit_error > bit_count)) {
*bit_error = ((u32)(data[1] & 0x3F) << 16) |
((u32)(data[2] & 0xFF) << 8) |
(u32)(data[3] & 0xFF);
*bit_count = ((u32)(data[8] & 0x3F) << 16) |
((u32)(data[9] & 0xFF) << 8) |
(u32)(data[10] & 0xFF);
if ((*bit_count == 0) || (*bit_error > *bit_count)) {
dev_dbg(&priv->i2c->dev,
"%s(): invalid bit_error %d, bit_count %d\n",
__func__, bit_error, bit_count);
__func__, *bit_error, *bit_count);
return -EINVAL;
}
temp_q = div_u64_rem(10000000ULL * bit_error,
bit_count, &temp_r);
if (bit_count != 1 && temp_r >= bit_count / 2)
temp_q++;
*ber = temp_q;
return 0;
}
dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__);
@ -1379,11 +1368,11 @@ static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv, u32 *ber)
}
static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber)
static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv,
u32 *bit_error, u32 *bit_count)
{
u8 data[5];
u32 bit_error, period;
u32 temp_q, temp_r;
u32 period;
/* Set SLV-T Bank : 0xB2 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2);
@ -1398,10 +1387,10 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber)
cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5);
if (data[0] & 0x01) {
/* Bit error count */
bit_error = ((u32)(data[1] & 0x0F) << 24) |
((u32)(data[2] & 0xFF) << 16) |
((u32)(data[3] & 0xFF) << 8) |
(u32)(data[4] & 0xFF);
*bit_error = ((u32)(data[1] & 0x0F) << 24) |
((u32)(data[2] & 0xFF) << 16) |
((u32)(data[3] & 0xFF) << 8) |
(u32)(data[4] & 0xFF);
/* Set SLV-T Bank : 0xA0 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
@ -1413,24 +1402,14 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber)
"%s(): period is 0\n", __func__);
return -EINVAL;
}
if (bit_error > (period * 64800)) {
if (*bit_error > (period * 64800)) {
dev_dbg(&priv->i2c->dev,
"%s(): invalid bit_err 0x%x period 0x%x\n",
__func__, bit_error, period);
__func__, *bit_error, period);
return -EINVAL;
}
/*
* BER = bitError / (period * 64800)
* = (bitError * 10^7) / (period * 64800)
* = (bitError * 10^5) / (period * 648)
* = (bitError * 12500) / (period * 81)
* = (bitError * 10) * 1250 / (period * 81)
*/
temp_q = div_u64_rem(12500ULL * bit_error,
period * 81, &temp_r);
if (temp_r >= period * 40)
temp_q++;
*ber = temp_q;
*bit_count = period * 64800;
return 0;
} else {
dev_dbg(&priv->i2c->dev,
@ -1439,13 +1418,12 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber)
return -EINVAL;
}
static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber)
static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv,
u32 *bit_error, u32 *bit_count)
{
u8 data[4];
u32 div, q, r;
u32 bit_err, period_exp, n_ldpc;
u32 period_exp, n_ldpc;
*ber = 0;
if (priv->state != STATE_ACTIVE_TC) {
dev_dbg(&priv->i2c->dev,
"%s(): invalid state %d\n", __func__, priv->state);
@ -1456,40 +1434,44 @@ static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber)
if (!(data[0] & 0x10)) {
dev_dbg(&priv->i2c->dev,
"%s(): no valid BER data\n", __func__);
return 0;
return -EINVAL;
}
bit_err = ((u32)(data[0] & 0x0f) << 24) |
((u32)data[1] << 16) |
((u32)data[2] << 8) |
(u32)data[3];
*bit_error = ((u32)(data[0] & 0x0f) << 24) |
((u32)data[1] << 16) |
((u32)data[2] << 8) |
(u32)data[3];
cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
period_exp = data[0] & 0x0f;
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22);
cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data);
n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800);
if (bit_err > ((1U << period_exp) * n_ldpc)) {
if (*bit_error > ((1U << period_exp) * n_ldpc)) {
dev_dbg(&priv->i2c->dev,
"%s(): invalid BER value\n", __func__);
return -EINVAL;
}
/*
* FIXME: the right thing would be to return bit_error untouched,
* but, as we don't know the scale returned by the counters, let's
* at least preserver BER = bit_error/bit_count.
*/
if (period_exp >= 4) {
div = (1U << (period_exp - 4)) * (n_ldpc / 200);
q = div_u64_rem(3125ULL * bit_err, div, &r);
*bit_count = (1U << (period_exp - 4)) * (n_ldpc / 200);
*bit_error *= 3125ULL;
} else {
div = (1U << period_exp) * (n_ldpc / 200);
q = div_u64_rem(50000ULL * bit_err, div, &r);
*bit_count = (1U << period_exp) * (n_ldpc / 200);
*bit_error *= 50000ULL;;
}
*ber = (r >= div / 2) ? q + 1 : q;
return 0;
}
static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber)
static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv,
u32 *bit_error, u32 *bit_count)
{
u8 data[2];
u32 div, q, r;
u32 bit_err, period;
u32 period;
*ber = 0;
if (priv->state != STATE_ACTIVE_TC) {
dev_dbg(&priv->i2c->dev,
"%s(): invalid state %d\n", __func__, priv->state);
@ -1503,12 +1485,17 @@ static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber)
return 0;
}
cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data));
bit_err = ((u32)data[0] << 8) | (u32)data[1];
*bit_error = ((u32)data[0] << 8) | (u32)data[1];
cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07));
div = period / 128;
q = div_u64_rem(78125ULL * bit_err, div, &r);
*ber = (r >= div / 2) ? q + 1 : q;
/*
* FIXME: the right thing would be to return bit_error untouched,
* but, as we don't know the scale returned by the counters, let's
* at least preserver BER = bit_error/bit_count.
*/
*bit_count = period / 128;
*bit_error *= 78125ULL;
return 0;
}
@ -1722,32 +1709,36 @@ static void cxd2841er_read_ber(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct cxd2841er_priv *priv = fe->demodulator_priv;
u32 ber = 0, ret;
u32 ret, bit_error = 0, bit_count = 0;
dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
switch (p->delivery_system) {
case SYS_DVBS:
ret = cxd2841er_mon_read_ber_s(priv, &ber);
ret = cxd2841er_mon_read_ber_s(priv, &bit_error, &bit_count);
break;
case SYS_DVBS2:
ret = cxd2841er_mon_read_ber_s2(priv, &ber);
ret = cxd2841er_mon_read_ber_s2(priv, &bit_error, &bit_count);
break;
case SYS_DVBT:
ret = cxd2841er_read_ber_t(priv, &ber);
ret = cxd2841er_read_ber_t(priv, &bit_error, &bit_count);
break;
case SYS_DVBT2:
ret = cxd2841er_read_ber_t2(priv, &ber);
ret = cxd2841er_read_ber_t2(priv, &bit_error, &bit_count);
break;
default:
p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
return;
}
if (!ret) {
p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
p->post_bit_error.stat[0].uvalue = ber;
p->post_bit_error.stat[0].uvalue = bit_error;
p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
p->post_bit_count.stat[0].uvalue = bit_count;
} else {
p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
}
@ -2988,6 +2979,7 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe,
p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
return 0;
}
@ -3052,6 +3044,7 @@ done:
p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
return ret;
}
@ -3426,6 +3419,8 @@ static void cxd2841er_init_stats(struct dvb_frontend *fe)
p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_error.len = 1;
p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_count.len = 1;
p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}