Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09b6d21e authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab
Browse files

[media] mb86a20s: calculate statistics at .read_status()



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: default avatarAntti Palosaari <crope@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 7cd4ece5
Loading
Loading
Loading
Loading
+224 −39
Original line number Diff line number Diff line
@@ -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,
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,
};