Add set LNB tone support.

Test:

dvbtune -s dvbs2 -i 1110000 -r 30000 -m psk8 -p h
SPACECAST2# i2cget -y 0 0x60 0
0x9c

dvbtune -s dvbs2 -i 1110000 -r 30000 -m psk8 -p v
SPACECAST2# i2cget -y 0 0x60 0
0x98

dvbtune -s dvbs2 -i 1110000 -r 30000 -m psk8 -p h
SPACECAST2# i2cget -y 0 0x60 0
0x90

dvbtune -s dvbs2 -i 1110000 -r 30000 -m psk8 -t
SPACECAST2# i2cget -y 0 0x60 0
0xb0

Google-Bug-Id: 19432272
Change-Id: Ibbc11ce2de4626e0014263a058557024eb6f9452
diff --git a/drivers/media/dvb/dvb-usb/sc100.c b/drivers/media/dvb/dvb-usb/sc100.c
index 82aad08..2905f27 100644
--- a/drivers/media/dvb/dvb-usb/sc100.c
+++ b/drivers/media/dvb/dvb-usb/sc100.c
@@ -13,7 +13,6 @@
 #include "dvbsky_m88rs6000.h"
 #include "mach/comcerto-2000.h"
 
-#define SC100_VOLTAGE_CTRL (0x60)
 #define SC100_READ_MSG 0
 #define SC100_WRITE_MSG 1
 #define SC100_MAC_ADDR_INDEX 1
@@ -25,37 +24,141 @@
 /* debug */
 static int dvb_usb_sc100_debug;
 module_param_named(debug, dvb_usb_sc100_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer 4=rc(or-able))."
-						DVB_USB_DEBUG_STATUS);
+MODULE_PARM_DESC(debug, "set debugging level (1=info)." DVB_USB_DEBUG_STATUS);
 
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
+/* Control register for LNP settings
+ * B7: I2C Control
+ *     1 - enabled, 0 - disabled
+ * B6: Reserved
+ * B5: Tone Gate
+ *     1 - on, 0 - off
+ * B4: Tone Mode
+ *     1 - internal, 0 - external
+ * B3: LNP output voltage
+ *     1 - enabled, 0 - disabled
+ * B2-1: Output voltage selection
+ *     0 - 13V, 4 - 18V
+ */
+#define LNB_I2C_ADDRESS (0x60)
+#define LNB_CONTROL_REGISTER_1 (0x00)
+#define LNB_CONTROL_REGISTER_1_I2C_CONTROL_MASK (0x80)
+#define LNB_CONTROL_REGISTER_1_I2C_CONTROL_SHIFT (0x07)
+#define LNB_CONTROL_REGISTER_1_I2C_CONTROL_ENABLED (0x01)
+#define LNB_CONTROL_REGISTER_1_I2C_CONTROL_DISABLED (0x00)
+#define LNB_CONTROL_REGISTER_1_TONE_GATE_MASK (0x20)
+#define LNB_CONTROL_REGISTER_1_TONE_GATE_SHIFT (0x05)
+#define LNB_CONTROL_REGISTER_1_TONE_GATE_ON (0x01)
+#define LNB_CONTROL_REGISTER_1_TONE_GATE_OFF (0x00)
+#define LNB_CONTROL_REGISTER_1_TONE_MODE_MASK (0x10)
+#define LNB_CONTROL_REGISTER_1_TONE_MODE_SHIFT (0x4)
+#define LNB_CONTROL_REGISTER_1_TONE_MODE_INTERNAL (0x01)
+#define LNB_CONTROL_REGISTER_1_TONE_MODE_EXTERNAL (0x00)
+#define LNB_CONTROL_REGISTER_1_LNB_OUTPUT_VOLTAGE_MASK (0x08)
+#define LNB_CONTROL_REGISTER_1_LNB_OUTPUT_VOLTAGE_SHIFT (0x03)
+#define LNB_CONTROL_REGISTER_1_LNB_OUTPUT_VOLTAGE_ON (0x01)
+#define LNB_CONTROL_REGISTER_1_LNB_OUTPUT_VOLTAGE_OFF 0x00
+#define LNB_CONTROL_REGISTER_1_LNB_VOLTAGE_SELECTION_MASK 0x07
+#define LNB_CONTROL_REGISTER_1_LNB_VOLTAGE_SELECTION_SHIFT 0x00
+#define LNB_CONTROL_REGISTER_1_LNB_VOLTAGE_SELECTION_13V 0x00
+#define LNB_CONTROL_REGISTER_1_LNB_VOLTAGE_SELECTION_18V 0x04
+#define LNB_FIELD_SET(data, field, value) \
+{ \
+	data &= ~(LNB_CONTROL_REGISTER_1_ ## field ## _MASK); \
+	data |= LNB_CONTROL_REGISTER_1_ ## field ## _ ## value \
+	<< (LNB_CONTROL_REGISTER_1_ ## field ## _ ## SHIFT); \
+}
+
+struct i2c_adapter *i2c_adap;
+
 static int sc100_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
 {
 	return comcerto_mac_addr_get(mac, SC100_MAC_ADDR_INDEX);
 };
 
+static int sc100_writereg(u8 reg, u8 data)
+{
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = LNB_I2C_ADDRESS,
+		.flags = 0, .buf = buf, .len = 2 };
+	int ret;
+
+	deb_info("%s: [W] R:0x%02x, V:0x%02x", __func__, reg, data);
+
+	ret = i2c_transfer(i2c_adap, &msg, 1);
+	if (ret != 1) {
+		err("%s: [W] R:0x%02x, V:0x02x, E:%d", __func__, reg, data, ret);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static u8 sc100_readreg(u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+
+	struct i2c_msg msg[] = {
+		{ .addr = LNB_I2C_ADDRESS, .flags = 0, .buf = b0, .len = 1 },
+		{ .addr = LNB_I2C_ADDRESS, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+	};
+	ret = i2c_transfer(i2c_adap, msg, 2);
+
+	if (ret != 2) {
+		err("%s: [R] R:0x%x, E:%d", __func__, reg, ret);
+		return ret;
+	}
+	deb_info("%s: [R] R:0x%02x, V:0x%02x", __func__, reg, b1[0]);
+
+	return b1[0];
+}
+
+static int sc100_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	u8 lnb_reg = sc100_readreg(LNB_CONTROL_REGISTER_1);
+	switch (tone) {
+	case SEC_TONE_ON:
+		info("%s: LNB TONE=ON\n", __func__);
+		LNB_FIELD_SET(lnb_reg, I2C_CONTROL, ENABLED);
+		LNB_FIELD_SET(lnb_reg, TONE_GATE, ON);
+		LNB_FIELD_SET(lnb_reg, TONE_MODE, INTERNAL);
+		break;
+	case SEC_TONE_OFF:
+		info("%s: LNB TONE=OFF\n", __func__);
+		LNB_FIELD_SET(lnb_reg, I2C_CONTROL, ENABLED);
+		LNB_FIELD_SET(lnb_reg, TONE_GATE, OFF);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return sc100_writereg(LNB_CONTROL_REGISTER_1, lnb_reg);
+}
+
 static int sc100_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 {
-	static u8 command_13v[] = {0x00, 0x88};
-	static u8 command_18v[] = {0x00, 0x8c};
-	static u8 command_off[] = {0x00, 0x80};
-	struct i2c_msg msg = {
-		.addr = SC100_VOLTAGE_CTRL,
-		.flags = 0,
-		.buf = command_off,
-		.len = 2,
-	};
-	struct i2c_adapter *i2c_adap = i2c_get_adapter(0);
-
-	if (voltage == SEC_VOLTAGE_18)
-		msg.buf = command_18v;
-	else if (voltage == SEC_VOLTAGE_13)
-		msg.buf = command_13v;
-
-	i2c_transfer(i2c_adap, &msg, 1);
-
-	return 0;
+	u8 lnb_reg = sc100_readreg(LNB_CONTROL_REGISTER_1);
+	switch (voltage) {
+	case SEC_VOLTAGE_18:
+		info("%s: LNB VOLTAGE=18V\n", __func__);
+		LNB_FIELD_SET(lnb_reg, I2C_CONTROL, ENABLED);
+		LNB_FIELD_SET(lnb_reg, LNB_OUTPUT_VOLTAGE, ON);
+		LNB_FIELD_SET(lnb_reg, LNB_VOLTAGE_SELECTION, 18V);
+		break;
+	case SEC_VOLTAGE_13:
+		info("%s: LNB VOLTAGE=13V\n", __func__);
+		LNB_FIELD_SET(lnb_reg, I2C_CONTROL, ENABLED);
+		LNB_FIELD_SET(lnb_reg, LNB_OUTPUT_VOLTAGE, ON);
+		LNB_FIELD_SET(lnb_reg, LNB_VOLTAGE_SELECTION, 13V);
+		break;
+	default:
+		info("%s: LNB VOLTAGE=OFF\n", __func__);
+		LNB_FIELD_SET(lnb_reg, I2C_CONTROL, ENABLED);
+		LNB_FIELD_SET(lnb_reg, LNB_OUTPUT_VOLTAGE, OFF);
+		break;
+	}
+	return sc100_writereg(LNB_CONTROL_REGISTER_1, lnb_reg);
 }
 
 static struct dvbsky_m88rs6000_config sc100_config = {
@@ -70,10 +173,12 @@
 
 static int sc100_frontend_attach(struct dvb_usb_adapter *d)
 {
-	struct i2c_adapter *i2c_adap = i2c_get_adapter(0);
+	/* Initializes i2c adapter when the frontend is attached. */
+	i2c_adap = i2c_get_adapter(0);
 	d->fe_adap[0].fe = dvb_attach(dvbsky_m88rs6000_attach, &sc100_config,
 				i2c_adap);
 	if (d->fe_adap[0].fe != NULL) {
+		d->fe_adap[0].fe->ops.set_tone = sc100_set_tone;
 		d->fe_adap[0].fe->ops.set_voltage = sc100_set_voltage;
 		info("Attached SC100!\n");
 		return 0;
diff --git a/drivers/media/dvb/dvb-usb/sc100.h b/drivers/media/dvb/dvb-usb/sc100.h
index 083397b..fbc229b 100644
--- a/drivers/media/dvb/dvb-usb/sc100.h
+++ b/drivers/media/dvb/dvb-usb/sc100.h
@@ -4,6 +4,5 @@
 #define DVB_USB_LOG_PREFIX "sc100"
 #include "dvb-usb.h"
 
-#define deb_xfer(args...) dprintk(dvb_usb_sc100_debug, 0x02, args)
-#define deb_rc(args...)   dprintk(dvb_usb_sc100_debug, 0x04, args)
+#define deb_info(args...)   dprintk(dvb_usb_sc100_debug, 0x01, args)
 #endif
diff --git a/drivers/media/dvb/frontends/dvbsky_m88rs6000.c b/drivers/media/dvb/frontends/dvbsky_m88rs6000.c
index 954c914..d3fbdcc 100644
--- a/drivers/media/dvb/frontends/dvbsky_m88rs6000.c
+++ b/drivers/media/dvb/frontends/dvbsky_m88rs6000.c
@@ -610,6 +610,9 @@
 		return -EINVAL;
 	}
 
+	if(state->config->set_tone)
+		state->config->set_tone(fe, tone);
+
 	data_a1 = m88rs6000_readreg(state, 0xa1);
 	data_a2 = m88rs6000_readreg(state, 0xa2);
 
diff --git a/drivers/media/dvb/frontends/dvbsky_m88rs6000.h b/drivers/media/dvb/frontends/dvbsky_m88rs6000.h
index f433519..e7e7f53 100644
--- a/drivers/media/dvb/frontends/dvbsky_m88rs6000.h
+++ b/drivers/media/dvb/frontends/dvbsky_m88rs6000.h
@@ -18,6 +18,8 @@
 	u8 tuner_readstops;
 	/* Set device param to start dma */
 	int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+	/* Set LNB tone */
+	int (*set_tone)(struct dvb_frontend *fe, const fe_sec_tone_mode_t t);
 	/* Set LNB voltage */
 	int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
 };