Commit 69ace6ee authored by Antti Palosaari's avatar Antti Palosaari Committed by Mauro Carvalho Chehab

[media] mn88473: refactor and fix statistics

Remove DVB-T2 BER as it does not work at all and I didn't find
how to fix.

Fix DVB-T and DVB-C BER. It seems to return new some realistic
looking values.

Use (1 << 24) base for CNR calculations to keep it in line with
dvb logarithm functions.

Move all statistic logic to mn88473_read_status() function.

Use regmap_bulk_read() for reading multiple registers as a one go.

Many more and less minor changes.

Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: default avatarAntti Palosaari <crope@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 61393b07
...@@ -234,465 +234,225 @@ static int mn88473_set_frontend(struct dvb_frontend *fe) ...@@ -234,465 +234,225 @@ static int mn88473_set_frontend(struct dvb_frontend *fe)
return ret; return ret;
} }
static int mn88473_update_ber_stat_t_c(struct dvb_frontend *fe, static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status)
enum fe_status *status)
{
struct i2c_client *client = fe->demodulator_priv;
struct mn88473_dev *dev = i2c_get_clientdata(client);
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u64 total;
unsigned int uitmp, value, errors;
if (*status & FE_HAS_LOCK) {
ret = regmap_read(dev->regmap[0], 0x5b, &value);
if (ret)
goto err;
ret = regmap_read(dev->regmap[0], 0xdf, &uitmp);
if (ret)
goto err;
value &= uitmp;
ret = regmap_write(dev->regmap[0], 0x5b, value);
if (ret)
goto err;
ret = regmap_read(dev->regmap[0], 0x60, &value);
if (ret)
goto err;
value &= 0xf0;
value |= 0x5;
ret = regmap_write(dev->regmap[0], 0x60, value);
if (ret)
goto err;
ret = regmap_read(dev->regmap[0], 0x92, &uitmp);
if (ret)
goto err;
errors = uitmp << 16;
ret = regmap_read(dev->regmap[0], 0x93, &uitmp);
if (ret)
goto err;
errors |= uitmp << 8;
ret = regmap_read(dev->regmap[0], 0x94, &uitmp);
if (ret)
goto err;
errors |= uitmp;
ret = regmap_read(dev->regmap[0], 0x95, &uitmp);
if (ret)
goto err;
total = uitmp << 8;
ret = regmap_read(dev->regmap[0], 0x96, &uitmp);
if (ret)
goto err;
total |= uitmp;
/* probably: (bytes -> bit) * (sizeof(TS packet) - 1) */
total *= 8 * 203;
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
c->post_bit_error.stat[0].uvalue += errors;
c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
c->post_bit_count.stat[0].uvalue += total;
} else {
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
return 0;
err:
dev_dbg(&client->dev, "%s failed=%d\n", __func__, ret);
return ret;
}
static int mn88473_update_ber_stat_t2(struct dvb_frontend *fe,
enum fe_status *status)
{ {
struct i2c_client *client = fe->demodulator_priv; struct i2c_client *client = fe->demodulator_priv;
struct mn88473_dev *dev = i2c_get_clientdata(client); struct mn88473_dev *dev = i2c_get_clientdata(client);
struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret; int ret, i, stmp;
u64 total; unsigned int utmp, utmp1, utmp2;
unsigned int uitmp, value, berlen, fec_type_m, errors; u8 buf[5];
static u16 fec_type_m_tbl0[] = {
32400, 38880, 43200, 48600, 51840, 54000, 0
};
static u16 fec_type_m_tbl1[] = {
28800, 38880, 43200, 47520, 50400, 53280, 0
};
if (*status & FE_HAS_LOCK) { if (!dev->active) {
ret = regmap_read(dev->regmap[2], 0x82, &value); ret = -EAGAIN;
if (ret)
goto err;
value |= 0x20;
value &= 0xef;
ret = regmap_write(dev->regmap[2], 0x82, value);
if (ret)
goto err;
ret = regmap_read(dev->regmap[2], 0xba, &uitmp);
if (ret)
goto err;
errors = uitmp << 16;
ret = regmap_read(dev->regmap[2], 0xbb, &uitmp);
if (ret)
goto err;
errors |= uitmp << 8;
ret = regmap_read(dev->regmap[2], 0xbc, &uitmp);
if (ret)
goto err;
errors |= uitmp;
ret = regmap_read(dev->regmap[2], 0x83, &berlen);
if (ret)
goto err;
ret = regmap_write(dev->regmap[2], 0xc0, 0x3);
if (ret)
goto err; goto err;
/* berlen[4:2] are the index in fec_type_m_tbl */
uitmp = (berlen >> 2) & 0x7;
if (BIT(0) & berlen)
fec_type_m = fec_type_m_tbl0[uitmp];
else
fec_type_m = fec_type_m_tbl1[uitmp];
total = ((berlen & 0xff) << 1) * fec_type_m;
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
c->post_bit_error.stat[0].uvalue += errors;
c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
c->post_bit_count.stat[0].uvalue += total;
} else {
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
} }
return 0; /* Lock detection */
switch (c->delivery_system) {
err: case SYS_DVBT:
dev_dbg(&client->dev, "%s failed=%d\n", __func__, ret); ret = regmap_read(dev->regmap[0], 0x62, &utmp);
return ret;
}
static inline u32 log10times1000(u32 value)
{
return (1000L * intlog10(value)) >> 24;
}
static int mn88473_read_status_t(struct dvb_frontend *fe,
enum fe_status *status)
{
struct i2c_client *client = fe->demodulator_priv;
struct mn88473_dev *dev = i2c_get_clientdata(client);
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
s32 cnr;
unsigned int uitmp, tmp_upper, tmp_lower;
ret = regmap_read(dev->regmap[0], 0x62, &uitmp);
if (ret) if (ret)
goto err; goto err;
if (!(uitmp & 0xa0)) { if (!(utmp & 0xa0)) {
if ((uitmp & 0x0f) >= 0x09) if ((utmp & 0x0f) >= 0x09)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK; FE_HAS_LOCK;
else if ((uitmp & 0x0f) >= 0x03) else if ((utmp & 0x0f) >= 0x03)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER; *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
}
/* CNR */
if (*status & FE_HAS_VITERBI) {
ret = regmap_read(dev->regmap[0], 0x8f, &tmp_upper);
if (ret)
goto err;
ret = regmap_read(dev->regmap[0], 0x90, &tmp_lower);
if (ret)
goto err;
uitmp = (tmp_upper << 8) | tmp_lower;
if (uitmp) {
cnr = log10times1000(65536);
cnr -= log10times1000(uitmp);
cnr += 200;
} else
cnr = 0;
if (cnr < 0)
cnr = 0;
c->cnr.stat[0].svalue = cnr * 10;
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
} else { } else {
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; *status = 0;
} }
break;
/* BER */ case SYS_DVBT2:
ret = mn88473_update_ber_stat_t_c(fe, status); ret = regmap_read(dev->regmap[2], 0x8b, &utmp);
if (ret)
goto err;
return 0;
err:
dev_dbg(&client->dev, "%s failed=%d\n", __func__, ret);
return ret;
}
static int mn88473_read_status_t2(struct dvb_frontend *fe,
enum fe_status *status)
{
struct i2c_client *client = fe->demodulator_priv;
struct mn88473_dev *dev = i2c_get_clientdata(client);
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
s32 cnr;
unsigned int uitmp, tmp_upper, tmp_lower, flag;
ret = regmap_read(dev->regmap[2], 0x8b, &uitmp);
if (ret) if (ret)
goto err; goto err;
if (!(uitmp & 0x40)) { if (!(utmp & 0x40)) {
if ((uitmp & 0x0f) >= 0x0d) if ((utmp & 0x0f) >= 0x0d)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK; FE_HAS_LOCK;
else if ((uitmp & 0x0f) >= 0x0a) else if ((utmp & 0x0f) >= 0x0a)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI; FE_HAS_VITERBI;
else if ((uitmp & 0x0f) >= 0x07) else if ((utmp & 0x0f) >= 0x07)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER; *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
}
/* CNR */
if (*status & FE_HAS_VITERBI) {
ret = regmap_read(dev->regmap[2], 0xb7, &flag);
if (ret)
goto err;
ret = regmap_read(dev->regmap[2], 0xb8, &tmp_upper);
if (ret)
goto err;
ret = regmap_read(dev->regmap[2], 0xb9, &tmp_lower);
if (ret)
goto err;
uitmp = (tmp_upper << 8) | tmp_lower;
if (uitmp) {
if (flag & BIT(2)) {
/* MISO */
cnr = log10times1000(16384);
cnr -= log10times1000(uitmp);
cnr -= 600;
} else { } else {
/* SISO */ *status = 0;
cnr = log10times1000(65536);
cnr -= log10times1000(uitmp);
cnr += 200;
}
} else
cnr = 0;
if (cnr < 0)
cnr = 0;
c->cnr.stat[0].svalue = cnr * 10;
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
} else {
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
} }
break;
/* BER */ case SYS_DVBC_ANNEX_A:
ret = mn88473_update_ber_stat_t2(fe, status); ret = regmap_read(dev->regmap[1], 0x85, &utmp);
if (ret)
goto err;
return 0;
err:
dev_dbg(&client->dev, "%s failed=%d\n", __func__, ret);
return ret;
}
static int mn88473_read_status_c(struct dvb_frontend *fe,
enum fe_status *status)
{
struct i2c_client *client = fe->demodulator_priv;
struct mn88473_dev *dev = i2c_get_clientdata(client);
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
unsigned int uitmp, tmp_upper, tmp_lower, signal, noise;
ret = regmap_read(dev->regmap[1], 0x85, &uitmp);
if (ret) if (ret)
goto err; goto err;
if (!(uitmp & 0x40)) { if (!(utmp & 0x40)) {
ret = regmap_read(dev->regmap[1], 0x89, &uitmp); ret = regmap_read(dev->regmap[1], 0x89, &utmp);
if (ret) if (ret)
goto err; goto err;
if (uitmp & 0x01) if (utmp & 0x01)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK; FE_HAS_LOCK;
} else {
*status = 0;
} }
break;
/* CNR */ default:
if (*status & FE_HAS_VITERBI) { ret = -EINVAL;
ret = regmap_read(dev->regmap[1], 0xa1, &tmp_upper);
if (ret)
goto err; goto err;
}
ret = regmap_read(dev->regmap[1], 0xa2, &tmp_lower); /* Signal strength */
if (*status & FE_HAS_SIGNAL) {
for (i = 0; i < 2; i++) {
ret = regmap_bulk_read(dev->regmap[2], 0x86 + i,
&buf[i], 1);
if (ret) if (ret)
goto err; goto err;
}
signal = (tmp_upper << 8) | tmp_lower; /* AGCRD[15:6] gives us a 10bit value ([5:0] are always 0) */
utmp1 = buf[0] << 8 | buf[1] << 0 | buf[0] >> 2;
dev_dbg(&client->dev, "strength=%u\n", utmp1);
ret = regmap_read(dev->regmap[1], 0xa3, &tmp_upper); c->strength.stat[0].scale = FE_SCALE_RELATIVE;
if (ret) c->strength.stat[0].uvalue = utmp1;
goto err; } else {
c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
ret = regmap_read(dev->regmap[1], 0xa4, &tmp_lower); /* CNR */
if (*status & FE_HAS_VITERBI && c->delivery_system == SYS_DVBT) {
/* DVB-T CNR */
ret = regmap_bulk_read(dev->regmap[0], 0x8f, buf, 2);
if (ret) if (ret)
goto err; goto err;
noise = (tmp_upper << 8) | tmp_lower; utmp = buf[0] << 8 | buf[1] << 0;
if (noise) if (utmp) {
uitmp = log10times1000(signal * 8 / noise); /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
else /* log10(65536) = 80807124, 0.2 = 3355443 */
uitmp = 0; stmp = div_u64(((u64)80807124 - intlog10(utmp)
+ 3355443) * 10000, 1 << 24);
dev_dbg(&client->dev, "cnr=%d value=%u\n", stmp, utmp);
} else {
stmp = 0;
}
c->cnr.stat[0].svalue = uitmp * 10; c->cnr.stat[0].svalue = stmp;
c->cnr.stat[0].scale = FE_SCALE_DECIBEL; c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
} else if (*status & FE_HAS_VITERBI &&
c->delivery_system == SYS_DVBT2) {
/* DVB-T2 CNR */
for (i = 0; i < 3; i++) {
ret = regmap_bulk_read(dev->regmap[2], 0xb7 + i,
&buf[i], 1);
if (ret)
goto err;
}
utmp = buf[1] << 8 | buf[2] << 0;
utmp1 = (buf[0] >> 2) & 0x01; /* 0=SISO, 1=MISO */
if (utmp) {
if (utmp1) {
/* CNR[dB]: 10 * (log10(16384 / value) - 0.6) */
/* log10(16384) = 70706234, 0.6 = 10066330 */
stmp = div_u64(((u64)70706234 - intlog10(utmp)
- 10066330) * 10000, 1 << 24);
dev_dbg(&client->dev, "cnr=%d value=%u MISO\n",
stmp, utmp);
} else { } else {
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
/* log10(65536) = 80807124, 0.2 = 3355443 */
stmp = div_u64(((u64)80807124 - intlog10(utmp)
+ 3355443) * 10000, 1 << 24);
dev_dbg(&client->dev, "cnr=%d value=%u SISO\n",
stmp, utmp);
} }
} else {
/* BER */ stmp = 0;
ret = mn88473_update_ber_stat_t_c(fe, status);
if (ret)
goto err;
return 0;
err:
dev_dbg(&client->dev, "%s failed=%d\n", __func__, ret);
return ret;
}
static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct i2c_client *client = fe->demodulator_priv;
struct mn88473_dev *dev = i2c_get_clientdata(client);
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
u16 errors, per_len;
unsigned int upper, lower;
if (!dev->active) {
ret = -EAGAIN;
goto err;
} }
*status = 0; c->cnr.stat[0].svalue = stmp;
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
switch (c->delivery_system) { } else if (*status & FE_HAS_VITERBI &&
case SYS_DVBT: c->delivery_system == SYS_DVBC_ANNEX_A) {
ret = mn88473_read_status_t(fe, status); /* DVB-C CNR */
break; ret = regmap_bulk_read(dev->regmap[1], 0xa1, buf, 4);
case SYS_DVBT2: if (ret)
ret = mn88473_read_status_t2(fe, status); goto err;
break;
case SYS_DVBC_ANNEX_A: utmp1 = buf[0] << 8 | buf[1] << 0; /* signal */
ret = mn88473_read_status_c(fe, status); utmp2 = buf[2] << 8 | buf[3] << 0; /* noise */
break; if (utmp1 && utmp2) {
default: /* CNR[dB]: 10 * log10(8 * (signal / noise)) */
ret = -EINVAL; /* log10(8) = 15151336 */
break; stmp = div_u64(((u64)15151336 + intlog10(utmp1)
- intlog10(utmp2)) * 10000, 1 << 24);
dev_dbg(&client->dev, "cnr=%d signal=%u noise=%u\n",
stmp, utmp1, utmp2);
} else {
stmp = 0;
} }
if (ret) c->cnr.stat[0].svalue = stmp;
goto err; c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
} else {
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
/* signal strength, derived from AGC */ /* BER */
if (*status & FE_HAS_SIGNAL) { if (*status & FE_HAS_LOCK && (c->delivery_system == SYS_DVBT ||
ret = regmap_read(dev->regmap[2], 0x86, &upper); c->delivery_system == SYS_DVBC_ANNEX_A)) {
/* DVB-T & DVB-C BER */
ret = regmap_bulk_read(dev->regmap[0], 0x92, buf, 5);
if (ret) if (ret)
goto err; goto err;
ret = regmap_read(dev->regmap[2], 0x87, &lower); utmp1 = buf[0] << 16 | buf[1] << 8 | buf[2] << 0;
if (ret) utmp2 = buf[3] << 8 | buf[4] << 0;
goto err; utmp2 = utmp2 * 8 * 204;
dev_dbg(&client->dev, "post_bit_error=%u post_bit_count=%u\n",
utmp1, utmp2);
/* AGCRD[15:6] gives us a 10bit value ([5:0] are always 0) */ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
c->strength.stat[0].scale = FE_SCALE_RELATIVE; c->post_bit_error.stat[0].uvalue += utmp1;
c->strength.stat[0].uvalue = (upper << 8) | lower; c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
c->post_bit_count.stat[0].uvalue += utmp2;
} else { } else {
c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
} }
/* PER */ /* PER */
if (*status & FE_HAS_LOCK) { if (*status & FE_HAS_LOCK) {
ret = regmap_read(dev->regmap[0], 0xdd, &upper); ret = regmap_bulk_read(dev->regmap[0], 0xdd, buf, 4);
if (ret)
goto err;
ret = regmap_read(dev->regmap[0], 0xde, &lower);
if (ret) if (ret)
goto err; goto err;
errors = (upper << 8) | lower; utmp1 = buf[0] << 8 | buf[1] << 0;
utmp2 = buf[2] << 8 | buf[3] << 0;
ret = regmap_read(dev->regmap[0], 0xdf, &upper); dev_dbg(&client->dev, "block_error=%u block_count=%u\n",
if (ret) utmp1, utmp2);
goto err;
ret = regmap_read(dev->regmap[0], 0xe0, &lower);
if (ret)
goto err;
per_len = (upper << 8) | lower;
c->block_error.stat[0].scale = FE_SCALE_COUNTER; c->block_error.stat[0].scale = FE_SCALE_COUNTER;
c->block_error.stat[0].uvalue += errors; c->block_error.stat[0].uvalue += utmp1;
c->block_count.stat[0].scale = FE_SCALE_COUNTER; c->block_count.stat[0].scale = FE_SCALE_COUNTER;
c->block_count.stat[0].uvalue += per_len; c->block_count.stat[0].uvalue += utmp2;
} else { } else {
c->block_error.stat[0].scale = FE_SCALE_COUNTER; c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
c->block_count.stat[0].scale = FE_SCALE_COUNTER; c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
} }
return 0; return 0;
err: err:
dev_dbg(&client->dev, "%s failed=%d\n", __func__, ret); dev_dbg(&client->dev, "failed=%d\n", ret);
return ret; return ret;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "dvb_frontend.h" #include "dvb_frontend.h"
#include "dvb_math.h" #include "dvb_math.h"
#include "mn88473.h" #include "mn88473.h"
#include <linux/math64.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/regmap.h> #include <linux/regmap.h>
......
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