As reported at: https://tvheadend.org/issues/5625 Retrieving certain status can cause discontinuity issues. Prevent that by adding a timeout to each status logic. Currently, the timeout is estimated based at the channel bandwidth. There are other parameters that may also affect the timeout, but that would require a per-delivery system calculus and probably more information about how cxd2481er works, with we don't have. So, do a poor man's best guess. Cc: stable@xxxxxxxxxxxxxxx Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@xxxxxxxxxx> --- drivers/media/dvb-frontends/cxd2841er.c | 68 +++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 1b30cf570803..218da631df19 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -60,6 +60,13 @@ struct cxd2841er_priv { enum cxd2841er_xtal xtal; enum fe_caps caps; u32 flags; + + unsigned long ber_interval; + unsigned long ucb_interval; + + unsigned long ber_time; + unsigned long ucb_time; + unsigned long snr_time; }; static const struct cxd2841er_cnr_data s_cn_data[] = { @@ -1941,6 +1948,10 @@ static void cxd2841er_read_ber(struct dvb_frontend *fe) struct cxd2841er_priv *priv = fe->demodulator_priv; u32 ret, bit_error = 0, bit_count = 0; + if (priv->ber_time && + (!time_after(jiffies, priv->ber_time))) + return; + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (p->delivery_system) { case SYS_DVBC_ANNEX_A: @@ -1953,9 +1964,15 @@ static void cxd2841er_read_ber(struct dvb_frontend *fe) break; case SYS_DVBS: ret = cxd2841er_mon_read_ber_s(priv, &bit_error, &bit_count); + + if (!priv->ber_interval && p->symbol_rate) + priv->ber_interval = (10000000) / (p->symbol_rate / 1000); break; case SYS_DVBS2: ret = cxd2841er_mon_read_ber_s2(priv, &bit_error, &bit_count); + + if (!priv->ber_interval && p->symbol_rate) + priv->ber_interval = (10000000) / (p->symbol_rate / 1000); break; case SYS_DVBT: ret = cxd2841er_read_ber_t(priv, &bit_error, &bit_count); @@ -1978,6 +1995,21 @@ static void cxd2841er_read_ber(struct dvb_frontend *fe) p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } + + /* + * If the per-delivery system doesn't specify, set a default timeout + * that will wait for ~12.5 seconds for 8MHz channels + */ + if (!priv->ber_interval && p->bandwidth_hz) + priv->ber_interval = (100000000) / (p->bandwidth_hz / 1000); + + if (priv->ber_interval < 1000) + priv->ber_interval = 1000; + +// HACK: +dev_info(&priv->i2c->dev, "BER interval: %d ms", priv->ber_interval); + + priv->ber_time = jiffies + msecs_to_jiffies(priv->ber_interval); } static void cxd2841er_read_signal_strength(struct dvb_frontend *fe) @@ -2037,6 +2069,16 @@ static void cxd2841er_read_snr(struct dvb_frontend *fe) struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct cxd2841er_priv *priv = fe->demodulator_priv; + if (priv->snr_time && + (!time_after(jiffies, priv->snr_time))) + return; + + /* Preventing asking SNR more than once per second */ + priv->snr_time = jiffies + msecs_to_jiffies(1000); + +// HACK +dev_info(&priv->i2c->dev, "SNR interval: %d ms", 1000); + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (p->delivery_system) { case SYS_DVBC_ANNEX_A: @@ -2081,6 +2123,10 @@ static void cxd2841er_read_ucblocks(struct dvb_frontend *fe) struct cxd2841er_priv *priv = fe->demodulator_priv; u32 ucblocks = 0; + if (priv->ucb_time && + (!time_after(jiffies, priv->ucb_time))) + return; + dev_dbg(&priv->i2c->dev, "%s()\n", __func__); switch (p->delivery_system) { case SYS_DVBC_ANNEX_A: @@ -2105,6 +2151,21 @@ static void cxd2841er_read_ucblocks(struct dvb_frontend *fe) p->block_error.stat[0].scale = FE_SCALE_COUNTER; p->block_error.stat[0].uvalue = ucblocks; + + /* + * If the per-delivery system doesn't specify, set a default timeout + * that will wait 20-30 seconds + */ + if (!priv->ucb_interval && p->bandwidth_hz) + priv->ucb_interval = (100 * 204 * 1000 * 8) / p->bandwidth_hz; + + if (priv->ucb_interval < 1000) + priv->ucb_interval = 1000; + +// HACK: +dev_info(&priv->i2c->dev, "UCB interval: %d ms", priv->ucb_interval); + + priv->ucb_time = jiffies + msecs_to_jiffies(priv->ucb_interval); } static int cxd2841er_dvbt2_set_profile( @@ -3360,6 +3421,13 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + /* Reset the wait for jiffies logic */ + priv->ber_interval = 0; + priv->ucb_interval = 0; + priv->ber_time = 0; + priv->ucb_time = 0; + priv->snr_time = 0; + return ret; } -- 2.21.0