diff -uraBN a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1018,15 +1018,14 @@
 	{ },
 };
 
-static int dummy_probe(struct i2c_client *client,
-		       const struct i2c_device_id *id)
+static int dummy_probe(struct i2c_client *client)
 {
 	return 0;
 }
 
 static struct i2c_driver dummy_driver = {
 	.driver.name	= "dummy",
-	.probe		= dummy_probe,
+	.probe_new	= dummy_probe,
 	.id_table	= dummy_id,
 };
 
@@ -2237,6 +2236,20 @@
 }
 EXPORT_SYMBOL_GPL(i2c_get_device_id);
 
+/**
+ * i2c_client_get_device_id - get the driver match table entry of a device
+ * @client: the device to query. The device must be bound to a driver
+ *
+ * Returns a pointer to the matching entry if found, NULL otherwise.
+ */
+const struct i2c_device_id *i2c_client_get_device_id(const struct i2c_client *client)
+{
+	const struct i2c_driver *drv = to_i2c_driver(client->dev.driver);
+
+	return i2c_match_id(drv->id_table, client);
+}
+EXPORT_SYMBOL_GPL(i2c_client_get_device_id);
+
 /* ----------------------------------------------------
  * the i2c address scanning function
  * Will not work for 10-bit addresses!

diff -uraBN a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -40,7 +40,7 @@
 static int dvb_shutdown_timeout;
 static int dvb_force_auto_inversion;
 static int dvb_override_tune_delay;
-static int dvb_powerdown_on_sleep = 1;
+static int dvb_powerdown_on_sleep;
 static int dvb_mfe_wait_time = 5;
 
 module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
@@ -918,6 +918,7 @@
 
 	/* If the standard is for satellite, convert frequencies to kHz */
 	switch (c->delivery_system) {
+	case SYS_DSS:
 	case SYS_DVBS:
 	case SYS_DVBS2:
 	case SYS_TURBO:
@@ -943,6 +944,7 @@
 	u32 step = max(fe_step, tuner_step);
 
 	switch (c->delivery_system) {
+	case SYS_DSS:
 	case SYS_DVBS:
 	case SYS_DVBS2:
 	case SYS_TURBO:
@@ -974,6 +976,7 @@
 
 	/* range check: symbol rate */
 	switch (c->delivery_system) {
+	case SYS_DSS:
 	case SYS_DVBS:
 	case SYS_DVBS2:
 	case SYS_TURBO:
@@ -1037,9 +1040,14 @@
 	}
 
 	c->stream_id = NO_STREAM_ID_FILTER;
+    c->modcode = MODCODE_ALL;
 	c->scrambling_sequence_index = 0;/* default sequence */
 
 	switch (c->delivery_system) {
+	case SYS_DSS:
+		c->modulation = QPSK;
+		c->rolloff = ROLLOFF_20;
+		break;
 	case SYS_DVBS:
 	case SYS_DVBS2:
 	case SYS_TURBO:
@@ -1111,7 +1119,7 @@
 	_DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING),
 
 	_DTV_CMD(DTV_STREAM_ID),
-	_DTV_CMD(DTV_DVBT2_PLP_ID_LEGACY),
+	_DTV_CMD(DTV_MODCODE),
 	_DTV_CMD(DTV_SCRAMBLING_SEQUENCE_INDEX),
 	_DTV_CMD(DTV_LNA),
 
@@ -1462,8 +1470,12 @@
 
 	/* Multistream support */
 	case DTV_STREAM_ID:
-	case DTV_DVBT2_PLP_ID_LEGACY:
 		tvp->u.data = c->stream_id;
+		break;
+
+	/* Modcode support */
+	case DTV_MODCODE:
+		tvp->u.data = c->modcode;
 		break;
 
 	/* Physical layer scrambling support */
@@ -1821,6 +1833,7 @@
 	} else {
 		/* default values */
 		switch (c->delivery_system) {
+		case SYS_DSS:
 		case SYS_DVBS:
 		case SYS_DVBS2:
 		case SYS_ISDBS:
@@ -1883,6 +1896,14 @@
 		dev_dbg(fe->dvb->device,
 			"%s: SET cmd 0x%08x (%s) to 0x%08x\n",
 			__func__, cmd, dtv_cmd_name(cmd), data);
+
+	/* Allow the frontend to validate incoming properties */
+	if (fe->ops.set_property) {
+		r = fe->ops.set_property(fe, cmd, data);
+		if (r < 0)
+			return r;
+	}
+
 	switch (cmd) {
 	case DTV_CLEAR:
 		/*
@@ -2016,8 +2037,12 @@
 
 	/* Multistream support */
 	case DTV_STREAM_ID:
-	case DTV_DVBT2_PLP_ID_LEGACY:
 		c->stream_id = data;
+		break;
+
+    /* Modcode support */
+	case DTV_MODCODE:
+		c->modcode = data;
 		break;
 
 	/* Physical layer scrambling support */
@@ -2288,6 +2313,9 @@
 	case SYS_DVBC_ANNEX_C:
 		rolloff = 113;
 		break;
+	case SYS_DSS:
+		rolloff = 120;
+		break;
 	case SYS_DVBS:
 	case SYS_TURBO:
 	case SYS_ISDBS:
@@ -2422,6 +2450,77 @@
 	dev_dbg(fe->dvb->device, "%s:\n", __func__);
 
 	switch (cmd) {
+
+    case FE_READ_TEMP:
+		if (fe->ops.read_temp) {
+				err = fe->ops.read_temp(fe, parg);
+		}
+		break;
+
+	case FE_ECP3FW_READ:
+		//printk("FE_ECP3FW_READ *****************");
+		if (fe->ops.spi_read) {
+			struct ecp3_info *info = parg;	
+			fe->ops.spi_read(fe, info);
+		}
+		err = 0;
+		break;
+	case FE_ECP3FW_WRITE:
+		//printk("FE_ECP3FW_WRITE *****************");
+		if (fe->ops.spi_write) {
+			struct ecp3_info *info = parg;	
+			fe->ops.spi_write(fe, info);
+		
+		}
+		err = 0;
+		break;
+
+	case FE_24CXX_READ:
+		//printk("FE_24CXX_READ *****************");
+		if (fe->ops.mcu_read) {
+			struct mcu24cxx_info *info = parg;	
+			fe->ops.mcu_read(fe, info);
+		}
+		err = 0;
+		break;
+	case FE_24CXX_WRITE:
+		//printk("FE_24CXX_WRITE *****************");
+		if (fe->ops.mcu_write) {
+			struct mcu24cxx_info *info = parg;	
+			fe->ops.mcu_write(fe, info);
+		
+		}
+		err = 0;
+		break;
+	case FE_REGI2C_READ:
+		if (fe->ops.reg_i2cread) {
+			struct usbi2c_access *info = parg;	
+			fe->ops.reg_i2cread(fe, info);
+		}
+		err = 0;
+		break;
+	case FE_REGI2C_WRITE:
+		if (fe->ops.reg_i2cwrite) {
+			struct usbi2c_access *info = parg;	
+			fe->ops.reg_i2cwrite(fe, info);
+		}
+		err = 0;
+		break;
+	case FE_EEPROM_READ:
+		if (fe->ops.eeprom_read) {
+			struct eeprom_info *info = parg;	
+			fe->ops.eeprom_read(fe, info);
+		}
+		err = 0;
+		break;
+	case FE_EEPROM_WRITE:
+		if (fe->ops.eeprom_write) {
+			struct eeprom_info *info = parg;	
+			fe->ops.eeprom_write(fe, info);
+		}
+		err = 0;
+		break;
+
 	case FE_SET_PROPERTY: {
 		struct dtv_properties *tvps = parg;
 		struct dtv_property *tvp = NULL;
@@ -2664,37 +2763,25 @@
 
 	case FE_READ_BER:
 		if (fe->ops.read_ber) {
-			if (fepriv->thread)
 				err = fe->ops.read_ber(fe, parg);
-			else
-				err = -EAGAIN;
 		}
 		break;
 
 	case FE_READ_SIGNAL_STRENGTH:
 		if (fe->ops.read_signal_strength) {
-			if (fepriv->thread)
 				err = fe->ops.read_signal_strength(fe, parg);
-			else
-				err = -EAGAIN;
 		}
 		break;
 
 	case FE_READ_SNR:
 		if (fe->ops.read_snr) {
-			if (fepriv->thread)
 				err = fe->ops.read_snr(fe, parg);
-			else
-				err = -EAGAIN;
 		}
 		break;
 
 	case FE_READ_UNCORRECTED_BLOCKS:
 		if (fe->ops.read_ucblocks) {
-			if (fepriv->thread)
 				err = fe->ops.read_ucblocks(fe, parg);
-			else
-				err = -EAGAIN;
 		}
 		break;
 
@@ -2754,7 +2841,17 @@
 	if (fe->exit == DVB_FE_DEVICE_REMOVED)
 		return -ENODEV;
 
-	if (adapter->mfe_shared) {
+	if (adapter->mfe_shared == 2) {
+		mutex_lock(&adapter->mfe_lock);
+		if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+			if (adapter->mfe_dvbdev &&
+			    !adapter->mfe_dvbdev->writers) {
+				mutex_unlock(&adapter->mfe_lock);
+				return -EBUSY;
+			}
+			adapter->mfe_dvbdev = dvbdev;
+		}
+	} else if (adapter->mfe_shared) {
 		mutex_lock(&adapter->mfe_lock);
 
 		if (!adapter->mfe_dvbdev)

diff -uraBN a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -922,6 +922,14 @@
 
 source "drivers/media/dvb-frontends/drx39xyj/Kconfig"
 
+config DVB_SI2183
+	tristate "Silicon Labs Si2183 DVB-T/T2/C/C2/S/S2/S2X/ISDB-T demodulator"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.
+
+
 comment "Common Interface (EN50221) controller drivers"
 	depends on DVB_CORE
 

diff -uraBN a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -136,3 +136,4 @@
 obj-$(CONFIG_DVB_ZL10036) += zl10036.o
 obj-$(CONFIG_DVB_ZL10039) += zl10039.o
 obj-$(CONFIG_DVB_ZL10353) += zl10353.o
+obj-$(CONFIG_DVB_SI2183) += si2183.o

diff -uraBN a/drivers/media/dvb-frontends/si2183.c b/drivers/media/dvb-frontends/si2183.c
--- a/drivers/media/dvb-frontends/si2183.c
+++ b/drivers/media/dvb-frontends/si2183.c
@@ -0,0 +1,1843 @@
+/*
+ * Silicon Labs Si2183(2) DVB-T/T2/C/C2/S/S2 demodulator driver
+ *
+ *    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.
+ */
+
+#include "si2183.h"
+#include <media/dvb_frontend.h>
+#include <linux/firmware.h>
+#include <linux/i2c-mux.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
+#define SI2183_USE_I2C_MUX
+#endif
+
+#define SI2183_B60_FIRMWARE "dvb-demod-si2183-b60-01.fw"
+
+#define SI2183_PROP_MODE	0x100a
+#define SI2183_PROP_DVBC_CONST	0x1101
+#define SI2183_PROP_DVBC_SR	0x1102
+#define SI2183_PROP_DVBT_HIER	0x1201
+#define SI2183_PROP_DVBT2_MODE	0x1304
+#define SI2183_PROP_DVBS2_SR	0x1401
+#define SI2183_PROP_DVBS_SR	0x1501
+#define SI2183_PROP_MCNS_CONST  0x1601
+#define SI2183_PROP_MCNS_SR     0x1602
+
+#define SI2183_ARGLEN      30
+struct si2183_cmd {
+	u8 args[SI2183_ARGLEN];
+	unsigned wlen;
+	unsigned rlen;
+};
+
+static const struct dvb_frontend_ops si2183_ops;
+
+LIST_HEAD(silist);
+
+struct si_base {
+	struct mutex i2c_mutex;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+	struct i2c_mux_core *muxc;
+#endif
+	struct list_head     silist;
+
+	u8                   adr;
+	struct i2c_adapter  *i2c;
+	u32                  count;
+
+	struct i2c_adapter  *tuner_adapter;
+
+#ifndef SI2183_USE_I2C_MUX
+	struct i2c_client *i2c_gate_client;
+#endif
+};
+
+/* state struct */
+struct si2183_dev {
+	struct dvb_frontend fe;
+	enum fe_delivery_system delivery_system;
+	enum fe_status fe_status;
+	u8 stat_resp;
+	u16 snr;
+	bool fw_loaded;
+	u8 ts_mode;
+	bool ts_clock_inv;
+	bool ts_clock_gapped;
+	int start_clk_mode;
+	u8 agc_mode;
+	struct si_base *base;
+	void (*RF_switch)(struct i2c_adapter * i2c,u8 rf_in,u8 flag);
+	u8 rf_in;
+	u8 active_fe;
+	void (*TS_switch)(struct i2c_adapter * i2c,u8 flag);
+	void (*LED_switch)(struct i2c_adapter * i2c,u8 flag);
+
+	void (*write_properties) (struct i2c_adapter *i2c,u8 reg, u32 buf);
+	void (*read_properties) (struct i2c_adapter *i2c,u8 reg, u32 *buf);
+
+	void (*write_eeprom) (struct i2c_adapter *i2c,u8 reg, u8 buf);
+	void (*read_eeprom) (struct i2c_adapter *i2c,u8 reg, u8 *buf);
+};
+
+/* Own I2C adapter locking is needed because of I2C gate logic. */
+static int si2183_i2c_master_send_unlocked(const struct i2c_client *client,
+					   const char *buf, int count)
+{
+	int ret;
+	struct i2c_msg msg = {
+		.addr = client->addr,
+		.flags = 0,
+		.len = count,
+		.buf = (char *)buf,
+	};
+
+	ret = __i2c_transfer(client->adapter, &msg, 1);
+	return (ret == 1) ? count : ret;
+}
+
+static int si2183_i2c_master_recv_unlocked(const struct i2c_client *client,
+					   char *buf, int count)
+{
+	int ret;
+	struct i2c_msg msg = {
+		.addr = client->addr,
+		.flags = I2C_M_RD,
+		.len = count,
+		.buf = buf,
+	};
+
+	ret = __i2c_transfer(client->adapter, &msg, 1);
+	return (ret == 1) ? count : ret;
+}
+
+/* execute firmware command */
+static int si2183_cmd_execute_unlocked(struct i2c_client *client,
+				       struct si2183_cmd *cmd)
+{
+	int ret;
+	unsigned long timeout;
+
+	if (cmd->wlen) {
+		/* write cmd and args for firmware */
+		ret = si2183_i2c_master_send_unlocked(client, cmd->args,
+						      cmd->wlen);
+		if (ret < 0) {
+			goto err;
+		} else if (ret != cmd->wlen) {
+			ret = -EREMOTEIO;
+			goto err;
+		}
+	}
+
+	if (cmd->rlen) {
+		/* wait cmd execution terminate */
+		#define TIMEOUT 500
+		timeout = jiffies + msecs_to_jiffies(TIMEOUT);
+		while (!time_after(jiffies, timeout)) {
+			ret = si2183_i2c_master_recv_unlocked(client, cmd->args,
+							      cmd->rlen);
+			if (ret < 0) {
+				goto err;
+			} else if (ret != cmd->rlen) {
+				ret = -EREMOTEIO;
+				goto err;
+			}
+
+			/* firmware ready? */
+			if ((cmd->args[0] >> 7) & 0x01)
+				break;
+		}
+
+		dev_dbg(&client->dev, "cmd execution took %d ms\n",
+				jiffies_to_msecs(jiffies) -
+				(jiffies_to_msecs(timeout) - TIMEOUT));
+
+		/* error bit set? */
+		if ((cmd->args[0] >> 6) & 0x01) {
+			ret = -EREMOTEIO;
+			goto err;
+		}
+
+		if (!((cmd->args[0] >> 7) & 0x01)) {
+			ret = -ETIMEDOUT;
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int si2183_cmd_execute(struct i2c_client *client, struct si2183_cmd *cmd)
+{
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	int ret;
+
+
+	mutex_lock(&dev->base->i2c_mutex);
+	ret = si2183_cmd_execute_unlocked(client, cmd);
+	mutex_unlock(&dev->base->i2c_mutex);
+
+	return ret;
+}
+
+static int si2183_set_prop(struct i2c_client *client, u16 prop, u16 *val)
+{
+	struct si2183_cmd cmd;
+	int ret;
+
+	cmd.args[0] = 0x14;
+	cmd.args[1] = 0x00;
+	cmd.args[2] = (u8) prop;
+	cmd.args[3] = (u8) (prop >> 8);
+	cmd.args[4] = (u8) (*val);
+	cmd.args[5] = (u8) (*val >> 8);
+	cmd.wlen = 6;
+	cmd.rlen = 4;
+	ret = si2183_cmd_execute(client, &cmd);
+	*val = (cmd.args[2] | (cmd.args[3] << 8));
+	return ret;
+}
+#if 0
+static int si2183_get_prop(struct i2c_client *client, u16 prop, u16 *val)
+{
+	struct si2183_cmd cmd;
+	int ret;
+
+	cmd.args[0] = 0x15;
+	cmd.args[1] = 0x00;
+	cmd.args[2] = (u8) prop;
+	cmd.args[3] = (u8) (prop >> 8);
+	cmd.wlen = 4;
+	cmd.rlen = 4;
+	ret = si2183_cmd_execute(client, &cmd);
+	*val = (cmd.args[2] | (cmd.args[3] << 8));
+	return ret;
+}
+#endif
+
+static int si2183_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	struct si2183_cmd cmd;
+	u16 agc;
+
+	*status = 0;
+
+	if (!dev->active_fe) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	if ((dev->delivery_system != c->delivery_system) || (dev->delivery_system == 0))
+		return 0;
+
+	switch (c->delivery_system) {
+	case SYS_DVBT:
+		memcpy(cmd.args, "\xa0\x01", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 13;
+		dev->snr = 2;
+		break;
+	case SYS_DVBC_ANNEX_A:
+		memcpy(cmd.args, "\x90\x01", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 9;
+		dev->snr = 2;
+		break;
+	case SYS_DVBC_ANNEX_B:
+		memcpy(cmd.args, "\x98\x01", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 10;
+		dev->snr = 2;
+		break;
+	case SYS_DVBT2:
+		memcpy(cmd.args, "\x50\x01", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 14;
+		dev->snr = 2;
+		break;
+	case SYS_DVBS:
+		memcpy(cmd.args, "\x60\x01", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 10;
+		dev->snr = 5;
+		break;
+	case SYS_DVBS2:
+		memcpy(cmd.args, "\x70\x01", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 13;
+		dev->snr = 5;
+		break;
+	case SYS_ISDBT:
+		memcpy(cmd.args, "\xa4\x01", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 14;
+		dev->snr = 2;
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = si2183_cmd_execute(client, &cmd);
+	if (ret) {
+		dev_err(&client->dev, "read_status fe%d cmd_exec failed=%d\n", fe->id, ret);
+		goto err;
+	}
+
+	dev->stat_resp = cmd.args[2];
+	switch ((dev->stat_resp >> 1) & 0x03) {
+	case 0x01:
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		break;
+	case 0x03:
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+				FE_HAS_SYNC | FE_HAS_LOCK;
+		dev->snr *= cmd.args[3] * 164;
+		c->cnr.len = 2;
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = (s64) cmd.args[3] * 250;
+		c->cnr.stat[1].scale = FE_SCALE_RELATIVE;
+		c->cnr.stat[1].svalue = dev->snr;
+
+		// writing missing properties
+		// CONSTELLATION or modulation
+		switch (cmd.args[8] & 0x3f){
+			case 0x03:
+			c->modulation = QPSK;
+			break;
+			case 0x07:
+			c->modulation = QAM_16;
+			break;
+			case 0x08:
+			c->modulation = QAM_32;
+			break;
+			case 0x09:
+			c->modulation = QAM_64;
+			break;
+			case 0x0a:
+			c->modulation = QAM_128;
+			break;
+			case 0x0b:
+			c->modulation = QAM_256;
+			break;
+			case 0x0e:
+			c->modulation = PSK_8;
+			break;
+			case 0x14:
+			c->modulation = APSK_16;
+			break;
+			case 0x17:
+			c->modulation = APSK_8L;
+			break;
+			case 0x18:
+			c->modulation = APSK_16L;
+			break;
+			case 0x15:
+			c->modulation = APSK_32;
+			break;
+			case 0x19:
+			c->modulation = APSK_32L;
+			break;
+			case 0x1a:
+			c->modulation = APSK_32;
+			break;
+			default:
+				c->modulation = QPSK;
+			break;	
+		}
+		// fec_inner
+		switch (c->delivery_system) {
+		case SYS_DVBT2:
+		switch (cmd.args[12] & 0x0f){
+			case 0x01:
+			c->fec_inner = FEC_1_2;
+			break;
+			case 0x02:
+			c->fec_inner = FEC_2_3;
+			break;
+			case 0x03:
+			c->fec_inner = FEC_3_4;
+			break;
+			case 0x04:
+			c->fec_inner = FEC_4_5;
+			break;
+			case 0x05:
+			c->fec_inner = FEC_5_6;
+			break;
+			case 0x0a:
+			c->fec_inner = FEC_1_3;
+			break;
+			case 0x0c:
+			c->fec_inner = FEC_2_5;
+			break;
+			case 0x0d:
+			c->fec_inner = FEC_3_5;
+			break;
+			default:
+				c->fec_inner = FEC_AUTO;
+			break;	
+		}
+		break;
+	case SYS_DVBS:
+		switch (cmd.args[9] & 0x0f){
+			case 0x01:
+			c->fec_inner = FEC_1_2;
+			break;
+			case 0x02:
+			c->fec_inner = FEC_2_3;
+			break;
+			case 0x03:
+			c->fec_inner = FEC_3_4;
+			break;
+			case 0x04:
+			c->fec_inner = FEC_4_5;
+			break;
+			case 0x05:
+			c->fec_inner = FEC_5_6;
+			break;
+			case 0x06:
+			c->fec_inner = FEC_6_7;
+			break;
+			case 0x07:
+			c->fec_inner = FEC_7_8;
+			break;
+			default:
+				c->fec_inner = FEC_AUTO;
+			break;	
+		}
+		break;
+	case SYS_DVBS2:
+		switch (cmd.args[9] & 0x1f){
+			case 0x01:
+			c->fec_inner = FEC_1_2;
+			break;
+			case 0x02:
+			c->fec_inner = FEC_2_3;
+			break;
+			case 0x03:
+			c->fec_inner = FEC_3_4;
+			break;
+			case 0x04:
+			c->fec_inner = FEC_4_5;
+			break;
+			case 0x05:
+			c->fec_inner = FEC_5_6;
+			break;
+			case 0x08:
+			c->fec_inner = FEC_8_9;
+			break;
+			case 0x09:
+			c->fec_inner = FEC_9_10;
+			break;
+			case 0x0a:
+			c->fec_inner = FEC_1_3;
+			break;
+			case 0x0b:
+			c->fec_inner = FEC_1_4;
+			break;
+			case 0x0c:
+			c->fec_inner = FEC_2_5;
+			break;
+			case 0x0d:
+			c->fec_inner = FEC_3_5;
+			break;
+			default:
+				c->fec_inner = FEC_AUTO;
+			break;	
+		}
+		break;
+		default:
+				c->fec_inner = FEC_AUTO;
+				break;
+		}
+		// rolloff and pilot (only available for dvb s2)
+		switch (c->delivery_system) {
+			case SYS_DVBS2:
+			// rolloff
+			switch (cmd.args[10] & 0x07){
+			case 0x00:
+			c->rolloff = ROLLOFF_35;
+			break;
+			case 0x01:
+			c->rolloff = ROLLOFF_25;
+			break;
+			case 0x02:
+			c->rolloff = ROLLOFF_20;
+			break;
+			case 0x04:
+			c->rolloff = ROLLOFF_15;
+			break;
+			case 0x05:
+			c->rolloff = ROLLOFF_10;
+			break;
+			case 0x06:
+			c->rolloff = ROLLOFF_5;
+			break;
+			default:
+				c->rolloff = ROLLOFF_AUTO;
+			break;	
+			}
+			// pilot
+			switch ((cmd.args[8] >> 7) & 0x01){
+			case 0x01:
+			c->pilot = PILOT_ON;
+			break;
+			case 0x00:
+			c->pilot = PILOT_OFF;
+			break;
+			default:
+				c->pilot = PILOT_AUTO;
+			break;	
+			}
+			break;
+			default:
+			c->rolloff = ROLLOFF_AUTO;
+			c->pilot = PILOT_AUTO;
+			break;	
+		}			
+		break;
+	default:
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		break;
+	}
+	
+	dev->fe_status = *status;
+
+	dev_dbg(&client->dev, "status=%02x args=%*ph\n",
+			*status, cmd.rlen, cmd.args); 
+
+	if (fe->ops.tuner_ops.get_rf_strength)
+	{
+		memcpy(cmd.args, "\x8a\x00\x00\x00\x00\x00", 6);
+		cmd.wlen = 6;
+		cmd.rlen = 3;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret) {
+			dev_err(&client->dev, "read_status fe%d cmd_exec failed=%d\n", fe->id, ret);
+			goto err;
+		}
+		dev_dbg(&client->dev, "status=%02x args=%*ph\n",
+			*status, cmd.rlen, cmd.args);
+
+		agc = cmd.args[1];
+		fe->ops.tuner_ops.get_rf_strength(fe, &agc);
+	}
+
+	if ( c->strength.len == 1 && c->strength.stat[0].scale == FE_SCALE_DECIBEL) {
+		c->strength.len ++;
+		c->strength.stat[1].scale = FE_SCALE_RELATIVE;
+		c->strength.stat[1].svalue = ((100000 + (s32)c->strength.stat[0].svalue) / 1000) * 656;
+    }
+
+	return 0;
+err:
+	dev_err(&client->dev, "read_status failed=%d\n", ret);
+	return ret;
+}
+
+static int si2183_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	
+	*snr = (dev->fe_status & FE_HAS_LOCK) ? dev->snr : 0;
+
+	return 0;
+}
+
+static int si2183_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int i;
+
+	*strength = 0;
+	for (i=0; i < c->strength.len; i++)
+	{
+		if (c->strength.stat[i].scale == FE_SCALE_RELATIVE)
+			*strength = (u16)c->strength.stat[i].uvalue;
+		else if (c->strength.stat[i].scale == FE_SCALE_DECIBEL)
+			*strength = ((100000 + (s32)c->strength.stat[i].svalue)/1000) * 656;
+	}
+
+	return 0;
+}
+
+static int si2183_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct si2183_cmd cmd;
+	int ret;
+	
+	if (dev->fe_status & FE_HAS_LOCK) {
+		memcpy(cmd.args, "\x82\x00", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 3;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret) {
+			dev_err(&client->dev, "read_ber fe%d cmd_exec failed=%d\n", fe->id, ret);
+			goto err;
+		}
+		*ber = (u32)cmd.args[2] * cmd.args[1] & 0xf;
+	} else *ber = 1;
+
+	return 0;
+err:
+	dev_err(&client->dev, "read_ber failed=%d\n", ret);
+	return ret;
+}
+
+static int si2183_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct si2183_cmd cmd;
+	int ret;
+	
+	if (dev->stat_resp & 0x10) {
+		memcpy(cmd.args, "\x84\x00", 2);
+		cmd.wlen = 2;
+		cmd.rlen = 3;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret) {
+		dev_err(&client->dev, "read_ucblocks fe%d cmd_exec failed=%d\n", fe->id, ret);
+			goto err;
+		}
+
+		*ucblocks = (u16)cmd.args[2] << 8 | cmd.args[1];
+	} else 	*ucblocks = 0;
+
+	return 0;
+err:
+	dev_err(&client->dev, "read_ucblocks failed=%d\n", ret);
+	return ret;
+}
+
+
+static int si2183_set_dvbc(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct si2183_cmd cmd;
+	int ret;
+	u16 prop;
+	
+	if(dev->LED_switch)
+		dev->LED_switch(dev->base->i2c,6);
+
+	memcpy(cmd.args, "\x89\x41\x06\x12\x0\x0", 6);
+	cmd.args[1]= (dev->agc_mode &0x07)<<4 |0x1;  
+ 	cmd.wlen = 6;
+ 	cmd.rlen = 3;
+ 	ret = si2183_cmd_execute(client, &cmd);
+ 	if(ret){
+ 		dev_err(&client->dev, "err set agc mode\n");
+ 	}
+	/* dvb-c mode */
+	prop = 0x38;
+	ret = si2183_set_prop(client, SI2183_PROP_MODE, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set dvb-c mode\n");
+		return ret;
+	}
+
+	switch (c->modulation) {
+	default:
+	case QAM_AUTO:
+		prop = 0;
+		break;
+	case QAM_16:
+		prop = 7;
+		break;
+	case QAM_32:
+		prop = 8;
+		break;
+	case QAM_64:
+		prop = 9;
+		break;
+	case QAM_128:
+		prop = 10;
+		break;
+	case QAM_256:
+		prop = 11;
+		break;
+	}
+	ret = si2183_set_prop(client, SI2183_PROP_DVBC_CONST, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set dvb-c constelation\n");
+		return ret;
+	}
+
+	/* symbol rate */
+	prop = c->symbol_rate / 1000;
+	ret = si2183_set_prop(client, SI2183_PROP_DVBC_SR, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set dvb-c symbol rate\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int si2183_set_mcns(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct si2183_cmd cmd;
+	int ret;
+	u16 prop,symb;
+
+	if(dev->LED_switch)
+		dev->LED_switch(dev->base->i2c,6);
+
+	memcpy(cmd.args, "\x89\x41\x06\x12\x0\x0", 6);
+	cmd.args[1]= (dev->agc_mode &0x07)<<4 |0x1;  
+ 	cmd.wlen = 6;
+ 	cmd.rlen = 3;
+ 	ret = si2183_cmd_execute(client, &cmd);
+ 	if(ret){
+ 		dev_err(&client->dev, "err set agc mode\n");
+ 	}
+	/* mcns mode */
+	prop = 0x18;
+	ret = si2183_set_prop(client, SI2183_PROP_MODE, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set mcns mode\n");
+		return ret;
+	}
+
+	switch (c->modulation) {
+	default:
+	case QAM_64:
+		prop = 9;
+		symb = 5057;
+		break;
+	case QAM_256:
+		prop = 11;
+		symb = 5361;
+		break;
+	}
+	ret = si2183_set_prop(client, SI2183_PROP_MCNS_CONST, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set mcns constelation\n");
+		return ret;
+	}
+
+	/* symbol rate */
+	ret = si2183_set_prop(client, SI2183_PROP_MCNS_SR, &symb);
+	if (ret) {
+		dev_err(&client->dev, "err set mcns symbol rate\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int gold_code_index (int gold_sequence_index)
+{
+	unsigned int i, k , x_init;
+	u8 GOLD_PRBS[19] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+	for (k=0; k<gold_sequence_index; k++) {
+		GOLD_PRBS[18] = (GOLD_PRBS[0] + GOLD_PRBS[7])%2;
+		/* Shifting 18 first values */
+		for (i=0; i<18; i++) 
+			GOLD_PRBS[i] = GOLD_PRBS[i+1];
+	}
+	x_init = 0;
+	for (i=0; i<18; i++) { x_init = x_init + GOLD_PRBS[i]*(1<<i); }
+
+	return x_init;
+}
+
+static int si2183_set_dvbs(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct si2183_cmd cmd;
+	int ret;
+	u16 prop;
+	u32 pls_mode, pls_code;
+	
+	if(dev->LED_switch)
+		dev->LED_switch(dev->base->i2c,2);
+
+	/*set SAT agc*/
+	memcpy(cmd.args, "\x8a\x1d\x12\x0\x0\x0", 6);
+	cmd.args[1]= dev->agc_mode|0x18;  
+ 	cmd.wlen = 6;
+ 	cmd.rlen = 3;
+ 	ret = si2183_cmd_execute(client, &cmd);
+ 	if(ret){
+ 		dev_err(&client->dev, "err set agc mode\n");
+ 	}
+
+	/* set mode */
+	prop = 0x8;
+	switch (c->delivery_system) {
+	default:
+	case SYS_DVBS:
+		prop |= 0x80;
+		break;
+	case SYS_DVBS2:
+		prop |= 0x90;
+		break;
+	case SYS_DSS:
+		prop |= 0xa0;
+		break;
+	}
+	if (c->inversion)
+		prop |= 0x100;
+	ret = si2183_set_prop(client, SI2183_PROP_MODE, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set dvb-s/s2 mode\n");
+		return ret;
+	}
+
+	/* symbol rate */
+	prop = c->symbol_rate / 1000;
+	switch (c->delivery_system) {
+	default:
+	case SYS_DSS:
+	case SYS_DVBS:
+		ret = si2183_set_prop(client, SI2183_PROP_DVBS_SR, &prop);
+		break;
+	case SYS_DVBS2:
+		ret = si2183_set_prop(client, SI2183_PROP_DVBS2_SR, &prop);
+		/* stream_id selection */
+		cmd.args[0] = 0x71;
+		cmd.args[1] = (u8) c->stream_id;
+		cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1;
+		cmd.wlen = 3;
+		cmd.rlen = 1;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret)
+			dev_warn(&client->dev, "dvb-s2: err selecting stream_id\n");
+
+		/* pls selection */
+		pls_mode = c->stream_id == NO_STREAM_ID_FILTER ? 0 : (c->stream_id >> 26) & 3;
+		pls_code = c->stream_id == NO_STREAM_ID_FILTER ? 0 : (c->stream_id >> 8) & 0x3FFFF;
+		if (pls_mode)
+			pls_code = gold_code_index(pls_code);
+		cmd.args[0] = 0x73;
+		cmd.args[1] = pls_code > 0;
+		cmd.args[2] = cmd.args[3] = 0;
+		cmd.args[4] = (u8) pls_code;
+		cmd.args[5] = (u8) (pls_code >> 8);
+		cmd.args[6] = (u8) (pls_code >> 16);
+		cmd.args[7] = (u8) (pls_code >> 24);
+		cmd.wlen = 8;
+		cmd.rlen = 1;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret)
+			dev_warn(&client->dev, "dvb-s2: err set pls\n");
+	}
+
+	return 0;
+}
+
+static int si2183_set_dvbt(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct si2183_cmd cmd;
+	int ret;
+	u16 prop;
+
+	if(dev->LED_switch)
+		dev->LED_switch(dev->base->i2c,1);
+
+	memcpy(cmd.args, "\x89\x41\x06\x12\x0\x0", 6);
+	cmd.args[1]= (dev->agc_mode &0x07)<<4 |0x1;  
+ 	cmd.wlen = 6;
+ 	cmd.rlen = 3;
+ 	ret = si2183_cmd_execute(client, &cmd);
+ 	if(ret){
+ 		dev_err(&client->dev, "err set agc mode\n");
+ 	}
+
+	if (c->bandwidth_hz == 0) {
+		return -EINVAL;
+	} else if (c->bandwidth_hz <= 2000000)
+		prop = 0x02;
+	else if (c->bandwidth_hz <= 5000000)
+		prop = 0x05;
+	else if (c->bandwidth_hz <= 6000000)
+		prop = 0x06;
+	else if (c->bandwidth_hz <= 7000000)
+		prop = 0x07;
+	else if (c->bandwidth_hz <= 8000000)
+		prop = 0x08;
+	else if (c->bandwidth_hz <= 9000000)
+		prop = 0x09;
+	else if (c->bandwidth_hz <= 10000000)
+		prop = 0x0a;
+	else
+		prop = 0x0f;
+
+	switch (c->delivery_system) {
+	default:
+	case SYS_DVBT:
+		prop |= 0x20;
+		break;
+	case SYS_DVBT2:
+		prop |= 0x70;
+		break;
+	}
+	ret = si2183_set_prop(client, SI2183_PROP_MODE, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set dvb-t mode\n");
+		return ret;
+	}
+
+	/* hierarchy - HP = 0 / LP = 1 */
+	prop = c->hierarchy == HIERARCHY_1 ? 1 : 0;
+	ret = si2183_set_prop(client, SI2183_PROP_DVBT_HIER, &prop);
+	if (ret)
+		dev_warn(&client->dev, "dvb-t: err set hierarchy\n");
+
+	if (c->delivery_system == SYS_DVBT2) {
+		/* stream_id selection */
+		cmd.args[0] = 0x52;
+		cmd.args[1] = (u8) c->stream_id;
+		cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1;
+		cmd.wlen = 3;
+		cmd.rlen = 1;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret)
+			dev_warn(&client->dev, "dvb-t2: err selecting stream_id\n");
+
+		/* dvb-t2 mode - any=0 / base=1 / lite=2 */
+		prop = 0;
+		ret = si2183_set_prop(client, SI2183_PROP_DVBT2_MODE, &prop);
+		if (ret)
+			dev_warn(&client->dev, "dvb-t2: err set mode\n");
+	}
+
+	return 0;
+}
+
+static int si2183_set_isdbt(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct si2183_cmd cmd;
+	int ret;
+	u16 prop;
+	
+	if(dev->LED_switch)
+		dev->LED_switch(dev->base->i2c,5);
+
+
+	memcpy(cmd.args, "\x89\x41\x06\x12\x0\x0", 6);
+	cmd.args[1]= (dev->agc_mode &0x07)<<4 |0x1; 
+ 	cmd.wlen = 6;
+ 	cmd.rlen = 3;
+ 	ret = si2183_cmd_execute(client, &cmd);
+ 	if(ret){
+ 		dev_err(&client->dev, "err set agc mode\n");
+ 	}
+	if (c->bandwidth_hz == 0) {
+		return -EINVAL;
+	} else if (c->bandwidth_hz <= 2000000)
+		prop = 0x02;
+	else if (c->bandwidth_hz <= 5000000)
+		prop = 0x05;
+	else if (c->bandwidth_hz <= 6000000)
+		prop = 0x06;
+	else if (c->bandwidth_hz <= 7000000)
+		prop = 0x07;
+	else if (c->bandwidth_hz <= 8000000)
+		prop = 0x08;
+	else if (c->bandwidth_hz <= 9000000)
+		prop = 0x09;
+	else if (c->bandwidth_hz <= 10000000)
+		prop = 0x0a;
+	else
+		prop = 0x0f;
+
+	/* ISDB-T mode */
+	prop |= 0x40;
+	ret = si2183_set_prop(client, SI2183_PROP_MODE, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set dvb-t mode\n");
+		return ret;
+	}
+	return 0;
+}
+
+static int si2183_set_frontend(struct dvb_frontend *fe)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	struct si2183_cmd cmd;
+
+	dev_dbg(&client->dev,
+			"delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u stream_id=%d\n",
+			c->delivery_system, c->modulation, c->frequency,
+			c->bandwidth_hz, c->symbol_rate, c->inversion,
+			c->stream_id);
+
+	if (!dev->active_fe) {
+		ret = -EAGAIN;
+		goto err;
+	}
+	if(dev->RF_switch)
+	{	
+		switch (c->delivery_system) {
+		case SYS_DVBT:
+		case SYS_DVBT2:
+		case SYS_DVBC_ANNEX_A:
+		case SYS_DVBC_ANNEX_B:
+		case SYS_ISDBT:
+			dev->RF_switch(dev->base->i2c,dev->rf_in,1);
+			
+			 break;
+			
+		case SYS_DVBS:
+		case SYS_DVBS2:
+		case SYS_DSS:
+		default:
+			dev->RF_switch(dev->base->i2c,dev->rf_in,0);
+			break;
+		
+		}
+	}
+
+	if(dev->TS_switch)
+		dev->TS_switch(dev->base->i2c,1);
+		
+	if (fe->ops.tuner_ops.set_params) {
+#ifndef SI2183_USE_I2C_MUX
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+#endif
+		ret = fe->ops.tuner_ops.set_params(fe);
+#ifndef SI2183_USE_I2C_MUX
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+#endif
+		if (ret) {
+			dev_err(&client->dev, "err setting tuner params\n");
+			goto err;
+		}
+	}
+
+	switch (c->delivery_system) {
+	case SYS_DVBT:
+	case SYS_DVBT2:
+		ret = si2183_set_dvbt(fe);
+		break;
+	case SYS_DVBC_ANNEX_A:
+		ret = si2183_set_dvbc(fe);
+		break;
+	case SYS_DVBC_ANNEX_B:
+		ret= si2183_set_mcns(fe);
+		break;
+	case SYS_ISDBT:
+		ret = si2183_set_isdbt(fe);
+		break;
+	case SYS_DVBS:
+	case SYS_DVBS2:
+	case SYS_DSS:
+		ret = si2183_set_dvbs(fe);
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* dsp restart */
+	memcpy(cmd.args, "\x85", 1);
+	cmd.wlen = 1;
+	cmd.rlen = 1;
+	ret = si2183_cmd_execute(client, &cmd);
+	if (ret) {
+		dev_err(&client->dev, "err restarting dsp\n");
+		return ret;
+	}
+
+	dev->delivery_system = c->delivery_system;
+	return 0;
+err:
+	dev_err(&client->dev, "set_params failed=%d\n", ret);
+	return ret;
+}
+
+static int si2183_init(struct dvb_frontend *fe)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret = 0, len, remaining;
+	const struct firmware *fw;
+	const char *fw_name;
+	struct si2183_cmd cmd;
+	unsigned int chip_id;
+	u16 prop;
+
+	dev_dbg(&client->dev, "\n");
+
+	if (dev->active_fe) {
+		dev->active_fe |= (1 << fe->id);
+		return 0;
+	}
+
+	c->cnr.len = 1;
+	c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+	/* initialize */
+	memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);	
+	if(dev->start_clk_mode == 1){
+	   cmd.args[3] =0;
+	   cmd.args[5] = 0x6;
+	}
+	
+	cmd.wlen = 13;
+	cmd.rlen = 0;
+	ret = si2183_cmd_execute(client, &cmd);
+	if (ret)
+		goto err;
+
+	if (dev->fw_loaded) {
+		/* resume */
+		memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8);
+		
+		if(dev->start_clk_mode==1)
+				cmd.args[6]=0x31;	
+		cmd.wlen = 8;
+		cmd.rlen = 1;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		memcpy(cmd.args, "\x85", 1);
+		cmd.wlen = 1;
+		cmd.rlen = 1;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret)
+			goto err;
+
+		goto warm;
+	}
+
+	/* power up */
+	memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
+	if(dev->start_clk_mode ==1 ){
+	   cmd.args[6]	= 0x30;
+	}
+	cmd.wlen = 8;
+	cmd.rlen = 1;
+	ret = si2183_cmd_execute(client, &cmd);
+	if (ret)
+		goto err;
+
+	/* query chip revision */
+	memcpy(cmd.args, "\x02", 1);
+	cmd.wlen = 1;
+	cmd.rlen = 13;
+	ret = si2183_cmd_execute(client, &cmd);
+	if (ret)
+		goto err;
+
+	chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 |
+			cmd.args[4] << 0;
+
+	#define SI2183_B60 ('B' << 24 | 83 << 16 | '6' << 8 | '0' << 0)
+
+	switch (chip_id) {
+	case SI2183_B60:
+		fw_name = SI2183_B60_FIRMWARE;
+		break;
+	default:
+		dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
+				cmd.args[2], cmd.args[1],
+				cmd.args[3], cmd.args[4]);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n",
+			cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
+
+	ret = request_firmware(&fw, fw_name, &client->dev);
+	if (ret) {
+		dev_err(&client->dev,
+				"firmware file '%s' not found\n",
+				fw_name);
+		goto err;
+	}
+
+	dev_info(&client->dev, "downloading firmware from file '%s'\n",
+			fw_name);
+
+	for (remaining = fw->size; remaining > 0; remaining -= 17) {
+		len = fw->data[fw->size - remaining];
+		if (len > SI2183_ARGLEN) {
+			ret = -EINVAL;
+			break;
+		}
+		memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
+		cmd.wlen = len;
+		cmd.rlen = 1;
+		ret = si2183_cmd_execute(client, &cmd);
+		if (ret)
+			break;
+	}
+	release_firmware(fw);
+
+	if (ret) {
+		dev_err(&client->dev, "firmware download failed %d\n", ret);
+		goto err;
+	}
+
+	memcpy(cmd.args, "\x01\x01", 2);
+	cmd.wlen = 2;
+	cmd.rlen = 1;
+	ret = si2183_cmd_execute(client, &cmd);
+	if (ret)
+		goto err;
+
+	/* query firmware version */
+	memcpy(cmd.args, "\x11", 1);
+	cmd.wlen = 1;
+	cmd.rlen = 10;
+	ret = si2183_cmd_execute(client, &cmd);
+	if (ret)
+		goto err;
+
+	dev_info(&client->dev, "firmware version: %c.%c.%d\n",
+			cmd.args[6], cmd.args[7], cmd.args[8]);
+
+	/* set ts mode */
+	prop = 0x10 | dev->ts_mode | (dev->ts_clock_gapped ? 0x40 : 0);
+	ret = si2183_set_prop(client, 0x1001, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set ts mode\n");
+	}
+
+	/* FER resol */
+	prop = 0x12;
+	ret = si2183_set_prop(client, 0x100c, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set FER resol\n");
+		return ret;
+	}
+
+	/* DD IEN */
+	prop = 0x00;
+	ret = si2183_set_prop(client, 0x1006, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set dd ien\n");
+		return ret;
+	}
+
+	/* int sense */
+	prop = 0x2000;
+	ret = si2183_set_prop(client, 0x1007, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set int sense\n");
+		return ret;
+	}
+
+	/* Control of SQI computation */
+	prop = 0x1e;
+	ret = si2183_set_prop(client, 0x100f, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set sqi comp\n");
+		return ret;
+	}
+
+	/* Transport Stream setting for parallel mode */
+	prop = 0x0104 | (dev->ts_clock_inv ? 0x0000 : 0x1000);
+	ret = si2183_set_prop(client, 0x1009, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set par_ts\n");
+		return ret;
+	}
+
+	/* Transport Stream setting for serial mode */
+	prop = 0x230C | (dev->ts_clock_inv ? 0x0000 : 0x1000);
+	ret = si2183_set_prop(client, 0x1008, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set ser_ts\n");
+		return ret;
+	}
+
+	/* Transport Stream setting for parallel mode - secondary*/
+	prop = 0x08e3;
+	ret = si2183_set_prop(client, 0x1015, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set int par_ts_sec\n");
+		return ret;
+	}
+
+	/* Transport Stream setting for serial mode - secondary*/
+	prop = 0x01c7;
+	ret = si2183_set_prop(client, 0x1016, &prop);
+	if (ret) {
+		dev_err(&client->dev, "err set int ser_ts_sec\n");
+		return ret;
+	}
+
+	dev->fw_loaded = true;
+warm:
+	dev->active_fe |= (1 << fe->id);
+	return 0;
+
+err:
+	dev_dbg(&client->dev, "init failed=%d\n", ret);
+	return ret;
+}
+
+static int si2183_sleep(struct dvb_frontend *fe)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	int ret;
+	struct si2183_cmd cmd;
+
+	dev_dbg(&client->dev, "\n");
+
+	dev->active_fe &= ~(1 << fe->id);
+	if (dev->active_fe)
+		return 0;
+
+	memcpy(cmd.args, "\x13", 1);
+	cmd.wlen = 1;
+	cmd.rlen = 0;
+	ret = si2183_cmd_execute(client, &cmd);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int si2183_get_tune_settings(struct dvb_frontend *fe,
+	struct dvb_frontend_tune_settings *s)
+{
+	s->min_delay_ms = 900;
+
+	return 0;
+}
+
+#ifdef SI2183_USE_I2C_MUX
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+static int si2183_select(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct i2c_client *client = i2c_mux_priv(muxc);
+#else
+static int si2183_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
+{
+	struct i2c_client *client = mux_priv;
+#endif
+	int ret;
+	struct si2183_cmd cmd;
+
+	/* open I2C gate */
+	memcpy(cmd.args, "\xc0\x0d\x01", 3);
+	cmd.wlen = 3;
+	cmd.rlen = 0;
+	ret = si2183_cmd_execute_unlocked(client, &cmd);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+static int si2183_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct i2c_client *client = i2c_mux_priv(muxc);
+#else
+static int si2183_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
+{
+	struct i2c_client *client = mux_priv;
+#endif
+	int ret;
+	struct si2183_cmd cmd;
+
+	/* close I2C gate */
+	memcpy(cmd.args, "\xc0\x0d\x00", 3);
+	cmd.wlen = 3;
+	cmd.rlen = 0;
+	ret = si2183_cmd_execute_unlocked(client, &cmd);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+#else
+static int i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+	struct si2183_cmd cmd;
+
+	memcpy(cmd.args, "\xc0\x0d\x00", 3);
+	if (enable)
+		cmd.args[2] = 1;
+	cmd.wlen = 3;
+	cmd.rlen = 0;
+	return si2183_cmd_execute(dev->base->i2c_gate_client, &cmd);
+}
+#endif
+
+static int si2183_tune(struct dvb_frontend *fe, bool re_tune,
+	unsigned int mode_flags, unsigned int *delay, enum fe_status *status)
+{
+	*delay = HZ / 5;
+	if (re_tune) {
+		int ret = si2183_set_frontend(fe);
+		if (ret)
+			return ret;
+	}
+	return si2183_read_status(fe, status);
+}
+
+static enum dvbfe_algo si2183_get_algo(struct dvb_frontend *fe)
+{
+	return DVBFE_ALGO_HW;
+}
+
+static int si2183_set_property(struct dvb_frontend *fe,
+		u32 cmd, u32 data)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case DTV_DELIVERY_SYSTEM:
+		switch (data) {
+		case SYS_DVBS:
+		case SYS_DVBS2:
+		case SYS_DSS:
+			fe->ops.info.frequency_min_hz = 950 * MHz;
+			fe->ops.info.frequency_max_hz = 2150 * MHz;
+			fe->ops.info.frequency_stepsize_hz = 0;
+			break;
+		case SYS_ISDBT:
+			fe->ops.info.frequency_min_hz = 42 * MHz;
+			fe->ops.info.frequency_max_hz = 1002 * MHz;
+			fe->ops.info.frequency_stepsize_hz = 0;
+			break;
+		case SYS_DVBC_ANNEX_A:
+		case SYS_DVBC_ANNEX_B:
+			fe->ops.info.frequency_min_hz = 47 * MHz;
+			fe->ops.info.frequency_max_hz = 862 * MHz;
+			fe->ops.info.frequency_stepsize_hz = 62500;
+			break;
+		case SYS_DVBT:
+		case SYS_DVBT2:
+		default:
+			fe->ops.info.frequency_min_hz = 174 * MHz;
+			fe->ops.info.frequency_max_hz = 862 * MHz;
+			fe->ops.info.frequency_stepsize_hz = 250000;
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+
+static int send_diseqc_cmd(struct dvb_frontend *fe,
+	u8 cont_tone, u8 tone_burst, u8 burst_sel,
+	u8 end_seq, u8 msg_len, u8 *msg)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_cmd cmd;
+	u8 enable = 1;
+
+	cmd.args[0] = 0x8c;
+	cmd.args[1] = enable | (cont_tone << 1)
+		    | (tone_burst << 2) | (burst_sel << 3)
+		    | (end_seq << 4) | (msg_len << 5);
+
+	if (msg_len > 0)
+		memcpy(&cmd.args[2], msg, msg_len);
+
+	cmd.wlen = 8;
+	cmd.rlen = 1;
+	return si2183_cmd_execute(client, &cmd);
+}
+
+static int si2183_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	int ret;
+	u8 cont_tone;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		cont_tone = 1;
+		break;
+	case SEC_TONE_OFF:
+		cont_tone = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = send_diseqc_cmd(fe, cont_tone, 0, 0, 1, 0, NULL);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_err(&client->dev, "set_tone failed=%d\n", ret);
+	return ret;
+}
+
+static int si2183_diseqc_send_burst(struct dvb_frontend *fe,
+	enum fe_sec_mini_cmd burst)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	int ret;
+	u8 burst_sel;
+
+	switch (burst) {
+	case SEC_MINI_A:
+		burst_sel = 0;
+		break;
+	case SEC_MINI_B:
+		burst_sel = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = send_diseqc_cmd(fe, 0, 1, burst_sel, 1, 0, NULL);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_err(&client->dev, "set_tone failed=%d\n", ret);
+	return ret;
+}
+
+static int si2183_diseqc_send_msg(struct dvb_frontend *fe,
+	struct dvb_diseqc_master_cmd *d)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	int ret;
+	u8 remaining = d->msg_len;
+	u8 *p = d->msg;
+	u8 len = 0;
+
+	while (remaining > 0) {
+		p += len;
+		len = (remaining > 6) ? 6 : remaining;
+		remaining -= len;
+		ret = send_diseqc_cmd(fe, 0, 0, 0,
+			(remaining == 0) ? 1 : 0, len, p);
+		if (ret)
+			goto err;
+		msleep(50);
+	}
+
+	return 0;
+err:
+	dev_err(&client->dev, "set_tone failed=%d\n", ret);
+	return ret;
+}
+
+static void spi_read(struct dvb_frontend *fe, struct ecp3_info *ecp3inf)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+
+
+	if (dev->read_properties)
+		dev->read_properties(client->adapter,ecp3inf->reg, &(ecp3inf->data));
+
+	return ;
+}
+
+static void spi_write(struct dvb_frontend *fe,struct ecp3_info *ecp3inf)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+
+	if (dev->write_properties)
+		dev->write_properties(client->adapter,ecp3inf->reg, ecp3inf->data);
+	return ;
+}
+
+static void eeprom_read(struct dvb_frontend *fe, struct eeprom_info *eepinf)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+
+	if (dev->read_eeprom)
+		dev->read_eeprom(client->adapter,eepinf->reg, &(eepinf->data));
+	return ;
+}
+
+static void eeprom_write(struct dvb_frontend *fe,struct eeprom_info *eepinf)
+{
+	struct i2c_client *client = fe->demodulator_priv;
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+
+	if (dev->write_eeprom)
+		dev->write_eeprom(client->adapter,eepinf->reg, eepinf->data);
+	return ;
+}
+
+static const struct dvb_frontend_ops si2183_ops = {
+	.delsys = {SYS_DVBT, SYS_DVBT2,
+		   SYS_ISDBT,
+		   SYS_DVBC_ANNEX_A,SYS_DVBC_ANNEX_B,
+		   SYS_DVBS, SYS_DVBS2, SYS_DSS},
+	.info = {
+		.name = "Silicon Labs Si2183",
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.caps =	FE_CAN_FEC_1_2 |
+			FE_CAN_FEC_2_3 |
+			FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 |
+			FE_CAN_FEC_7_8 |
+			FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK |
+			FE_CAN_QAM_16 |
+			FE_CAN_QAM_32 |
+			FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 |
+			FE_CAN_QAM_256 |
+			FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_HIERARCHY_AUTO |
+			FE_CAN_MUTE_TS |
+			FE_CAN_2G_MODULATION |
+			FE_CAN_MULTISTREAM
+	},
+
+	.get_tune_settings = si2183_get_tune_settings,
+
+	.init = si2183_init,
+	.sleep = si2183_sleep,
+
+	.set_frontend = si2183_set_frontend,
+
+	.read_status = si2183_read_status,
+	.read_signal_strength	= si2183_read_signal_strength,
+	.read_snr		= si2183_read_snr,
+	.read_ber		= si2183_read_ber,
+	.read_ucblocks		= si2183_read_ucblocks,
+
+	.get_frontend_algo = si2183_get_algo,
+	.tune = si2183_tune,
+
+	.set_property		= si2183_set_property,
+
+	.set_tone			= si2183_set_tone,
+	.diseqc_send_burst	= si2183_diseqc_send_burst,
+	.diseqc_send_master_cmd		= si2183_diseqc_send_msg,
+#ifndef SI2183_USE_I2C_MUX
+	.i2c_gate_ctrl		= i2c_gate_ctrl,
+#endif
+
+	.spi_read			= spi_read,
+	.spi_write			= spi_write,
+	.eeprom_read		= eeprom_read,
+	.eeprom_write		= eeprom_write,
+};
+
+
+static struct si_base *match_base(struct i2c_adapter *i2c, u8 adr)
+{
+	struct si_base *p;
+
+	list_for_each_entry(p, &silist, silist)
+		if (p->i2c == i2c)// && p->adr == adr) lja: TO FIX
+			return p;
+	return NULL;
+}
+
+static int si2183_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct si2183_config *config = client->dev.platform_data;
+	struct si2183_dev *dev;
+	struct si_base *base;
+	int ret;
+
+	dev_dbg(&client->dev, "\n");
+	
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		dev_err(&client->dev, "kzalloc() failed\n");
+		goto err;
+	}
+
+	base = match_base(client->adapter, client->addr);
+	if (base) {
+		base->count++;
+		dev->base = base;
+	} else {
+		base = kzalloc(sizeof(struct si_base), GFP_KERNEL);
+		if (!base)
+			goto err_kfree;
+		base->i2c = client->adapter;
+		base->adr = client->addr;
+		base->count = 1;
+		dev->base = base;
+		list_add(&base->silist, &silist);
+
+		mutex_init(&base->i2c_mutex);
+#ifdef SI2183_USE_I2C_MUX
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+		/* create mux i2c adapter for tuner */
+		base->muxc = i2c_mux_alloc(client->adapter, &client->adapter->dev,
+					  1, 0, I2C_MUX_LOCKED,
+					  si2183_select, si2183_deselect);
+		if (!base->muxc) {
+			ret = -ENOMEM;
+			goto err_base_kfree;
+		}
+		base->muxc->priv = client;
+		ret = i2c_mux_add_adapter(base->muxc, 0, 0, 0);
+		if (ret)
+			goto err_base_kfree;
+		base->tuner_adapter = base->muxc->adapter[0];
+#else
+		/* create mux i2c adapter for tuners */
+		base->tuner_adapter = i2c_add_mux_adapter(client->adapter, &client->adapter->dev,
+				client, 0, 0, 0, si2183_select, si2183_deselect);
+		if (base->tuner_adapter == NULL) {
+			ret = -ENODEV;
+			goto err_base_kfree;
+		}
+#endif
+#else
+		base->tuner_adapter = client->adapter;
+		base->i2c_gate_client = client;
+#endif
+	}
+
+	/* create dvb_frontend */
+	memcpy(&dev->fe.ops, &si2183_ops, sizeof(struct dvb_frontend_ops));
+	dev->fe.demodulator_priv = client;
+	*config->i2c_adapter = base->tuner_adapter;
+	*config->fe = &dev->fe;
+	dev->ts_mode = config->ts_mode;
+	dev->ts_clock_inv = config->ts_clock_inv;
+	dev->ts_clock_gapped = config->ts_clock_gapped;
+ 	dev->agc_mode = config->agc_mode;
+	dev->RF_switch = config->RF_switch;
+	dev->rf_in  = config->rf_in;
+	dev->fw_loaded = false;
+	dev->delivery_system = 0;
+	dev->snr = 0;
+	dev->stat_resp = 0;
+	dev->active_fe = 0;
+	dev->start_clk_mode = config->start_clk_mode;
+	dev->TS_switch = config->TS_switch;
+	dev->LED_switch = config->LED_switch;
+	
+	dev->write_properties = config->write_properties;
+	dev->read_properties = config->read_properties;
+	dev->write_eeprom = config->write_eeprom;
+	dev->read_eeprom = config->read_eeprom;
+
+	i2c_set_clientdata(client, dev);
+
+#ifndef SI2183_USE_I2C_MUX
+	/* leave gate open for tuner to init */
+	i2c_gate_ctrl(&dev->fe, 1);
+#endif
+	dev_info(&client->dev, "Silicon Labs Si2183 successfully attached\n");
+	return 0;
+err_base_kfree:
+	kfree(base);
+err_kfree:
+	kfree(dev);
+err:
+	dev_dbg(&client->dev, "probe failed=%d\n", ret);
+	return ret;
+}
+
+static void si2183_remove(struct i2c_client *client)
+{
+	struct si2183_dev *dev = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "\n");
+
+	dev->base->count--;
+	if (dev->base->count == 0) {
+#ifdef SI2183_USE_I2C_MUX
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+		i2c_mux_del_adapters(dev->base->muxc);
+#else
+		i2c_del_mux_adapter(dev->base->tuner_adapter);
+#endif
+#endif
+		list_del(&dev->base->silist);
+		kfree(dev->base);
+	}
+
+	dev->fe.ops.release = NULL;
+	dev->fe.demodulator_priv = NULL;
+
+	kfree(dev);
+}
+
+static const struct i2c_device_id si2183_id_table[] = {
+	{"si2183", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, si2183_id_table);
+
+static struct i2c_driver si2183_driver = {
+	.driver = {
+		.name	= "si2183",
+	},
+	.probe		= si2183_probe,
+	.remove		= si2183_remove,
+	.id_table	= si2183_id_table,
+};
+
+module_i2c_driver(si2183_driver);
+
+MODULE_AUTHOR("Luis Alves <ljalvs@gmail.com>");
+MODULE_DESCRIPTION("Silicon Labs Si2183 DVB-T/T2/C/C2/S/S2 demodulator driver");
+MODULE_LICENSE("GPL");

diff -uraBN a/drivers/media/dvb-frontends/si2183.h b/drivers/media/dvb-frontends/si2183.h
--- a/drivers/media/dvb-frontends/si2183.h
+++ b/drivers/media/dvb-frontends/si2183.h
@@ -0,0 +1,66 @@
+/*
+ * Silicon Labs Si2183(2) DVB-T/T2/C/C2/S/S2 demodulator driver
+ *
+ *    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.
+ */
+
+#ifndef SI2183_H
+#define SI2183_H
+
+#include <linux/dvb/frontend.h>
+/*
+ * I2C address
+ * 0x64
+ */
+struct si2183_config {
+	/*
+	 * frontend
+	 * returned by driver
+	 */
+	struct dvb_frontend **fe;
+
+	/*
+	 * tuner I2C adapter
+	 * returned by driver
+	 */
+	struct i2c_adapter **i2c_adapter;
+
+	/* TS mode */
+#define SI2183_TS_PARALLEL	0x06
+#define SI2183_TS_SERIAL	0x03
+	u8 ts_mode;
+
+	/* TS clock inverted */
+	bool ts_clock_inv;
+
+	int  start_clk_mode;  //0 terrestrial mode 1: satellite mode
+	
+	/* TS clock gapped */
+	bool ts_clock_gapped;
+	/*agc*/
+	u8 agc_mode;
+
+	/*rf switch*/
+	void (*RF_switch)(struct i2c_adapter * i2c,u8 rf_in,u8 flag);
+	/*rf no.*/
+	u8 rf_in;
+
+	void (*TS_switch)(struct i2c_adapter * i2c,u8 flag);
+	void (*LED_switch)(struct i2c_adapter * i2c,u8 flag);
+	//update the FW.
+	void (*write_properties) (struct i2c_adapter *i2c,u8 reg, u32 buf);
+	void (*read_properties) (struct i2c_adapter *i2c,u8 reg, u32 *buf);
+	// EEPROM access
+	void (*write_eeprom) (struct i2c_adapter *i2c,u8 reg, u8 buf);
+	void (*read_eeprom) (struct i2c_adapter *i2c,u8 reg, u8 *buf);
+};
+
+#endif

diff -uraBN a/drivers/media/tuners/av201x.c b/drivers/media/tuners/av201x.c
--- a/drivers/media/tuners/av201x.c
+++ b/drivers/media/tuners/av201x.c
@@ -0,0 +1,302 @@
+/*
+ * AV201x Airoha Technology silicon tuner driver
+ *
+ * Copyright (C) 2014 Luis Alves <ljalvs@gmail.com>
+ *
+ *    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.
+ */
+
+#include "av201x.h"
+#include "av201x_priv.h"
+
+/* write multiple (continuous) registers */
+static int av201x_wrm(struct av201x_priv *priv, u8 *buf, int len)
+{
+	int ret;
+	struct i2c_msg msg = {
+		.addr = priv->cfg->i2c_address,
+		.flags = 0, .buf = buf, .len = len };
+
+	dev_dbg(&priv->i2c->dev, "%s() i2c wrm @0x%02x (len=%d) ",
+		__func__, buf[0], len);
+
+	ret = i2c_transfer(priv->i2c, &msg, 1);
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: i2c wrm err(%i) @0x%02x (len=%d)\n",
+			KBUILD_MODNAME, ret, buf[0], len);
+		return ret;
+	}
+	return 0;
+}
+
+/* write one register */
+static int av201x_wr(struct av201x_priv *priv, u8 addr, u8 data)
+{
+	u8 buf[] = { addr, data };
+	return av201x_wrm(priv, buf, 2);
+}
+
+/* read multiple (continuous) registers starting at addr */
+static int av201x_rdm(struct av201x_priv *priv, u8 addr, u8 *buf, int len)
+{
+	int ret;
+	struct i2c_msg msg[] = {
+		{ .addr = priv->cfg->i2c_address, .flags = 0,
+			.buf = &addr, .len = 1 },
+		{ .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
+			.buf = buf, .len = len }
+	};
+
+	dev_dbg(&priv->i2c->dev, "%s() i2c rdm @0x%02x (len=%d)\n",
+		__func__, addr, len);
+
+	ret = i2c_transfer(priv->i2c, msg, 2);
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: i2c rdm err(%i) @0x%02x (len=%d)\n",
+			KBUILD_MODNAME, ret, addr, len);
+		return ret;
+	}
+	return 0;
+}
+
+/* read one register */
+static int av201x_rd(struct av201x_priv *priv, u8 addr, u8 *data)
+{
+	return av201x_rdm(priv, addr, data, 1);
+}
+
+/* read register, apply masks, write back */
+static int av201x_regmask(struct av201x_priv *priv,
+	u8 reg, u8 setmask, u8 clrmask)
+{
+	int ret;
+	u8 b = 0;
+	if (clrmask != 0xff) {
+		ret = av201x_rd(priv, reg, &b);
+		if (ret)
+			return ret;
+		b &= ~clrmask;
+	}
+	return av201x_wr(priv, reg, b | setmask);
+}
+
+static int av201x_wrtable(struct av201x_priv *priv,
+	struct av201x_regtable *regtable, int len)
+{
+	int ret, i;
+
+	for (i = 0; i < len; i++) {
+		ret = av201x_regmask(priv, regtable[i].addr,
+			regtable[i].setmask, regtable[i].clrmask);
+		if (ret)
+			return ret;
+		if (regtable[i].sleep)
+			msleep(regtable[i].sleep);
+	}
+	return 0;
+}
+
+static void av201x_release(struct dvb_frontend *fe)
+{
+	struct av201x_priv *priv = fe->tuner_priv;
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+}
+
+static int av201x_init(struct dvb_frontend *fe)
+{
+	struct av201x_priv *priv = fe->tuner_priv;
+	int ret;
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+
+	ret = av201x_wrtable(priv, av201x_inittuner0,
+		ARRAY_SIZE(av201x_inittuner0));
+
+	switch (priv->cfg->id) {
+	case ID_AV2011:
+		ret |= av201x_wrtable(priv, av201x_inittuner1a,
+			ARRAY_SIZE(av201x_inittuner1a));
+		break;
+	case ID_AV2012:
+	default:
+		ret |= av201x_wrtable(priv, av201x_inittuner1b,
+			ARRAY_SIZE(av201x_inittuner1b));
+		break;
+	}
+
+	ret |= av201x_wrtable(priv, av201x_inittuner2,
+		ARRAY_SIZE(av201x_inittuner2));
+
+	ret |= av201x_wr(priv, REG_TUNER_CTRL, 0x96);
+
+	msleep(120);
+
+	if (ret)
+		dev_dbg(&priv->i2c->dev, "%s() failed\n", __func__);
+	return ret;
+}
+
+static int av201x_sleep(struct dvb_frontend *fe)
+{
+	struct av201x_priv *priv = fe->tuner_priv;
+	int ret;
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+
+	ret = av201x_regmask(priv, REG_TUNER_CTRL, AV201X_SLEEP, 0);
+	if (ret)
+		dev_dbg(&priv->i2c->dev, "%s() failed\n", __func__);
+	return ret;
+}
+
+static int av201x_set_params(struct dvb_frontend *fe)
+{
+	struct av201x_priv *priv = fe->tuner_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u32 n, bw, bf;
+	u8 buf[5];
+	int ret;
+
+	dev_dbg(&priv->i2c->dev, "%s() delivery_system=%d frequency=%d " \
+			"symbol_rate=%d\n", __func__,
+			c->delivery_system, c->frequency, c->symbol_rate);
+
+	/*
+	   ** PLL setup **
+	   RF = (pll_N * ref_freq) / pll_M
+	   pll_M = fixed 0x10000
+	   PLL output is divided by 2
+	   REG_FN = pll_M<24:0>
+	*/
+	buf[0] = REG_FN;
+	n = DIV_ROUND_CLOSEST(c->frequency, priv->cfg->xtal_freq);
+	buf[1] = (n > 0xff) ? 0xff : (u8) n;
+	n = DIV_ROUND_CLOSEST((c->frequency / 1000) << 17, priv->cfg->xtal_freq / 1000);
+	buf[2] = (u8) (n >> 9);
+	buf[3] = (u8) (n >> 1);
+	buf[4] = (u8) (((n << 7) & 0x80) | 0x50);
+	ret = av201x_wrm(priv, buf, 5);
+	if (ret)
+		goto exit;
+
+	msleep(20);
+
+	/* set bandwidth */
+	bw = (c->symbol_rate / 1000) * 135/200;
+	if (c->symbol_rate < 6500000)
+		bw += 6000;
+	bw += 2000;
+	bw *= 108/100;
+
+	/* check limits (4MHz < bw < 40MHz) */
+	if (bw > 40000)
+		bw = 40000;
+	else if (bw < 4000)
+		bw = 4000;
+
+	/* bandwidth step = 211kHz */
+	bf = DIV_ROUND_CLOSEST(bw * 127, 21100);
+	ret = av201x_wr(priv, REG_BWFILTER, (u8) bf);
+
+	/* enable fine tune agc */
+	ret |= av201x_wr(priv, REG_FT_CTRL, AV201X_FT_EN | AV201X_FT_BLK);
+
+	ret |= av201x_wr(priv, REG_TUNER_CTRL, 0x96);
+	msleep(20);
+exit:
+	if (ret)
+		dev_dbg(&priv->i2c->dev, "%s() failed\n", __func__);
+	return ret;
+}
+
+static  int   AV201x_agc         [] = {     0,  82,   100,  116,  140,  162,  173,  187,  210,  223,  254,  255};
+static  int   AV201x_level_dBm_10[] = {    90, -50,  -263, -361, -463, -563, -661, -761, -861, -891, -904, -910};
+
+static int av201x_get_rf_strength(struct dvb_frontend *fe, u16 *st)
+{
+	struct av201x_priv *priv = fe->tuner_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int   if_agc, index, table_length, slope, *x, *y;
+
+	if_agc = *st;
+	x = AV201x_agc;
+	y = AV201x_level_dBm_10;
+	table_length = sizeof(AV201x_agc)/sizeof(int);
+
+	
+	/* Finding in which segment the if_agc value is */
+	for (index = 0; index < table_length; index ++)
+		if (x[index] > if_agc ) break;
+
+	/* Computing segment slope */
+	slope =  ((y[index]-y[index-1])*1000)/(x[index]-x[index-1]);
+	/* Linear approximation of rssi value in segment (rssi values will be in 0.1dBm unit: '-523' means -52.3 dBm) */
+	*st = 1000 + ((y[index-1] + ((if_agc - x[index-1])*slope + 500)/1000))/10;
+
+	c->strength.len = 1;
+	c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+	c->strength.stat[0].svalue = ((y[index-1] + ((if_agc - x[index-1])*slope + 500)/1000)) * 100;
+
+	return 0;
+}
+
+
+static const struct dvb_tuner_ops av201x_tuner_ops = {
+	.info = {
+		.name           = "Airoha Technology AV201x",
+		.frequency_min_hz = 850 * MHz,
+		.frequency_max_hz = 2300 * MHz,
+	},
+
+	.release = av201x_release,
+
+	.init = av201x_init,
+	.sleep = av201x_sleep,
+	.set_params = av201x_set_params,
+	.get_rf_strength = av201x_get_rf_strength,
+};
+
+struct dvb_frontend *av201x_attach(struct dvb_frontend *fe,
+		struct av201x_config *cfg, struct i2c_adapter *i2c)
+{
+	struct av201x_priv *priv = NULL;
+
+	priv = kzalloc(sizeof(struct av201x_priv), GFP_KERNEL);
+	if (priv == NULL) {
+		dev_dbg(&i2c->dev, "%s() attach failed\n", __func__);
+		return NULL;
+	}
+
+	priv->cfg = cfg;
+	priv->i2c = i2c;
+
+	dev_info(&priv->i2c->dev,
+		"%s: Airoha Technology AV201x successfully attached\n",
+		KBUILD_MODNAME);
+
+	memcpy(&fe->ops.tuner_ops, &av201x_tuner_ops,
+			sizeof(struct dvb_tuner_ops));
+
+	fe->tuner_priv = priv;
+	return fe;
+}
+EXPORT_SYMBOL(av201x_attach);
+
+MODULE_DESCRIPTION("Airoha Technology AV201x silicon tuner driver");
+MODULE_AUTHOR("Luis Alves <ljalvs@gmail.com>");
+MODULE_LICENSE("GPL");

diff -uraBN a/drivers/media/tuners/av201x.h b/drivers/media/tuners/av201x.h
--- a/drivers/media/tuners/av201x.h
+++ b/drivers/media/tuners/av201x.h
@@ -0,0 +1,55 @@
+/*
+ * AV201x Airoha Technology silicon tuner driver
+ *
+ * Copyright (C) 2014 Luis Alves <ljalvs@gmail.com>
+ *
+ *    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 AV201X_H
+#define AV201X_H
+
+#include <linux/kconfig.h>
+#include <media/dvb_frontend.h>
+
+typedef enum av201x_id {
+	ID_AV2011,
+	ID_AV2012,
+	ID_AV2018,
+} av201x_id_t;
+
+struct av201x_config {
+	/* tuner i2c address */
+	u8 i2c_address;
+	/* tuner type */
+	av201x_id_t id;
+
+	/* crystal freq in kHz */
+	u32 xtal_freq;
+};
+
+#if IS_REACHABLE(CONFIG_MEDIA_TUNER_AV201X)
+extern struct dvb_frontend *av201x_attach(struct dvb_frontend *fe,
+		struct av201x_config *cfg, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *av201x_attach(struct dvb_frontend *fe,
+		struct av201x_config *cfg, struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif /* AV201X_H */

diff -uraBN a/drivers/media/tuners/av201x_priv.h b/drivers/media/tuners/av201x_priv.h
--- a/drivers/media/tuners/av201x_priv.h
+++ b/drivers/media/tuners/av201x_priv.h
@@ -0,0 +1,110 @@
+/*
+ * AV201x Airoha Technology silicon tuner driver
+ *
+ * Copyright (C) 2014 Luis Alves <ljalvs@gmail.com>
+ *
+ *    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 AV201X_PRIV_H
+#define AV201X_PRIV_H
+
+struct av201x_priv {
+	struct av201x_config *cfg;
+	struct i2c_adapter *i2c;
+};
+
+enum av201x_regs_addr {
+	REG_FN		= 0x00,
+	REG_BWFILTER	= 0x05,
+	REG_TUNER_STAT	= 0x0b,
+	REG_TUNER_CTRL	= 0x0c,
+	REG_FT_CTRL	= 0x25,
+};
+
+/* REG_TUNER_STAT */
+#define AV201X_PLLLOCK		(1<<0)
+
+/* REG_TUNER_CTRL */
+#define AV201X_SLEEP		(1<<5)
+#define AV201X_RFLP		(1<<6)
+
+/* REG_FT_CTRL */
+#define AV201X_FT_EN		(1<<1)
+#define AV201X_FT_BLK		(1<<2)
+
+struct av201x_regtable {
+	u8 addr;
+	u8 setmask;
+	u8 clrmask;
+	int sleep;
+};
+
+static struct av201x_regtable av201x_inittuner0[] = {
+	{0x00, 0x38, 0xff, 0},
+	{0x01, 0x00, 0xff, 0},
+	{0x02, 0x00, 0xff, 0},
+	{0x03, 0x50, 0xff, 0},
+	{0x04, 0x1f, 0xff, 0},
+	{0x05, 0xa3, 0xff, 0},
+	{0x06, 0xfd, 0xff, 0},
+	{0x07, 0x58, 0xff, 0},
+	{0x08, 0x36, 0xff, 0},
+	{0x09, 0xc2, 0xff, 0},
+	{0x0a, 0x88, 0xff, 0},
+	{0x0b, 0xb4, 0xff, 20},
+	{0x0d, 0x40, 0xff, 0},
+};
+
+static struct av201x_regtable av201x_inittuner1a[] = {
+	{0x0e, 0x94, 0xff, 0},
+	{0x0f, 0x9a, 0xff, 0},
+};
+
+static struct av201x_regtable av201x_inittuner1b[] = {
+	{0x0e, 0x5b, 0xff, 0},
+	{0x0f, 0x6a, 0xff, 0},
+};
+
+static struct av201x_regtable av201x_inittuner2[] = {
+	{0x10, 0x66, 0xff, 0},
+	{0x11, 0x40, 0xff, 0},
+	{0x12, 0x80, 0xff, 0},
+	{0x13, 0x2b, 0xff, 0},
+	{0x14, 0x6a, 0xff, 0},
+	{0x15, 0x50, 0xff, 0},
+	{0x16, 0x91, 0xff, 0},
+	{0x17, 0x27, 0xff, 0},
+	{0x18, 0x8f, 0xff, 0},
+	{0x19, 0xcc, 0xff, 0},
+	{0x1a, 0x21, 0xff, 0},
+	{0x1b, 0x10, 0xff, 0},
+	{0x1c, 0x80, 0xff, 0},
+	{0x1d, 0x02, 0xff, 0},
+	{0x1e, 0xf5, 0xff, 0},
+	{0x1f, 0x7f, 0xff, 0},
+	{0x20, 0x4a, 0xff, 0},
+	{0x21, 0x9b, 0xff, 0},
+	{0x22, 0xe0, 0xff, 0},
+	{0x23, 0xe0, 0xff, 0},
+	{0x24, 0x36, 0xff, 0},
+	{0x25, 0x00, 0xff, 0},
+	{0x26, 0xab, 0xff, 0},
+	{0x27, 0x97, 0xff, 0},
+	{0x28, 0xc5, 0xff, 0},
+	{0x29, 0xa8, 0xff, 20},
+};
+
+#endif /* AV201X_PRIV_H */

diff -uraBN a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -297,4 +297,11 @@
 	  This device is only used inside a SiP called together with a
 	  demodulator for now.
 
+config MEDIA_TUNER_AV201X
+	tristate "Airoha Technology AV201x silicon tuner"
+	depends on MEDIA_SUPPORT && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Airoha Technology AV201x silicon tuner driver.
+
 endmenu

diff -uraBN a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile
--- a/drivers/media/tuners/Makefile
+++ b/drivers/media/tuners/Makefile
@@ -46,3 +46,4 @@
 obj-$(CONFIG_MEDIA_TUNER_XC2028) += xc2028.o
 obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o
 obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o
+obj-$(CONFIG_MEDIA_TUNER_AV201X) += av201x.o

diff -uraBN a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// // SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver
  *
@@ -35,7 +35,7 @@
 
 	if (cmd->rlen) {
 		/* wait cmd execution terminate */
-		#define TIMEOUT 80
+		#define TIMEOUT 500
 		timeout = jiffies + msecs_to_jiffies(TIMEOUT);
 		while (!time_after(jiffies, timeout)) {
 			ret = i2c_master_recv(client, cmd->args, cmd->rlen);
@@ -188,9 +188,9 @@
 	/* Update the part id based on device's report */
 	dev->part_id = part_id;
 
-	dev_info(&client->dev,
-		 "found a 'Silicon Labs Si21%d-%c%c%c ROM 0x%02x'\n",
-		 part_id, cmd.args[1], cmd.args[3], cmd.args[4], rom_id);
+	//dev_info(&client->dev,
+	//	 "found a 'Silicon Labs Si21%d-%c%c%c ROM 0x%02x'\n",
+	//	 part_id, cmd.args[1], cmd.args[3], cmd.args[4], rom_id);
 
 	if (fw_name)
 		ret = si2157_load_firmware(fe, fw_name);
@@ -223,6 +223,9 @@
 	int ret;
 
 	dev_dbg(&client->dev, "\n");
+
+	if (dev->active)
+		return 0;	
 
 	/* Try to get Xtal trim property, to verify tuner still running */
 	memcpy(cmd.args, "\x15\x00\x02\x04", 4);
@@ -285,8 +288,8 @@
 	if (ret)
 		goto err;
 
-	dev_info(&client->dev, "firmware version: %c.%c.%d\n",
-			cmd.args[6], cmd.args[7], cmd.args[8]);
+	//dev_info(&client->dev, "firmware version: %c.%c.%d\n",
+	//		cmd.args[6], cmd.args[7], cmd.args[8]);
 
 	/* enable tuner status flags */
 	memcpy(cmd.args, "\x14\x00\x01\x05\x01\x00", 6);
@@ -478,30 +481,27 @@
 
 	switch (c->delivery_system) {
 	case SYS_ATSC:
-			delivery_system = 0x00;
-			if_frequency = 3250000;
-			break;
-	case SYS_DVBC_ANNEX_B:
-			delivery_system = 0x10;
-			if_frequency = 4000000;
-			break;
+		delivery_system = 0x00;
+		if_frequency = 3250000;
+		break;
 	case SYS_DVBT:
 	case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */
-			delivery_system = 0x20;
-			break;
+		delivery_system = 0x20;
+		break;
 	case SYS_DVBC_ANNEX_A:
+	case SYS_DVBC_ANNEX_B:
 	case SYS_DVBC_ANNEX_C:
-			delivery_system = 0x30;
-			break;
+		delivery_system = 0x30;
+		break;
 	case SYS_ISDBT:
-			delivery_system = 0x40;
-			break;
+		delivery_system = 0x40;
+		break;
 	case SYS_DTMB:
-			delivery_system = 0x60;
-			break;
+		delivery_system = 0x60;
+		break;
 	default:
-			ret = -EINVAL;
-			goto err;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6);
@@ -525,6 +525,16 @@
 	ret = si2157_cmd_execute(client, &cmd);
 	if (ret)
 		goto err;
+
+#if 0	/* set LIF out amp */
+	memcpy(cmd.args, "\x14\x00\x07\x07\x94\x20", 6);
+	cmd.args[5] = delivery_system == 0x30 ? 0x2B : 0x20;
+	cmd.wlen = 6;
+	cmd.rlen = 4;
+	ret = si2157_cmd_execute(client, &cmd);
+	if (ret)
+		goto err;
+#endif
 
 	/* set digital if frequency if needed */
 	if (if_frequency != dev->if_frequency) {
@@ -865,19 +875,24 @@
 	if (ret)
 		goto err;
 
+	c->strength.len = 2;
 	c->strength.stat[0].scale = FE_SCALE_DECIBEL;
-	c->strength.stat[0].svalue = (s8) cmd.args[3] * 1000;
+	c->strength.stat[0].svalue = (s8)cmd.args[3] * 1000;
+
+	c->strength.stat[1].scale = FE_SCALE_RELATIVE;
+	c->strength.stat[1].uvalue = (100 + (s8)cmd.args[3]) * 656;
 
 	schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
 	return;
 err:
+	c->strength.len = 1;
 	c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
 	dev_dbg(&client->dev, "failed=%d\n", ret);
 }
 
-static int si2157_probe(struct i2c_client *client,
-			const struct i2c_device_id *id)
-{
+static int si2157_probe(struct i2c_client *client)
+{
+	const struct i2c_device_id *id = i2c_client_get_device_id(client);
 	struct si2157_config *cfg = client->dev.platform_data;
 	struct dvb_frontend *fe = cfg->fe;
 	struct si2157_dev *dev;
@@ -990,7 +1005,7 @@
 		.name		     = "si2157",
 		.suppress_bind_attrs = true,
 	},
-	.probe		= si2157_probe,
+	.probe_new	= si2157_probe,
 	.remove		= si2157_remove,
 	.id_table	= si2157_id_table,
 };
@@ -1000,6 +1015,7 @@
 MODULE_DESCRIPTION("Silicon Labs Si2141/Si2146/2147/2148/2157/2158 silicon tuner driver");
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
 MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(SI2157_A30_FIRMWARE);
 MODULE_FIRMWARE(SI2158_A20_FIRMWARE);
 MODULE_FIRMWARE(SI2141_A10_FIRMWARE);
 MODULE_FIRMWARE(SI2157_A30_FIRMWARE);

diff -uraBN a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
--- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
@@ -9,6 +9,8 @@
  */
 #include "dvb-usb-common.h"
 #include <media/media-device.h>
+
+#undef CONFIG_MEDIA_CONTROLLER_DVB
 
 /* does the complete input transfer handling */
 static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
@@ -317,6 +319,22 @@
 				return 0;
 		}
 
+		if(adap->fe_adap[i].fe2!=NULL){
+			if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe2)) {
+				err("Frontend %d registration failed.", i);
+				dvb_frontend_detach(adap->fe_adap[i].fe2);
+				adap->fe_adap[i].fe2 = NULL;
+				/* In error case, do not try register more FEs,
+				 * still leaving already registered FEs alive. */
+				if (i == 0)
+					return -ENODEV;
+				else
+					return 0;
+			}
+
+
+		}
+			
 		/* only attach the tuner if the demod is there */
 		if (adap->props.fe[i].tuner_attach != NULL)
 			adap->props.fe[i].tuner_attach(adap);
@@ -343,6 +361,10 @@
 			dvb_unregister_frontend(adap->fe_adap[i].fe);
 			dvb_frontend_detach(adap->fe_adap[i].fe);
 		}
+		if (adap->fe_adap[i].fe2 != NULL) {
+			dvb_unregister_frontend(adap->fe_adap[i].fe2);
+			dvb_frontend_detach(adap->fe_adap[i].fe2);
+		}
 	}
 	adap->num_frontends_initialized = 0;
 

diff -uraBN a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h
--- a/drivers/media/usb/dvb-usb/dvb-usb.h
+++ b/drivers/media/usb/dvb-usb/dvb-usb.h
@@ -372,7 +372,8 @@
  */
 struct dvb_usb_fe_adapter {
 	struct dvb_frontend *fe;
-
+	struct dvb_frontend *fe2;
+	struct dvb_frontend _fe2;
 	int (*fe_init)  (struct dvb_frontend *);
 	int (*fe_sleep) (struct dvb_frontend *);
 

diff -uraBN a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig
--- a/drivers/media/usb/dvb-usb/Kconfig
+++ b/drivers/media/usb/dvb-usb/Kconfig
@@ -346,4 +346,13 @@
 
 	  DVB-T USB2.0 receivers.
 
+config DVB_USB_TBS5580
+        tristate "Turbosight TBS5580 CI support"
+        depends on DVB_USB
+        select DVB_SI2183 if MEDIA_SUBDRV_AUTOSELECT
+        select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_AV201X if MEDIA_SUBDRV_AUTOSELECT
+        help
+          Say Y here to support the Turbosight TBS5580 CI USB2 DVB-T/T2/C/C2/S/S2/S2x device
+
 endif

diff -uraBN a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile
--- a/drivers/media/usb/dvb-usb/Makefile
+++ b/drivers/media/usb/dvb-usb/Makefile
@@ -80,6 +80,9 @@
 dvb-usb-technisat-usb2-objs := technisat-usb2.o
 obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o
 
+dvb-usb-tbs5580-objs = tbs5580.o
+obj-$(CONFIG_DVB_USB_TBS5580) += dvb-usb-tbs5580.o
+
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends/
 # due to tuner-xc3028
 ccflags-y += -I$(srctree)/drivers/media/tuners

diff -uraBN a/drivers/media/usb/dvb-usb/tbs5580.c b/drivers/media/usb/dvb-usb/tbs5580.c
--- a/drivers/media/usb/dvb-usb/tbs5580.c
+++ b/drivers/media/usb/dvb-usb/tbs5580.c
@@ -0,0 +1,793 @@
+/*
+ * TurboSight TBS 5580se  driver
+ *
+ * Copyright (c) 2017 Davin zhang <smiledavin@gmail.com>
+ *
+ * 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, version 2.
+ *
+ */
+
+#include <linux/version.h>
+#include "tbs5580.h"
+#include "si2183.h"
+#include "si2157.h"
+#include "av201x.h"
+#include <media/dvb_ca_en50221.h>
+
+#define TBS5580_READ_MSG 0
+#define TBS5580_WRITE_MSG 1
+
+#define TBS5580_VOLTAGE_CTRL (0x1800)
+
+
+struct tbs5580_state {
+	struct i2c_client *i2c_client_demod;
+	struct i2c_client *i2c_client_tuner; 
+	struct dvb_ca_en50221 ca;
+	struct mutex ca_mutex;
+};
+
+static struct av201x_config tbs5580_av201x_cfg = {
+	.i2c_address = 0x62,
+	.id          = ID_AV2018,
+	.xtal_freq   = 27000,
+};
+
+/* debug */
+static int dvb_usb_tbs5580_debug;
+module_param_named(debug, dvb_usb_tbs5580_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer (or-able))." 
+							DVB_USB_DEBUG_STATUS);
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int tbs5580_op_rw(struct usb_device *dev, u8 request, u16 value,
+				u16 index, u8 * data, u16 len, int flags)
+{
+	int ret;
+	void *u8buf;
+
+	unsigned int pipe = (flags == TBS5580_READ_MSG) ?
+			usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0);
+	u8 request_type = (flags == TBS5580_READ_MSG) ? USB_DIR_IN : 
+								USB_DIR_OUT;
+	u8buf = kmalloc(len, GFP_KERNEL);
+	if (!u8buf)
+		return -ENOMEM;
+
+	if (flags == TBS5580_WRITE_MSG)
+		memcpy(u8buf, data, len);
+	ret = usb_control_msg(dev, pipe, request, request_type | 
+			USB_TYPE_VENDOR, value, index , u8buf, len, 2000);
+
+	if (flags == TBS5580_READ_MSG)
+		memcpy(data, u8buf, len);
+	kfree(u8buf);
+	return ret;
+}
+
+static int tbs5580_read_attribute_mem(struct dvb_ca_en50221 *ca,
+                                                	int slot, int address)
+{
+	struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	u8 buf[4], rbuf[3];
+	int ret;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	buf[0] = 1;
+	buf[1] = 0;
+	buf[2] = (address >> 8) & 0x0f;
+	buf[3] = address;
+
+	//msleep(10);
+
+	mutex_lock(&state->ca_mutex);
+
+	ret = tbs5580_op_rw(d->udev, 0xa4, 0, 0,
+						buf, 4, TBS5580_WRITE_MSG);
+
+	//msleep(1);
+
+	ret = tbs5580_op_rw(d->udev, 0xa5, 0, 0,
+						rbuf, 1, TBS5580_READ_MSG);
+
+	mutex_unlock(&state->ca_mutex);
+
+	if (ret < 0)
+		return ret;
+
+	return rbuf[0];
+}
+
+static int tbs5580_write_attribute_mem(struct dvb_ca_en50221 *ca,
+						int slot, int address, u8 value)
+{
+	struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	u8 buf[5];//, rbuf[1];
+	int ret;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	buf[0] = 1;
+	buf[1] = 0;
+	buf[2] = (address >> 8) & 0x0f;
+	buf[3] = address;
+	buf[4] = value;
+
+	mutex_lock(&state->ca_mutex);
+
+	ret = tbs5580_op_rw(d->udev, 0xa2, 0, 0,
+						buf, 5, TBS5580_WRITE_MSG);
+
+	//msleep(1);
+
+	//ret = tbs5580_op_rw(d->udev, 0xa5, 0, 0,
+	//					rbuf, 1, TBS5580_READ_MSG);
+
+	mutex_unlock(&state->ca_mutex);
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tbs5580_read_cam_control(struct dvb_ca_en50221 *ca, int slot, 
+								u8 address)
+{
+	struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	u8 buf[4], rbuf[1];
+	int ret;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	buf[0] = 1;
+	buf[1] = 1;
+	buf[2] = (address >> 8) & 0x0f;
+	buf[3] = address;
+
+	mutex_lock(&state->ca_mutex);
+
+	ret = tbs5580_op_rw(d->udev, 0xa4, 0, 0,
+						buf, 4, TBS5580_WRITE_MSG);
+
+	//msleep(10);
+
+	ret = tbs5580_op_rw(d->udev, 0xa5, 0, 0,
+						rbuf, 1, TBS5580_READ_MSG);
+
+	mutex_unlock(&state->ca_mutex);
+
+	if (ret < 0)
+		return ret;
+
+	return rbuf[0];
+}
+
+static int tbs5580_write_cam_control(struct dvb_ca_en50221 *ca, int slot, 
+							u8 address, u8 value)
+{
+	struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	u8 buf[5];//, rbuf[1];
+	int ret;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	buf[0] = 1;
+	buf[1] = 1;
+	buf[2] = (address >> 8) & 0x0f;
+	buf[3] = address;
+	buf[4] = value;
+
+	mutex_lock(&state->ca_mutex);
+
+	ret = tbs5580_op_rw(d->udev, 0xa2, 0, 0,
+						buf, 5, TBS5580_WRITE_MSG);
+
+	//msleep(1);
+
+	//ret = tbs5580_op_rw(d->udev, 0xa5, 0, 0,
+	//					rbuf, 1, TBS5580_READ_MSG);
+
+	mutex_unlock(&state->ca_mutex);
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tbs5580_set_video_port(struct dvb_ca_en50221 *ca, 
+							int slot, int enable)
+{
+	struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	u8 buf[2];
+	int ret;
+
+	if (0 != slot)
+		return -EINVAL;
+
+	buf[0] = 2;
+	buf[1] = enable;
+
+	mutex_lock(&state->ca_mutex);
+
+	ret = tbs5580_op_rw(d->udev, 0xa6, 0, 0,
+						buf, 2, TBS5580_WRITE_MSG);
+
+	mutex_unlock(&state->ca_mutex);
+
+	if (ret < 0)
+		return ret;
+
+	if (enable != buf[1]) {
+		err("CI not %sabled.", enable ? "en" : "dis");
+		return -EIO;
+	}
+
+	info("CI %sabled.", enable ? "en" : "dis");
+	return 0;
+}
+
+static int tbs5580_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+	return tbs5580_set_video_port(ca, slot, /* enable */ 0);
+}
+
+static int tbs5580_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+	return tbs5580_set_video_port(ca, slot, /* enable */ 1);
+}
+
+static int tbs5580_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	u8 buf[2];
+	int ret;
+
+	if (0 != slot) {
+		return -EINVAL;
+	}
+
+	buf[0] = 1;
+	buf[1] = 0;
+
+	mutex_lock (&state->ca_mutex);
+
+	ret = tbs5580_op_rw(d->udev, 0xa6, 0, 0,
+						buf, 2, TBS5580_WRITE_MSG);
+
+	msleep (5);
+
+	buf[1] = 1;
+
+	ret = tbs5580_op_rw(d->udev, 0xa6, 0, 0,
+						buf, 2, TBS5580_WRITE_MSG);
+
+	msleep (1400);
+
+	mutex_unlock (&state->ca_mutex);
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tbs5580_poll_slot_status(struct dvb_ca_en50221 *ca,
+							int slot, int open)
+{
+	struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	u8 buf[3];
+
+	if (0 != slot)
+		return -EINVAL;
+
+	mutex_lock(&state->ca_mutex);
+
+	tbs5580_op_rw(d->udev, 0xa8, 0, 0,
+					buf, 3, TBS5580_READ_MSG);
+
+	mutex_unlock(&state->ca_mutex);
+
+	if ((1 == buf[2]) && (1 == buf[1]) && (0xa9 == buf[0])) {
+		return (DVB_CA_EN50221_POLL_CAM_PRESENT |
+				DVB_CA_EN50221_POLL_CAM_READY);
+	} else {
+		return 0;
+	}
+}
+
+static void tbs5580_uninit(struct dvb_usb_device *d)
+{
+	struct tbs5580_state *state;
+
+	if (NULL == d)
+		return;
+
+	state = (struct tbs5580_state *)d->priv;
+	if (NULL == state)
+		return;
+
+	if (NULL == state->ca.data)
+		return;
+
+	/* Error ignored. */
+	tbs5580_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0);
+
+	dvb_ca_en50221_release(&state->ca);
+
+	memset(&state->ca, 0, sizeof(state->ca));
+}
+
+static int tbs5580_init(struct dvb_usb_adapter *a)
+{
+
+	struct dvb_usb_device *d = a->dev;
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	int ret;
+
+	state->ca.owner = THIS_MODULE;
+	state->ca.read_attribute_mem = tbs5580_read_attribute_mem;
+	state->ca.write_attribute_mem = tbs5580_write_attribute_mem;
+	state->ca.read_cam_control = tbs5580_read_cam_control;
+	state->ca.write_cam_control = tbs5580_write_cam_control;
+	state->ca.slot_reset = tbs5580_slot_reset;
+	state->ca.slot_shutdown = tbs5580_slot_shutdown;
+	state->ca.slot_ts_enable = tbs5580_slot_ts_enable;
+	state->ca.poll_slot_status = tbs5580_poll_slot_status;
+	state->ca.data = d;
+
+	ret = dvb_ca_en50221_init (&a->dvb_adap, &state->ca,
+						/* flags */ 0, /* n_slots */ 1);
+
+	if (0 != ret) {
+		err ("Cannot initialize CI: Error %d.", ret);
+		memset (&state->ca, 0, sizeof (state->ca));
+		return ret;
+	}
+
+	info ("CI initialized.");
+
+	ret = tbs5580_poll_slot_status(&state->ca, 0, 0);
+	if (0 == ret)
+		tbs5580_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0);
+
+	return 0;
+}
+
+/* I2C */
+static int tbs5580_i2c_transfer(struct i2c_adapter *adap, 
+					struct i2c_msg msg[], int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	struct tbs5580_state *state = (struct tbs5580_state *)d->priv;
+	int i = 0;
+	u8 buf6[20];
+	u8 inbuf[20];
+
+	if (!d)
+		return -ENODEV;
+	mutex_lock(&state->ca_mutex);
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	switch (num) {
+	case 2:
+		buf6[0]=msg[1].len;//lenth
+		buf6[1]=msg[0].addr<<1;//demod addr
+		//register
+		buf6[2] = msg[0].buf[0];
+
+		tbs5580_op_rw(d->udev, 0x90, 0, 0,
+					buf6, 3, TBS5580_WRITE_MSG);
+		//msleep(5);
+		tbs5580_op_rw(d->udev, 0x91, 0, 0,
+					inbuf, buf6[0], TBS5580_READ_MSG);
+		memcpy(msg[1].buf, inbuf, msg[1].len);
+		break;
+	case 1:
+		switch (msg[0].addr) {
+			case 0x67:
+			case 0x62:
+			case 0x61:
+			if (msg[0].flags == 0) {
+				buf6[0] = msg[0].len+1;//lenth
+				buf6[1] = msg[0].addr<<1;//addr
+				for(i=0;i<msg[0].len;i++) {
+					buf6[2+i] = msg[0].buf[i];//register
+				}
+				tbs5580_op_rw(d->udev, 0x80, 0, 0,
+					buf6, msg[0].len+2, TBS5580_WRITE_MSG);
+			} else {
+				buf6[0] = msg[0].len;//length
+				buf6[1] = (msg[0].addr<<1) | 0x01;//addr
+				tbs5580_op_rw(d->udev, 0x93, 0, 0,
+						buf6, 2, TBS5580_WRITE_MSG);
+				//msleep(5);
+				tbs5580_op_rw(d->udev, 0x91, 0, 0,
+					inbuf, buf6[0], TBS5580_READ_MSG);
+				memcpy(msg[0].buf, inbuf, msg[0].len);
+			}
+			//msleep(3);
+			break;
+			case (TBS5580_VOLTAGE_CTRL):
+				buf6[0] = 3;
+				buf6[1] = msg[0].buf[0];
+				tbs5580_op_rw(d->udev, 0x8a, 0, 0,
+						buf6, 2, TBS5580_WRITE_MSG);
+			break;
+		}
+
+		break;
+	}
+
+	mutex_unlock(&d->i2c_mutex);
+	mutex_unlock(&state->ca_mutex);
+	return num;
+}
+
+static u32 tbs5580_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm tbs5580_i2c_algo = {
+	.master_xfer = tbs5580_i2c_transfer,
+	.functionality = tbs5580_i2c_func,
+};
+
+static int tbs5580_set_voltage(struct dvb_frontend *fe, 
+						enum fe_sec_voltage voltage)
+{
+	static u8 command_13v[1] = {0x00};
+	static u8 command_18v[1] = {0x01};
+	struct i2c_msg msg[] = {
+		{.addr = TBS5580_VOLTAGE_CTRL, .flags = 0,
+			.buf = command_13v, .len = 1},
+	};
+	
+	struct dvb_usb_adapter *udev_adap =
+		(struct dvb_usb_adapter *)(fe->dvb->priv);
+	if (voltage == SEC_VOLTAGE_18)
+		msg[0].buf = command_18v;
+
+	
+	i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1);
+	
+	return 0;
+}
+
+static int tbs5580_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+	int i,ret;
+	u8 ibuf[3] = {0, 0,0};
+	u8 eeprom[256], eepromline[16];
+
+	for (i = 0; i < 256; i++) {
+		ibuf[0]=1;//lenth
+		ibuf[1]=0xa0;//eeprom addr
+		ibuf[2]=i;//register
+		ret = tbs5580_op_rw(d->udev, 0x90, 0, 0,
+					ibuf, 3, TBS5580_WRITE_MSG);
+		ret = tbs5580_op_rw(d->udev, 0x91, 0, 0,
+					ibuf, 1, TBS5580_READ_MSG);
+			if (ret < 0) {
+				err("read eeprom failed.");
+				return -1;
+			} else {
+				eepromline[i%16] = ibuf[0];
+				eeprom[i] = ibuf[0];
+			}
+			
+			if ((i % 16) == 15) {
+				deb_xfer("%02x: ", i - 15);
+				debug_dump(eepromline, 16, deb_xfer);
+			}
+	}
+	memcpy(mac, eeprom + 16, 6);
+	return 0;
+};
+
+static struct dvb_usb_device_properties tbs5580_properties;
+
+static int tbs5580_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap->dev;
+	struct tbs5580_state *st = d->priv;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client_demod;
+	struct i2c_client *client_tuner;
+	struct i2c_board_info info;
+	struct si2183_config si2183_config;
+	struct si2157_config si2157_config;
+	u8 buf[20];
+
+	mutex_init(&st->ca_mutex);
+	/* attach frontend */
+	memset(&si2183_config,0,sizeof(si2183_config));
+	si2183_config.i2c_adapter = &adapter;
+	si2183_config.fe = &adap->fe_adap[0].fe;
+	si2183_config.ts_mode = SI2183_TS_PARALLEL;
+	si2183_config.ts_clock_gapped = true;
+	si2183_config.rf_in = 0;
+	si2183_config.RF_switch = NULL;
+	si2183_config.agc_mode = 0x5 ;
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	strlcpy(info.type, "si2183", I2C_NAME_SIZE);
+	info.addr = 0x67;
+	info.platform_data = &si2183_config;
+	request_module(info.type);
+	client_demod = i2c_new_client_device(&d->i2c_adap, &info);
+	if (!i2c_client_has_driver(client_demod))
+		return -ENODEV;
+
+	if (!try_module_get(client_demod->dev.driver->owner)) {
+		i2c_unregister_device(client_demod);
+		return -ENODEV;
+	}
+	st->i2c_client_demod = client_demod;
+
+	/* dvb core doesn't support 2 tuners for 1 demod so
+	   we split the adapter in 2 frontends */
+
+	adap->fe_adap[0].fe2 = &adap->fe_adap[0]._fe2;
+	memcpy(adap->fe_adap[0].fe2, adap->fe_adap[0].fe, sizeof(struct dvb_frontend));
+
+	/* terrestrial tuner */
+	memset(adap->fe_adap[0].fe->ops.delsys, 0, MAX_DELSYS);
+	adap->fe_adap[0].fe->ops.delsys[0] = SYS_DVBT;
+	adap->fe_adap[0].fe->ops.delsys[1] = SYS_DVBT2;
+	adap->fe_adap[0].fe->ops.delsys[2] = SYS_DVBC_ANNEX_A;
+	adap->fe_adap[0].fe->ops.delsys[3] = SYS_ISDBT;
+	adap->fe_adap[0].fe->ops.delsys[4] = SYS_DVBC_ANNEX_B;
+
+	/* attach ter tuner */
+	memset(&si2157_config, 0, sizeof(si2157_config));
+	si2157_config.fe = adap->fe_adap[0].fe;
+	si2157_config.if_port = 1;
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+	info.addr = 0x61;
+	info.platform_data = &si2157_config;
+	request_module(info.type);
+	client_tuner = i2c_new_client_device(adapter, &info);
+	if (!i2c_client_has_driver(client_tuner)) {
+		module_put(client_demod->dev.driver->owner);
+		i2c_unregister_device(client_demod);
+		return -ENODEV;
+	}
+	if (!try_module_get(client_tuner->dev.driver->owner)) {
+		i2c_unregister_device(client_tuner);
+		module_put(client_demod->dev.driver->owner);
+		i2c_unregister_device(client_demod);
+		return -ENODEV;
+	}
+
+	st->i2c_client_tuner = client_tuner;
+
+	/*attach SAT tuner*/
+	memset(adap->fe_adap[0].fe2->ops.delsys, 0, MAX_DELSYS);
+	adap->fe_adap[0].fe2->ops.delsys[0] = SYS_DVBS;
+	adap->fe_adap[0].fe2->ops.delsys[1] = SYS_DVBS2;
+	adap->fe_adap[0].fe2->ops.delsys[2] = SYS_DSS;
+	adap->fe_adap[0].fe2->id = 1;
+
+	if (dvb_attach(av201x_attach, adap->fe_adap[0].fe2, &tbs5580_av201x_cfg,
+			    adapter) == NULL) {
+			return -ENODEV;
+		}
+	else {
+		buf[0] = 1;
+		buf[1] = 0;
+		tbs5580_op_rw(d->udev, 0x8a, 0, 0,
+					buf, 2, TBS5580_WRITE_MSG);
+		
+		adap->fe_adap[0].fe2->ops.set_voltage = tbs5580_set_voltage;
+		
+	}
+
+	buf[0] = 0;
+	buf[1] = 0;
+	tbs5580_op_rw(d->udev, 0xb7, 0, 0,
+			buf, 2, TBS5580_WRITE_MSG);
+	buf[0] = 8;
+	buf[1] = 1;
+	tbs5580_op_rw(d->udev, 0x8a, 0, 0,
+			buf, 2, TBS5580_WRITE_MSG);
+	
+	tbs5580_init(adap);
+	
+	strlcpy(adap->fe_adap[0].fe->ops.info.name,d->props.devices[0].name,52);
+	strcat(adap->fe_adap[0].fe->ops.info.name," DVB-T/T2/C/C2/ISDB-T");
+	strlcpy(adap->fe_adap[0].fe2->ops.info.name,d->props.devices[0].name,52);
+	strcat(adap->fe_adap[0].fe2->ops.info.name," DVB-S/S2/S2X");
+
+	return 0;
+}
+
+static struct usb_device_id tbs5580_table[] = {
+	{USB_DEVICE(0x734c, 0x5580)},
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, tbs5580_table);
+
+static int tbs5580_load_firmware(struct usb_device *dev,
+			const struct firmware *frmwr)
+{
+	u8 *b, *p;
+	int ret = 0, i;
+	u8 reset;
+	const struct firmware *fw;
+	switch (dev->descriptor.idProduct) {
+	case 0x5580:
+		ret = request_firmware(&fw, tbs5580_properties.firmware, &dev->dev);
+		if (ret != 0) {
+			err("did not find the firmware file. (%s) "
+			"Please see linux/Documentation/dvb/ for more details "
+			"on firmware-problems.", tbs5580_properties.firmware);
+			return ret;
+		}
+		break;
+	default:
+		fw = frmwr;
+		break;
+	}
+	info("start downloading TBS5580 firmware");
+	p = kmalloc(fw->size, GFP_KERNEL);
+	reset = 1;
+	/*stop the CPU*/
+	tbs5580_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, TBS5580_WRITE_MSG);
+	tbs5580_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, TBS5580_WRITE_MSG);
+
+	if (p != NULL) {
+		memcpy(p, fw->data, fw->size);
+		for (i = 0; i < fw->size; i += 0x40) {
+			b = (u8 *) p + i;
+			if (tbs5580_op_rw(dev, 0xa0, i, 0, b , 0x40,
+					TBS5580_WRITE_MSG) != 0x40) {
+				err("error while transferring firmware");
+				ret = -EINVAL;
+				break;
+			}
+		}
+		/* restart the CPU */
+		reset = 0;
+		if (ret || tbs5580_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1,
+					TBS5580_WRITE_MSG) != 1) {
+			err("could not restart the USB controller CPU.");
+			ret = -EINVAL;
+		}
+		if (ret || tbs5580_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1,
+					TBS5580_WRITE_MSG) != 1) {
+			err("could not restart the USB controller CPU.");
+			ret = -EINVAL;
+		}
+
+		msleep(100);
+		kfree(p);
+	}
+	return ret;
+}
+
+static struct dvb_usb_device_properties tbs5580_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.firmware = "dvb-usb-id5580.fw",
+	.size_of_priv = sizeof(struct tbs5580_state),
+	.no_reconnect = 1,
+
+	.i2c_algo = &tbs5580_i2c_algo,
+
+	.generic_bulk_ctrl_endpoint = 0x81,
+	/* parameter for the MPEG2-data transfer */
+	.num_adapters = 1,
+	.download_firmware = tbs5580_load_firmware,
+	.read_mac_address = tbs5580_read_mac_address,
+	.adapter = {{
+		.num_frontends = 1,
+		.fe = {{
+			.frontend_attach = tbs5580_frontend_attach,
+			.streaming_ctrl = NULL,
+			.tuner_attach = NULL,
+			.stream = {
+				.type = USB_BULK,
+				.count = 8,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
+		}},
+	}},
+
+	.num_device_descs = 1,
+	.devices = {
+		{"TBS 5580 CI USB2.0",
+			{&tbs5580_table[0], NULL},
+			{NULL},
+		}
+	}
+};
+
+static int tbs5580_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	if (0 == dvb_usb_device_init(intf, &tbs5580_properties,
+			THIS_MODULE, NULL, adapter_nr)) {
+		return 0;
+	}
+	return -ENODEV;
+}
+
+static void tbs5580_disconnect(struct usb_interface *intf)
+{
+
+	struct dvb_usb_device *d = usb_get_intfdata(intf);
+#if 0	
+	struct tbs5580_state *st = d->priv;
+	struct i2c_client *client;
+
+	/* remove I2C client for tuner */
+	client = st->i2c_client_tuner;
+	if (client) {
+		module_put(client->dev.driver->owner);
+		i2c_unregister_device(client);
+	}
+
+	/* remove I2C client for demodulator */
+	client = st->i2c_client_demod;
+	if (client) {
+		module_put(client->dev.driver->owner);
+		i2c_unregister_device(client);
+	}
+#endif	
+	tbs5580_uninit(d);
+	dvb_usb_device_exit(intf);
+}
+
+static struct usb_driver tbs5580_driver = {
+	.name = "tbs5580",
+	.probe = tbs5580_probe,
+	.disconnect = tbs5580_disconnect,
+	.id_table = tbs5580_table,
+};
+
+static int __init tbs5580_module_init(void)
+{
+	int ret =  usb_register(&tbs5580_driver);
+	if (ret)
+		err("usb_register failed. Error number %d", ret);
+
+	return ret;
+}
+
+static void __exit tbs5580_module_exit(void)
+{
+	usb_deregister(&tbs5580_driver);
+}
+
+module_init(tbs5580_module_init);
+module_exit(tbs5580_module_exit);
+
+MODULE_AUTHOR("Davin zhang <smiledavin@gmail.com>");
+MODULE_DESCRIPTION("TurboSight TBS 5580 driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");

diff -uraBN a/drivers/media/usb/dvb-usb/tbs5580.h b/drivers/media/usb/dvb-usb/tbs5580.h
--- a/drivers/media/usb/dvb-usb/tbs5580.h
+++ b/drivers/media/usb/dvb-usb/tbs5580.h
@@ -0,0 +1,8 @@
+#ifndef _TBS5580_H_
+#define _TBS5580_H_
+
+#define DVB_USB_LOG_PREFIX "tbs5580"
+#include "dvb-usb.h"
+
+#define deb_xfer(args...) dprintk(dvb_usb_tbs5580_debug, 0x02, args)
+#endif

diff -uraBN a/include/linux/i2c.h b/include/linux/i2c.h
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -189,6 +189,7 @@
 					      u8 *values);
 int i2c_get_device_id(const struct i2c_client *client,
 		      struct i2c_device_identity *id);
+const struct i2c_device_id *i2c_client_get_device_id(const struct i2c_client *client);
 #endif /* I2C */
 
 /**

diff -uraBN a/include/media/dvb_frontend.h b/include/media/dvb_frontend.h
--- a/include/media/dvb_frontend.h
+++ b/include/media/dvb_frontend.h
@@ -433,8 +433,10 @@
  * @ts_bus_ctrl:	callback function used to take control of the TS bus.
  * @set_lna:		callback function to power on/off/auto the LNA.
  * @search:		callback function used on some custom algo search algos.
- * @tuner_ops:		pointer to &struct dvb_tuner_ops
- * @analog_ops:		pointer to &struct analog_demod_ops
+ * @tuner_ops:		pointer to struct dvb_tuner_ops
+ * @analog_ops:		pointer to struct analog_demod_ops
+ * @set_property:	callback function to allow the frontend to validade
+ *			incoming properties. Should not be used on new drivers.
  */
 struct dvb_frontend_ops {
 	struct dvb_frontend_internal_info info;
@@ -497,6 +499,22 @@
 
 	struct dvb_tuner_ops tuner_ops;
 	struct analog_demod_ops analog_ops;
+
+	int (*set_property)(struct dvb_frontend* fe, u32 cmd, u32 data);
+
+	void(*spi_read)( struct dvb_frontend *fe,struct ecp3_info *ecp3inf);
+	void(*spi_write)( struct dvb_frontend *fe,struct ecp3_info *ecp3inf);
+
+	void(*mcu_read)( struct dvb_frontend *fe,struct mcu24cxx_info *mcu24cxxinf);
+	void(*mcu_write)( struct dvb_frontend *fe,struct mcu24cxx_info *mcu24cxxinf);
+
+	void(*reg_i2cread)( struct dvb_frontend *fe,struct usbi2c_access *pi2cinf);
+	void(*reg_i2cwrite)( struct dvb_frontend *fe,struct usbi2c_access *pi2cinf);
+
+	void(*eeprom_read)( struct dvb_frontend *fe,struct eeprom_info *peepinf);
+	void(*eeprom_write)( struct dvb_frontend *fe,struct eeprom_info *peepinf);
+    
+    int (*read_temp)(struct dvb_frontend* fe, s16* temp);
 };
 
 #ifdef __DVB_CORE__
@@ -628,8 +646,9 @@
 
 	/* Multistream specifics */
 	u32			stream_id;
-
-	/* Physical Layer Scrambling specifics */
+	u32			modcode;
+
+    /* Physical Layer Scrambling specifics */
 	u32			scrambling_sequence_index;
 
 	/* ATSC-MH specifics */

diff -uraBN a/include/uapi/linux/dvb/frontend.h b/include/uapi/linux/dvb/frontend.h
--- a/include/uapi/linux/dvb/frontend.h
+++ b/include/uapi/linux/dvb/frontend.h
@@ -7,21 +7,6 @@
  *		    Holger Waechtler <holger@convergence.de>
  *		    Andre Draszik <ad@convergence.de>
  *		    for convergence integrated media GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * 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 Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- *
  */
 
 #ifndef _DVBFRONTEND_H_
@@ -69,6 +54,7 @@
  *					automatically
  * @FE_CAN_MUTE_TS:			Can stop spurious TS data output
  */
+
 enum fe_caps {
 	FE_IS_STUPID			= 0,
 	FE_CAN_INVERSION_AUTO		= 0x1,
@@ -282,7 +268,6 @@
 /**
  * enum fe_code_rate - Type of Forward Error Correction (FEC)
  *
- *
  * @FEC_NONE: No Forward Error Correction Code
  * @FEC_1_2:  Forward Error Correction Code 1/2
  * @FEC_2_3:  Forward Error Correction Code 2/3
@@ -296,6 +281,22 @@
  * @FEC_3_5:  Forward Error Correction Code 3/5
  * @FEC_9_10: Forward Error Correction Code 9/10
  * @FEC_2_5:  Forward Error Correction Code 2/5
+ * @FEC_1_3:  Forward Error Correction Code 1/3
+ * @FEC_1_4:  Forward Error Correction Code 1/4
+ * @FEC_5_9:  Forward Error Correction Code 5/9
+ * @FEC_7_9:  Forward Error Correction Code 7/9
+ * @FEC_8_15:  Forward Error Correction Code 8/15
+ * @FEC_11_15: Forward Error Correction Code 11/15
+ * @FEC_13_18: Forward Error Correction Code 13/18
+ * @FEC_9_20:  Forward Error Correction Code 9/20
+ * @FEC_11_20: Forward Error Correction Code 11/20
+ * @FEC_23_36: Forward Error Correction Code 23/36
+ * @FEC_25_36: Forward Error Correction Code 25/36
+ * @FEC_13_45: Forward Error Correction Code 13/45
+ * @FEC_26_45: Forward Error Correction Code 26/45
+ * @FEC_28_45: Forward Error Correction Code 28/45
+ * @FEC_32_45: Forward Error Correction Code 32/45
+ * @FEC_77_90: Forward Error Correction Code 77/90
  *
  * Please note that not all FEC types are supported by a given standard.
  */
@@ -313,6 +314,32 @@
 	FEC_3_5,
 	FEC_9_10,
 	FEC_2_5,
+	FEC_1_3,
+	FEC_1_4,
+	FEC_5_9,
+	FEC_7_9,
+	FEC_4_15,
+	FEC_7_15,
+	FEC_8_15,
+	FEC_11_15,
+	FEC_13_18,
+	FEC_9_20,
+	FEC_11_20,
+	FEC_23_36,
+	FEC_25_36,
+	FEC_11_45,
+	FEC_13_45,
+	FEC_14_45,
+	FEC_26_45,
+	FEC_28_45,
+	FEC_29_45,
+	FEC_31_45,
+	FEC_32_45,
+	FEC_77_90, 
+	FEC_R_58,
+	FEC_R_60,
+	FEC_R_62,
+	FEC_R_5E,
 };
 
 /**
@@ -331,6 +358,13 @@
  * @APSK_32:	32-APSK modulation
  * @DQPSK:	DQPSK modulation
  * @QAM_4_NR:	4-QAM-NR modulation
+ * @QAM_1024:	1024-QAM modulation
+ * @QAM_4096:	4096-QAM modulation
+ * @APSK_8_L:	8APSK-L modulation
+ * @APSK_16_L:	16APSK-L modulation
+ * @APSK_32_L:	32APSK-L modulation
+ * @APSK_64:	64APSK modulation
+ * @APSK_64_L:	64APSK-L modulation
  *
  * Please note that not all modulations are supported by a given standard.
  *
@@ -350,6 +384,19 @@
 	APSK_32,
 	DQPSK,
 	QAM_4_NR,
+	QAM_512,
+	QAM_1024,
+	QAM_4096,
+	APSK_64,
+	APSK_128,
+	APSK_256,
+	APSK_8L,
+	APSK_16L,
+	APSK_32L,
+	APSK_64L,
+	APSK_128L,
+	APSK_256L,
+	APSK_1024,
 };
 
 /**
@@ -404,6 +451,7 @@
  * @GUARD_INTERVAL_PN420:	PN length 420 (1/4)
  * @GUARD_INTERVAL_PN595:	PN length 595 (1/6)
  * @GUARD_INTERVAL_PN945:	PN length 945 (1/9)
+ * @GUARD_INTERVAL_1_64:	Guard interval 1/64
  *
  * Please note that not all guard intervals are supported by a given standard.
  */
@@ -419,6 +467,7 @@
 	GUARD_INTERVAL_PN420,
 	GUARD_INTERVAL_PN595,
 	GUARD_INTERVAL_PN945,
+	GUARD_INTERVAL_1_64,
 };
 
 /**
@@ -513,7 +562,8 @@
 
 #define DTV_STREAM_ID		42
 #define DTV_ISDBS_TS_ID_LEGACY	DTV_STREAM_ID
-#define DTV_DVBT2_PLP_ID_LEGACY	43
+#define DTV_DVBT2_PLP_ID_LEGACY	DTV_STREAM_ID
+#define DTV_MODCODE		43
 
 #define DTV_ENUM_DELSYS		44
 
@@ -571,6 +621,9 @@
  * @ROLLOFF_20:		Roloff factor: α=20%
  * @ROLLOFF_25:		Roloff factor: α=25%
  * @ROLLOFF_AUTO:	Auto-detect the roloff factor.
+ * @ROLLOFF_15:		Rolloff factor: α=15%
+ * @ROLLOFF_10:		Rolloff factor: α=10%
+ * @ROLLOFF_5:		Rolloff factor: α=5%
  *
  * .. note:
  *
@@ -581,6 +634,9 @@
 	ROLLOFF_20,
 	ROLLOFF_25,
 	ROLLOFF_AUTO,
+	ROLLOFF_15,
+	ROLLOFF_10,
+	ROLLOFF_5,
 };
 
 /**
@@ -594,6 +650,8 @@
  *	Cable TV: DVB-C following ITU-T J.83 Annex B spec (ClearQAM)
  * @SYS_DVBC_ANNEX_C:
  *	Cable TV: DVB-C following ITU-T J.83 Annex C spec
+ * @SYS_DVBC2:
+ *      Cable TV: DVB-C2
  * @SYS_ISDBC:
  *	Cable TV: ISDB-C (no drivers yet)
  * @SYS_DVBT:
@@ -611,7 +669,7 @@
  * @SYS_DVBS:
  *	Satellite TV: DVB-S
  * @SYS_DVBS2:
- *	Satellite TV: DVB-S2
+ *	Satellite TV: DVB-S2 and DVB-S2X
  * @SYS_TURBO:
  *	Satellite TV: DVB-S Turbo
  * @SYS_ISDBS:
@@ -645,6 +703,7 @@
 	SYS_DVBT2,
 	SYS_TURBO,
 	SYS_DVBC_ANNEX_C,
+	SYS_DVBC2,
 };
 
 /* backward compatibility definitions for delivery systems */
@@ -720,7 +779,7 @@
 };
 
 /**
- * enum atscmh_rs_code_mode
+ * enum atscmh_rs_code_mode - ATSC-M/H Reed Solomon modes
  * @ATSCMH_RSCODE_211_187:	Reed Solomon code (211,187).
  * @ATSCMH_RSCODE_223_187:	Reed Solomon code (223,187).
  * @ATSCMH_RSCODE_235_187:	Reed Solomon code (235,187).
@@ -734,7 +793,8 @@
 };
 
 #define NO_STREAM_ID_FILTER	(~0U)
-#define LNA_AUTO                (~0U)
+#define LNA_AUTO            (~0U)
+#define MODCODE_ALL         (~0U)
 
 /**
  * enum fecap_scale_params - scale types for the quality parameters.
@@ -1008,4 +1068,45 @@
 
 #endif
 
-#endif /*_DVBFRONTEND_H_*/
+struct ecp3_info
+{
+	__u8 reg;
+	__u32 data;
+};
+
+struct mcu24cxx_info
+{
+	__u32 bassaddr;
+	__u8 reg;
+	__u32 data;
+};
+
+struct usbi2c_access
+{
+	__u8 chip_addr;
+	__u8 reg;
+	__u8 num;
+	__u8 buf[8];
+};
+
+struct eeprom_info
+{
+	__u8 reg;
+	__u8 data;
+};
+
+#define FE_ECP3FW_READ    _IOR('o', 90, struct ecp3_info)
+#define FE_ECP3FW_WRITE   _IOW('o', 91, struct ecp3_info)
+
+#define FE_24CXX_READ    _IOR('o', 92, struct mcu24cxx_info)
+#define FE_24CXX_WRITE   _IOW('o', 93, struct mcu24cxx_info)
+
+#define FE_REGI2C_READ    _IOR('o', 94, struct usbi2c_access)
+#define FE_REGI2C_WRITE   _IOW('o', 95, struct usbi2c_access)
+
+#define FE_EEPROM_READ    _IOR('o', 96, struct eeprom_info)
+#define FE_EEPROM_WRITE   _IOW('o', 97, struct eeprom_info)
+
+#define FE_READ_TEMP	  _IOR('o', 98, __s16)
+
+#endif /* _DVBFRONTEND_H_ */

