--- a/drivers/media/dvb/frontends/rtl2832.c	2012-05-01 12:28:26.407776103 +0200
+++ b/drivers/media/dvb/frontends/rtl2832.c	2012-05-05 18:35:28.778377211 +0200
@@ -19,10 +19,11 @@
  */
 
 #include "rtl2832_priv.h"
+#include "rtl28xxu_tuners.h"
 
 
 
-int rtl2832_debug = 1;
+int rtl2832_debug;
 module_param_named(debug, rtl2832_debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
@@ -391,6 +392,58 @@ err:
 
 }
 
+static int rtl2832_wr_i2c(struct rtl2832_priv *priv, u8 addr, u8 reg, u8 *val, int len)
+{
+	int ret;
+	u8 buf[1+len];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = addr,
+			.flags = 0,
+			.len = 1+len,
+			.buf = buf,
+		}
+	};
+
+	buf[0] = reg;
+	memcpy(&buf[1], val, len);
+
+	ret = i2c_transfer(priv->i2c, msg, 1);
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
+
+static int rtl2832_rd_i2c(struct rtl2832_priv *priv, u8 addr, u8 reg, u8 *val, int len)
+{
+	int ret;
+	struct i2c_msg msg[2] = {
+		{
+			.addr = addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		}, {
+			.addr = addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = val,
+		}
+	};
+
+	ret = i2c_transfer(priv->i2c, msg, 2);
+	if (ret == 2) {
+		ret = 0;
+	} else {
+		warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
 
 static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
 {
@@ -683,7 +736,9 @@ static int rtl2832_read_status(struct dv
 	*status = 0;
 
 
+#if 0
 	info("%s", __func__);
+#endif
 	if (priv->sleeping)
 		return 0;
 
@@ -707,32 +762,198 @@ err:
 	return ret;
 }
 
+#define RTL2832_CE_EST_EVM_MAX_VALUE 65535
+#define RTL2832_SNR_FRAC_BIT_NUM 10
+#define RTL2832_SNR_DB_DEN 3402
+
 static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
-	info("%s", __func__);
-	*snr = 0;
+	struct rtl2832_priv *priv = fe->demodulator_priv;
+	int ret;
+	u32 fsm_stage, ce_est_evm, constellation, hierarchy;
+	int num;
+	static const int snr_db_num_const[3][4] =
+	{
+		{122880,        122880,         122880,         122880,   },
+		{146657,        146657,         156897,         171013,   },
+		{167857,        167857,         173127,         181810,   },
+	};
+
+	ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &fsm_stage);
+	if (ret)
+		goto err;
+
+	if (fsm_stage < 10)
+		ce_est_evm = RTL2832_CE_EST_EVM_MAX_VALUE;
+	else {
+		ret = rtl2832_rd_demod_reg(priv, DVBT_CE_EST_EVM, &ce_est_evm);
+		if (ret)
+			goto err;
+	}
+
+	ret = rtl2832_rd_demod_reg(priv, DVBT_RX_CONSTEL, &constellation);
+	if (ret)
+		goto err;
+	if (constellation > 2)
+		goto err;
+
+	ret = rtl2832_rd_demod_reg(priv, DVBT_RX_HIER, &hierarchy);
+	if (ret)
+		goto err;
+	if (hierarchy > 3)
+		goto err;
+
+	num = snr_db_num_const[constellation][hierarchy] -
+		10*ilog2(ce_est_evm*ce_est_evm)*0x200;
+	if (num < 0)
+		*snr = 0;
+	else
+		*snr = 256 * num / RTL2832_SNR_DB_DEN;
 	return 0;
+
+err:
+	info("%s: failed=%d", __func__, ret);
+	return ret;
 }
 
 static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
-	info("%s", __func__);
-	*ber = 0;
+	struct rtl2832_priv *priv = fe->demodulator_priv;
+	u32 tmp;
+	int ret;
+
+	ret = rtl2832_rd_demod_reg(priv, DVBT_RSD_BER_EST, &tmp);
+	if (ret)
+		goto err;
+	*ber = tmp;
 	return 0;
+
+err:
+	info("%s: failed=%d", __func__, ret);
+	*ber = 0;
+	return ret;
 }
 
 static int rtl2832_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 {
-	info("%s", __func__);
 	*ucblocks = 0;
 	return 0;
 }
 
-static int rtl2832_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+#define INPUT_ADC_LEVEL   -8
+
+const int fc001x_lna_gain_table[] = {
+	/* low gain */
+	-63, -58, -99, -73,
+	-63, -65, -54, -60,
+	/* middle gain */
+	 71,  70,  68,  67,
+	 65,  63,  61,  58,
+	/* high gain */
+	197, 191, 188, 186,
+	184, 182, 181, 179,
+};
+
+static int rtl2832_fc001x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 {
-	info("%s", __func__);
+	struct rtl2832_priv *priv = fe->demodulator_priv;
+	int ret;
+	u8 val, reg;
+	int int_temp, lna_gain, int_lna, tot_agc_gain, power;
+
+	if (priv->tuner == TUNER_RTL2832_FC0013)
+		reg = 0x13;
+	else
+		reg = 0x12;
+
+	ret = rtl2832_i2c_gate_ctrl(fe, true);
+	if (ret)
+		goto err;
+
+	val = 0x00;
+	ret = rtl2832_wr_i2c(priv, 0xc6 >> 1, reg, &val, 1);
+	if (ret) {
+		err("%s: rtl2832_wr_i2c failed, ret=%d", __func__, ret);
+		goto err_gate;
+	} else {
+		ret = rtl2832_rd_i2c(priv, 0xc6 >> 1, reg, &val, 1);
+		if (ret)
+			err("%s: rtl2832_rd_i2c failed, ret=%d", __func__, ret);
+//		info("fc001x read 0x12: 0x%02x", val);
+		int_temp = val;
+
+		ret = rtl2832_rd_i2c(priv, 0xc6 >> 1, reg + 1, &val, 1);
+		if (ret)
+			err("%s: rtl2832_rd_i2c failed, ret=%d", __func__, ret);
+//		info("fc001x read 0x13: 0x%02x", val);
+		lna_gain = val & 0x1f;
+	}
+
+	ret = rtl2832_i2c_gate_ctrl(fe, false);
+	if (ret)
+		goto err;
+
+	if (lna_gain < ARRAY_SIZE(fc001x_lna_gain_table)) {
+		int_lna = fc001x_lna_gain_table[lna_gain];
+		tot_agc_gain = (abs((int_temp >> 5) - 7) -2) * 2 +
+			(int_temp & 0x1f) * 2;
+		power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10;
+		
+		if (power >= 45)
+			*strength = 255;	/* 100% */
+		else if (power < -95)
+			*strength = 0;
+		else
+			*strength = (power + 95) * 255 / 140;
+		*strength |= *strength << 8;
+	} else {
+		ret = -1;
+		goto err;
+	}
+
+	return 0;
+
+err_gate:
+	ret = rtl2832_i2c_gate_ctrl(fe, false);
+err:
 	*strength = 0;
+	return ret;
+}
+
+static int rtl2832_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct rtl2832_priv *priv = fe->demodulator_priv;
+	int ret, if_agc;
+	u32 fsm_stage, if_agc_raw;
+
+	if ((priv->tuner == TUNER_RTL2832_FC0012) ||
+	    (priv->tuner == TUNER_RTL2832_FC0013))
+		return rtl2832_fc001x_read_signal_strength(fe, strength);
+
+	ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &fsm_stage);
+	if (ret)
+		goto err;
+	if (fsm_stage < 10)
+		*strength = 0;
+	else {
+		/* if_agc is read as a 10bit binary */
+		ret = rtl2832_rd_demod_reg(priv, DVBT_IF_AGC_VAL, &if_agc_raw);
+		if (ret)
+			goto err;
+	info("%s: if_agc_raw: 0x%04x", __func__, if_agc_raw);
+		if (if_agc_raw < (1 << 9))
+			if_agc = if_agc_raw;
+		else {
+			if_agc = -(~(if_agc_raw-1) & 0x1ff);
+		}
+		*strength = 55 - if_agc / 182;
+		*strength |= *strength << 8;
+	}
+
 	return 0;
+err:
+	info("%s: failed=%d", __func__, ret);
+	return ret;
 }
 
 static struct dvb_frontend_ops rtl2832_ops;
--- /dev/null	2012-05-05 09:43:48.063333275 +0200
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu_tuners.h	2012-05-02 22:43:24.446371878 +0200
@@ -0,0 +1,44 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL28XXU_TUNERS_H
+#define RTL28XXU_TUNERS_H
+
+enum rtl28xxu_tuner {
+	TUNER_NONE,
+
+	TUNER_RTL2830_QT1010,
+	TUNER_RTL2830_MT2060,
+	TUNER_RTL2830_MXL5005S,
+
+	TUNER_RTL2832_MT2266,
+	TUNER_RTL2832_FC2580,
+	TUNER_RTL2832_MT2063,
+	TUNER_RTL2832_MAX3543,
+	TUNER_RTL2832_TUA9001,
+	TUNER_RTL2832_MXL5007T,
+	TUNER_RTL2832_FC0012,
+	TUNER_RTL2832_E4000,
+	TUNER_RTL2832_TDA18272,
+	TUNER_RTL2832_FC0013,
+};
+
+#endif
--- a/drivers/media/dvb/dvb-usb/rtl28xxu.h	2012-02-29 05:45:38.000000000 +0100
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.h	2012-05-02 22:43:53.002893237 +0200
@@ -84,25 +84,6 @@ enum rtl28xxu_chip_id {
 	CHIP_ID_RTL2832U,
 };
 
-enum rtl28xxu_tuner {
-	TUNER_NONE,
-
-	TUNER_RTL2830_QT1010,
-	TUNER_RTL2830_MT2060,
-	TUNER_RTL2830_MXL5005S,
-
-	TUNER_RTL2832_MT2266,
-	TUNER_RTL2832_FC2580,
-	TUNER_RTL2832_MT2063,
-	TUNER_RTL2832_MAX3543,
-	TUNER_RTL2832_TUA9001,
-	TUNER_RTL2832_MXL5007T,
-	TUNER_RTL2832_FC0012,
-	TUNER_RTL2832_E4000,
-	TUNER_RTL2832_TDA18272,
-	TUNER_RTL2832_FC0013,
-};
-
 struct rtl28xxu_req {
 	u16 value;
 	u16 index;
--- a/drivers/media/dvb/dvb-usb/rtl28xxu.c      2012-05-01 12:28:26.407776103 +0200
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c  2012-05-05 14:25:04.212866649 +0200
@@ -21,6 +21,7 @@
  */
 
 #include "rtl28xxu.h"
+#include "rtl28xxu_tuners.h"
 
 #include "rtl2830.h"
 #include "rtl2832.h"
