diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2013-03-20 11:21:52 (GMT) |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2013-03-21 13:27:05 (GMT) |
commit | 59a7a23c4755f12757fe17234c693188a9e6bcf5 (patch) | |
tree | e202191ff14e7876c0548d6e6397354a592c49de | |
parent | 8f3741e02831d1181be9ca0ea711dd0c8d7f8a7b (diff) | |
download | linux-59a7a23c4755f12757fe17234c693188a9e6bcf5.tar.xz |
[media] drxk: use a better calculus for RF strength
The AZ6007 driver released by Terratec has a better way to
estimate the signal strength, at CtrlSigStrength(). Port it
to the driver.
It should be noticed that there are two parameters there that
are tuner-specific.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/dvb-frontends/drxk_hard.c | 126 |
1 files changed, 109 insertions, 17 deletions
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 8c4de7c..6e25053 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -6390,6 +6390,107 @@ static int drxk_set_parameters(struct dvb_frontend *fe) return 0; } +static int get_strength(struct drxk_state *state, u64 *strength) +{ + int status; + struct SCfgAgc rfAgc, ifAgc; + u32 totalGain = 0; + u32 atten = 0; + u32 agcRange = 0; + u16 scu_lvl = 0; + u16 scu_coc = 0; + /* FIXME: those are part of the tuner presets */ + u16 tunerRfGain = 50; /* Default value on az6007 driver */ + u16 tunerIfGain = 40; /* Default value on az6007 driver */ + + *strength = 0; + + if (IsDVBT(state)) { + rfAgc = state->m_dvbtRfAgcCfg; + ifAgc = state->m_dvbtIfAgcCfg; + } else if (IsQAM(state)) { + rfAgc = state->m_qamRfAgcCfg; + ifAgc = state->m_qamIfAgcCfg; + } else { + rfAgc = state->m_atvRfAgcCfg; + ifAgc = state->m_atvIfAgcCfg; + } + + if (rfAgc.ctrlMode == DRXK_AGC_CTRL_AUTO) { + /* SCU outputLevel */ + status = read16(state, SCU_RAM_AGC_RF_IACCU_HI__A, &scu_lvl); + if (status < 0) + return status; + + /* SCU c.o.c. */ + read16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, &scu_coc); + if (status < 0) + return status; + + if (((u32) scu_lvl + (u32) scu_coc) < 0xffff) + rfAgc.outputLevel = scu_lvl + scu_coc; + else + rfAgc.outputLevel = 0xffff; + + /* Take RF gain into account */ + totalGain += tunerRfGain; + + /* clip output value */ + if (rfAgc.outputLevel < rfAgc.minOutputLevel) + rfAgc.outputLevel = rfAgc.minOutputLevel; + if (rfAgc.outputLevel > rfAgc.maxOutputLevel) + rfAgc.outputLevel = rfAgc.maxOutputLevel; + + agcRange = (u32) (rfAgc.maxOutputLevel - rfAgc.minOutputLevel); + if (agcRange > 0) { + atten += 100UL * + ((u32)(tunerRfGain)) * + ((u32)(rfAgc.outputLevel - rfAgc.minOutputLevel)) + / agcRange; + } + } + + if (ifAgc.ctrlMode == DRXK_AGC_CTRL_AUTO) { + status = read16(state, SCU_RAM_AGC_IF_IACCU_HI__A, + &ifAgc.outputLevel); + if (status < 0) + return status; + + status = read16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, + &ifAgc.top); + if (status < 0) + return status; + + /* Take IF gain into account */ + totalGain += (u32) tunerIfGain; + + /* clip output value */ + if (ifAgc.outputLevel < ifAgc.minOutputLevel) + ifAgc.outputLevel = ifAgc.minOutputLevel; + if (ifAgc.outputLevel > ifAgc.maxOutputLevel) + ifAgc.outputLevel = ifAgc.maxOutputLevel; + + agcRange = (u32) (ifAgc.maxOutputLevel - ifAgc.minOutputLevel); + if (agcRange > 0) { + atten += 100UL * + ((u32)(tunerIfGain)) * + ((u32)(ifAgc.outputLevel - ifAgc.minOutputLevel)) + / agcRange; + } + } + + /* + * Convert to 0..65535 scale. + * If it can't be measured (AGC is disabled), just show 100%. + */ + if (totalGain > 0) + *strength = (65535UL * atten / totalGain); + else + *strength = 65535; + + return 0; +} + static int drxk_get_stats(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; @@ -6404,7 +6505,7 @@ static int drxk_get_stats(struct dvb_frontend *fe) u32 pre_bit_count; u32 pkt_count; u32 pkt_error_count; - s32 cnr, gain; + s32 cnr; if (state->m_DrxkState == DRXK_NO_DEV) return -ENODEV; @@ -6421,6 +6522,13 @@ static int drxk_get_stats(struct dvb_frontend *fe) if (stat == DEMOD_LOCK) state->fe_status |= 0x07; + /* + * Estimate signal strength from AGC + */ + get_strength(state, &c->strength.stat[0].uvalue); + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + + if (stat >= DEMOD_LOCK) { GetSignalToNoise(state, &cnr); c->cnr.stat[0].svalue = cnr * 100; @@ -6500,22 +6608,6 @@ static int drxk_get_stats(struct dvb_frontend *fe) c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; c->post_bit_count.stat[0].uvalue += post_bit_count; - /* - * Read AGC gain - * - * IFgain = (IQM_AF_AGC_IF__A * 26.75) (nA) - */ - status = read16(state, IQM_AF_AGC_IF__A, ®16); - if (status < 0) { - printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); - return status; - } - gain = 2675 * (reg16 - DRXK_AGC_DAC_OFFSET) / 100; - - /* FIXME: it makes sense to fix the scale here to dBm */ - c->strength.stat[0].scale = FE_SCALE_RELATIVE; - c->strength.stat[0].uvalue = gain; - error: return status; } |