summaryrefslogtreecommitdiff
path: root/drivers/media/dvb-frontends/dib8000.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb-frontends/dib8000.c')
-rw-r--r--drivers/media/dvb-frontends/dib8000.c142
1 files changed, 141 insertions, 1 deletions
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index 13fdc3d5f762..2dbf89365a97 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -124,6 +124,8 @@ struct dib8000_state {
u16 agc2_max;
u16 agc2_min;
#endif
+
+ unsigned long get_stats_time;
};
enum dib8000_power_mode {
@@ -804,7 +806,7 @@ int dib8000_update_pll(struct dvb_frontend *fe,
dprintk("PLL: Update ratio (prediv: %d, ratio: %d)", state->cfg.pll->pll_prediv, ratio);
dib8000_write_word(state, 901, (state->cfg.pll->pll_prediv << 8) | (ratio << 0)); /* only the PLL ratio is updated. */
}
-}
+ }
return 0;
}
@@ -983,6 +985,32 @@ static u16 dib8000_identify(struct i2c_device *client)
return value;
}
+static void dib8000_reset_stats(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+
+ memset(&c->strength, 0, sizeof(c->strength));
+ memset(&c->cnr, 0, sizeof(c->cnr));
+ memset(&c->post_bit_error, 0, sizeof(c->post_bit_error));
+ memset(&c->post_bit_count, 0, sizeof(c->post_bit_count));
+ memset(&c->block_error, 0, sizeof(c->block_error));
+
+ c->strength.len = 1;
+ c->cnr.len = 1;
+ c->block_error.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = 0;
+
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+}
+
static int dib8000_reset(struct dvb_frontend *fe)
{
struct dib8000_state *state = fe->demodulator_priv;
@@ -1088,6 +1116,8 @@ static int dib8000_reset(struct dvb_frontend *fe)
dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY);
+ dib8000_reset_stats(fe);
+
return 0;
}
@@ -2983,6 +3013,8 @@ static int dib8000_tune(struct dvb_frontend *fe)
switch (*tune_state) {
case CT_DEMOD_START: /* 30 */
+ dib8000_reset_stats(fe);
+
if (state->revision == 0x8090)
dib8090p_init_sdram(state);
state->status = FE_STATUS_TUNE_PENDING;
@@ -3654,6 +3686,8 @@ static int dib8000_set_frontend(struct dvb_frontend *fe)
return 0;
}
+static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat);
+
static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
{
struct dib8000_state *state = fe->demodulator_priv;
@@ -3691,6 +3725,7 @@ static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
if (lock & 0x01)
*stat |= FE_HAS_VITERBI;
}
+ dib8000_get_stats(fe, *stat);
return 0;
}
@@ -3797,6 +3832,111 @@ static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr)
return 0;
}
+struct per_layer_regs {
+ u16 lock, ber, per;
+};
+
+static const struct per_layer_regs per_layer_regs[] = {
+ { 554, 560, 562 },
+ { 555, 576, 578 },
+ { 556, 581, 583 },
+};
+
+static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ int i, lock;
+ u32 snr, val;
+ u16 strength;
+
+ /* Get Signal strength */
+ dib8000_read_signal_strength(fe, &strength);
+ c->strength.stat[0].uvalue = strength;
+
+ /* Check if 1 second was elapsed */
+ if (!time_after(jiffies, state->get_stats_time))
+ return 0;
+ state->get_stats_time = jiffies + msecs_to_jiffies(1000);
+
+ /* Get SNR */
+ snr = dib8000_get_snr(fe);
+ for (i = 1; i < MAX_NUMBER_OF_FRONTENDS; i++) {
+ if (state->fe[i])
+ snr += dib8000_get_snr(state->fe[i]);
+ }
+ snr = snr >> 16;
+
+ if (snr) {
+ snr = 10 * intlog10(snr);
+ snr = (1000L * snr) >> 24;
+ } else {
+ snr = 0;
+ }
+ c->cnr.stat[0].svalue = snr;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ /* UCB/BER measures require lock */
+ if (!(stat & FE_HAS_LOCK)) {
+ c->block_error.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ /* Get UCB and post-BER measures */
+
+ /* FIXME: need to check if 1.25e6 bits already passed */
+ dib8000_read_ber(fe, &val);
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += val;
+
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += 100000000;
+
+ /*
+ * FIXME: this is refreshed on every second, but a time
+ * drift between dib8000 and PC clock may cause troubles
+ */
+ dib8000_read_unc_blocks(fe, &val);
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += val;
+
+ if (state->revision < 0x8002)
+ return 0;
+
+ c->block_error.len = 4;
+ c->post_bit_error.len = 4;
+ c->post_bit_count.len = 4;
+
+ for (i = 0; i < 3; i++) {
+ lock = dib8000_read_word(state, per_layer_regs[i].lock);
+ if (lock & 0x01) {
+ /* FIXME: need to check if 1.25e6 bits already passed */
+ val = dib8000_read_word(state, per_layer_regs[i].ber);
+ c->post_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[1 + i].uvalue += val;
+
+ c->post_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[1 + i].uvalue += 100000000;
+
+ /*
+ * FIXME: this is refreshed on every second, but a time
+ * drift between dib8000 and PC clock may cause troubles
+ */
+ val = dib8000_read_word(state, per_layer_regs[i].per);
+
+ c->block_error.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[1 + i].uvalue += val;
+ }
+ }
+ return 0;
+}
+
int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
{
struct dib8000_state *state = fe->demodulator_priv;