|
@@ -11,6 +11,7 @@
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/i2c.h>
|
|
|
#include <linux/mutex.h>
|
|
|
+#include <asm/div64.h>
|
|
|
|
|
|
#include "dvb_math.h"
|
|
|
#include "dvb_frontend.h"
|
|
@@ -72,6 +73,12 @@ struct dib7000p_state {
|
|
|
struct mutex i2c_buffer_lock;
|
|
|
|
|
|
u8 input_mode_mpeg;
|
|
|
+
|
|
|
+ /* for DVBv5 stats */
|
|
|
+ s64 old_ucb;
|
|
|
+ unsigned long per_jiffies_stats;
|
|
|
+ unsigned long ber_jiffies_stats;
|
|
|
+ unsigned long get_stats_time;
|
|
|
};
|
|
|
|
|
|
enum dib7000p_power_mode {
|
|
@@ -631,6 +638,8 @@ static u16 dib7000p_defaults[] = {
|
|
|
0,
|
|
|
};
|
|
|
|
|
|
+static void dib7000p_reset_stats(struct dvb_frontend *fe);
|
|
|
+
|
|
|
static int dib7000p_demod_reset(struct dib7000p_state *state)
|
|
|
{
|
|
|
dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
|
|
@@ -1354,6 +1363,9 @@ static int dib7000p_tune(struct dvb_frontend *demod)
|
|
|
dib7000p_spur_protect(state, ch->frequency / 1000, BANDWIDTH_TO_KHZ(ch->bandwidth_hz));
|
|
|
|
|
|
dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz));
|
|
|
+
|
|
|
+ dib7000p_reset_stats(demod);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1546,6 +1558,8 @@ static int dib7000p_set_frontend(struct dvb_frontend *fe)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int dib7000p_get_stats(struct dvb_frontend *fe, fe_status_t stat);
|
|
|
+
|
|
|
static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat)
|
|
|
{
|
|
|
struct dib7000p_state *state = fe->demodulator_priv;
|
|
@@ -1564,6 +1578,8 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat)
|
|
|
if ((lock & 0x0038) == 0x38)
|
|
|
*stat |= FE_HAS_LOCK;
|
|
|
|
|
|
+ dib7000p_get_stats(fe, *stat);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1589,7 +1605,7 @@ static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 * strength
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr)
|
|
|
+static u32 dib7000p_get_snr(struct dvb_frontend *fe)
|
|
|
{
|
|
|
struct dib7000p_state *state = fe->demodulator_priv;
|
|
|
u16 val;
|
|
@@ -1619,10 +1635,311 @@ static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr)
|
|
|
else
|
|
|
result -= intlog10(2) * 10 * noise_exp - 100;
|
|
|
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static int dib7000p_read_snr(struct dvb_frontend *fe, u16 *snr)
|
|
|
+{
|
|
|
+ u32 result;
|
|
|
+
|
|
|
+ result = dib7000p_get_snr(fe);
|
|
|
+
|
|
|
*snr = result / ((1 << 24) / 10);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void dib7000p_reset_stats(struct dvb_frontend *demod)
|
|
|
+{
|
|
|
+ struct dib7000p_state *state = demod->demodulator_priv;
|
|
|
+ struct dtv_frontend_properties *c = &demod->dtv_property_cache;
|
|
|
+ u32 ucb;
|
|
|
+
|
|
|
+ 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->block_count.len = 1;
|
|
|
+ c->post_bit_error.len = 1;
|
|
|
+ c->post_bit_count.len = 1;
|
|
|
+
|
|
|
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
|
|
|
+ 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->block_count.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;
|
|
|
+
|
|
|
+ dib7000p_read_unc_blocks(demod, &ucb);
|
|
|
+
|
|
|
+ state->old_ucb = ucb;
|
|
|
+ state->ber_jiffies_stats = 0;
|
|
|
+ state->per_jiffies_stats = 0;
|
|
|
+}
|
|
|
+
|
|
|
+struct linear_segments {
|
|
|
+ unsigned x;
|
|
|
+ signed y;
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Table to estimate signal strength in dBm.
|
|
|
+ * This table should be empirically determinated by measuring the signal
|
|
|
+ * strength generated by a RF generator directly connected into
|
|
|
+ * a device.
|
|
|
+ */
|
|
|
+/* FIXME: Calibrate the table */
|
|
|
+
|
|
|
+#define DB_OFFSET 0
|
|
|
+
|
|
|
+static struct linear_segments strength_to_db_table[] = {
|
|
|
+ { 65535, 65535},
|
|
|
+ { 0, 0},
|
|
|
+};
|
|
|
+
|
|
|
+static u32 interpolate_value(u32 value, struct linear_segments *segments,
|
|
|
+ unsigned len)
|
|
|
+{
|
|
|
+ u64 tmp64;
|
|
|
+ u32 dx;
|
|
|
+ s32 dy;
|
|
|
+ int i, ret;
|
|
|
+
|
|
|
+ if (value >= segments[0].x)
|
|
|
+ return segments[0].y;
|
|
|
+ if (value < segments[len-1].x)
|
|
|
+ return segments[len-1].y;
|
|
|
+
|
|
|
+ for (i = 1; i < len - 1; i++) {
|
|
|
+ /* If value is identical, no need to interpolate */
|
|
|
+ if (value == segments[i].x)
|
|
|
+ return segments[i].y;
|
|
|
+ if (value > segments[i].x)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Linear interpolation between the two (x,y) points */
|
|
|
+ dy = segments[i - 1].y - segments[i].y;
|
|
|
+ dx = segments[i - 1].x - segments[i].x;
|
|
|
+
|
|
|
+ tmp64 = value - segments[i].x;
|
|
|
+ tmp64 *= dy;
|
|
|
+ do_div(tmp64, dx);
|
|
|
+ ret = segments[i].y + tmp64;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/* FIXME: may require changes - this one was borrowed from dib8000 */
|
|
|
+static u32 dib7000p_get_time_us(struct dvb_frontend *demod, int layer)
|
|
|
+{
|
|
|
+ struct dtv_frontend_properties *c = &demod->dtv_property_cache;
|
|
|
+ u64 time_us, tmp64;
|
|
|
+ u32 tmp, denom;
|
|
|
+ int guard, rate_num, rate_denum = 1, bits_per_symbol;
|
|
|
+ int interleaving = 0, fft_div;
|
|
|
+
|
|
|
+ switch (c->guard_interval) {
|
|
|
+ case GUARD_INTERVAL_1_4:
|
|
|
+ guard = 4;
|
|
|
+ break;
|
|
|
+ case GUARD_INTERVAL_1_8:
|
|
|
+ guard = 8;
|
|
|
+ break;
|
|
|
+ case GUARD_INTERVAL_1_16:
|
|
|
+ guard = 16;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ case GUARD_INTERVAL_1_32:
|
|
|
+ guard = 32;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (c->transmission_mode) {
|
|
|
+ case TRANSMISSION_MODE_2K:
|
|
|
+ fft_div = 4;
|
|
|
+ break;
|
|
|
+ case TRANSMISSION_MODE_4K:
|
|
|
+ fft_div = 2;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ case TRANSMISSION_MODE_8K:
|
|
|
+ fft_div = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (c->modulation) {
|
|
|
+ case DQPSK:
|
|
|
+ case QPSK:
|
|
|
+ bits_per_symbol = 2;
|
|
|
+ break;
|
|
|
+ case QAM_16:
|
|
|
+ bits_per_symbol = 4;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ case QAM_64:
|
|
|
+ bits_per_symbol = 6;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch ((c->hierarchy == 0 || 1 == 1) ? c->code_rate_HP : c->code_rate_LP) {
|
|
|
+ case FEC_1_2:
|
|
|
+ rate_num = 1;
|
|
|
+ rate_denum = 2;
|
|
|
+ break;
|
|
|
+ case FEC_2_3:
|
|
|
+ rate_num = 2;
|
|
|
+ rate_denum = 3;
|
|
|
+ break;
|
|
|
+ case FEC_3_4:
|
|
|
+ rate_num = 3;
|
|
|
+ rate_denum = 4;
|
|
|
+ break;
|
|
|
+ case FEC_5_6:
|
|
|
+ rate_num = 5;
|
|
|
+ rate_denum = 6;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ case FEC_7_8:
|
|
|
+ rate_num = 7;
|
|
|
+ rate_denum = 8;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ interleaving = interleaving;
|
|
|
+
|
|
|
+ denom = bits_per_symbol * rate_num * fft_div * 384;
|
|
|
+
|
|
|
+ /* If calculus gets wrong, wait for 1s for the next stats */
|
|
|
+ if (!denom)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Estimate the period for the total bit rate */
|
|
|
+ time_us = rate_denum * (1008 * 1562500L);
|
|
|
+ tmp64 = time_us;
|
|
|
+ do_div(tmp64, guard);
|
|
|
+ time_us = time_us + tmp64;
|
|
|
+ time_us += denom / 2;
|
|
|
+ do_div(time_us, denom);
|
|
|
+
|
|
|
+ tmp = 1008 * 96 * interleaving;
|
|
|
+ time_us += tmp + tmp / guard;
|
|
|
+
|
|
|
+ return time_us;
|
|
|
+}
|
|
|
+
|
|
|
+static int dib7000p_get_stats(struct dvb_frontend *demod, fe_status_t stat)
|
|
|
+{
|
|
|
+ struct dib7000p_state *state = demod->demodulator_priv;
|
|
|
+ struct dtv_frontend_properties *c = &demod->dtv_property_cache;
|
|
|
+ int i;
|
|
|
+ int show_per_stats = 0;
|
|
|
+ u32 time_us = 0, val, snr;
|
|
|
+ u64 blocks, ucb;
|
|
|
+ s32 db;
|
|
|
+ u16 strength;
|
|
|
+
|
|
|
+ /* Get Signal strength */
|
|
|
+ dib7000p_read_signal_strength(demod, &strength);
|
|
|
+ val = strength;
|
|
|
+ db = interpolate_value(val,
|
|
|
+ strength_to_db_table,
|
|
|
+ ARRAY_SIZE(strength_to_db_table)) - DB_OFFSET;
|
|
|
+ c->strength.stat[0].svalue = db;
|
|
|
+
|
|
|
+ /* FIXME: Remove this when calibrated to DB */
|
|
|
+ c->strength.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
+
|
|
|
+ /* UCB/BER/CNR measures require lock */
|
|
|
+ if (!(stat & FE_HAS_LOCK)) {
|
|
|
+ c->cnr.len = 1;
|
|
|
+ c->block_count.len = 1;
|
|
|
+ c->block_error.len = 1;
|
|
|
+ c->post_bit_error.len = 1;
|
|
|
+ c->post_bit_count.len = 1;
|
|
|
+ c->cnr.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;
|
|
|
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if time for stats was elapsed */
|
|
|
+ if (time_after(jiffies, state->per_jiffies_stats)) {
|
|
|
+ state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
|
|
|
+
|
|
|
+ /* Get SNR */
|
|
|
+ snr = dib7000p_get_snr(demod);
|
|
|
+ if (snr)
|
|
|
+ snr = (1000L * snr) >> 24;
|
|
|
+ else
|
|
|
+ snr = 0;
|
|
|
+ c->cnr.stat[0].svalue = snr;
|
|
|
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
|
|
|
+
|
|
|
+ /* Get UCB measures */
|
|
|
+ dib7000p_read_unc_blocks(demod, &val);
|
|
|
+ ucb = val - state->old_ucb;
|
|
|
+ if (val < state->old_ucb)
|
|
|
+ ucb += 0x100000000LL;
|
|
|
+
|
|
|
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
+ c->block_error.stat[0].uvalue = ucb;
|
|
|
+
|
|
|
+ /* Estimate the number of packets based on bitrate */
|
|
|
+ if (!time_us)
|
|
|
+ time_us = dib7000p_get_time_us(demod, -1);
|
|
|
+
|
|
|
+ if (time_us) {
|
|
|
+ blocks = 1250000ULL * 1000000ULL;
|
|
|
+ do_div(blocks, time_us * 8 * 204);
|
|
|
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
+ c->block_count.stat[0].uvalue += blocks;
|
|
|
+ }
|
|
|
+
|
|
|
+ show_per_stats = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Get post-BER measures */
|
|
|
+ if (time_after(jiffies, state->ber_jiffies_stats)) {
|
|
|
+ time_us = dib7000p_get_time_us(demod, -1);
|
|
|
+ state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000);
|
|
|
+
|
|
|
+ dprintk("Next all layers stats available in %u us.", time_us);
|
|
|
+
|
|
|
+ dib7000p_read_ber(demod, &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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Get PER measures */
|
|
|
+ if (show_per_stats) {
|
|
|
+ dib7000p_read_unc_blocks(demod, &val);
|
|
|
+
|
|
|
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
+ c->block_error.stat[0].uvalue += val;
|
|
|
+
|
|
|
+ time_us = dib7000p_get_time_us(demod, i);
|
|
|
+ if (time_us) {
|
|
|
+ blocks = 1250000ULL * 1000000ULL;
|
|
|
+ do_div(blocks, time_us * 8 * 204);
|
|
|
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
|
|
|
+ c->block_count.stat[0].uvalue += blocks;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int dib7000p_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
|
|
|
{
|
|
|
tune->min_delay_ms = 1000;
|
|
@@ -2408,6 +2725,8 @@ static struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_a
|
|
|
|
|
|
dib7000p_demod_reset(st);
|
|
|
|
|
|
+ dib7000p_reset_stats(demod);
|
|
|
+
|
|
|
if (st->version == SOC7090) {
|
|
|
dib7090_set_output_mode(demod, st->cfg.output_mode);
|
|
|
dib7090_set_diversity_in(demod, 0);
|