Commit 4216be14 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

[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: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent eca2d34b
...@@ -1330,11 +1330,10 @@ static int cxd2841er_read_packet_errors_i( ...@@ -1330,11 +1330,10 @@ static int cxd2841er_read_packet_errors_i(
return 0; 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]; u8 data[11];
u32 bit_error, bit_count;
u32 temp_q, temp_r;
/* Set SLV-T Bank : 0xA0 */ /* Set SLV-T Bank : 0xA0 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 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) ...@@ -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); cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11);
if (data[0] & 0x01) { if (data[0] & 0x01) {
bit_error = ((u32)(data[1] & 0x3F) << 16) | *bit_error = ((u32)(data[1] & 0x3F) << 16) |
((u32)(data[2] & 0xFF) << 8) | ((u32)(data[2] & 0xFF) << 8) |
(u32)(data[3] & 0xFF); (u32)(data[3] & 0xFF);
bit_count = ((u32)(data[8] & 0x3F) << 16) | *bit_count = ((u32)(data[8] & 0x3F) << 16) |
((u32)(data[9] & 0xFF) << 8) | ((u32)(data[9] & 0xFF) << 8) |
(u32)(data[10] & 0xFF); (u32)(data[10] & 0xFF);
/* if ((*bit_count == 0) || (*bit_error > *bit_count)) {
* BER = bitError / bitCount
* = (bitError * 10^7) / bitCount
* = ((bitError * 625 * 125 * 128) / bitCount
*/
if ((bit_count == 0) || (bit_error > bit_count)) {
dev_dbg(&priv->i2c->dev, dev_dbg(&priv->i2c->dev,
"%s(): invalid bit_error %d, bit_count %d\n", "%s(): invalid bit_error %d, bit_count %d\n",
__func__, bit_error, bit_count); __func__, *bit_error, *bit_count);
return -EINVAL; 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; return 0;
} }
dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__); 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) ...@@ -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]; u8 data[5];
u32 bit_error, period; u32 period;
u32 temp_q, temp_r;
/* Set SLV-T Bank : 0xB2 */ /* Set SLV-T Bank : 0xB2 */
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2);
...@@ -1398,7 +1387,7 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber) ...@@ -1398,7 +1387,7 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber)
cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5); cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5);
if (data[0] & 0x01) { if (data[0] & 0x01) {
/* Bit error count */ /* Bit error count */
bit_error = ((u32)(data[1] & 0x0F) << 24) | *bit_error = ((u32)(data[1] & 0x0F) << 24) |
((u32)(data[2] & 0xFF) << 16) | ((u32)(data[2] & 0xFF) << 16) |
((u32)(data[3] & 0xFF) << 8) | ((u32)(data[3] & 0xFF) << 8) |
(u32)(data[4] & 0xFF); (u32)(data[4] & 0xFF);
...@@ -1413,24 +1402,14 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber) ...@@ -1413,24 +1402,14 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber)
"%s(): period is 0\n", __func__); "%s(): period is 0\n", __func__);
return -EINVAL; return -EINVAL;
} }
if (bit_error > (period * 64800)) { if (*bit_error > (period * 64800)) {
dev_dbg(&priv->i2c->dev, dev_dbg(&priv->i2c->dev,
"%s(): invalid bit_err 0x%x period 0x%x\n", "%s(): invalid bit_err 0x%x period 0x%x\n",
__func__, bit_error, period); __func__, *bit_error, period);
return -EINVAL; return -EINVAL;
} }
/* *bit_count = period * 64800;
* 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;
return 0; return 0;
} else { } else {
dev_dbg(&priv->i2c->dev, dev_dbg(&priv->i2c->dev,
...@@ -1439,13 +1418,12 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber) ...@@ -1439,13 +1418,12 @@ static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv, u32 *ber)
return -EINVAL; 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]; u8 data[4];
u32 div, q, r; u32 period_exp, n_ldpc;
u32 bit_err, period_exp, n_ldpc;
*ber = 0;
if (priv->state != STATE_ACTIVE_TC) { if (priv->state != STATE_ACTIVE_TC) {
dev_dbg(&priv->i2c->dev, dev_dbg(&priv->i2c->dev,
"%s(): invalid state %d\n", __func__, priv->state); "%s(): invalid state %d\n", __func__, priv->state);
...@@ -1456,9 +1434,9 @@ static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber) ...@@ -1456,9 +1434,9 @@ static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber)
if (!(data[0] & 0x10)) { if (!(data[0] & 0x10)) {
dev_dbg(&priv->i2c->dev, dev_dbg(&priv->i2c->dev,
"%s(): no valid BER data\n", __func__); "%s(): no valid BER data\n", __func__);
return 0; return -EINVAL;
} }
bit_err = ((u32)(data[0] & 0x0f) << 24) | *bit_error = ((u32)(data[0] & 0x0f) << 24) |
((u32)data[1] << 16) | ((u32)data[1] << 16) |
((u32)data[2] << 8) | ((u32)data[2] << 8) |
(u32)data[3]; (u32)data[3];
...@@ -1467,29 +1445,33 @@ static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber) ...@@ -1467,29 +1445,33 @@ static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber)
cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22); cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22);
cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data); cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data);
n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800); 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, dev_dbg(&priv->i2c->dev,
"%s(): invalid BER value\n", __func__); "%s(): invalid BER value\n", __func__);
return -EINVAL; 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) { if (period_exp >= 4) {
div = (1U << (period_exp - 4)) * (n_ldpc / 200); *bit_count = (1U << (period_exp - 4)) * (n_ldpc / 200);
q = div_u64_rem(3125ULL * bit_err, div, &r); *bit_error *= 3125ULL;
} else { } else {
div = (1U << period_exp) * (n_ldpc / 200); *bit_count = (1U << period_exp) * (n_ldpc / 200);
q = div_u64_rem(50000ULL * bit_err, div, &r); *bit_error *= 50000ULL;;
} }
*ber = (r >= div / 2) ? q + 1 : q;
return 0; 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]; u8 data[2];
u32 div, q, r; u32 period;
u32 bit_err, period;
*ber = 0;
if (priv->state != STATE_ACTIVE_TC) { if (priv->state != STATE_ACTIVE_TC) {
dev_dbg(&priv->i2c->dev, dev_dbg(&priv->i2c->dev,
"%s(): invalid state %d\n", __func__, priv->state); "%s(): invalid state %d\n", __func__, priv->state);
...@@ -1503,12 +1485,17 @@ static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber) ...@@ -1503,12 +1485,17 @@ static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber)
return 0; return 0;
} }
cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data)); 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); cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07)); 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; return 0;
} }
...@@ -1722,32 +1709,36 @@ static void cxd2841er_read_ber(struct dvb_frontend *fe) ...@@ -1722,32 +1709,36 @@ static void cxd2841er_read_ber(struct dvb_frontend *fe)
{ {
struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct cxd2841er_priv *priv = fe->demodulator_priv; 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__); dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
switch (p->delivery_system) { switch (p->delivery_system) {
case SYS_DVBS: case SYS_DVBS:
ret = cxd2841er_mon_read_ber_s(priv, &ber); ret = cxd2841er_mon_read_ber_s(priv, &bit_error, &bit_count);
break; break;
case SYS_DVBS2: case SYS_DVBS2:
ret = cxd2841er_mon_read_ber_s2(priv, &ber); ret = cxd2841er_mon_read_ber_s2(priv, &bit_error, &bit_count);
break; break;
case SYS_DVBT: case SYS_DVBT:
ret = cxd2841er_read_ber_t(priv, &ber); ret = cxd2841er_read_ber_t(priv, &bit_error, &bit_count);
break; break;
case SYS_DVBT2: case SYS_DVBT2:
ret = cxd2841er_read_ber_t2(priv, &ber); ret = cxd2841er_read_ber_t2(priv, &bit_error, &bit_count);
break; break;
default: default:
p->post_bit_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; return;
} }
if (!ret) { if (!ret) {
p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; 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 { } else {
p->post_bit_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;
} }
} }
...@@ -2988,6 +2979,7 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe, ...@@ -2988,6 +2979,7 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe,
p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->block_error.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_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
} }
return 0; return 0;
} }
...@@ -3052,6 +3044,7 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) ...@@ -3052,6 +3044,7 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->block_error.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_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
return ret; return ret;
} }
...@@ -3426,6 +3419,8 @@ static void cxd2841er_init_stats(struct dvb_frontend *fe) ...@@ -3426,6 +3419,8 @@ static void cxd2841er_init_stats(struct dvb_frontend *fe)
p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_error.len = 1; p->post_bit_error.len = 1;
p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 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;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment