[media] mb86a20s: calculate statistics at .read_status()
authorMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 22 Jan 2013 15:28:31 +0000 (12:28 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 23 Jan 2013 21:10:27 +0000 (19:10 -0200)
Instead of providing separate callbacks to read the several FE
stats 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 statistics.
As the DVB frontend thread will call this read_status callback
on every 3 seconds, and userspace can even call it earlier,
all stats data and layers layout will be updated together if
available, with is a good thing.

Reviewed-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb-frontends/mb86a20s.c

index 4b3ffc4..d7668e6 100644 (file)
@@ -27,6 +27,7 @@ MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
 struct mb86a20s_state {
        struct i2c_adapter *i2c;
        const struct mb86a20s_config *config;
+       u32 last_frequency;
 
        struct dvb_frontend frontend;
 
@@ -80,17 +81,26 @@ static struct regdata mb86a20s_init[] = {
        { 0x04, 0x13 }, { 0x05, 0xff },
        { 0x04, 0x15 }, { 0x05, 0x4e },
        { 0x04, 0x16 }, { 0x05, 0x20 },
-       { 0x52, 0x01 },
-       { 0x50, 0xa7 }, { 0x51, 0xff },
+
+       /*
+        * On this demod, when the bit count reaches the count below,
+        * it collects the bit error count. The bit counters are initialized
+        * to 65535 here. This warrants that all of them will be quickly
+        * calculated when device gets locked. As TMCC is parsed, the values
+        * can be adjusted later in the driver's code.
+        */
+       { 0x52, 0x01 },                         /* Turn on BER before Viterbi */
+       { 0x50, 0xa7 }, { 0x51, 0x00 },
        { 0x50, 0xa8 }, { 0x51, 0xff },
        { 0x50, 0xa9 }, { 0x51, 0xff },
-       { 0x50, 0xaa }, { 0x51, 0xff },
+       { 0x50, 0xaa }, { 0x51, 0x00 },
        { 0x50, 0xab }, { 0x51, 0xff },
        { 0x50, 0xac }, { 0x51, 0xff },
-       { 0x50, 0xad }, { 0x51, 0xff },
+       { 0x50, 0xad }, { 0x51, 0x00 },
        { 0x50, 0xae }, { 0x51, 0xff },
        { 0x50, 0xaf }, { 0x51, 0xff },
-       { 0x5e, 0x07 },
+
+       { 0x5e, 0x00 },                         /* Turn off BER after Viterbi */
        { 0x50, 0xdc }, { 0x51, 0x01 },
        { 0x50, 0xdd }, { 0x51, 0xf4 },
        { 0x50, 0xde }, { 0x51, 0x01 },
@@ -105,8 +115,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 },
@@ -163,12 +173,23 @@ 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_per_reset[] = {
+       { 0x50, 0xb1 }, /* PER Counter reset */
+       { 0x51, 0x07 },
+       { 0x51, 0x00 },
+};
+
 /*
  * I2C read/write functions and macros
  */
 
 static int mb86a20s_i2c_writereg(struct mb86a20s_state *state,
-                            u8 i2c_addr, int reg, int data)
+                            u8 i2c_addr, u8 reg, u8 data)
 {
        u8 buf[] = { reg, data };
        struct i2c_msg msg = {
@@ -230,6 +251,12 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state,
        mb86a20s_i2c_writeregdata(state, state->config->demod_address, \
        regdata, ARRAY_SIZE(regdata))
 
+/*
+ * Ancillary internal routines (likely compiled inlined)
+ *
+ * The functions below assume that gateway lock has already obtained
+ */
+
 static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status)
 {
        struct mb86a20s_state *state = fe->demodulator_priv;
@@ -262,42 +289,49 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status)
        return 0;
 }
 
-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;
-
-       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;
+                       rf = (rf_max + rf_min) / 2;
+
+                       /* Rescale it from 2^12 (4096) to 2^16 */
+                       rf <<= (16 - 12);
                        dev_dbg(&state->i2c->dev,
                                "%s: signal strength = %d (%d < RF=%d < %d)\n",
                                __func__, rf, rf_min, rf >> 4, rf_max);
-                       break;
+                       return rf;
                }
        } while (1);
 
-       if (fe->ops.i2c_gate_ctrl)
-               fe->ops.i2c_gate_ctrl(fe, 1);
-
        return 0;
 }
 
@@ -462,9 +496,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)
@@ -550,15 +581,119 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe)
                        break;
                }
        }
+       return 0;
 
 noperlayer_error:
-       if (fe->ops.i2c_gate_ctrl)
-               fe->ops.i2c_gate_ctrl(fe, 1);
 
+       /* 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;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int rc, val;
+
+       dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+       /* Reset the counters, if the channel changed */
+       if (state->last_frequency != c->frequency) {
+               memset(&c->strength, 0, sizeof(c->strength));
+               memset(&c->cnr, 0, sizeof(c->cnr));
+               memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error));
+               memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count));
+               memset(&c->block_error, 0, sizeof(c->block_error));
+               memset(&c->block_count, 0, sizeof(c->block_count));
+
+               state->last_frequency = c->frequency;
+       }
+
+       /* Clear status for most stats */
+
+       /* BER counter reset */
+       rc = mb86a20s_writeregdata(state, mb86a20s_vber_reset);
+       if (rc < 0)
+               goto err;
+
+       /* MER, PER counter reset */
+       rc = mb86a20s_writeregdata(state, mb86a20s_per_reset);
+       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:
        return rc;
+}
+
+static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
+{
+       struct mb86a20s_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int i;
+
+       dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
 
+       /* 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->pre_bit_error.len = 4;
+       c->pre_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->pre_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+               c->pre_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;
+       }
 }
 
+
+/*
+ * The functions below are called via DVB callbacks, so they need to
+ * properly use the I2C gate control
+ */
+
 static int mb86a20s_initfe(struct dvb_frontend *fe)
 {
        struct mb86a20s_state *state = fe->demodulator_priv;
@@ -637,30 +772,80 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe)
        if (fe->ops.i2c_gate_ctrl)
                fe->ops.i2c_gate_ctrl(fe, 0);
        rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception);
+       mb86a20s_reset_counters(fe);
        if (fe->ops.i2c_gate_ctrl)
                fe->ops.i2c_gate_ctrl(fe, 1);
 
        return rc;
 }
 
-static int mb86a20s_read_status_gate(struct dvb_frontend *fe,
-                                    fe_status_t *status)
+static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
+                                         fe_status_t *status)
 {
-       int ret;
+       struct mb86a20s_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int rc;
 
-       *status = 0;
+       dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
 
        if (fe->ops.i2c_gate_ctrl)
                fe->ops.i2c_gate_ctrl(fe, 0);
 
-       ret = mb86a20s_read_status(fe, status);
+       /* 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;
+       }
+
+       mb86a20s_stats_not_ready(fe);
 
        if (fe->ops.i2c_gate_ctrl)
                fe->ops.i2c_gate_ctrl(fe, 1);
+error:
+       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 ret;
+       return 0;
 }
 
+static int mb86a20s_get_frontend_dummy(struct dvb_frontend *fe)
+{
+       /*
+        * get_frontend is now handled together with other stats
+        * retrival, when read_status() is called, as some statistics
+        * will depend on the layers detection.
+        */
+       return 0;
+};
+
 static int mb86a20s_tune(struct dvb_frontend *fe,
                        bool re_tune,
                        unsigned int mode_flags,
@@ -676,7 +861,7 @@ static int mb86a20s_tune(struct dvb_frontend *fe,
                rc = mb86a20s_set_frontend(fe);
 
        if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
-               mb86a20s_read_status_gate(fe, status);
+               mb86a20s_read_status_and_stats(fe, status);
 
        return rc;
 }
@@ -759,9 +944,9 @@ 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_gate,
-       .read_signal_strength = mb86a20s_read_signal_strength,
+       .get_frontend = mb86a20s_get_frontend_dummy,
+       .read_status = mb86a20s_read_status_and_stats,
+       .read_signal_strength = mb86a20s_read_signal_strength_from_cache,
        .tune = mb86a20s_tune,
 };