diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index 9a354708bd20a24f5dc946e05a1f25972a5899a7..db3ef58dafba4bad96a89fe6b3e9e9dd52fe80e1 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 @@ -46,9 +43,13 @@ #include #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 59b7c5b9012d6e89e4ac688eb3bfb5e3783a2d55..38c76695abfe0d19904c9c78b1283fcc5ff0d198 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, };