struct regmap_config regmap_config;
struct regmap *regmap;
struct dvb_frontend *fe;
+ int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm);
/* i2c details */
int i2c_address;
struct i2c_adapter *i2c;
return 0;
}
-/* read TS2020 signal strength */
-static int ts2020_read_signal_strength(struct dvb_frontend *fe,
- u16 *signal_strength)
+/*
+ * Get the tuner gain.
+ * @fe: The front end for which we're determining the gain
+ * @v_agc: The voltage of the AGC from the demodulator (0-2600mV)
+ * @_gain: Where to store the gain (in 0.001dB units)
+ *
+ * Returns 0 or a negative error code.
+ */
+static int ts2020_read_tuner_gain(struct dvb_frontend *fe, unsigned v_agc,
+ __s64 *_gain)
{
struct ts2020_priv *priv = fe->tuner_priv;
- unsigned int utmp;
- u16 sig_reading, sig_strength;
- u8 rfgain, bbgain;
+ unsigned long gain1, gain2, gain3;
+ unsigned utmp;
+ int ret;
+
+ /* Read the RF gain */
+ ret = regmap_read(priv->regmap, 0x3d, &utmp);
+ if (ret < 0)
+ return ret;
+ gain1 = utmp & 0x1f;
+
+ /* Read the baseband gain */
+ ret = regmap_read(priv->regmap, 0x21, &utmp);
+ if (ret < 0)
+ return ret;
+ gain2 = utmp & 0x1f;
+
+ switch (priv->tuner) {
+ case TS2020_M88TS2020:
+ gain1 = clamp_t(long, gain1, 0, 15);
+ gain2 = clamp_t(long, gain2, 0, 13);
+ v_agc = clamp_t(long, v_agc, 400, 1100);
+
+ *_gain = -(gain1 * 2330 +
+ gain2 * 3500 +
+ v_agc * 24 / 10 * 10 +
+ 10000);
+ /* gain in range -19600 to -116850 in units of 0.001dB */
+ break;
+
+ case TS2020_M88TS2022:
+ ret = regmap_read(priv->regmap, 0x66, &utmp);
+ if (ret < 0)
+ return ret;
+ gain3 = (utmp >> 3) & 0x07;
+
+ gain1 = clamp_t(long, gain1, 0, 15);
+ gain2 = clamp_t(long, gain2, 2, 16);
+ gain3 = clamp_t(long, gain3, 0, 6);
+ v_agc = clamp_t(long, v_agc, 600, 1600);
+
+ *_gain = -(gain1 * 2650 +
+ gain2 * 3380 +
+ gain3 * 2850 +
+ v_agc * 176 / 100 * 10 -
+ 30000);
+ /* gain in range -47320 to -158950 in units of 0.001dB */
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Get the AGC information from the demodulator and use that to calculate the
+ * tuner gain.
+ */
+static int ts2020_get_tuner_gain(struct dvb_frontend *fe, __s64 *_gain)
+{
+ struct ts2020_priv *priv = fe->tuner_priv;
+ int v_agc = 0, ret;
+ u8 agc_pwm;
- regmap_read(priv->regmap, 0x3d, &utmp);
- rfgain = utmp & 0x1f;
- regmap_read(priv->regmap, 0x21, &utmp);
- bbgain = utmp & 0x1f;
+ /* Read the AGC PWM rate from the demodulator */
+ if (priv->get_agc_pwm) {
+ ret = priv->get_agc_pwm(fe, &agc_pwm);
+ if (ret < 0)
+ return ret;
- if (rfgain > 15)
- rfgain = 15;
- if (bbgain > 13)
- bbgain = 13;
+ switch (priv->tuner) {
+ case TS2020_M88TS2020:
+ v_agc = (int)agc_pwm * 20 - 1166;
+ break;
+ case TS2020_M88TS2022:
+ v_agc = (int)agc_pwm * 16 - 670;
+ break;
+ }
- sig_reading = rfgain * 2 + bbgain * 3;
+ if (v_agc < 0)
+ v_agc = 0;
+ }
- sig_strength = 40 + (64 - sig_reading) * 50 / 64 ;
+ return ts2020_read_tuner_gain(fe, v_agc, _gain);
+}
- /* cook the value to be suitable for szap-s2 human readable output */
- *signal_strength = sig_strength * 1000;
+/*
+ * Read TS2020 signal strength in v3 format.
+ */
+static int ts2020_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ unsigned strength;
+ __s64 gain;
+ int ret;
+
+ /* Determine the total gain of the tuner */
+ ret = ts2020_get_tuner_gain(fe, &gain);
+ if (ret < 0)
+ return ret;
+
+ /* Calculate the signal strength based on the total gain of the tuner */
+ if (gain < -85000)
+ /* 0%: no signal or weak signal */
+ strength = 0;
+ else if (gain < -65000)
+ /* 0% - 60%: weak signal */
+ strength = 0 + (85000 + gain) * 3 / 1000;
+ else if (gain < -45000)
+ /* 60% - 90%: normal signal */
+ strength = 60 + (65000 + gain) * 3 / 2000;
+ else
+ /* 90% - 99%: strong signal */
+ strength = 90 + (45000 + gain) / 5000;
+ *signal_strength = strength * 65535 / 100;
return 0;
}
dev->clk_out_div = pdata->clk_out_div;
dev->frequency_div = pdata->frequency_div;
dev->fe = fe;
+ dev->get_agc_pwm = pdata->get_agc_pwm;
fe->tuner_priv = dev;
dev->client = client;
/* attach tuner */
memset(&ts2020_config, 0, sizeof(ts2020_config));
ts2020_config.fe = fe0->dvb.frontend;
+ ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
info.addr = 0x60;
/* attach tuner */
memset(&ts2020_config, 0, sizeof(ts2020_config));
ts2020_config.fe = fe0->dvb.frontend;
+ ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
info.addr = 0x60;
/* attach tuner */
memset(&ts2020_config, 0, sizeof(ts2020_config));
ts2020_config.fe = fe0->dvb.frontend;
+ ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
info.addr = 0x60;