summaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb/frontends
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb/frontends')
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.c243
-rw-r--r--drivers/media/dvb/frontends/lgdt330x_priv.h15
2 files changed, 132 insertions, 126 deletions
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index 9a354708bd20..db3ef58dafba 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -31,9 +31,6 @@
* Air2PC/AirStar 2 ATSC 3rd generation (HD5000)
* pcHDTV HD5500
*
- * TODO:
- * signal strength always returns 0.
- *
*/
#include <linux/kernel.h>
@@ -46,9 +43,13 @@
#include <asm/byteorder.h>
#include "dvb_frontend.h"
+#include "dvb_math.h"
#include "lgdt330x_priv.h"
#include "lgdt330x.h"
+/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */
+/* #define USE_EQMSE */
+
static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");
@@ -68,6 +69,7 @@ struct lgdt330x_state
/* Demodulator private data */
fe_modulation_t current_modulation;
+ u32 snr; /* Result of last SNR calculation */
/* Tuner private data */
u32 current_frequency;
@@ -543,151 +545,150 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
return 0;
}
-static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+/* Calculate SNR estimation (scaled by 2^24)
+
+ 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM
+ equations from LGDT3303 datasheet. VSB is the same between the '02
+ and '03, so maybe QAM is too? Perhaps someone with a newer datasheet
+ that has QAM information could verify?
+
+ For 8-VSB: (two ways, take your pick)
+ LGDT3302:
+ SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE)
+ LGDT3303:
+ SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE)
+ LGDT3302 & LGDT3303:
+ SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one)
+ For 64-QAM:
+ SNR = 10 * log10( 688128 / MSEQAM)
+ For 256-QAM:
+ SNR = 10 * log10( 696320 / MSEQAM)
+
+ We re-write the snr equation as:
+ SNR * 2^24 = 10*(c - intlog10(MSE))
+ Where for 256-QAM, c = log10(696320) * 2^24, and so on. */
+
+static u32 calculate_snr(u32 mse, u32 c)
{
- /* not directly available. */
- *strength = 0;
- return 0;
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = intlog10(mse);
+ if (mse > c) {
+ /* Negative SNR, which is possible, but realisticly the
+ demod will lose lock before the signal gets this bad. The
+ API only allows for unsigned values, so just return 0 */
+ return 0;
+ }
+ return 10*(c - mse);
}
static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr)
{
-#ifdef SNR_IN_DB
- /*
- * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise)
- * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker
- * respectively. The following tables are built on these formulas.
- * The usual definition is SNR = 20 log10(signal/noise)
- * If the specification is wrong the value retuned is 1/2 the actual SNR in db.
- *
- * This table is a an ordered list of noise values computed by the
- * formula from the spec sheet such that the index into the table
- * starting at 43 or 45 is the SNR value in db. There are duplicate noise
- * value entries at the beginning because the SNR varies more than
- * 1 db for a change of 1 digit in noise at very small values of noise.
- *
- * Examples from SNR_EQ table:
- * noise SNR
- * 0 43
- * 1 42
- * 2 39
- * 3 37
- * 4 36
- * 5 35
- * 6 34
- * 7 33
- * 8 33
- * 9 32
- * 10 32
- * 11 31
- * 12 31
- * 13 30
- */
-
- static const u32 SNR_EQ[] =
- { 1, 2, 2, 2, 3, 3, 4, 4, 5, 7,
- 9, 11, 13, 17, 21, 26, 33, 41, 52, 65,
- 81, 102, 129, 162, 204, 257, 323, 406, 511, 644,
- 810, 1020, 1284, 1616, 2035, 2561, 3224, 4059, 5110, 6433,
- 8098, 10195, 12835, 16158, 20341, 25608, 32238, 40585, 51094, 64323,
- 80978, 101945, 128341, 161571, 203406, 256073, 0x40000
- };
-
- static const u32 SNR_PH[] =
- { 1, 2, 2, 2, 3, 3, 4, 5, 6, 8,
- 10, 12, 15, 19, 23, 29, 37, 46, 58, 73,
- 91, 115, 144, 182, 229, 288, 362, 456, 574, 722,
- 909, 1144, 1440, 1813, 2282, 2873, 3617, 4553, 5732, 7216,
- 9084, 11436, 14396, 18124, 22817, 28724, 36161, 45524, 57312, 72151,
- 90833, 114351, 143960, 181235, 228161, 0x080000
- };
-
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
- static u32 snr_db; /* index into SNR_EQ[] */
struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
- /* read both equalizer and phase tracker noise data */
- i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf));
-
- if (state->current_modulation == VSB_8) {
- /* Equalizer Mean-Square Error Register for VSB */
+ switch(state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -15.61 to +41.58 */
noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
-
- /*
- * Look up noise value in table.
- * A better search algorithm could be used...
- * watch out there are duplicate entries.
- */
- for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) {
- if (noise < SNR_EQ[snr_db]) {
- *snr = 43 - snr_db;
- break;
- }
- }
- } else {
- /* Phase Tracker Mean-Square Error Register for QAM */
- noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
-
- /* Look up noise value in table. */
- for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) {
- if (noise < SNR_PH[snr_db]) {
- *snr = 45 - snr_db;
- break;
- }
- }
- }
+ c = 69765745; /* log10(25*24^2)*2^24 */
#else
- /* Return the raw noise value */
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
-
- /* read both equalizer and pase tracker noise data */
- i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf));
-
- if (state->current_modulation == VSB_8) {
- /* Phase Tracker Mean-Square Error Register for VSB */
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
- } else {
-
- /* Carrier Recovery Mean-Square Error for QAM */
- i2c_read_demod_bytes(state, 0x1a, buf, 2);
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
noise = ((buf[0] & 3) << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
+ __FUNCTION__);
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
}
- /* Small values for noise mean signal is better so invert noise */
- *snr = ~noise;
-#endif
+ state->snr = calculate_snr(noise, c);
+ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
- dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
return 0;
}
static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr)
{
- /* Return the raw noise value */
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
- if (state->current_modulation == VSB_8) {
-
- i2c_read_demod_bytes(state, 0x6e, buf, 5);
- /* Phase Tracker Mean-Square Error Register for VSB */
+ switch(state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -16.12 to +44.08 */
+ noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#else
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4];
- } else {
-
- /* Carrier Recovery Mean-Square Error for QAM */
- i2c_read_demod_bytes(state, 0x1a, buf, 2);
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
noise = (buf[0] << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
+ __FUNCTION__);
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
}
- /* Small values for noise mean signal is better so invert noise */
- *snr = ~noise;
+ state->snr = calculate_snr(noise, c);
+ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
+
+ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
+
+ return 0;
+}
+
+static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ /* Calculate Strength from SNR up to 35dB */
+ /* Even though the SNR can go higher than 35dB, there is some comfort */
+ /* factor in having a range of strong signals that can show at 100% */
+ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u16 snr;
+ int ret;
- dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
return 0;
}
diff --git a/drivers/media/dvb/frontends/lgdt330x_priv.h b/drivers/media/dvb/frontends/lgdt330x_priv.h
index 59b7c5b9012d..38c76695abfe 100644
--- a/drivers/media/dvb/frontends/lgdt330x_priv.h
+++ b/drivers/media/dvb/frontends/lgdt330x_priv.h
@@ -51,14 +51,19 @@ enum I2C_REG {
AGC_RFIF_ACC2= 0x3b,
AGC_STATUS= 0x3f,
SYNC_STATUS_VSB= 0x43,
- EQPH_ERR0= 0x47,
- EQ_ERR1= 0x48,
- EQ_ERR2= 0x49,
- PH_ERR1= 0x4a,
- PH_ERR2= 0x4b,
DEMUX_CONTROL= 0x66,
+ LGDT3302_EQPH_ERR0= 0x47,
+ LGDT3302_EQ_ERR1= 0x48,
+ LGDT3302_EQ_ERR2= 0x49,
+ LGDT3302_PH_ERR1= 0x4a,
+ LGDT3302_PH_ERR2= 0x4b,
LGDT3302_PACKET_ERR_COUNTER1= 0x6a,
LGDT3302_PACKET_ERR_COUNTER2= 0x6b,
+ LGDT3303_EQPH_ERR0= 0x6e,
+ LGDT3303_EQ_ERR1= 0x6f,
+ LGDT3303_EQ_ERR2= 0x70,
+ LGDT3303_PH_ERR1= 0x71,
+ LGDT3303_PH_ERR2= 0x72,
LGDT3303_PACKET_ERR_COUNTER1= 0x8b,
LGDT3303_PACKET_ERR_COUNTER2= 0x8c,
};
OpenPOWER on IntegriCloud