[media] drxk: use a better calculus for RF strength
authorMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 20 Mar 2013 11:21:52 +0000 (08:21 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 21 Mar 2013 13:27:05 +0000 (10:27 -0300)
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>
drivers/media/dvb-frontends/drxk_hard.c

index 8c4de7c..6e25053 100644 (file)
@@ -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, &reg16);
-       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;
 }