@@ -43,6 +43,12 @@ struct mb86a20s_state {
struct dvb_frontend frontend;
bool need_init;
+
+ /*
+ * QoS measure flags to be used to know when it is possible to
+ * artificially generate a "global" measure, based on all 3 layers
+ */
+ bool read_ber[3];
};
struct regdata {
@@ -92,7 +98,7 @@ static struct regdata mb86a20s_init[] = {
{ 0x04, 0x13 }, { 0x05, 0xff },
{ 0x04, 0x15 }, { 0x05, 0x4e },
{ 0x04, 0x16 }, { 0x05, 0x20 },
- { 0x52, 0x01 },
+ { 0x52, 0x01 }, /* Turn on BER before Viterbi */
{ 0x50, 0xa7 }, { 0x51, 0xff },
{ 0x50, 0xa8 }, { 0x51, 0xff },
{ 0x50, 0xa9 }, { 0x51, 0xff },
@@ -117,8 +123,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 },
@@ -174,6 +180,20 @@ 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_clear_stats[] = {
+ { 0x5f, 0x00 }, /* SBER Counter reset */
+ { 0x5f, 0x07 },
+
+ { 0x50, 0xb1 }, /* PBER Counter reset */
+ { 0x51, 0x07 },
+ { 0x51, 0x01 },
+};
+
static int mb86a20s_i2c_writereg(struct mb86a20s_state *state,
u8 i2c_addr, int reg, int data)
{
@@ -221,7 +241,7 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state,
if (rc != 2) {
rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc);
- return rc;
+ return (rc < 0) ? rc : -EIO;
}
return val;
@@ -276,59 +296,61 @@ err:
return rc;
}
-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;
-
- dprintk("\n");
-
- 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;
- break;
+ rf = (rf_max + rf_min) / 2;
+
+ /* Rescale it from 2^12 (4096) to 2^16 */
+ rf <<= (16 - 12);
+ dprintk("signal strength = %d\n", rf);
+ return (rf);
}
} while (1);
- dprintk("signal strength = %d\n", *strength);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
return 0;
}
static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct mb86a20s_state *state = fe->demodulator_priv;
- u8 val;
+ int val;
dprintk("\n");
*status = 0;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
val = mb86a20s_readreg(state, 0x0a) & 0xf;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
+ if (val < 0)
+ return val;
if (val >= 2)
*status |= FE_HAS_SIGNAL;
@@ -530,9 +552,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)
@@ -609,15 +628,295 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe)
break;
}
}
+ return 0;
+
+error:
+ /* 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;
+ int rc, val, i;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ /* Set the QoS clear status for most stats */
+
+ /* BER counter reset */
+ rc = mb86a20s_writeregdata(state, mb86a20s_vber_reset);
+ if (rc < 0)
+ goto err;
+ for (i = 0; i < 3; i++)
+ state->read_ber[i] = true;
+
+ /* MER, PER counter reset */
+ rc = mb86a20s_writeregdata(state, mb86a20s_clear_stats);
+ 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:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ 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;
+ u8 byte[3];
+ int rc;
+
+ 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)))
+ return -EBUSY;
+
+ /* Read Bit Error Count */
+ rc = mb86a20s_readreg(state, 0x55 + layer * 3);
+ if (rc < 0)
+ return rc;
+ byte[0] = rc;
+ rc = mb86a20s_readreg(state, 0x56 + layer * 3);
+ if (rc < 0)
+ return rc;
+ byte[1] = rc;
+ rc = mb86a20s_readreg(state, 0x57 + layer * 3);
+ if (rc < 0)
+ return rc;
+ byte[2] = rc;
+ *error = byte[0] << 16 | byte[1] << 8 | byte[2];
+
+ /* 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;
+ byte[0] = rc;
+ rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ byte[1] = rc;
+ rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ byte[2] = rc;
+ *count = byte[0] << 16 | byte[1] << 8 | byte[2];
+
+ return rc;
+}
+
+static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int i;
+
+ /* 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;
+ }
+}
+
+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++;
+
+ if (state->read_ber[i]) {
+ /* 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;
+
+ state->read_ber[i] = false;
+ } 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;
+ }
+ }
+ if (!state->read_ber[i]) {
+ /* Update total BER counter */
+ t_bit_error += c->bit_error.stat[1 + i].uvalue;
+ t_bit_count += c->bit_count.stat[1 + i].uvalue;
+
+ ber_layers++;
+ }
+ }
+ }
+
+ if (active_layers == ber_layers) {
+ /*
+ * All BER values are read. We can now calculate the total BER
+ * And ask for another BER measure
+ *
+ * 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;
+
+ /* Reset counters to collect new data */
+ rc = mb86a20s_writeregdata(state, mb86a20s_vber_reset);
+
+ /* All BER measures need to be collected when ready */
+ for (i = 0; i < 3; i++)
+ state->read_ber[i] = true;
+ }
+ return rc;
+}
+
+static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
+ fe_status_t *status)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ /* 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;
+
+ /* Get QoS statistics */
+ rc = mb86a20s_get_stats(fe);
+ if (rc < 0)
+ goto error;
+ }
+ goto ok;
error:
+ mb86a20s_stats_not_ready(fe);
+
+ok:
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
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 0;
}
+
static int mb86a20s_tune(struct dvb_frontend *fe,
bool re_tune,
unsigned int mode_flags,
@@ -632,7 +931,7 @@ static int mb86a20s_tune(struct dvb_frontend *fe,
rc = mb86a20s_set_frontend(fe);
if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
- mb86a20s_read_status(fe, status);
+ mb86a20s_read_status_and_stats(fe, status);
return rc;
}
@@ -712,10 +1011,11 @@ 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,
- .read_signal_strength = mb86a20s_read_signal_strength,
+ .read_status = mb86a20s_read_status_and_stats,
+ .read_signal_strength = mb86a20s_read_signal_strength_from_cache,
.tune = mb86a20s_tune,
+
+ .reset_qos_counters = mb86a20s_reset_counters,
};
MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
Instead of providing separate callbacks to read the several FE status 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 status. As the DVB frontend thread will call this method on every 3 seconds, all QoS data will be updated together, with is a good thing. It also prevents userspace to generate undesired I2C traffic. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/dvb-frontends/mb86a20s.c | 372 +++++++++++++++++++++++++++++---- 1 file changed, 336 insertions(+), 36 deletions(-)