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	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/dvb-core/dvb_frontend.c	2021-09-12 15:58:47.929555972 +0200
@@ -23,6 +23,7 @@
 #include <linux/poll.h>
 #include <linux/semaphore.h>
 #include <linux/module.h>
+#include <linux/nospec.h>
 #include <linux/list.h>
 #include <linux/freezer.h>
 #include <linux/jiffies.h>
@@ -39,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);
@@ -481,6 +482,10 @@
 	struct dvb_frontend_private *fepriv = fe->frontend_priv;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
 
+	if (fepriv->max_drift)
+		dev_warn_once(fe->dvb->device,
+			      "Frontend requested software zigzag, but didn't set the frequency step size\n");
+
 	/* if we've got no parameters, just keep idling */
 	if (fepriv->state & FESTATE_IDLE) {
 		fepriv->delay = 3 * HZ;
@@ -984,6 +989,7 @@
 				 fe->ops.info.symbol_rate_max);
 			return -EINVAL;
 		}
+		break;
 	default:
 		break;
 	}
@@ -1031,6 +1037,7 @@
 	}
 
 	c->stream_id = NO_STREAM_ID_FILTER;
+    c->modcode = MODCODE_ALL;
 	c->scrambling_sequence_index = 0;/* default sequence */
 
 	switch (c->delivery_system) {
@@ -1058,107 +1065,97 @@
 	return 0;
 }
 
-#define _DTV_CMD(n, s, b) \
-[n] = { \
-	.name = #n, \
-	.cmd  = n, \
-	.set  = s,\
-	.buffer = b \
-}
-
-struct dtv_cmds_h {
-	char	*name;		/* A display name for debugging purposes */
-
-	__u32	cmd;		/* A unique ID */
-
-	/* Flags */
-	__u32	set:1;		/* Either a set or get property */
-	__u32	buffer:1;	/* Does this property use the buffer? */
-	__u32	reserved:30;	/* Align */
-};
+#define _DTV_CMD(n) \
+	[n] =  #n
 
-static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
-	_DTV_CMD(DTV_TUNE, 1, 0),
-	_DTV_CMD(DTV_CLEAR, 1, 0),
+static char *dtv_cmds[DTV_MAX_COMMAND + 1] = {
+	_DTV_CMD(DTV_TUNE),
+	_DTV_CMD(DTV_CLEAR),
 
 	/* Set */
-	_DTV_CMD(DTV_FREQUENCY, 1, 0),
-	_DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0),
-	_DTV_CMD(DTV_MODULATION, 1, 0),
-	_DTV_CMD(DTV_INVERSION, 1, 0),
-	_DTV_CMD(DTV_DISEQC_MASTER, 1, 1),
-	_DTV_CMD(DTV_SYMBOL_RATE, 1, 0),
-	_DTV_CMD(DTV_INNER_FEC, 1, 0),
-	_DTV_CMD(DTV_VOLTAGE, 1, 0),
-	_DTV_CMD(DTV_TONE, 1, 0),
-	_DTV_CMD(DTV_PILOT, 1, 0),
-	_DTV_CMD(DTV_ROLLOFF, 1, 0),
-	_DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0),
-	_DTV_CMD(DTV_HIERARCHY, 1, 0),
-	_DTV_CMD(DTV_CODE_RATE_HP, 1, 0),
-	_DTV_CMD(DTV_CODE_RATE_LP, 1, 0),
-	_DTV_CMD(DTV_GUARD_INTERVAL, 1, 0),
-	_DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0),
-	_DTV_CMD(DTV_INTERLEAVING, 1, 0),
-
-	_DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0),
-	_DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0),
-	_DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0),
-	_DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0),
-	_DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0),
-	_DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0),
-
-	_DTV_CMD(DTV_STREAM_ID, 1, 0),
-	_DTV_CMD(DTV_DVBT2_PLP_ID_LEGACY, 1, 0),
-	_DTV_CMD(DTV_SCRAMBLING_SEQUENCE_INDEX, 1, 0),
-	_DTV_CMD(DTV_LNA, 1, 0),
+	_DTV_CMD(DTV_FREQUENCY),
+	_DTV_CMD(DTV_BANDWIDTH_HZ),
+	_DTV_CMD(DTV_MODULATION),
+	_DTV_CMD(DTV_INVERSION),
+	_DTV_CMD(DTV_DISEQC_MASTER),
+	_DTV_CMD(DTV_SYMBOL_RATE),
+	_DTV_CMD(DTV_INNER_FEC),
+	_DTV_CMD(DTV_VOLTAGE),
+	_DTV_CMD(DTV_TONE),
+	_DTV_CMD(DTV_PILOT),
+	_DTV_CMD(DTV_ROLLOFF),
+	_DTV_CMD(DTV_DELIVERY_SYSTEM),
+	_DTV_CMD(DTV_HIERARCHY),
+	_DTV_CMD(DTV_CODE_RATE_HP),
+	_DTV_CMD(DTV_CODE_RATE_LP),
+	_DTV_CMD(DTV_GUARD_INTERVAL),
+	_DTV_CMD(DTV_TRANSMISSION_MODE),
+	_DTV_CMD(DTV_INTERLEAVING),
+
+	_DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION),
+	_DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING),
+	_DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID),
+	_DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX),
+	_DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT),
+	_DTV_CMD(DTV_ISDBT_LAYER_ENABLED),
+	_DTV_CMD(DTV_ISDBT_LAYERA_FEC),
+	_DTV_CMD(DTV_ISDBT_LAYERA_MODULATION),
+	_DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT),
+	_DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING),
+	_DTV_CMD(DTV_ISDBT_LAYERB_FEC),
+	_DTV_CMD(DTV_ISDBT_LAYERB_MODULATION),
+	_DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT),
+	_DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING),
+	_DTV_CMD(DTV_ISDBT_LAYERC_FEC),
+	_DTV_CMD(DTV_ISDBT_LAYERC_MODULATION),
+	_DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT),
+	_DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING),
+
+	_DTV_CMD(DTV_STREAM_ID),
+	_DTV_CMD(DTV_MODCODE),
+	_DTV_CMD(DTV_SCRAMBLING_SEQUENCE_INDEX),
+	_DTV_CMD(DTV_LNA),
 
 	/* Get */
-	_DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
-	_DTV_CMD(DTV_API_VERSION, 0, 0),
+	_DTV_CMD(DTV_DISEQC_SLAVE_REPLY),
+	_DTV_CMD(DTV_API_VERSION),
 
-	_DTV_CMD(DTV_ENUM_DELSYS, 0, 0),
+	_DTV_CMD(DTV_ENUM_DELSYS),
 
-	_DTV_CMD(DTV_ATSCMH_PARADE_ID, 1, 0),
-	_DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 1, 0),
+	_DTV_CMD(DTV_ATSCMH_PARADE_ID),
+	_DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE),
 
-	_DTV_CMD(DTV_ATSCMH_FIC_VER, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_NOG, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_TNOG, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_SGN, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_PRC, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0),
-	_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0),
+	_DTV_CMD(DTV_ATSCMH_FIC_VER),
+	_DTV_CMD(DTV_ATSCMH_NOG),
+	_DTV_CMD(DTV_ATSCMH_TNOG),
+	_DTV_CMD(DTV_ATSCMH_SGN),
+	_DTV_CMD(DTV_ATSCMH_PRC),
+	_DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE),
+	_DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI),
+	_DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC),
+	_DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE),
+	_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A),
+	_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B),
+	_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C),
+	_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D),
 
 	/* Statistics API */
-	_DTV_CMD(DTV_STAT_SIGNAL_STRENGTH, 0, 0),
-	_DTV_CMD(DTV_STAT_CNR, 0, 0),
-	_DTV_CMD(DTV_STAT_PRE_ERROR_BIT_COUNT, 0, 0),
-	_DTV_CMD(DTV_STAT_PRE_TOTAL_BIT_COUNT, 0, 0),
-	_DTV_CMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0, 0),
-	_DTV_CMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0, 0),
-	_DTV_CMD(DTV_STAT_ERROR_BLOCK_COUNT, 0, 0),
-	_DTV_CMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0, 0),
+	_DTV_CMD(DTV_STAT_SIGNAL_STRENGTH),
+	_DTV_CMD(DTV_STAT_CNR),
+	_DTV_CMD(DTV_STAT_PRE_ERROR_BIT_COUNT),
+	_DTV_CMD(DTV_STAT_PRE_TOTAL_BIT_COUNT),
+	_DTV_CMD(DTV_STAT_POST_ERROR_BIT_COUNT),
+	_DTV_CMD(DTV_STAT_POST_TOTAL_BIT_COUNT),
+	_DTV_CMD(DTV_STAT_ERROR_BLOCK_COUNT),
+	_DTV_CMD(DTV_STAT_TOTAL_BLOCK_COUNT),
 };
 
+static char *dtv_cmd_name(u32 cmd)
+{
+	cmd = array_index_nospec(cmd, DTV_MAX_COMMAND);
+	return dtv_cmds[cmd];
+}
+
 /* Synchronise the legacy tuning parameters into the cache, so that demodulator
  * drivers can use a single set_frontend tuning function, regardless of whether
  * it's being used for the legacy or new API, reducing code and complexity.
@@ -1341,6 +1338,7 @@
 				    struct file *file)
 {
 	int ncaps;
+	unsigned int len = 1;
 
 	switch (tvp->cmd) {
 	case DTV_ENUM_DELSYS:
@@ -1350,6 +1348,7 @@
 			ncaps++;
 		}
 		tvp->u.buffer.len = ncaps;
+		len = ncaps;
 		break;
 	case DTV_FREQUENCY:
 		tvp->u.data = c->frequency;
@@ -1464,10 +1463,14 @@
 
 	/* 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 */
 	case DTV_SCRAMBLING_SEQUENCE_INDEX:
 		tvp->u.data = c->scrambling_sequence_index;
@@ -1527,27 +1530,51 @@
 	/* Fill quality measures */
 	case DTV_STAT_SIGNAL_STRENGTH:
 		tvp->u.st = c->strength;
+		if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+			tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+		len = tvp->u.buffer.len;
 		break;
 	case DTV_STAT_CNR:
 		tvp->u.st = c->cnr;
+		if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+			tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+		len = tvp->u.buffer.len;
 		break;
 	case DTV_STAT_PRE_ERROR_BIT_COUNT:
 		tvp->u.st = c->pre_bit_error;
+		if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+			tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+		len = tvp->u.buffer.len;
 		break;
 	case DTV_STAT_PRE_TOTAL_BIT_COUNT:
 		tvp->u.st = c->pre_bit_count;
+		if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+			tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+		len = tvp->u.buffer.len;
 		break;
 	case DTV_STAT_POST_ERROR_BIT_COUNT:
 		tvp->u.st = c->post_bit_error;
+		if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+			tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+		len = tvp->u.buffer.len;
 		break;
 	case DTV_STAT_POST_TOTAL_BIT_COUNT:
 		tvp->u.st = c->post_bit_count;
+		if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+			tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+		len = tvp->u.buffer.len;
 		break;
 	case DTV_STAT_ERROR_BLOCK_COUNT:
 		tvp->u.st = c->block_error;
+		if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+			tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+		len = tvp->u.buffer.len;
 		break;
 	case DTV_STAT_TOTAL_BLOCK_COUNT:
 		tvp->u.st = c->block_count;
+		if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+			tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+		len = tvp->u.buffer.len;
 		break;
 	default:
 		dev_dbg(fe->dvb->device,
@@ -1556,18 +1583,13 @@
 		return -EINVAL;
 	}
 
-	if (!dtv_cmds[tvp->cmd].buffer)
-		dev_dbg(fe->dvb->device,
-			"%s: GET cmd 0x%08x (%s) = 0x%08x\n",
-			__func__, tvp->cmd, dtv_cmds[tvp->cmd].name,
-			tvp->u.data);
-	else
-		dev_dbg(fe->dvb->device,
-			"%s: GET cmd 0x%08x (%s) len %d: %*ph\n",
-			__func__,
-			tvp->cmd, dtv_cmds[tvp->cmd].name,
-			tvp->u.buffer.len,
-			tvp->u.buffer.len, tvp->u.buffer.data);
+	if (len < 1)
+		len = 1;
+
+	dev_dbg(fe->dvb->device,
+		"%s: GET cmd 0x%08x (%s) len %d: %*ph\n",
+		__func__, tvp->cmd, dtv_cmd_name(tvp->cmd),
+		tvp->u.buffer.len, tvp->u.buffer.len, tvp->u.buffer.data);
 
 	return 0;
 }
@@ -1790,6 +1812,53 @@
 	return emulate_delivery_system(fe, delsys);
 }
 
+static void prepare_tuning_algo_parameters(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct dvb_frontend_private *fepriv = fe->frontend_priv;
+	struct dvb_frontend_tune_settings fetunesettings = { 0 };
+
+	/* get frontend-specific tuning settings */
+	if (fe->ops.get_tune_settings && (fe->ops.get_tune_settings(fe, &fetunesettings) == 0)) {
+		fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
+		fepriv->max_drift = fetunesettings.max_drift;
+		fepriv->step_size = fetunesettings.step_size;
+	} else {
+		/* default values */
+		switch (c->delivery_system) {
+		case SYS_DVBS:
+		case SYS_DVBS2:
+		case SYS_ISDBS:
+		case SYS_TURBO:
+		case SYS_DVBC_ANNEX_A:
+		case SYS_DVBC_ANNEX_C:
+			fepriv->min_delay = HZ / 20;
+			fepriv->step_size = c->symbol_rate / 16000;
+			fepriv->max_drift = c->symbol_rate / 2000;
+			break;
+		case SYS_DVBT:
+		case SYS_DVBT2:
+		case SYS_ISDBT:
+		case SYS_DTMB:
+			fepriv->min_delay = HZ / 20;
+			fepriv->step_size = dvb_frontend_get_stepsize(fe) * 2;
+			fepriv->max_drift = fepriv->step_size + 1;
+			break;
+		default:
+			/*
+			 * FIXME: This sounds wrong! if freqency_stepsize is
+			 * defined by the frontend, why not use it???
+			 */
+			fepriv->min_delay = HZ / 20;
+			fepriv->step_size = 0; /* no zigzag */
+			fepriv->max_drift = 0;
+			break;
+		}
+	}
+	if (dvb_override_tune_delay > 0)
+		fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
+}
+
 /**
  * dtv_property_process_set -  Sets a single DTV property
  * @fe:		Pointer to &struct dvb_frontend
@@ -1818,7 +1887,15 @@
 	else
 		dev_dbg(fe->dvb->device,
 			"%s: SET cmd 0x%08x (%s) to 0x%08x\n",
-			__func__, cmd, dtv_cmds[cmd].name, data);
+			__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:
 		/*
@@ -1952,10 +2029,14 @@
 
 	/* 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 */
 	case DTV_SCRAMBLING_SEQUENCE_INDEX:
 		c->scrambling_sequence_index = data;
@@ -2182,7 +2263,6 @@
 {
 	struct dvb_frontend_private *fepriv = fe->frontend_priv;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-	struct dvb_frontend_tune_settings fetunesettings;
 	u32 rolloff = 0;
 
 	if (dvb_frontend_check_parameters(fe) < 0)
@@ -2260,46 +2340,7 @@
 	if (c->hierarchy == HIERARCHY_NONE && c->code_rate_LP == FEC_NONE)
 		c->code_rate_LP = FEC_AUTO;
 
-	/* get frontend-specific tuning settings */
-	memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
-	if (fe->ops.get_tune_settings && (fe->ops.get_tune_settings(fe, &fetunesettings) == 0)) {
-		fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
-		fepriv->max_drift = fetunesettings.max_drift;
-		fepriv->step_size = fetunesettings.step_size;
-	} else {
-		/* default values */
-		switch (c->delivery_system) {
-		case SYS_DVBS:
-		case SYS_DVBS2:
-		case SYS_ISDBS:
-		case SYS_TURBO:
-		case SYS_DVBC_ANNEX_A:
-		case SYS_DVBC_ANNEX_C:
-			fepriv->min_delay = HZ / 20;
-			fepriv->step_size = c->symbol_rate / 16000;
-			fepriv->max_drift = c->symbol_rate / 2000;
-			break;
-		case SYS_DVBT:
-		case SYS_DVBT2:
-		case SYS_ISDBT:
-		case SYS_DTMB:
-			fepriv->min_delay = HZ / 20;
-			fepriv->step_size = dvb_frontend_get_stepsize(fe) * 2;
-			fepriv->max_drift = (dvb_frontend_get_stepsize(fe) * 2) + 1;
-			break;
-		default:
-			/*
-			 * FIXME: This sounds wrong! if freqency_stepsize is
-			 * defined by the frontend, why not use it???
-			 */
-			fepriv->min_delay = HZ / 20;
-			fepriv->step_size = 0; /* no zigzag */
-			fepriv->max_drift = 0;
-			break;
-		}
-	}
-	if (dvb_override_tune_delay > 0)
-		fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
+	prepare_tuning_algo_parameters(fe);
 
 	fepriv->state = FESTATE_RETUNE;
 
@@ -2398,6 +2439,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;
@@ -2643,37 +2755,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;
 
diff -uraBN a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
--- a/drivers/media/dvb-frontends/Kconfig	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/dvb-frontends/Kconfig	2021-09-12 15:42:10.064330819 +0200
@@ -289,6 +289,13 @@
 	help
 	  A DVB-S tuner module. Say Y when you want to support this frontend.
 
+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.
+
 config DVB_TS2020
 	tristate "Montage Tehnology TS2020 based tuners"
 	depends on DVB_CORE && I2C
diff -uraBN a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
--- a/drivers/media/dvb-frontends/Makefile	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/dvb-frontends/Makefile	2021-09-12 15:43:02.509024048 +0200
@@ -115,6 +115,7 @@
 obj-$(CONFIG_DVB_STV6111) += stv6111.o
 obj-$(CONFIG_DVB_MXL5XX) += mxl5xx.o
 obj-$(CONFIG_DVB_SI2165) += si2165.o
+obj-$(CONFIG_DVB_SI2183) += si2183.o
 obj-$(CONFIG_DVB_A8293) += a8293.o
 obj-$(CONFIG_DVB_SP2) += sp2.o
 obj-$(CONFIG_DVB_TDA10071) += tda10071.o
diff -uraBN a/drivers/media/dvb-frontends/si2183.c b/drivers/media/dvb-frontends/si2183.c
--- a/drivers/media/dvb-frontends/si2183.c	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/media/dvb-frontends/si2183.c	2021-09-12 15:43:58.589765667 +0200
@@ -0,0 +1,1834 @@
+/*
+ * 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>
+#include <linux/version.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;
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;			
+		c->cnr.stat[0].svalue = (s64) cmd.args[3] * 250;
+		dev->snr *= cmd.args[3] * 164;
+		// 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);
+	}
+
+	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;
+	
+	*strength = c->strength.stat[0].scale == FE_SCALE_DECIBEL ? ((100000 + (s32)c->strength.stat[0].svalue) / 1000) * 656 : 0;
+
+	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;
+	}
+
+   // set pins
+   memcpy(cmd.args, "\x12\x8\x0", 3);
+   cmd.wlen = 3;
+   cmd.rlen = 3;
+   si2183_cmd_execute(client, &cmd);
+
+	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 int 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);
+
+	return 0;
+}
+
+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	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/media/dvb-frontends/si2183.h	2021-09-12 15:44:07.969889754 +0200
@@ -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	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/media/tuners/av201x.c	2021-09-12 15:49:07.937861984 +0200
@@ -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	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/media/tuners/av201x.h	2021-09-12 15:49:14.141944212 +0200
@@ -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	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/media/tuners/av201x_priv.h	2021-09-12 15:49:21.294039011 +0200
@@ -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	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/tuners/Kconfig	2021-09-12 15:47:53.980882022 +0200
@@ -296,4 +296,11 @@
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Sharp QM1D1B0004 ISDB-S tuner driver.
+
+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	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/tuners/Makefile	2021-09-12 15:48:22.341257769 +0200
@@ -44,5 +44,6 @@
 obj-$(CONFIG_MEDIA_TUNER_QM1D1B0004) += qm1d1b0004.o
 obj-$(CONFIG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o
 obj-$(CONFIG_MEDIA_TUNER_TDA18250) += tda18250.o
+obj-$(CONFIG_MEDIA_TUNER_AV201X) += av201x.o
 
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
diff -uraBN a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
--- a/drivers/media/tuners/si2157.c	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/tuners/si2157.c	2021-09-12 16:29:46.743139468 +0200
@@ -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);
@@ -422,23 +422,28 @@
 
 	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:
-			delivery_system = 0x30;
-			break;
+		delivery_system = 0x30;
+		break;
+	case SYS_DVBC_ANNEX_B:
+		delivery_system = 0x10;
+		break;
+	case SYS_ISDBT:
+		delivery_system = 0x40;
+		break;
+	case SYS_DTMB:
+		delivery_system = 0x60;
+		break;
 	default:
-			ret = -EINVAL;
-			goto err;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6);
@@ -838,7 +843,21 @@
 	INIT_DELAYED_WORK(&dev->stat_work, si2157_stat_work);
 
 	/* check if the tuner is there */
-	cmd.wlen = 0;
+	/* wake tuner */
+	switch (dev->chiptype)
+	{
+	case SI2157_CHIPTYPE_SI2141:
+		memcpy(cmd.args, "\xc0\x08\x01\x02\x00\x08\x01", 7);
+		cmd.wlen = 7;
+		break;
+	case SI2157_CHIPTYPE_SI2146:
+		memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9);
+		cmd.wlen = 9;
+		break;
+	default:
+		memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15);
+		cmd.wlen = 15;
+	}
 	cmd.rlen = 1;
 	ret = si2157_cmd_execute(client, &cmd);
 	if (ret && ret != -EAGAIN)
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	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c	2021-09-12 16:27:37.101279711 +0200
@@ -10,6 +10,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	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/usb/dvb-usb/dvb-usb.h	2021-09-12 15:51:24.335670269 +0200
@@ -28,18 +28,19 @@
 
 /* debug */
 #ifdef CONFIG_DVB_USB_DEBUG
-#define dprintk(var,level,args...) \
-	    do { if ((var & level)) { printk(args); } } while (0)
+#define dprintk(var, level, args...) \
+	    do { if (((var) & (level))) { printk(args); } } while (0)
 
-#define debug_dump(b,l,func) {\
+#define debug_dump(b, l, func) {\
 	int loop_; \
-	for (loop_ = 0; loop_ < l; loop_++) func("%02x ", b[loop_]); \
+	for (loop_ = 0; loop_ < (l); loop_++) \
+		func("%02x ", b[loop_]); \
 	func("\n");\
 }
 #define DVB_USB_DEBUG_STATUS
 #else
-#define dprintk(args...)
-#define debug_dump(b,l,func)
+#define dprintk(var, level, args...) no_printk(args)
+#define debug_dump(b, l, func) do { } while (0)
 
 #define DVB_USB_DEBUG_STATUS " (debugging is not enabled)"
 
@@ -95,7 +96,7 @@
 struct dvb_usb_adapter;
 struct usb_data_stream;
 
-/**
+/*
  * Properties of USB streaming - TODO this structure should be somewhere else
  * describes the kind of USB transfer used for data-streaming.
  *  (BULK or ISOC)
@@ -120,7 +121,7 @@
 };
 
 /**
- * struct dvb_usb_adapter_properties - properties of a dvb-usb-adapter.
+ * struct dvb_usb_adapter_fe_properties - properties of a dvb-usb-adapter.
  *    A DVB-USB-Adapter is basically a dvb_adapter which is present on a USB-device.
  * @caps: capabilities of the DVB USB device.
  * @pid_filter_count: number of PID filter position in the optional hardware
@@ -139,6 +140,7 @@
  * @tuner_attach: called to attach the correct tuner and to fill pll_addr,
  *  pll_desc and pll_init_buf of struct dvb_usb_device).
  * @stream: configuration of the USB streaming
+ * @size_of_priv: size of the priv memory in struct dvb_usb_adapter
  */
 struct dvb_usb_adapter_fe_properties {
 #define DVB_USB_ADAP_HAS_PID_FILTER               0x01
@@ -191,15 +193,17 @@
 };
 
 /**
- * struct dvb_rc properties of remote controller, using rc-core
+ * struct dvb_rc - properties of remote controller, using rc-core
  * @rc_codes: name of rc codes table
  * @protocol: type of protocol(s) currently used by the driver
  * @allowed_protos: protocol(s) supported by the driver
  * @driver_type: Used to point if a device supports raw mode
  * @change_protocol: callback to change protocol
+ * @module_name: module name
  * @rc_query: called to query an event event.
  * @rc_interval: time in ms between two queries.
  * @bulk_mode: device supports bulk mode for RC (disable polling mode)
+ * @scancode_mask: scancode mask
  */
 struct dvb_rc {
 	char *rc_codes;
@@ -219,6 +223,9 @@
  *		       based on rc-core
  * This is initialized/used only inside dvb-usb-remote.c.
  * It shouldn't be set by the drivers.
+ *
+ * @DVB_RC_LEGACY: legacy driver
+ * @DVB_RC_CORE: rc-core driver
  */
 enum dvb_usb_mode {
 	DVB_RC_LEGACY,
@@ -227,6 +234,7 @@
 
 /**
  * struct dvb_usb_device_properties - properties of a dvb-usb-device
+ * @caps: capabilities
  * @usb_ctrl: which USB device-side controller is in use. Needed for firmware
  *  download.
  * @firmware: name of the firmware file.
@@ -243,6 +251,8 @@
  * @priv_destroy: just like priv_init, only called before deallocating
  * the memory pointed by private field of struct dvb_usb_device.
  *
+ * @num_adapters: the number of adapters in @adapters
+ * @adapter: the adapters
  * @power_ctrl: called to enable/disable power of the device.
  * @read_mac_address: called to read the MAC address of the device.
  * @identify_state: called to determine the state (cold or warm), when it
@@ -267,9 +277,8 @@
  * @devices: array of struct dvb_usb_device_description compatibles with these
  *  properties.
  */
-#define MAX_NO_OF_ADAPTER_PER_DEVICE 2
 struct dvb_usb_device_properties {
-
+#define MAX_NO_OF_ADAPTER_PER_DEVICE 2
 #define DVB_USB_IS_AN_I2C_ADAPTER            0x01
 	int caps;
 
@@ -313,6 +322,11 @@
 
 /**
  * struct usb_data_stream - generic object of an USB stream
+ * @udev: the USB device
+ * @props: data stream properties
+ * @state: state of the stream
+ * @complete: complete callback
+ * @urb_list: list of URBs
  * @buf_num: number of buffer allocated.
  * @buf_size: size of each buffer in buf_list.
  * @buf_list: array containing all allocate buffers for streaming.
@@ -320,9 +334,10 @@
  *
  * @urbs_initialized: number of URBs initialized.
  * @urbs_submitted: number of URBs submitted.
+ * @user_priv: for private use.
  */
-#define MAX_NO_URBS_FOR_DATA_STREAM 10
 struct usb_data_stream {
+#define MAX_NO_URBS_FOR_DATA_STREAM 10
 	struct usb_device                 *udev;
 	struct usb_data_stream_properties  props;
 
@@ -345,33 +360,20 @@
 };
 
 /**
- * struct dvb_usb_adapter - a DVB adapter on a USB device
- * @id: index of this adapter (starting with 0).
- *
- * @feedcount: number of requested feeds (used for streaming-activation)
- * @pid_filtering: is hardware pid_filtering used or not.
- *
- * @pll_addr: I2C address of the tuner for programming
- * @pll_init: array containing the initialization buffer
- * @pll_desc: pointer to the appropriate struct dvb_pll_desc
- * @tuner_pass_ctrl: called to (de)activate tuner passthru of the demod or the board
- *
- * @dvb_adap: device's dvb_adapter.
- * @dmxdev: device's dmxdev.
- * @demux: device's software demuxer.
- * @dvb_net: device's dvb_net interfaces.
- * @dvb_frontend: device's frontend.
- * @max_feed_count: how many feeds can be handled simultaneously by this
- *  device
- *
+ * struct dvb_usb_fe_adapter - a DVB adapter on a USB device
+ * @fe: frontend
  * @fe_init:  rerouted frontend-init (wakeup) function.
  * @fe_sleep: rerouted frontend-sleep function.
- *
  * @stream: the usb data stream.
+ * @pid_filtering: is hardware pid_filtering used or not.
+ * @max_feed_count: how many feeds can be handled simultaneously by this
+ *  device
+ * @priv: private pointer
  */
 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 *);
 
@@ -383,6 +385,25 @@
 	void *priv;
 };
 
+/**
+ * struct dvb_usb_adapter - a DVB adapter on a USB device
+ * @dev: DVB USB device pointer
+ * @props: properties
+ * @state: status
+ * @id: index of this adapter (starting with 0).
+ *
+ * @feedcount: number of requested feeds (used for streaming-activation)
+ *
+ * @dvb_adap: device's dvb_adapter.
+ * @dmxdev: device's dmxdev.
+ * @demux: device's software demuxer.
+ * @dvb_net: device's dvb_net interfaces.
+ *
+ * @fe_adap: frontend adapters
+ * @active_fe: active frontend
+ * @num_frontends_initialized: number of initialized frontends
+ * @priv: private pointer
+ */
 struct dvb_usb_adapter {
 	struct dvb_usb_device *dev;
 	struct dvb_usb_adapter_properties props;
@@ -427,8 +448,12 @@
  *
  * @i2c_adap: device's i2c_adapter if it uses I2CoverUSB
  *
+ * @num_adapters_initialized: number of initialized adapters
+ * @adapter: adapters
+ *
  * @rc_dev: rc device for the remote control (rc-core mode)
  * @input_dev: input device for the remote control (legacy mode)
+ * @rc_phys: rc device path
  * @rc_query_work: struct work_struct frequent rc queries
  * @last_event: last triggered event
  * @last_state: last state (no, pressed, repeat)
@@ -487,7 +512,8 @@
 dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16);
 
 /* commonly used remote control parsing */
-extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[5], u32 *, int *);
+int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d, u8 keybuf[5],
+				u32 *event, int *state);
 
 /* commonly used firmware download types and function */
 struct hexline {
diff -uraBN a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig
--- a/drivers/media/usb/dvb-usb/Kconfig	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/usb/dvb-usb/Kconfig	2021-09-12 15:26:30.788037907 +0200
@@ -343,3 +343,12 @@
 	select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Say Y here to support the Technisat USB2 DVB-S/S2 device
+
+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
diff -uraBN a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile
--- a/drivers/media/usb/dvb-usb/Makefile	2021-07-27 10:07:06.000000000 +0200
+++ b/drivers/media/usb/dvb-usb/Makefile	2021-09-12 15:30:48.891379919 +0200
@@ -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	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/media/usb/dvb-usb/tbs5580.c	2021-09-12 15:33:05.525164092 +0200
@@ -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 (client_demod == NULL || client_demod->dev.driver == NULL)
+		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 (client_tuner == NULL || client_tuner->dev.driver == NULL) {
+		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	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/media/usb/dvb-usb/tbs5580.h	2021-09-12 15:33:23.053393545 +0200
@@ -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/media/dvb_frontend.h b/include/media/dvb_frontend.h
--- a/include/media/dvb_frontend.h	2021-07-27 10:07:06.000000000 +0200
+++ b/include/media/dvb_frontend.h	2021-09-12 16:24:38.741844324 +0200
@@ -429,8 +429,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;
@@ -491,6 +493,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__
@@ -622,8 +640,9 @@
 
 	/* Multistream specifics */
 	u32			stream_id;
+	u32			modcode;
 
-	/* Physical Layer Scrambling specifics */
+    /* 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	2021-07-27 10:07:06.000000000 +0200
+++ b/include/uapi/linux/dvb/frontend.h	2021-09-12 16:32:14.341136692 +0200
@@ -313,6 +314,8 @@
 	FEC_3_5,
 	FEC_9_10,
 	FEC_2_5,
+	FEC_1_4,
+	FEC_1_3,  
 };
 
 /**
@@ -350,6 +353,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,
 };
 
 /**
@@ -513,7 +529,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
 
@@ -581,6 +598,9 @@
 	ROLLOFF_20,
 	ROLLOFF_25,
 	ROLLOFF_AUTO,
+	ROLLOFF_15,
+	ROLLOFF_10,
+	ROLLOFF_5,
 };
 
 /**
@@ -645,6 +665,7 @@
 	SYS_DVBT2,
 	SYS_TURBO,
 	SYS_DVBC_ANNEX_C,
+	SYS_DVBC2,
 };
 
 /* backward compatibility definitions for delivery systems */
@@ -734,7 +755,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 +1030,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_ */
