diff mbox

[RFCv10,04/15] mb86a20s: Update QoS statistics at FE read_status

Message ID 1358217061-14982-5-git-send-email-mchehab@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Mauro Carvalho Chehab Jan. 15, 2013, 2:30 a.m. UTC
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(-)
diff mbox

Patch

diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index 4ff3a0c..0f8d9bc 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -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");