diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 49db718..6ddcfc9 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -248,6 +248,20 @@ config DVB_CX24117
 	help
 	  A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend.
 
+config DVB_DVBSKY_M88DS3103
+	tristate "Montage M88DS3103 based (dvbsky)"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_DVBSKY_M88DC2800
+	tristate "Montage M88DC2800 based (dvbsky)"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  A DVB-C tuner module. Say Y when you want to support this frontend.
+	  	  
 config DVB_SI21XX
 	tristate "Silicon Labs SI21XX based"
 	depends on DVB_CORE && I2C
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 9b21488..edaae0d 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -112,4 +112,5 @@ obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
 obj-$(CONFIG_DVB_RTL2832) += rtl2832.o
 obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
 obj-$(CONFIG_DVB_AF9033) += af9033.o
-
+obj-$(CONFIG_DVB_DVBSKY_M88DS3103) += dvbsky_m88ds3103.o
+obj-$(CONFIG_DVB_DVBSKY_M88DC2800) += dvbsky_m88dc2800.o
diff --git a/drivers/media/dvb-frontends/dvbsky_m88dc2800.c b/drivers/media/dvb-frontends/dvbsky_m88dc2800.c
new file mode 100644
index 0000000..3c933b4
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvbsky_m88dc2800.c
@@ -0,0 +1,2124 @@
+/*
+    M88DC2800/M88TC2800  - DVB-C demodulator and tuner from Montage
+
+    Copyright (C) 2012 Max nibble<nibble.max@gmail.com>
+    Copyright (C) 2011 Montage Technology / www.montage-tech.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include "dvb_frontend.h"
+#include "dvbsky_m88dc2800.h"
+
+struct dvbsky_m88dc2800_state {
+	struct i2c_adapter *i2c;
+	const struct dvbsky_m88dc2800_config *config;
+	struct dvb_frontend frontend;
+	u32 freq;
+	u32 ber;
+	u32 sym;
+	u16 qam;
+	u8 inverted;
+	u32 xtal;
+	/* tuner state */
+	u8 tuner_init_OK;	/* Tuner initialize status */
+	u8 tuner_dev_addr;	/* Tuner device address */
+	u32 tuner_freq;		/* RF frequency to be set, unit: KHz */
+	u16 tuner_qam;		/* Reserved */
+	u16 tuner_mode;
+	u8 tuner_bandwidth;	/* Bandwidth of the channel, unit: MHz, 6/7/8 */
+	u8 tuner_loopthrough;	/* Tuner loop through switch, 0/1 */
+	u32 tuner_crystal;	/* Tuner crystal frequency, unit: KHz */
+	u32 tuner_dac;		/* Tuner DAC frequency, unit: KHz */
+	u16 tuner_mtt;		/* Tuner chip version, D1: 0x0d, E0: 0x0e, E1: 0x8e */
+	u16 tuner_custom_cfg;
+	u32 tuner_version;	/* Tuner driver version number */
+	u32 tuner_time;
+};
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+#define dprintk(args...) \
+	do { \
+		if (debug) \
+			printk(KERN_INFO "m88dc2800: " args); \
+	} while (0)
+
+
+static int dvbsky_m88dc2800_i2c_write(struct dvbsky_m88dc2800_state *state, u8 addr,
+			       u8 * p_data, u8 len)
+{
+	struct i2c_msg msg = { .flags = 0 };
+
+	msg.addr = addr;
+	msg.buf = p_data;
+	msg.len = len;
+
+	return i2c_transfer(state->i2c, &msg, 1);
+}
+
+static int dvbsky_m88dc2800_i2c_read(struct dvbsky_m88dc2800_state *state, u8 addr,
+			      u8 * p_data, u8 len)
+{
+	struct i2c_msg msg = { .flags = I2C_M_RD };
+
+	msg.addr = addr;
+	msg.buf = p_data;
+	msg.len = len;
+
+	return i2c_transfer(state->i2c, &msg, 1);
+}
+
+/*demod register operations.*/
+static int WriteReg(struct dvbsky_m88dc2800_state *state, u8 reg, u8 data)
+{
+	u8 buf[] = { reg, data };
+	u8 addr = state->config->demod_address;
+	int err;
+
+	dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data);
+
+	err = dvbsky_m88dc2800_i2c_write(state, addr, buf, 2);
+
+	if (err != 1) {
+		printk(KERN_ERR
+		       "%s: writereg error(err == %i, reg == 0x%02x,"
+		       " value == 0x%02x)\n", __func__, err, reg, data);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int ReadReg(struct dvbsky_m88dc2800_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	u8 addr = state->config->demod_address;
+
+	ret = dvbsky_m88dc2800_i2c_write(state, addr, b0, 1);
+
+	if (ret != 1) {
+		printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
+		       __func__, reg, ret);
+		return -EIO;
+	}
+
+	ret = dvbsky_m88dc2800_i2c_read(state, addr, b1, 1);
+
+	dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]);
+	return b1[0];
+}
+
+static int _mt_fe_tn_set_reg(struct dvbsky_m88dc2800_state *state, u8 reg,
+			     u8 data)
+{
+	int ret;
+	u8 buf[2];
+	u8 addr = state->tuner_dev_addr;
+
+	buf[1] = ReadReg(state, 0x86);
+	buf[1] |= 0x80;
+	ret = WriteReg(state, 0x86, buf[1]);
+
+	buf[0] = reg;
+	buf[1] = data;
+
+	ret = dvbsky_m88dc2800_i2c_write(state, addr, buf, 2);
+	if (ret != 1)
+		return -EIO;
+	return 0;
+}
+
+static int _mt_fe_tn_get_reg(struct dvbsky_m88dc2800_state *state, u8 reg,
+			     u8 * p_data)
+{
+	int ret;
+	u8 buf[2];
+	u8 addr = state->tuner_dev_addr;
+
+	buf[1] = ReadReg(state, 0x86);
+	buf[1] |= 0x80;
+	ret = WriteReg(state, 0x86, buf[1]);
+
+	buf[0] = reg;
+	ret = dvbsky_m88dc2800_i2c_write(state, addr, buf, 1);
+
+	msleep(1);
+
+	buf[1] = ReadReg(state, 0x86);
+	buf[1] |= 0x80;
+	ret = WriteReg(state, 0x86, buf[1]);
+
+	return dvbsky_m88dc2800_i2c_read(state, addr, p_data, 1);
+}
+
+/* Tuner operation functions.*/
+static int _mt_fe_tn_set_RF_front_tc2800(struct dvbsky_m88dc2800_state *state)
+{
+	u32 freq_KHz = state->tuner_freq;
+	u8 a, b, c;
+	if (state->tuner_mtt == 0xD1) {	/* D1 */
+		if (freq_KHz <= 123000) {
+			if (freq_KHz <= 56000) {
+				a = 0x00; b = 0x00; c = 0x00;
+			} else if (freq_KHz <= 64000) {
+				a = 0x10; b = 0x01; c = 0x08;
+			} else if (freq_KHz <= 72000) {
+				a = 0x20; b = 0x02; c = 0x10;
+			} else if (freq_KHz <= 80000) {
+				a = 0x30; b = 0x03; c = 0x18;
+			} else if (freq_KHz <= 88000) {
+				a = 0x40; b = 0x04; c = 0x20;
+			} else if (freq_KHz <= 96000) {
+				a = 0x50; b = 0x05; c = 0x28;
+			} else if (freq_KHz <= 104000) {
+				a = 0x60; b = 0x06; c = 0x30;
+			} else {
+				a = 0x70; b = 0x07; c = 0x38;
+			}
+			_mt_fe_tn_set_reg(state, 0x58, 0x9b);
+			_mt_fe_tn_set_reg(state, 0x59, a);
+			_mt_fe_tn_set_reg(state, 0x5d, b);
+			_mt_fe_tn_set_reg(state, 0x5e, c);
+			_mt_fe_tn_set_reg(state, 0x5a, 0x75);
+			_mt_fe_tn_set_reg(state, 0x73, 0x0c);
+		} else {	/* if (freq_KHz > 112000) */
+			_mt_fe_tn_set_reg(state, 0x58, 0x7b);
+			if (freq_KHz <= 304000) {
+				if (freq_KHz <= 136000) {
+					_mt_fe_tn_set_reg(state, 0x5e, 0x40);
+				} else if (freq_KHz <= 160000) {
+					_mt_fe_tn_set_reg(state, 0x5e, 0x48);
+				} else if (freq_KHz <= 184000) {
+					_mt_fe_tn_set_reg(state, 0x5e, 0x50);
+				} else if (freq_KHz <= 208000) {
+					_mt_fe_tn_set_reg(state, 0x5e, 0x58);
+				} else if (freq_KHz <= 232000) {
+					_mt_fe_tn_set_reg(state, 0x5e, 0x60);
+				} else if (freq_KHz <= 256000) {
+					_mt_fe_tn_set_reg(state, 0x5e, 0x68);
+				} else if (freq_KHz <= 280000) {
+					_mt_fe_tn_set_reg(state, 0x5e, 0x70);
+				} else {	/* if (freq_KHz <= 304000) */
+					_mt_fe_tn_set_reg(state, 0x5e, 0x78);
+				}
+				if (freq_KHz <= 171000) {
+					_mt_fe_tn_set_reg(state, 0x73, 0x08);
+				} else if (freq_KHz <= 211000) {
+					_mt_fe_tn_set_reg(state, 0x73, 0x0a);
+				} else {
+					_mt_fe_tn_set_reg(state, 0x73, 0x0e);
+				}
+			} else {	/* if (freq_KHz > 304000) */
+				_mt_fe_tn_set_reg(state, 0x5e, 0x88);
+				if (freq_KHz <= 400000) {
+					_mt_fe_tn_set_reg(state, 0x73, 0x0c);
+				} else if (freq_KHz <= 450000) {
+					_mt_fe_tn_set_reg(state, 0x73, 0x09);
+				} else if (freq_KHz <= 550000) {
+					_mt_fe_tn_set_reg(state, 0x73, 0x0e);
+				} else if (freq_KHz <= 650000) {
+					_mt_fe_tn_set_reg(state, 0x73, 0x0d);
+				} else {	/*if (freq_KHz > 650000) */
+					_mt_fe_tn_set_reg(state, 0x73, 0x0e);
+				}
+			}
+		}
+		if (freq_KHz > 800000)
+			_mt_fe_tn_set_reg(state, 0x87, 0x24);
+		else if (freq_KHz > 700000)
+			_mt_fe_tn_set_reg(state, 0x87, 0x34);
+		else if (freq_KHz > 500000)
+			_mt_fe_tn_set_reg(state, 0x87, 0x44);
+		else if (freq_KHz > 300000)
+			_mt_fe_tn_set_reg(state, 0x87, 0x43);
+		else if (freq_KHz > 220000)
+			_mt_fe_tn_set_reg(state, 0x87, 0x54);
+		else if (freq_KHz > 110000)
+			_mt_fe_tn_set_reg(state, 0x87, 0x14);
+		else
+			_mt_fe_tn_set_reg(state, 0x87, 0x54);
+		if (freq_KHz > 600000)
+			_mt_fe_tn_set_reg(state, 0x6a, 0x53);
+		else if (freq_KHz > 500000)
+			_mt_fe_tn_set_reg(state, 0x6a, 0x57);
+		else
+			_mt_fe_tn_set_reg(state, 0x6a, 0x59);
+		if (freq_KHz < 200000) {
+			_mt_fe_tn_set_reg(state, 0x20, 0x5d);
+		} else if (freq_KHz < 500000) {
+			_mt_fe_tn_set_reg(state, 0x20, 0x7d);
+		} else {
+			_mt_fe_tn_set_reg(state, 0x20, 0xfd);
+		}		/* end of 0xD1 */
+	} else if (state->tuner_mtt == 0xE1) {	/* E1 */
+		if (freq_KHz <= 112000) {	/* 123MHz */
+			if (freq_KHz <= 56000) {
+				_mt_fe_tn_set_reg(state, 0x5c, 0x01);
+			} else if (freq_KHz <= 64000) {
+				_mt_fe_tn_set_reg(state, 0x5c, 0x09);
+			} else if (freq_KHz <= 72000) {
+				_mt_fe_tn_set_reg(state, 0x5c, 0x11);
+			} else if (freq_KHz <= 80000) {
+				_mt_fe_tn_set_reg(state, 0x5c, 0x19);
+			} else if (freq_KHz <= 88000) {
+				_mt_fe_tn_set_reg(state, 0x5c, 0x21);
+			} else if (freq_KHz <= 96000) {
+				_mt_fe_tn_set_reg(state, 0x5c, 0x29);
+			} else if (freq_KHz <= 104000) {
+				_mt_fe_tn_set_reg(state, 0x5c, 0x31);
+			} else {	/* if (freq_KHz <= 112000) */
+				_mt_fe_tn_set_reg(state, 0x5c, 0x39);
+			}
+			_mt_fe_tn_set_reg(state, 0x5b, 0x30);
+		} else {	/* if (freq_KHz > 112000) */
+			if (freq_KHz <= 304000) {
+				if (freq_KHz <= 136000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x41);
+				} else if (freq_KHz <= 160000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x49);
+				} else if (freq_KHz <= 184000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x51);
+				} else if (freq_KHz <= 208000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x59);
+				} else if (freq_KHz <= 232000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x61);
+				} else if (freq_KHz <= 256000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x69);
+				} else if (freq_KHz <= 280000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x71);
+				} else {	/* if (freq_KHz <= 304000) */
+					_mt_fe_tn_set_reg(state, 0x5c, 0x79);
+				}
+				if (freq_KHz <= 150000) {
+					_mt_fe_tn_set_reg(state, 0x5b, 0x28);
+				} else if (freq_KHz <= 256000) {
+					_mt_fe_tn_set_reg(state, 0x5b, 0x29);
+				} else {
+					_mt_fe_tn_set_reg(state, 0x5b, 0x2a);
+				}
+			} else {	/* if (freq_KHz > 304000) */
+				if (freq_KHz <= 400000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x89);
+				} else if (freq_KHz <= 450000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x91);
+				} else if (freq_KHz <= 650000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0x98);
+				} else if (freq_KHz <= 850000) {
+					_mt_fe_tn_set_reg(state, 0x5c, 0xa0);
+				} else {
+					_mt_fe_tn_set_reg(state, 0x5c, 0xa8);
+				}
+				_mt_fe_tn_set_reg(state, 0x5b, 0x08);
+			}
+		}
+	}			/* end of 0xE1 */
+	return 0;
+}
+
+static int _mt_fe_tn_cali_PLL_tc2800(struct dvbsky_m88dc2800_state *state,
+				     u32 freq_KHz,
+				     u32 cali_freq_thres_div2,
+				     u32 cali_freq_thres_div3r,
+				     u32 cali_freq_thres_div3)
+{
+	s32 N, F, MUL;
+	u8 buf, tmp, tmp2;
+	s32 M;
+	const s32 crystal_KHz = state->tuner_crystal;
+	 if (state->tuner_mtt == 0xD1) {
+		M = state->tuner_crystal / 4000;
+		if (freq_KHz > cali_freq_thres_div2) {
+			MUL = 4;
+			tmp = 2;
+		} else if (freq_KHz > 300000) {
+			MUL = 8;
+			tmp = 3;
+		} else if (freq_KHz > (cali_freq_thres_div2 / 2)) {
+			MUL = 8;
+			tmp = 4;
+		} else if (freq_KHz > (cali_freq_thres_div2 / 4)) {
+			MUL = 16;
+			tmp = 5;
+		} else if (freq_KHz > (cali_freq_thres_div2 / 8)) {
+			MUL = 32;
+			tmp = 6;
+		} else if (freq_KHz > (cali_freq_thres_div2 / 16)) {
+			MUL = 64;
+			tmp = 7;
+		} else {	/* invalid */
+			MUL = 0;
+			tmp = 0;
+			return 1;
+		}
+	} else if (state->tuner_mtt == 0xE1) {
+		M = state->tuner_crystal / 1000;
+		_mt_fe_tn_set_reg(state, 0x30, 0xff);
+		_mt_fe_tn_set_reg(state, 0x32, 0xe0);
+		_mt_fe_tn_set_reg(state, 0x33, 0x86);
+		_mt_fe_tn_set_reg(state, 0x37, 0x70);
+		_mt_fe_tn_set_reg(state, 0x38, 0x20);
+		_mt_fe_tn_set_reg(state, 0x39, 0x18);
+		_mt_fe_tn_set_reg(state, 0x89, 0x83);
+		if (freq_KHz > cali_freq_thres_div2) {
+			M = M / 4;
+			MUL = 4;
+			tmp = 2;
+			tmp2 = M + 16;	/* 48 */
+		} else if (freq_KHz > cali_freq_thres_div3r) {
+			M = M / 3;
+			MUL = 6;
+			tmp = 2;
+			tmp2 = M + 32;	/* 32 */
+		} else if (freq_KHz > cali_freq_thres_div3) {
+			M = M / 3;
+			MUL = 6;
+			tmp = 2;
+			tmp2 = M;	/* 16 */
+		} else if (freq_KHz > 304000) {
+			M = M / 4;
+			MUL = 8;
+			tmp = 3;
+			tmp2 = M + 16;	/* 48 */
+		} else if (freq_KHz > (cali_freq_thres_div2 / 2)) {
+			M = M / 4;
+			MUL = 8;
+			tmp = 4;
+			tmp2 = M + 16;	/* 48 */
+		} else if (freq_KHz > (cali_freq_thres_div3r / 2)) {
+			M = M / 3;
+			MUL = 12;
+			tmp = 4;
+			tmp2 = M + 32;	/* 32 */
+		} else if (freq_KHz > (cali_freq_thres_div3 / 2)) {
+			M = M / 3;
+			MUL = 12;
+			tmp = 4;
+			tmp2 = M;	/* 16 */
+		} else if (freq_KHz > (cali_freq_thres_div2 / 4)) {
+			M = M / 4;
+			MUL = 16;
+			tmp = 5;
+			tmp2 = M + 16;	/* 48 */
+		} else if (freq_KHz > (cali_freq_thres_div3r / 4)) {
+			M = M / 3;
+			MUL = 24;
+			tmp = 5;
+			tmp2 = M + 32;	/* 32 */
+		} else if (freq_KHz > (cali_freq_thres_div3 / 4)) {
+			M = M / 3;
+			MUL = 24;
+			tmp = 5;
+			tmp2 = M;	/* 16 */
+		} else if (freq_KHz > (cali_freq_thres_div2 / 8)) {
+			M = M / 4;
+			MUL = 32;
+			tmp = 6;
+			tmp2 = M + 16;	/* 48 */
+		} else if (freq_KHz > (cali_freq_thres_div3r / 8)) {
+			M = M / 3;
+			MUL = 48;
+			tmp = 6;
+			tmp2 = M + 32;	/* 32 */
+		} else if (freq_KHz > (cali_freq_thres_div3 / 8)) {
+			M = M / 3;
+			MUL = 48;
+			tmp = 6;
+			tmp2 = M;	/* 16 */
+		} else if (freq_KHz > (cali_freq_thres_div2 / 16)) {
+			M = M / 4;
+			MUL = 64;
+			tmp = 7;
+			tmp2 = M + 16;	/* 48 */
+		} else if (freq_KHz > (cali_freq_thres_div3r / 16)) {
+			M = M / 3;
+			MUL = 96;
+			tmp = 7;
+			tmp2 = M + 32;	/* 32 */
+		} else if (freq_KHz > (cali_freq_thres_div3 / 16)) {
+			M = M / 3;
+			MUL = 96;
+			tmp = 7;
+			tmp2 = M;	/* 16 */
+		} else {	/* invalid */
+			M = M / 4;
+			MUL = 0;
+			tmp = 0;
+			tmp2 = 48;
+			return 1;
+		}
+		if (freq_KHz == 291000) {
+			M = state->tuner_crystal / 1000 / 3;
+			MUL = 12;
+			tmp = 4;
+			tmp2 = M + 32;	/* 32 */
+		}
+		/*
+		   if (freq_KHz == 578000) {
+		   M = state->tuner_crystal / 1000 / 4;
+		   MUL = 4;
+		   tmp = 2;
+		   tmp2 = M + 16;	// 48
+		   }
+		 */
+		if (freq_KHz == 690000) {
+			M = state->tuner_crystal / 1000 / 3;
+			MUL = 4;
+			tmp = 2;
+			tmp2 = M + 16;	/* 48 */
+		}
+		_mt_fe_tn_get_reg(state, 0x33, &buf);
+		buf &= 0xc0;
+		buf += tmp2;
+		_mt_fe_tn_set_reg(state, 0x33, buf);
+	} else {
+		return 1;
+	}
+	_mt_fe_tn_get_reg(state, 0x39, &buf);
+	buf &= 0xf8;
+	buf += tmp;
+	_mt_fe_tn_set_reg(state, 0x39, buf);
+	N = (freq_KHz * MUL * M / crystal_KHz) / 2 * 2 - 256;
+	buf = (N >> 8) & 0xcf;
+	if (state->tuner_mtt == 0xE1) {
+		buf |= 0x30;
+	}
+	_mt_fe_tn_set_reg(state, 0x34, buf);
+	buf = N & 0xff;
+	_mt_fe_tn_set_reg(state, 0x35, buf);
+	F = ((freq_KHz * MUL * M / (crystal_KHz / 1000) / 2) -
+	     (freq_KHz * MUL * M / crystal_KHz / 2 * 1000)) * 64 / 1000;
+	buf = F & 0xff;
+	_mt_fe_tn_set_reg(state, 0x36, buf);
+	if (F == 0) {
+		if (state->tuner_mtt == 0xD1) {
+			_mt_fe_tn_set_reg(state, 0x3d, 0xca);
+		} else if (state->tuner_mtt == 0xE1) {
+			_mt_fe_tn_set_reg(state, 0x3d, 0xfe);
+		} else {
+			return 1;
+		}
+		_mt_fe_tn_set_reg(state, 0x3e, 0x9c);
+		_mt_fe_tn_set_reg(state, 0x3f, 0x34);
+	}
+	if (F > 0) {
+		if (state->tuner_mtt == 0xD1) {
+			if ((F == 32) || (F == 16) || (F == 48)) {
+				_mt_fe_tn_set_reg(state, 0x3e, 0xa4);
+				_mt_fe_tn_set_reg(state, 0x3d, 0x4a);
+				_mt_fe_tn_set_reg(state, 0x3f, 0x36);
+			} else {
+				_mt_fe_tn_set_reg(state, 0x3e, 0xa4);
+				_mt_fe_tn_set_reg(state, 0x3d, 0x4a);
+				_mt_fe_tn_set_reg(state, 0x3f, 0x36);
+			}
+		} else if (state->tuner_mtt == 0xE1) {
+			_mt_fe_tn_set_reg(state, 0x3e, 0xa4);
+			_mt_fe_tn_set_reg(state, 0x3d, 0x7e);
+			_mt_fe_tn_set_reg(state, 0x3f, 0x36);
+			_mt_fe_tn_set_reg(state, 0x89, 0x84);
+			_mt_fe_tn_get_reg(state, 0x39, &buf);
+			buf = buf & 0x1f;
+			_mt_fe_tn_set_reg(state, 0x39, buf);
+			_mt_fe_tn_get_reg(state, 0x32, &buf);
+			buf = buf | 0x02;
+			_mt_fe_tn_set_reg(state, 0x32, buf);
+		} else {
+			return 1;
+		}
+	}
+	_mt_fe_tn_set_reg(state, 0x41, 0x00);
+	if (state->tuner_mtt == 0xD1) {
+		msleep(5);
+	} else if (state->tuner_mtt == 0xE1) {
+		msleep(2);
+	} else {
+		return 1;
+	}
+	_mt_fe_tn_set_reg(state, 0x41, 0x02);
+	_mt_fe_tn_set_reg(state, 0x30, 0x7f);
+	_mt_fe_tn_set_reg(state, 0x30, 0xff);
+	_mt_fe_tn_set_reg(state, 0x31, 0x80);
+	_mt_fe_tn_set_reg(state, 0x31, 0x00);
+
+	return 0;
+}
+
+static int _mt_fe_tn_set_PLL_freq_tc2800(struct dvbsky_m88dc2800_state *state)
+{
+	u8 buf, buf1;
+	u32 freq_thres_div2_KHz, freq_thres_div3r_KHz,
+	    freq_thres_div3_KHz;
+	const u32 freq_KHz = state->tuner_freq;
+	if (state->tuner_mtt == 0xD1) {
+		_mt_fe_tn_set_reg(state, 0x32, 0xe1);
+		_mt_fe_tn_set_reg(state, 0x33, 0xa6);
+		_mt_fe_tn_set_reg(state, 0x37, 0x7f);
+		_mt_fe_tn_set_reg(state, 0x38, 0x20);
+		_mt_fe_tn_set_reg(state, 0x39, 0x18);
+		_mt_fe_tn_set_reg(state, 0x40, 0x40);
+		 freq_thres_div2_KHz = 520000;
+		_mt_fe_tn_cali_PLL_tc2800(state, freq_KHz,
+					   freq_thres_div2_KHz, 0, 0);
+		 msleep(5);
+		_mt_fe_tn_get_reg(state, 0x3a, &buf);
+		buf1 = buf;
+		buf = buf & 0x03;
+		buf1 = buf1 & 0x01;
+		if ((buf1 == 0) || (buf == 3)) {
+			freq_thres_div2_KHz = 420000;
+			_mt_fe_tn_cali_PLL_tc2800(state, freq_KHz,
+						   freq_thres_div2_KHz, 0,
+						   0);
+			msleep(5);
+			 _mt_fe_tn_get_reg(state, 0x3a, &buf);
+			buf = buf & 0x07;
+			if (buf == 5) {
+				freq_thres_div2_KHz = 520000;
+				_mt_fe_tn_cali_PLL_tc2800(state, freq_KHz,
+							   freq_thres_div2_KHz,
+							   0, 0);
+				msleep(5);
+			}
+		}
+		 _mt_fe_tn_get_reg(state, 0x38, &buf);
+		_mt_fe_tn_set_reg(state, 0x38, buf);
+		 _mt_fe_tn_get_reg(state, 0x32, &buf);
+		buf = buf | 0x10;
+		_mt_fe_tn_set_reg(state, 0x32, buf);
+		 _mt_fe_tn_set_reg(state, 0x30, 0x7f);
+		_mt_fe_tn_set_reg(state, 0x30, 0xff);
+		 _mt_fe_tn_get_reg(state, 0x32, &buf);
+		buf = buf & 0xdf;
+		_mt_fe_tn_set_reg(state, 0x32, buf);
+		_mt_fe_tn_set_reg(state, 0x40, 0x0);
+		 _mt_fe_tn_set_reg(state, 0x30, 0x7f);
+		_mt_fe_tn_set_reg(state, 0x30, 0xff);
+		_mt_fe_tn_set_reg(state, 0x31, 0x80);
+		_mt_fe_tn_set_reg(state, 0x31, 0x00);
+		msleep(5);
+		 _mt_fe_tn_get_reg(state, 0x39, &buf);
+		buf = buf >> 5;
+		if (buf < 5) {
+			_mt_fe_tn_get_reg(state, 0x39, &buf);
+			buf = buf | 0xa0;
+			buf = buf & 0xbf;
+			_mt_fe_tn_set_reg(state, 0x39, buf);
+			 _mt_fe_tn_get_reg(state, 0x32, &buf);
+			buf = buf | 0x02;
+			_mt_fe_tn_set_reg(state, 0x32, buf);
+		}
+		 _mt_fe_tn_get_reg(state, 0x37, &buf);
+		if (buf > 0x70) {
+			buf = 0x7f;
+			_mt_fe_tn_set_reg(state, 0x40, 0x40);
+		}
+		_mt_fe_tn_set_reg(state, 0x37, buf);
+		  _mt_fe_tn_get_reg(state, 0x38, &buf);
+		if (buf < 0x0f) {
+			buf = (buf & 0x0f) << 2;
+			buf = buf + 0x0f;
+			_mt_fe_tn_set_reg(state, 0x37, buf);
+		} else if (buf < 0x1f) {
+			buf = buf + 0x0f;
+			_mt_fe_tn_set_reg(state, 0x37, buf);
+		}
+		 _mt_fe_tn_get_reg(state, 0x32, &buf);
+		buf = (buf | 0x20) & 0xef;
+		_mt_fe_tn_set_reg(state, 0x32, buf);
+		 _mt_fe_tn_set_reg(state, 0x41, 0x00);
+		msleep(5);
+		_mt_fe_tn_set_reg(state, 0x41, 0x02);
+	} else if (state->tuner_mtt == 0xE1) {
+		freq_thres_div2_KHz = 580000;
+		freq_thres_div3r_KHz = 500000;
+		freq_thres_div3_KHz = 440000;
+		_mt_fe_tn_cali_PLL_tc2800(state, freq_KHz,
+					   freq_thres_div2_KHz,
+					   freq_thres_div3r_KHz,
+					   freq_thres_div3_KHz);
+		msleep(3);
+		_mt_fe_tn_get_reg(state, 0x38, &buf);
+		_mt_fe_tn_set_reg(state, 0x38, buf);
+		_mt_fe_tn_set_reg(state, 0x30, 0x7f);
+		_mt_fe_tn_set_reg(state, 0x30, 0xff);
+		_mt_fe_tn_set_reg(state, 0x31, 0x80);
+		_mt_fe_tn_set_reg(state, 0x31, 0x00);
+		msleep(3);
+		_mt_fe_tn_get_reg(state, 0x38, &buf);
+		_mt_fe_tn_set_reg(state, 0x38, buf);
+		_mt_fe_tn_get_reg(state, 0x32, &buf);
+		buf = buf | 0x10;
+		_mt_fe_tn_set_reg(state, 0x32, buf);
+		 _mt_fe_tn_set_reg(state, 0x30, 0x7f);
+		_mt_fe_tn_set_reg(state, 0x30, 0xff);
+		_mt_fe_tn_get_reg(state, 0x32, &buf);
+		buf = buf & 0xdf;
+		_mt_fe_tn_set_reg(state, 0x32, buf);
+		_mt_fe_tn_set_reg(state, 0x31, 0x80);
+		_mt_fe_tn_set_reg(state, 0x31, 0x00);
+		msleep(3);
+		_mt_fe_tn_get_reg(state, 0x37, &buf);
+		_mt_fe_tn_set_reg(state, 0x37, buf);
+		/*
+		   if ((freq_KHz == 802000) || (freq_KHz == 826000)) {
+		   _mt_fe_tn_set_reg(state, 0x37, 0x5e);
+		   }
+		 */
+		_mt_fe_tn_get_reg(state, 0x32, &buf);
+		buf = (buf & 0xef) | 0x30;
+		_mt_fe_tn_set_reg(state, 0x32, buf);
+		 _mt_fe_tn_set_reg(state, 0x41, 0x00);
+		msleep(2);
+		_mt_fe_tn_set_reg(state, 0x41, 0x02);
+	} else {
+		return 1;
+	}
+	return 0;
+}
+
+static int _mt_fe_tn_set_BB_tc2800(struct dvbsky_m88dc2800_state *state)
+{
+	return 0;
+}
+
+ static int _mt_fe_tn_set_appendix_tc2800(struct dvbsky_m88dc2800_state *state)
+
+{
+	u8 buf;
+	const u32 freq_KHz = state->tuner_freq;
+	if (state->tuner_mtt == 0xD1) {
+		if ((freq_KHz == 123000) || (freq_KHz == 147000) ||
+		    (freq_KHz == 171000) || (freq_KHz == 195000)) {
+			_mt_fe_tn_set_reg(state, 0x20, 0x1b);
+		}
+		if ((freq_KHz == 371000) || (freq_KHz == 419000) ||
+		    (freq_KHz == 610000) || (freq_KHz == 730000) ||
+		    (freq_KHz == 754000) || (freq_KHz == 826000)) {
+			_mt_fe_tn_get_reg(state, 0x0d, &buf);
+			_mt_fe_tn_set_reg(state, 0x0d, (u8) (buf + 1));
+		}
+		if ((freq_KHz == 522000) || (freq_KHz == 578000) ||
+		    (freq_KHz == 634000) || (freq_KHz == 690000) ||
+		    (freq_KHz == 834000)) {
+			_mt_fe_tn_get_reg(state, 0x0d, &buf);
+			_mt_fe_tn_set_reg(state, 0x0d, (u8) (buf - 1));
+		}
+	} else if (state->tuner_mtt == 0xE1) {
+		_mt_fe_tn_set_reg(state, 0x20, 0xfc);
+		if (freq_KHz == 123000 || freq_KHz == 147000 ||
+		    freq_KHz == 171000 || freq_KHz == 195000 ||
+		    freq_KHz == 219000 || freq_KHz == 267000 ||
+		    freq_KHz == 291000 || freq_KHz == 339000 ||
+		    freq_KHz == 387000 || freq_KHz == 435000 ||
+		    freq_KHz == 482000 || freq_KHz == 530000 ||
+		    freq_KHz == 722000 ||
+		    (state->tuner_custom_cfg == 1 && freq_KHz == 315000)) {
+			_mt_fe_tn_set_reg(state, 0x20, 0x5c);
+		}
+	}
+	return 0;
+}
+
+ static int _mt_fe_tn_set_DAC_tc2800(struct dvbsky_m88dc2800_state *state)
+{
+	u8 buf, tempnumber;
+	s32 N;
+	s32 f1f2number, f1, f2, delta1, Totalnum1;
+	s32 cntT, cntin, NCOI, z0, z1, z2, tmp;
+	u32 fc, fadc, fsd, f2d;
+	u32 FreqTrue108_Hz;
+	s32 M = state->tuner_crystal / 4000;
+	/* const u8 bandwidth = state->tuner_bandwidth; */
+	const u16 DAC_fre = 108;
+	const u32 crystal_KHz = state->tuner_crystal;
+	const u32 DACFreq_KHz = state->tuner_dac;
+	const u32 freq_KHz = state->tuner_freq;
+
+	if (state->tuner_mtt == 0xE1) {
+		_mt_fe_tn_get_reg(state, 0x33, &buf);
+		M = buf & 0x0f;
+		if (M == 0)
+			M = 6;
+	}
+	_mt_fe_tn_get_reg(state, 0x34, &buf);
+	N = buf & 0x07;
+	_mt_fe_tn_get_reg(state, 0x35, &buf);
+	N = (N << 8) + buf;
+	buf = ((N + 256) * crystal_KHz / M / DAC_fre + 500) / 1000;
+	if (state->tuner_mtt == 0xE1) {
+		_mt_fe_tn_set_appendix_tc2800(state);
+		if (freq_KHz == 187000 || freq_KHz == 195000 ||
+		    freq_KHz == 131000 || freq_KHz == 211000 ||
+		    freq_KHz == 219000 || freq_KHz == 227000 ||
+		    freq_KHz == 267000 || freq_KHz == 299000 ||
+		    freq_KHz == 347000 || freq_KHz == 363000 ||
+		    freq_KHz == 395000 || freq_KHz == 403000 ||
+		    freq_KHz == 435000 || freq_KHz == 482000 ||
+		    freq_KHz == 474000 || freq_KHz == 490000 ||
+		    freq_KHz == 610000 || freq_KHz == 642000 ||
+		    freq_KHz == 666000 || freq_KHz == 722000 ||
+		    freq_KHz == 754000 ||
+		    ((freq_KHz == 379000 || freq_KHz == 467000 ||
+		      freq_KHz == 762000) && state->tuner_custom_cfg != 1)) {
+			buf = buf + 1;
+		}
+		if (freq_KHz == 123000 || freq_KHz == 139000 ||
+		    freq_KHz == 147000 || freq_KHz == 171000 ||
+		    freq_KHz == 179000 || freq_KHz == 203000 ||
+		    freq_KHz == 235000 || freq_KHz == 251000 ||
+		    freq_KHz == 259000 || freq_KHz == 283000 ||
+		    freq_KHz == 331000 || freq_KHz == 363000 ||
+		    freq_KHz == 371000 || freq_KHz == 387000 ||
+		    freq_KHz == 411000 || freq_KHz == 427000 ||
+		    freq_KHz == 443000 || freq_KHz == 451000 ||
+		    freq_KHz == 459000 || freq_KHz == 506000 ||
+		    freq_KHz == 514000 || freq_KHz == 538000 ||
+		    freq_KHz == 546000 || freq_KHz == 554000 ||
+		    freq_KHz == 562000 || freq_KHz == 570000 ||
+		    freq_KHz == 578000 || freq_KHz == 602000 ||
+		    freq_KHz == 626000 || freq_KHz == 658000 ||
+		    freq_KHz == 690000 || freq_KHz == 714000 ||
+		    freq_KHz == 746000 || freq_KHz == 522000 ||
+		    freq_KHz == 826000 || freq_KHz == 155000 ||
+		    freq_KHz == 530000 ||
+		    ((freq_KHz == 275000 || freq_KHz == 355000) &&
+		     state->tuner_custom_cfg != 1) ||
+		    ((freq_KHz == 467000 || freq_KHz == 762000 ||
+		      freq_KHz == 778000 || freq_KHz == 818000) &&
+		     state->tuner_custom_cfg == 1)) {
+			buf = buf - 1;
+		}
+	}
+	 _mt_fe_tn_set_reg(state, 0x0e, buf);
+	_mt_fe_tn_set_reg(state, 0x0d, buf);
+	f1f2number =
+	    (((DACFreq_KHz * M * buf) / crystal_KHz) << 16) / (N + 256) +
+	    (((DACFreq_KHz * M * buf) % crystal_KHz) << 16) / ((N + 256) *
+								crystal_KHz);
+	_mt_fe_tn_set_reg(state, 0xf1, (f1f2number & 0xff00) >> 8);
+	_mt_fe_tn_set_reg(state, 0xf2, f1f2number & 0x00ff);
+	 FreqTrue108_Hz =
+	    (N + 256) * crystal_KHz / (M * buf) * 1000 +
+	    (((N + 256) * crystal_KHz) % (M * buf)) * 1000 / (M * buf);
+	f1 = 4096;
+	fc = FreqTrue108_Hz;
+	fadc = fc / 4;
+	fsd = 27000000;
+	f2d = state->tuner_bandwidth * 1000 / 2 - 150;
+	f2 = (fsd / 250) * f2d / ((fc + 500) / 1000);
+	delta1 = ((f1 - f2) << 15) / f2;
+	Totalnum1 = ((f1 - f2) << 15) - delta1 * f2;
+	cntT = f2;
+	cntin = Totalnum1;
+	NCOI = delta1;
+	 z0 = cntin;
+	z1 = cntT;
+	z2 = NCOI;
+	tempnumber = (z0 & 0xff00) >> 8;
+	_mt_fe_tn_set_reg(state, 0xc9, (u8) (tempnumber & 0x0f));
+	tempnumber = (z0 & 0xff);
+	_mt_fe_tn_set_reg(state, 0xca, tempnumber);
+	 tempnumber = (z1 & 0xff00) >> 8;
+	_mt_fe_tn_set_reg(state, 0xcb, tempnumber);
+	tempnumber = (z1 & 0xff);
+	_mt_fe_tn_set_reg(state, 0xcc, tempnumber);
+	 tempnumber = (z2 & 0xff00) >> 8;
+	_mt_fe_tn_set_reg(state, 0xcd, tempnumber);
+	tempnumber = (z2 & 0xff);
+	_mt_fe_tn_set_reg(state, 0xce, tempnumber);
+	 tmp = f1;
+	f1 = f2;
+	f2 = tmp / 2;
+	delta1 = ((f1 - f2) << 15) / f2;
+	Totalnum1 = ((f1 - f2) << 15) - delta1 * f2;
+	NCOI = (f1 << 15) / f2 - (1 << 15);
+	cntT = f2;
+	cntin = Totalnum1;
+	z0 = cntin;
+	z1 = cntT;
+	z2 = NCOI;
+	tempnumber = (z0 & 0xff00) >> 8;
+	_mt_fe_tn_set_reg(state, 0xd9, (u8) (tempnumber & 0x0f));
+	tempnumber = (z0 & 0xff);
+	_mt_fe_tn_set_reg(state, 0xda, tempnumber);
+	 tempnumber = (z1 & 0xff00) >> 8;
+	_mt_fe_tn_set_reg(state, 0xdb, tempnumber);
+	tempnumber = (z1 & 0xff);
+	_mt_fe_tn_set_reg(state, 0xdc, tempnumber);
+	 tempnumber = (z2 & 0xff00) >> 8;
+	_mt_fe_tn_set_reg(state, 0xdd, tempnumber);
+	tempnumber = (z2 & 0xff);
+	_mt_fe_tn_set_reg(state, 0xde, tempnumber);
+
+	return 0;
+}
+
+static int _mt_fe_tn_preset_tc2800(struct dvbsky_m88dc2800_state *state)
+{
+	if (state->tuner_mtt == 0xD1) {
+		_mt_fe_tn_set_reg(state, 0x19, 0x4a);
+		_mt_fe_tn_set_reg(state, 0x1b, 0x4b);
+		 _mt_fe_tn_set_reg(state, 0x04, 0x04);
+		_mt_fe_tn_set_reg(state, 0x17, 0x0d);
+		_mt_fe_tn_set_reg(state, 0x62, 0x6c);
+		_mt_fe_tn_set_reg(state, 0x63, 0xf4);
+		_mt_fe_tn_set_reg(state, 0x1f, 0x0e);
+		_mt_fe_tn_set_reg(state, 0x6b, 0xf4);
+		_mt_fe_tn_set_reg(state, 0x14, 0x01);
+		_mt_fe_tn_set_reg(state, 0x5a, 0x75);
+		_mt_fe_tn_set_reg(state, 0x66, 0x74);
+		_mt_fe_tn_set_reg(state, 0x72, 0xe0);
+		_mt_fe_tn_set_reg(state, 0x70, 0x07);
+		_mt_fe_tn_set_reg(state, 0x15, 0x7b);
+		_mt_fe_tn_set_reg(state, 0x55, 0x71);
+		 _mt_fe_tn_set_reg(state, 0x75, 0x55);
+		_mt_fe_tn_set_reg(state, 0x76, 0xac);
+		_mt_fe_tn_set_reg(state, 0x77, 0x6c);
+		_mt_fe_tn_set_reg(state, 0x78, 0x8b);
+		_mt_fe_tn_set_reg(state, 0x79, 0x42);
+		_mt_fe_tn_set_reg(state, 0x7a, 0xd2);
+		 _mt_fe_tn_set_reg(state, 0x81, 0x01);
+		_mt_fe_tn_set_reg(state, 0x82, 0x00);
+		_mt_fe_tn_set_reg(state, 0x82, 0x02);
+		_mt_fe_tn_set_reg(state, 0x82, 0x04);
+		_mt_fe_tn_set_reg(state, 0x82, 0x06);
+		_mt_fe_tn_set_reg(state, 0x82, 0x08);
+		_mt_fe_tn_set_reg(state, 0x82, 0x09);
+		_mt_fe_tn_set_reg(state, 0x82, 0x29);
+		_mt_fe_tn_set_reg(state, 0x82, 0x49);
+		_mt_fe_tn_set_reg(state, 0x82, 0x58);
+		_mt_fe_tn_set_reg(state, 0x82, 0x59);
+		_mt_fe_tn_set_reg(state, 0x82, 0x98);
+		_mt_fe_tn_set_reg(state, 0x82, 0x99);
+		_mt_fe_tn_set_reg(state, 0x10, 0x05);
+		_mt_fe_tn_set_reg(state, 0x10, 0x0d);
+		_mt_fe_tn_set_reg(state, 0x11, 0x95);
+		_mt_fe_tn_set_reg(state, 0x11, 0x9d);
+		if (state->tuner_loopthrough != 0) {
+			_mt_fe_tn_set_reg(state, 0x67, 0x25);
+		} else {
+			_mt_fe_tn_set_reg(state, 0x67, 0x05);
+		}
+	} else if (state->tuner_mtt == 0xE1) {
+		_mt_fe_tn_set_reg(state, 0x1b, 0x47);
+		if (state->tuner_mode == 0) {	/* DVB-C */
+			_mt_fe_tn_set_reg(state, 0x66, 0x74);
+			_mt_fe_tn_set_reg(state, 0x62, 0x2c);
+			_mt_fe_tn_set_reg(state, 0x63, 0x54);
+			_mt_fe_tn_set_reg(state, 0x68, 0x0b);
+			_mt_fe_tn_set_reg(state, 0x14, 0x00);
+		} else {			/* CTTB */
+			_mt_fe_tn_set_reg(state, 0x66, 0x74);
+			_mt_fe_tn_set_reg(state, 0x62, 0x0c);
+			_mt_fe_tn_set_reg(state, 0x63, 0x54);
+			_mt_fe_tn_set_reg(state, 0x68, 0x0b);
+			_mt_fe_tn_set_reg(state, 0x14, 0x05);
+		}
+		_mt_fe_tn_set_reg(state, 0x6f, 0x00);
+		_mt_fe_tn_set_reg(state, 0x84, 0x04);
+		_mt_fe_tn_set_reg(state, 0x5e, 0xbe);
+		_mt_fe_tn_set_reg(state, 0x87, 0x07);
+		_mt_fe_tn_set_reg(state, 0x8a, 0x1f);
+		_mt_fe_tn_set_reg(state, 0x8b, 0x1f);
+		_mt_fe_tn_set_reg(state, 0x88, 0x30);
+		_mt_fe_tn_set_reg(state, 0x58, 0x34);
+		_mt_fe_tn_set_reg(state, 0x61, 0x8c);
+		_mt_fe_tn_set_reg(state, 0x6a, 0x42);
+	}
+	return 0;
+}
+
+static int mt_fe_tn_wakeup_tc2800(struct dvbsky_m88dc2800_state *state)
+{
+	_mt_fe_tn_set_reg(state, 0x16, 0xb1);
+	_mt_fe_tn_set_reg(state, 0x09, 0x7d);
+	return 0;
+}
+
+  static int mt_fe_tn_sleep_tc2800(struct dvbsky_m88dc2800_state *state)
+{
+	_mt_fe_tn_set_reg(state, 0x16, 0xb0);
+	_mt_fe_tn_set_reg(state, 0x09, 0x6d);
+	return 0;
+}
+
+ static int mt_fe_tn_init_tc2800(struct dvbsky_m88dc2800_state *state)
+{
+	if (state->tuner_init_OK != 1) {
+		state->tuner_dev_addr = 0x61;	/* TUNER_I2C_ADDR_TC2800 */
+		state->tuner_freq = 650000;
+		state->tuner_qam = 0;
+		state->tuner_mode = 0;	// 0: DVB-C, 1: CTTB
+		state->tuner_bandwidth = 8;
+		state->tuner_loopthrough = 0;
+		state->tuner_crystal = 24000;
+		state->tuner_dac = 7200;
+		state->tuner_mtt = 0x00;
+		state->tuner_custom_cfg = 0;
+		state->tuner_version = 30022;	/* Driver version number */
+		state->tuner_time = 12092611;
+		state->tuner_init_OK = 1;
+	}
+	_mt_fe_tn_set_reg(state, 0x2b, 0x46);
+	_mt_fe_tn_set_reg(state, 0x2c, 0x75);
+	if (state->tuner_mtt == 0x00) {
+		u8 tmp = 0;
+		_mt_fe_tn_get_reg(state, 0x01, &tmp);
+		printk(KERN_INFO "m88dc2800: tuner id = 0x%02x ", tmp);
+		switch (tmp) {
+		case 0x0d:
+			state->tuner_mtt = 0xD1;
+			break;
+		case 0x8e:
+		default:
+			state->tuner_mtt = 0xE1;
+			break;
+		}
+	}
+	return 0;
+}
+
+ static int mt_fe_tn_set_freq_tc2800(struct dvbsky_m88dc2800_state *state,
+				       u32 freq_KHz)
+{
+	u8 buf;
+	u8 buf1;
+
+	mt_fe_tn_init_tc2800(state);
+	state->tuner_freq = freq_KHz;
+	_mt_fe_tn_set_reg(state, 0x21, freq_KHz > 500000 ? 0xb9 : 0x99);
+	mt_fe_tn_wakeup_tc2800(state);
+	 _mt_fe_tn_set_reg(state, 0x05, 0x7f);
+	_mt_fe_tn_set_reg(state, 0x06, 0xf8);
+	 _mt_fe_tn_set_RF_front_tc2800(state);
+	_mt_fe_tn_set_PLL_freq_tc2800(state);
+	_mt_fe_tn_set_DAC_tc2800(state);
+	_mt_fe_tn_set_BB_tc2800(state);
+	_mt_fe_tn_preset_tc2800(state);
+	 _mt_fe_tn_set_reg(state, 0x05, 0x00);
+	_mt_fe_tn_set_reg(state, 0x06, 0x00);
+	 if (state->tuner_mtt == 0xD1) {
+		_mt_fe_tn_set_reg(state, 0x00, 0x01);
+		_mt_fe_tn_set_reg(state, 0x00, 0x00);
+		 msleep(5);
+		_mt_fe_tn_set_reg(state, 0x41, 0x00);
+		msleep(5);
+		_mt_fe_tn_set_reg(state, 0x41, 0x02);
+
+		_mt_fe_tn_get_reg(state, 0x69, &buf1);
+		buf1 = buf1 & 0x0f;
+		_mt_fe_tn_get_reg(state, 0x61, &buf);
+		buf = buf & 0x0f;
+		if (buf == 0x0c)
+			_mt_fe_tn_set_reg(state, 0x6a, 0x59);
+		if (buf1 > 0x02) {
+			if (freq_KHz > 600000)
+				_mt_fe_tn_set_reg(state, 0x66, 0x44);
+			else if (freq_KHz > 500000)
+				_mt_fe_tn_set_reg(state, 0x66, 0x64);
+			else
+				_mt_fe_tn_set_reg(state, 0x66, 0x74);
+		}		
+		if (buf1 < 0x03) {
+			if (freq_KHz > 800000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x64);
+			else if (freq_KHz > 600000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x54);
+			else if (freq_KHz > 500000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x54);
+			else if (freq_KHz > 300000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x43);
+			else if (freq_KHz > 220000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x54);
+			else if (freq_KHz > 110000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x14);
+			else
+				_mt_fe_tn_set_reg(state, 0x87, 0x54);
+			msleep(5);
+		} else if (buf < 0x0c) {
+			if (freq_KHz > 800000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x14);
+			else if (freq_KHz > 600000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x14);
+			else if (freq_KHz > 500000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x34);
+			else if (freq_KHz > 300000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x43);
+			else if (freq_KHz > 220000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x54);
+			else if (freq_KHz > 110000)
+				_mt_fe_tn_set_reg(state, 0x87, 0x14);
+			else
+				_mt_fe_tn_set_reg(state, 0x87, 0x54);
+			msleep(5);
+		}
+	} else if ((state->tuner_mtt == 0xE1)) {
+		_mt_fe_tn_set_reg(state, 0x00, 0x01);
+		_mt_fe_tn_set_reg(state, 0x00, 0x00);
+		 msleep(20);
+		 _mt_fe_tn_get_reg(state, 0x32, &buf);
+		buf = (buf & 0xef) | 0x28;
+		_mt_fe_tn_set_reg(state, 0x32, buf);
+		 msleep(50);
+		_mt_fe_tn_get_reg(state, 0x38, &buf);
+		_mt_fe_tn_set_reg(state, 0x38, buf);
+		_mt_fe_tn_get_reg(state, 0x32, &buf);
+		buf = (buf & 0xf7) | 0x10;
+		_mt_fe_tn_set_reg(state, 0x32, buf);
+		 msleep(10);
+		 _mt_fe_tn_get_reg(state, 0x69, &buf);
+		buf = buf & 0x03;
+		_mt_fe_tn_set_reg(state, 0x2a, buf);
+		if (buf > 0) {
+			msleep(20);
+			_mt_fe_tn_get_reg(state, 0x84, &buf);
+			buf = buf & 0x1f;
+			_mt_fe_tn_set_reg(state, 0x68, 0x0a);
+			_mt_fe_tn_get_reg(state, 0x88, &buf1);
+			buf1 = buf1 & 0x1f;
+			if (buf <= buf1)
+				_mt_fe_tn_set_reg(state, 0x66, 0x44);
+			else
+				_mt_fe_tn_set_reg(state, 0x66, 0x74);
+		} else {
+			if (freq_KHz <= 600000)
+				_mt_fe_tn_set_reg(state, 0x68, 0x0c);
+			else
+				_mt_fe_tn_set_reg(state, 0x68, 0x0e);
+			_mt_fe_tn_set_reg(state, 0x30, 0xfb);
+			_mt_fe_tn_set_reg(state, 0x30, 0xff);
+			_mt_fe_tn_set_reg(state, 0x31, 0x04);
+			_mt_fe_tn_set_reg(state, 0x31, 0x00);
+		}
+		if (state->tuner_loopthrough != 0) {
+			_mt_fe_tn_get_reg(state, 0x28, &buf);
+			if (buf == 0) {
+				_mt_fe_tn_set_reg(state, 0x28, 0xff);
+				_mt_fe_tn_get_reg(state, 0x61, &buf);
+				buf = buf & 0x0f;
+				if (buf > 9)
+					_mt_fe_tn_set_reg(state, 0x67, 0x74);
+				else if (buf > 6)
+					_mt_fe_tn_set_reg(state, 0x67, 0x64);
+				else if (buf > 3)
+					_mt_fe_tn_set_reg(state, 0x67, 0x54);
+				else
+					_mt_fe_tn_set_reg(state, 0x67, 0x44);
+			}
+		} else {
+			_mt_fe_tn_set_reg(state, 0x67, 0x34);
+		}
+	} else {
+		return 1;
+	}
+	return 0;
+}
+
+
+/*
+static int mt_fe_tn_set_BB_filter_band_tc2800(struct dvbsky_m88dc2800_state *state,
+					      u8 bandwidth)
+{
+	u8 buf, tmp;
+
+	_mt_fe_tn_get_reg(state, 0x53, &tmp);
+
+	if (bandwidth == 6)
+		buf = 0x01 << 1;
+	else if (bandwidth == 7)
+		buf = 0x02 << 1;
+	else if (bandwidth == 8)
+		buf = 0x04 << 1;
+	else
+		buf = 0x04 << 1;
+
+	tmp &= 0xf1;
+	tmp |= buf;
+	_mt_fe_tn_set_reg(state, 0x53, tmp);
+	state->tuner_bandwidth = bandwidth;
+	return 0;
+}
+*/
+
+static s32 mt_fe_tn_get_signal_strength_tc2800(struct dvbsky_m88dc2800_state
+					       *state)
+{
+	s32 level = -107;
+	s32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
+	s32 val1, val2, val;
+	s32 result2, result3, result4, result5, result6;
+	s32 append;
+	u8 tmp;
+	s32 freq_KHz = (s32) state->tuner_freq;
+	if (state->tuner_mtt == 0xD1) {
+		_mt_fe_tn_get_reg(state, 0x61, &tmp);
+		tmp1 = tmp & 0x0f;
+		 _mt_fe_tn_get_reg(state, 0x69, &tmp);
+		tmp2 = tmp & 0x0f;
+		 _mt_fe_tn_get_reg(state, 0x73, &tmp);
+		tmp3 = tmp & 0x07;
+		 _mt_fe_tn_get_reg(state, 0x7c, &tmp);
+		tmp4 = (tmp >> 4) & 0x0f;
+		 _mt_fe_tn_get_reg(state, 0x7b, &tmp);
+		tmp5 = tmp & 0x0f;
+		 _mt_fe_tn_get_reg(state, 0x7f, &tmp);
+		tmp6 = (tmp >> 5) & 0x01;
+		if (tmp1 > 6) {
+			val1 = 0;
+			if (freq_KHz <= 200000) {
+				val2 = (tmp1 - 6) * 267;
+			} else if (freq_KHz <= 600000) {
+				val2 = (tmp1 - 6) * 280;
+			} else {
+				val2 = (tmp1 - 6) * 290;
+			}
+			val = val1 + val2;
+		} else {
+			if (tmp1 == 0) {
+				val1 = -550;
+			} else {
+				val1 = 0;
+			}
+			if ((tmp1 < 4) && (freq_KHz >= 506000)) {
+				val1 = -850;
+			}
+			val2 = 0;
+			val = val1 + val2;
+		}
+		if (freq_KHz <= 95000) {
+			result2 = tmp2 * 289;
+		} else if (freq_KHz <= 155000) {
+			result2 = tmp2 * 278;
+		} else if (freq_KHz <= 245000) {
+			result2 = tmp2 * 267;
+		} else if (freq_KHz <= 305000) {
+			result2 = tmp2 * 256;
+		} else if (freq_KHz <= 335000) {
+			result2 = tmp2 * 244;
+		} else if (freq_KHz <= 425000) {
+			result2 = tmp2 * 233;
+		} else if (freq_KHz <= 575000) {
+			result2 = tmp2 * 222;
+		} else if (freq_KHz <= 665000) {
+			result2 = tmp2 * 211;
+		} else {
+			result2 = tmp2 * 200;
+		}
+		result3 = (6 - tmp3) * 100;
+		result4 = 300 * tmp4;
+		result5 = 50 * tmp5;
+		result6 = 300 * tmp6;
+		if (freq_KHz < 105000) {
+			append = -450;
+		} else if (freq_KHz <= 227000) {
+			append = -4 * (freq_KHz / 1000 - 100) + 150;
+		} else if (freq_KHz <= 305000) {
+			append = -4 * (freq_KHz / 1000 - 100);
+		} else if (freq_KHz <= 419000) {
+			append = 500 - 40 * (freq_KHz / 1000 - 300) / 17 + 130;
+		} else if (freq_KHz <= 640000) {
+			append = 500 - 40 * (freq_KHz / 1000 - 300) / 17;
+		} else {
+			append = -500;
+		}
+		level = append - (val + result2 + result3 + result4 +
+				  result5 + result6);
+		level /= 100;
+	} else if (state->tuner_mtt == 0xE1) {
+		_mt_fe_tn_get_reg(state, 0x61, &tmp);
+		tmp1 = tmp & 0x0f;
+		 _mt_fe_tn_get_reg(state, 0x84, &tmp);
+		tmp2 = tmp & 0x1f;
+		 _mt_fe_tn_get_reg(state, 0x69, &tmp);
+		tmp3 = tmp & 0x03;
+		 _mt_fe_tn_get_reg(state, 0x73, &tmp);
+		tmp4 = tmp & 0x0f;
+		 _mt_fe_tn_get_reg(state, 0x7c, &tmp);
+		tmp5 = (tmp >> 4) & 0x0f;
+		 _mt_fe_tn_get_reg(state, 0x7b, &tmp);
+		tmp6 = tmp & 0x0f;
+		if (freq_KHz < 151000) {
+			result2 = (1150 - freq_KHz / 100) * 163 / 33 + 4230;
+			result3 = (1150 - freq_KHz / 100) * 115 / 33 + 1850;
+			result4 = -3676 * (freq_KHz / 1000) / 100 + 6115;
+		} else if (freq_KHz < 257000) {
+			result2 = (1540 - freq_KHz / 100) * 11 / 4 + 3870;
+			result3 = (1540 - freq_KHz / 100) * 205 / 96 + 2100;
+			result4 = -21 * freq_KHz / 1000 + 5084;
+		} else if (freq_KHz < 305000) {
+			result2 = (2620 - freq_KHz / 100) * 5 / 3 + 2770;
+			result3 = (2620 - freq_KHz / 100) * 10 / 7 + 1700;
+			result4 = 650;
+		} else if (freq_KHz < 449000) {
+			result2 = (307 - freq_KHz / 1000) * 82 / 27 + 11270;
+			result3 = (3100 - freq_KHz / 100) * 5 / 3 + 10000;
+			result4 = 134 * freq_KHz / 10000 + 11875;
+		} else {
+			result2 = (307 - freq_KHz / 1000) * 82 / 27 + 11270;
+			result3 = 8400;
+			result4 = 5300;
+		}
+		if (tmp1 > 6) {
+			val1 = result2;
+			val2 = 2900;
+			val = 500;
+		} else if (tmp1 > 0) {
+			val1 = result3;
+			val2 = 2700;
+			val = 500;
+		} else {
+			val1 = result4;
+			val2 = 2700;
+			val = 400;
+		}
+		level = val1 - (val2 * tmp1 + 500 * tmp2 + 3000 * tmp3 -
+			    500 * tmp4 + 3000 * tmp5 + val * tmp6) - 1000;
+		level /= 1000;
+	}
+	return level;
+}
+
+
+/* m88dc2800 operation functions */
+u8 M88DC2000GetLock(struct dvbsky_m88dc2800_state * state)
+{
+	u8 u8ret = 0;
+	if (ReadReg(state, 0x80) < 0x06) {
+		if ((ReadReg(state, 0xdf) & 0x80) == 0x80
+		     &&(ReadReg(state, 0x91) & 0x23) == 0x03
+		     &&(ReadReg(state, 0x43) & 0x08) == 0x08)
+			u8ret = 1;
+		else
+			u8ret = 0;
+	} else {
+		if ((ReadReg(state, 0x85) & 0x08) == 0x08)
+			u8ret = 1;
+		else
+			u8ret = 0;
+	}
+	dprintk("%s, lock=%d\n", __func__, u8ret);
+	return u8ret;
+}
+
+static int M88DC2000SetTsType(struct dvbsky_m88dc2800_state *state, u8 type)
+{
+	u8 regC2H;
+
+	if (type == 3) {
+		WriteReg(state, 0x84, 0x6A);
+		WriteReg(state, 0xC0, 0x43);
+		WriteReg(state, 0xE2, 0x06);
+		regC2H = ReadReg(state, 0xC2);
+		regC2H &= 0xC0;
+		regC2H |= 0x1B;
+		WriteReg(state, 0xC2, regC2H);
+		WriteReg(state, 0xC1, 0x60);	/* common interface */
+	} else if (type == 1) {
+		WriteReg(state, 0x84, 0x6A);
+		WriteReg(state, 0xC0, 0x47);	/* serial format */
+		WriteReg(state, 0xE2, 0x02);
+		regC2H = ReadReg(state, 0xC2);
+		regC2H &= 0xC7;
+		WriteReg(state, 0xC2, regC2H);
+		WriteReg(state, 0xC1, 0x00);
+	} else {
+		WriteReg(state, 0x84, 0x6C);
+		WriteReg(state, 0xC0, 0x43);	/* parallel format */
+		WriteReg(state, 0xE2, 0x06);
+		regC2H = ReadReg(state, 0xC2);
+		regC2H &= 0xC7;
+		WriteReg(state, 0xC2, regC2H);
+		WriteReg(state, 0xC1, 0x00);
+	}
+	return 0;
+}
+
+static int M88DC2000RegInitial_TC2800(struct dvbsky_m88dc2800_state *state)
+{
+	u8 RegE3H, RegE4H;
+
+	WriteReg(state, 0x00, 0x48);
+	WriteReg(state, 0x01, 0x09);
+	WriteReg(state, 0xFB, 0x0A);
+	WriteReg(state, 0xFC, 0x0B);
+	WriteReg(state, 0x02, 0x0B);
+	WriteReg(state, 0x03, 0x18);
+	WriteReg(state, 0x05, 0x0D);
+	WriteReg(state, 0x36, 0x80);
+	WriteReg(state, 0x43, 0x40);
+	WriteReg(state, 0x55, 0x7A);
+	WriteReg(state, 0x56, 0xD9);
+	WriteReg(state, 0x57, 0xDF);
+	WriteReg(state, 0x58, 0x39);
+	WriteReg(state, 0x5A, 0x00);
+	WriteReg(state, 0x5C, 0x71);
+	WriteReg(state, 0x5D, 0x23);
+	WriteReg(state, 0x86, 0x40);
+	WriteReg(state, 0xF9, 0x08);
+	WriteReg(state, 0x61, 0x40);
+	WriteReg(state, 0x62, 0x0A);
+	WriteReg(state, 0x90, 0x06);
+	WriteReg(state, 0xDE, 0x00);
+	WriteReg(state, 0xA0, 0x03);
+	WriteReg(state, 0xDF, 0x81);
+	WriteReg(state, 0xFA, 0x40);
+	WriteReg(state, 0x37, 0x10);
+	WriteReg(state, 0xF0, 0x40);
+	WriteReg(state, 0xF2, 0x9C);
+	WriteReg(state, 0xF3, 0x40);
+	RegE3H = ReadReg(state, 0xE3);
+	RegE4H = ReadReg(state, 0xE4);
+	if (((RegE3H & 0xC0) == 0x00) && ((RegE4H & 0xC0) == 0x00)) {
+		WriteReg(state, 0x30, 0xFF);
+		WriteReg(state, 0x31, 0x00);
+		WriteReg(state, 0x32, 0x00);
+		WriteReg(state, 0x33, 0x00);
+		WriteReg(state, 0x35, 0x32);
+		WriteReg(state, 0x40, 0x00);
+		WriteReg(state, 0x41, 0x10);
+		WriteReg(state, 0xF1, 0x02);
+		WriteReg(state, 0xF4, 0x04);
+		WriteReg(state, 0xF5, 0x00);
+		WriteReg(state, 0x42, 0x14);
+		WriteReg(state, 0xE1, 0x25);
+	} else if (((RegE3H & 0xC0) == 0x80) && ((RegE4H & 0xC0) == 0x40)) {
+		WriteReg(state, 0x30, 0xFF);
+		WriteReg(state, 0x31, 0x00);
+		WriteReg(state, 0x32, 0x00);
+		WriteReg(state, 0x33, 0x00);
+		WriteReg(state, 0x35, 0x32);
+		WriteReg(state, 0x39, 0x00);
+		WriteReg(state, 0x3A, 0x00);
+		WriteReg(state, 0x40, 0x00);
+		WriteReg(state, 0x41, 0x10);
+		WriteReg(state, 0xF1, 0x00);
+		WriteReg(state, 0xF4, 0x00);
+		WriteReg(state, 0xF5, 0x40);
+		WriteReg(state, 0x42, 0x14);
+		WriteReg(state, 0xE1, 0x25);
+	} else if ((RegE3H == 0x80 || RegE3H == 0x81)
+		    && (RegE4H == 0x80 || RegE4H == 0x81)) {
+		WriteReg(state, 0x30, 0xFF);
+		WriteReg(state, 0x31, 0x00);
+		WriteReg(state, 0x32, 0x00);
+		WriteReg(state, 0x33, 0x00);
+		WriteReg(state, 0x35, 0x32);
+		WriteReg(state, 0x39, 0x00);
+		WriteReg(state, 0x3A, 0x00);
+		WriteReg(state, 0xF1, 0x00);
+		WriteReg(state, 0xF4, 0x00);
+		WriteReg(state, 0xF5, 0x40);
+		WriteReg(state, 0x42, 0x24);
+		WriteReg(state, 0xE1, 0x25);
+		WriteReg(state, 0x92, 0x7F);
+		WriteReg(state, 0x93, 0x91);
+		WriteReg(state, 0x95, 0x00);
+		WriteReg(state, 0x2B, 0x33);
+		WriteReg(state, 0x2A, 0x2A);
+		WriteReg(state, 0x2E, 0x80);
+		WriteReg(state, 0x25, 0x25);
+		WriteReg(state, 0x2D, 0xFF);
+		WriteReg(state, 0x26, 0xFF);
+		WriteReg(state, 0x27, 0x00);
+		WriteReg(state, 0x24, 0x25);
+		WriteReg(state, 0xA4, 0xFF);
+		WriteReg(state, 0xA3, 0x0D);
+	} else {
+		WriteReg(state, 0x30, 0xFF);
+		WriteReg(state, 0x31, 0x00);
+		WriteReg(state, 0x32, 0x00);
+		WriteReg(state, 0x33, 0x00);
+		WriteReg(state, 0x35, 0x32);
+		WriteReg(state, 0x39, 0x00);
+		WriteReg(state, 0x3A, 0x00);
+		WriteReg(state, 0xF1, 0x00);
+		WriteReg(state, 0xF4, 0x00);
+		WriteReg(state, 0xF5, 0x40);
+		WriteReg(state, 0x42, 0x24);
+		WriteReg(state, 0xE1, 0x27);
+		WriteReg(state, 0x92, 0x7F);
+		WriteReg(state, 0x93, 0x91);
+		WriteReg(state, 0x95, 0x00);
+		WriteReg(state, 0x2B, 0x33);
+		WriteReg(state, 0x2A, 0x2A);
+		WriteReg(state, 0x2E, 0x80);
+		WriteReg(state, 0x25, 0x25);
+		WriteReg(state, 0x2D, 0xFF);
+		WriteReg(state, 0x26, 0xFF);
+		WriteReg(state, 0x27, 0x00);
+		WriteReg(state, 0x24, 0x25);
+		WriteReg(state, 0xA4, 0xFF);
+		WriteReg(state, 0xA3, 0x10);
+	}
+	WriteReg(state, 0xF6, 0x4E);
+	WriteReg(state, 0xF7, 0x20);
+	WriteReg(state, 0x89, 0x02);
+	WriteReg(state, 0x14, 0x08);
+	WriteReg(state, 0x6F, 0x0D);
+	WriteReg(state, 0x10, 0xFF);
+	WriteReg(state, 0x11, 0x00);
+	WriteReg(state, 0x12, 0x30);
+	WriteReg(state, 0x13, 0x23);
+	WriteReg(state, 0x60, 0x00);
+	WriteReg(state, 0x69, 0x00);
+	WriteReg(state, 0x6A, 0x03);
+	WriteReg(state, 0xE0, 0x75);
+	WriteReg(state, 0x8D, 0x29);
+	WriteReg(state, 0x4E, 0xD8);
+	WriteReg(state, 0x88, 0x80);
+	WriteReg(state, 0x52, 0x79);
+	WriteReg(state, 0x53, 0x03);
+	WriteReg(state, 0x59, 0x30);
+	WriteReg(state, 0x5E, 0x02);
+	WriteReg(state, 0x5F, 0x0F);
+	WriteReg(state, 0x71, 0x03);
+	WriteReg(state, 0x72, 0x12);
+	WriteReg(state, 0x73, 0x12);
+
+	return 0;
+}
+
+static int M88DC2000AutoTSClock_P(struct dvbsky_m88dc2800_state *state, u32 sym,
+				  u16 qam)
+{
+	u32 dataRate;
+	u8 clk_div, value;
+	printk(KERN_INFO
+	       "m88dc2800: M88DC2000AutoTSClock_P, symrate=%d qam=%d\n",
+	       sym, qam);
+	switch (qam) {
+	case 16:
+		dataRate = 4;
+		break;
+	case 32:
+		dataRate = 5;
+		break;
+	case 128:
+		dataRate = 7;
+		break;
+	case 256:
+		dataRate = 8;
+		break;
+	case 64:
+	default:
+		dataRate = 6;
+		break;
+	}
+	dataRate *= sym * 105;
+	dataRate /= 800;
+	if (dataRate <= 4115)
+		clk_div = 0x05;
+	else if (dataRate <= 4800)
+		clk_div = 0x04;
+	else if (dataRate <= 5760)
+		clk_div = 0x03;
+	else if (dataRate <= 7200)
+		clk_div = 0x02;
+	else if (dataRate <= 9600)
+		clk_div = 0x01;
+	else
+		clk_div = 0x00;
+	value = ReadReg(state, 0xC2);
+	value &= 0xc0;
+	value |= clk_div;
+	WriteReg(state, 0xC2, value);
+	return 0;
+}
+
+static int M88DC2000AutoTSClock_C(struct dvbsky_m88dc2800_state *state, u32 sym,
+				  u16 qam)
+{
+	u32 dataRate;
+	u8 clk_div, value;
+	printk(KERN_INFO
+	       "m88dc2800: M88DC2000AutoTSClock_C, symrate=%d qam=%d\n",
+	       sym, qam);
+	switch (qam) {
+	case 16:
+		dataRate = 4;
+		break;
+	case 32:
+		dataRate = 5;
+		break;
+	case 128:
+		dataRate = 7;
+		break;
+	case 256:
+		dataRate = 8;
+		break;
+	case 64:
+	default:
+		dataRate = 6;
+		break;
+	}
+	dataRate *= sym * 105;
+	dataRate /= 800;
+	if (dataRate <= 4115)
+		clk_div = 0x3F;
+	else if (dataRate <= 4800)
+		clk_div = 0x36;
+	else if (dataRate <= 5760)
+		clk_div = 0x2D;
+	else if (dataRate <= 7200)
+		clk_div = 0x24;
+	else if (dataRate <= 9600)
+		clk_div = 0x1B;
+	else
+		clk_div = 0x12;
+	value = ReadReg(state, 0xC2);
+	value &= 0xc0;
+	value |= clk_div;
+	WriteReg(state, 0xC2, value);
+	return 0;
+}
+
+static int M88DC2000SetTxMode(struct dvbsky_m88dc2800_state *state, u8 inverted,
+			      u8 j83)
+{
+	u8 value = 0;
+	if (inverted)
+		value |= 0x08;	/*	spectrum inverted	*/
+	if (j83)
+		value |= 0x01;	/*	J83C			*/
+	WriteReg(state, 0x83, value);
+	return 0;
+}
+
+static int M88DC2000SoftReset(struct dvbsky_m88dc2800_state *state)
+{
+	WriteReg(state, 0x80, 0x01);
+	WriteReg(state, 0x82, 0x00);
+	msleep(1);
+	WriteReg(state, 0x80, 0x00);
+	return 0;
+}
+
+static int M88DC2000SetSym(struct dvbsky_m88dc2800_state *state, u32 sym, u32 xtal)
+{
+	u8 value;
+	u8 reg6FH, reg12H;
+	u64 fValue;
+	u32 dwValue;
+
+	printk(KERN_INFO "%s, sym=%d, xtal=%d\n", __func__, sym, xtal);
+	fValue = 4294967296 * (sym + 10);
+	do_div(fValue, xtal);
+
+	/* fValue  = 4294967296 * (sym + 10) / xtal; */
+	dwValue = (u32) fValue;
+	printk(KERN_INFO "%s, fvalue1=%x\n", __func__, dwValue);
+	WriteReg(state, 0x58, (u8) ((dwValue >> 24) & 0xff));
+	WriteReg(state, 0x57, (u8) ((dwValue >> 16) & 0xff));
+	WriteReg(state, 0x56, (u8) ((dwValue >> 8) & 0xff));
+	WriteReg(state, 0x55, (u8) ((dwValue >> 0) & 0xff));
+
+	/* fValue = 2048 * xtal / sym; */
+	fValue = 2048 * xtal;
+	do_div(fValue, sym);
+	dwValue = (u32) fValue;
+	printk(KERN_INFO "%s, fvalue2=%x\n", __func__, dwValue);
+	WriteReg(state, 0x5D, (u8) ((dwValue >> 8) & 0xff));
+	WriteReg(state, 0x5C, (u8) ((dwValue >> 0) & 0xff));
+	value = ReadReg(state, 0x5A);
+	if (((dwValue >> 16) & 0x0001) == 0)
+		value &= 0x7F;
+	else
+		value |= 0x80;
+	WriteReg(state, 0x5A, value);
+	value = ReadReg(state, 0x89);
+	if (sym <= 1800)
+		value |= 0x01;
+	else
+		value &= 0xFE;
+	WriteReg(state, 0x89, value);
+	if (sym >= 6700) {
+		reg6FH = 0x0D;
+		reg12H = 0x30;
+	} else if (sym >= 4000) {
+		fValue = 22 * 4096 / sym;
+		reg6FH = (u8) fValue;
+		reg12H = 0x30;
+	} else if (sym >= 2000) {
+		fValue = 14 * 4096 / sym;
+		reg6FH = (u8) fValue;
+		reg12H = 0x20;
+	} else {
+		fValue = 7 * 4096 / sym;
+		reg6FH = (u8) fValue;
+		reg12H = 0x10;
+	}
+	WriteReg(state, 0x6F, reg6FH);
+	WriteReg(state, 0x12, reg12H);
+	if (((ReadReg(state, 0xE3) & 0x80) == 0x80)
+	       && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) {
+		if (sym < 3000) {
+			WriteReg(state, 0x6C, 0x16);
+			WriteReg(state, 0x6D, 0x10);
+			WriteReg(state, 0x6E, 0x18);
+		} else {
+			WriteReg(state, 0x6C, 0x14);
+			WriteReg(state, 0x6D, 0x0E);
+			WriteReg(state, 0x6E, 0x36);
+		}
+	} else {
+		WriteReg(state, 0x6C, 0x16);
+		WriteReg(state, 0x6D, 0x10);
+		WriteReg(state, 0x6E, 0x18);
+	}
+	return 0;
+}
+
+static int M88DC2000SetQAM(struct dvbsky_m88dc2800_state *state, u16 qam)
+{
+	u8 reg00H, reg4AH, regC2H, reg44H, reg4CH, reg4DH, reg74H, value;
+	u8 reg8BH, reg8EH;
+	printk(KERN_INFO "%s, qam=%d\n", __func__, qam);
+	regC2H = ReadReg(state, 0xC2);
+	regC2H &= 0xF8;
+	switch (qam) {
+	case 16:		/* 16 QAM */
+		reg00H = 0x08;
+		reg4AH = 0x0F;
+		regC2H |= 0x02;
+		reg44H = 0xAA;
+		reg4CH = 0x0C;
+		reg4DH = 0xF7;
+		reg74H = 0x0E;
+		if (((ReadReg(state, 0xE3) & 0x80) == 0x80)
+		     && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) {
+			reg8BH = 0x5A;
+			reg8EH = 0xBD;
+		} else {
+			reg8BH = 0x5B;
+			reg8EH = 0x9D;
+		}
+		WriteReg(state, 0x6E, 0x18);
+		break;
+	case 32:		/* 32 QAM */
+		reg00H = 0x18;
+		reg4AH = 0xFB;
+		regC2H |= 0x02;
+		reg44H = 0xAA;
+		reg4CH = 0x0C;
+		reg4DH = 0xF7;
+		reg74H = 0x0E;
+		if (((ReadReg(state, 0xE3) & 0x80) == 0x80)
+		     && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) {
+			reg8BH = 0x5A;
+			reg8EH = 0xBD;
+		} else {
+			reg8BH = 0x5B;
+			reg8EH = 0x9D;
+		}
+		WriteReg(state, 0x6E, 0x18);
+		break;
+	case 64:		/* 64 QAM */
+		reg00H = 0x48;
+		reg4AH = 0xCD;
+		regC2H |= 0x02;
+		reg44H = 0xAA;
+		reg4CH = 0x0C;
+		reg4DH = 0xF7;
+		reg74H = 0x0E;
+		if (((ReadReg(state, 0xE3) & 0x80) == 0x80)
+		     && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) {
+			reg8BH = 0x5A;
+			reg8EH = 0xBD;
+		} else {
+			reg8BH = 0x5B;
+			reg8EH = 0x9D;
+		}
+		break;
+	case 128:		/* 128 QAM */
+		reg00H = 0x28;
+		reg4AH = 0xFF;
+		regC2H |= 0x02;
+		reg44H = 0xA9;
+		reg4CH = 0x08;
+		reg4DH = 0xF5;
+		reg74H = 0x0E;
+		reg8BH = 0x5B;
+		reg8EH = 0x9D;
+		break;
+	case 256:		/* 256 QAM */
+		reg00H = 0x38;
+		reg4AH = 0xCD;
+		if (((ReadReg(state, 0xE3) & 0x80) == 0x80)
+		     && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) {
+			regC2H |= 0x02;
+		} else {
+			regC2H |= 0x01;
+		}
+		reg44H = 0xA9;
+		reg4CH = 0x08;
+		reg4DH = 0xF5;
+		reg74H = 0x0E;
+		reg8BH = 0x5B;
+		reg8EH = 0x9D;
+		break;
+	default:		/* 64 QAM */
+		reg00H = 0x48;
+		reg4AH = 0xCD;
+		regC2H |= 0x02;
+		reg44H = 0xAA;
+		reg4CH = 0x0C;
+		reg4DH = 0xF7;
+		reg74H = 0x0E;
+		if (((ReadReg(state, 0xE3) & 0x80) == 0x80)
+		     && ((ReadReg(state, 0xE4) & 0x80) == 0x80)) {
+			reg8BH = 0x5A;
+			reg8EH = 0xBD;
+		} else {
+			reg8BH = 0x5B;
+			reg8EH = 0x9D;
+		}
+		break;
+	}
+	WriteReg(state, 0x00, reg00H);
+	value = ReadReg(state, 0x88);
+	value |= 0x08;
+	WriteReg(state, 0x88, value);
+	WriteReg(state, 0x4B, 0xFF);
+	WriteReg(state, 0x4A, reg4AH);
+	value &= 0xF7;
+	WriteReg(state, 0x88, value);
+	WriteReg(state, 0xC2, regC2H);
+	WriteReg(state, 0x44, reg44H);
+	WriteReg(state, 0x4C, reg4CH);
+	WriteReg(state, 0x4D, reg4DH);
+	WriteReg(state, 0x74, reg74H);
+	WriteReg(state, 0x8B, reg8BH);
+	WriteReg(state, 0x8E, reg8EH);
+	return 0;
+}
+
+static int M88DC2000WriteTuner_TC2800(struct dvbsky_m88dc2800_state *state,
+				      u32 freq_KHz)
+{
+	printk(KERN_INFO "%s, freq=%d KHz\n", __func__, freq_KHz);
+	return mt_fe_tn_set_freq_tc2800(state, freq_KHz);
+}
+
+static int dvbsky_m88dc2800_init(struct dvb_frontend *fe)
+{
+	dprintk("%s()\n", __func__);
+	return 0;
+}
+
+static int dvbsky_m88dc2800_set_parameters(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u8 is_annex_c, is_update;
+	u16 temp_qam;
+	s32 waiting_time;
+	struct dvbsky_m88dc2800_state *state = fe->demodulator_priv;
+
+	is_annex_c = c->delivery_system == SYS_DVBC_ANNEX_C ? 1 : 0;
+
+	switch (c->modulation) {
+	case QAM_16:
+		temp_qam = 16;
+		break;
+	case QAM_32:
+		temp_qam = 32;
+		break;
+	case QAM_128:
+		temp_qam = 128;
+		break;
+	case QAM_256:
+		temp_qam = 256;
+		break;
+	default:		/* QAM_64 */
+		temp_qam = 64;
+		break;
+	}
+
+	state->inverted = c->inversion == INVERSION_ON ? 1 : 0;
+
+	printk(KERN_INFO
+	     "m88dc2800: state, freq=%d qam=%d sym=%d inverted=%d xtal=%d\n",
+	     state->freq, state->qam, state->sym, state->inverted,
+	     state->xtal);
+	printk(KERN_INFO
+	     "m88dc2800: set frequency to %d qam=%d symrate=%d annex-c=%d\n",
+	     c->frequency, temp_qam, c->symbol_rate, is_annex_c);
+
+	is_update = 0;
+	WriteReg(state, 0x80, 0x01);
+	if (c->frequency != state->freq) {
+		M88DC2000WriteTuner_TC2800(state, c->frequency / 1000);
+		state->freq = c->frequency;
+	}
+	if (c->symbol_rate != state->sym) {
+		M88DC2000SetSym(state, c->symbol_rate / 1000, state->xtal);
+		state->sym = c->symbol_rate;
+		is_update = 1;
+	}
+	if (temp_qam != state->qam) {
+		M88DC2000SetQAM(state, temp_qam);
+		state->qam = temp_qam;
+		is_update = 1;
+	}
+
+	if (is_update != 0) {
+		if (state->config->ts_mode == 3)
+			M88DC2000AutoTSClock_C(state, state->sym / 1000,
+					       temp_qam);
+		else
+			M88DC2000AutoTSClock_P(state, state->sym / 1000,
+					       temp_qam);
+	}
+
+	M88DC2000SetTxMode(state, state->inverted, is_annex_c);
+	M88DC2000SoftReset(state);
+	if (((ReadReg(state, 0xE3) & 0x80) == 0x80)
+	    && ((ReadReg(state, 0xE4) & 0x80) == 0x80))
+		waiting_time = 800;
+	else
+		waiting_time = 500;
+	while (waiting_time > 0) {
+		msleep(50);
+		waiting_time -= 50;
+		if (M88DC2000GetLock(state))
+			return 0;
+	}
+
+	state->inverted = (state->inverted != 0) ? 0 : 1;
+	M88DC2000SetTxMode(state, state->inverted, is_annex_c);
+	M88DC2000SoftReset(state);
+	if (((ReadReg(state, 0xE3) & 0x80) == 0x80) &&
+	    ((ReadReg(state, 0xE4) & 0x80) == 0x80))
+		waiting_time = 800;
+	else
+		waiting_time = 500;
+	while (waiting_time > 0) {
+		msleep(50);
+		waiting_time -= 50;
+		if (M88DC2000GetLock(state))
+			return 0;
+	}
+	return 0;
+}
+
+static int dvbsky_m88dc2800_read_status(struct dvb_frontend *fe,
+				 fe_status_t * status)
+{
+	struct dvbsky_m88dc2800_state *state = fe->demodulator_priv;
+	*status = 0;
+
+	if (M88DC2000GetLock(state)) {
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER
+		    |FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_LOCK;
+	}
+	return 0;
+}
+
+static int dvbsky_m88dc2800_read_ber(struct dvb_frontend *fe, u32 * ber)
+{
+	struct dvbsky_m88dc2800_state *state = fe->demodulator_priv;
+	u16 tmp;
+
+	if (M88DC2000GetLock(state) == 0) {
+		state->ber = 0;
+	} else if ((ReadReg(state, 0xA0) & 0x80) != 0x80) {
+		tmp = ReadReg(state, 0xA2) << 8;
+		tmp += ReadReg(state, 0xA1);
+		state->ber = tmp;
+		WriteReg(state, 0xA0, 0x05);
+		WriteReg(state, 0xA0, 0x85);
+	}
+	*ber = state->ber;
+	return 0;
+}
+
+static int dvbsky_m88dc2800_read_signal_strength(struct dvb_frontend *fe,
+					  u16 * strength)
+{
+	struct dvbsky_m88dc2800_state *state = fe->demodulator_priv;
+	s16 tuner_strength;
+
+	tuner_strength = mt_fe_tn_get_signal_strength_tc2800(state);
+	*strength = tuner_strength < -107 ? 0 : tuner_strength + 107;
+
+	return 0;
+}
+
+static int dvbsky_m88dc2800_read_snr(struct dvb_frontend *fe, u16 * snr)
+{
+	static const u32 mes_log[] = {
+		0, 3010, 4771, 6021, 6990, 7781, 8451, 9031, 9542, 10000,
+		10414, 10792, 11139, 11461, 11761, 12041, 12304, 12553, 12788,
+		13010, 13222, 13424, 13617, 13802, 13979, 14150, 14314, 14472,
+		14624, 14771, 14914, 15052, 15185, 15315, 15441, 15563, 15682,
+		15798, 15911, 16021, 16128, 16232, 16335, 16435, 16532, 16628,
+		16721, 16812, 16902, 16990, 17076, 17160, 17243, 17324, 17404,
+		17482, 17559, 17634, 17709, 17782, 17853, 17924, 17993, 18062,
+		18129, 18195, 18261, 18325, 18388, 18451, 18513, 18573, 18633,
+		18692, 18751, 18808, 18865, 18921, 18976, 19031
+	};
+	struct dvbsky_m88dc2800_state *state = fe->demodulator_priv;
+	u8 i;
+	u32 _snr, mse;
+
+	if ((ReadReg(state, 0x91) & 0x23) != 0x03) {
+		*snr = 0;
+		return 0;
+	}
+	mse = 0;
+	for (i = 0; i < 30; i++) {
+		mse += (ReadReg(state, 0x08) << 8) + ReadReg(state, 0x07);
+	}
+	mse /= 30;
+	if (mse > 80)
+		mse = 80;
+	switch (state->qam) {
+	case 16:
+		_snr = 34080;
+		break;		/*      16QAM                           */
+	case 32:
+		_snr = 37600;
+		break;		/*      32QAM                           */
+	case 64:
+		_snr = 40310;
+		break;		/*      64QAM                           */
+	case 128:
+		_snr = 43720;
+		break;		/*      128QAM                          */
+	case 256:
+		_snr = 46390;
+		break;		/*      256QAM                          */
+	default:
+		_snr = 40310;
+		break;
+	}
+	_snr -= mes_log[mse - 1];	/*      C - 10*log10(MSE)       */
+	_snr /= 1000;
+	if (_snr > 0xff)
+		_snr = 0xff;
+	*snr = _snr;
+	return 0;
+}
+
+static int dvbsky_m88dc2800_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
+{
+	struct dvbsky_m88dc2800_state *state = fe->demodulator_priv;
+	u8 u8Value;
+
+	u8Value = ReadReg(state, 0xdf);
+	u8Value |= 0x02;	/* Hold */
+	WriteReg(state, 0xdf, u8Value);
+
+	*ucblocks = ReadReg(state, 0xd5);
+	*ucblocks = (*ucblocks << 8) | ReadReg(state, 0xd4);
+
+	u8Value &= 0xfe;	/* Clear */
+	WriteReg(state, 0xdf, u8Value);
+	u8Value &= 0xfc;	/* Update */
+	u8Value |= 0x01;
+	WriteReg(state, 0xdf, u8Value);
+
+	return 0;
+}
+
+static int dvbsky_m88dc2800_sleep(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88dc2800_state *state = fe->demodulator_priv;
+
+	mt_fe_tn_sleep_tc2800(state);
+	state->freq = 0;
+
+	return 0;
+}
+
+static void dvbsky_m88dc2800_release(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88dc2800_state *state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops dvbsky_m88dc2800_ops;
+
+struct dvb_frontend *dvbsky_m88dc2800_attach(const struct dvbsky_m88dc2800_config
+				      *config, struct i2c_adapter *i2c)
+{
+	struct dvbsky_m88dc2800_state *state = NULL;
+
+	/* allocate memory for the internal state */
+	state = kzalloc(sizeof(struct dvbsky_m88dc2800_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	state->xtal = 28800;
+
+	WriteReg(state, 0x80, 0x01);
+	M88DC2000RegInitial_TC2800(state);
+	M88DC2000SetTsType(state, state->config->ts_mode);
+	mt_fe_tn_init_tc2800(state);
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &dvbsky_m88dc2800_ops,
+	       sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+      error:
+	kfree(state);
+	return NULL;
+}
+
+EXPORT_SYMBOL(dvbsky_m88dc2800_attach);
+
+static struct dvb_frontend_ops dvbsky_m88dc2800_ops = {
+	.delsys = {SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C},
+	.info = {
+		 .name = "Montage M88DC2800 DVB-C",
+		 .frequency_stepsize = 62500,
+		 .frequency_min = 48000000,
+		 .frequency_max = 870000000,
+		 .symbol_rate_min = 870000,
+		 .symbol_rate_max = 9000000,
+		 .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+			 FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO
+	},
+	.release = dvbsky_m88dc2800_release,
+	.init = dvbsky_m88dc2800_init,
+	.sleep = dvbsky_m88dc2800_sleep,
+	.set_frontend = dvbsky_m88dc2800_set_parameters,
+	.read_status = dvbsky_m88dc2800_read_status,
+	.read_ber = dvbsky_m88dc2800_read_ber,
+	.read_signal_strength = dvbsky_m88dc2800_read_signal_strength,
+	.read_snr = dvbsky_m88dc2800_read_snr,
+	.read_ucblocks = dvbsky_m88dc2800_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("Montage DVB-C demodulator driver");
+MODULE_AUTHOR("Max Nibble <nibble.max@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.00");
diff --git a/drivers/media/dvb-frontends/dvbsky_m88dc2800.h b/drivers/media/dvb-frontends/dvbsky_m88dc2800.h
new file mode 100644
index 0000000..0844a66
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvbsky_m88dc2800.h
@@ -0,0 +1,44 @@
+/*
+    M88DC2800/M88TC2800  - DVB-C demodulator and tuner from Montage
+
+    Copyright (C) 2012 Max Nibble <nibble.max@gmail.com>
+    Copyright (C) 2011 Montage Technology - www.montage-tech.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef dvbsky_m88dc2800_H
+#define dvbsky_m88dc2800_H
+
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+
+struct dvbsky_m88dc2800_config {
+	u8 demod_address;
+	u8 ts_mode;
+};
+
+#if IS_ENABLED(CONFIG_DVB_DVBSKY_M88DC2800)
+extern struct dvb_frontend* dvbsky_m88dc2800_attach(const struct dvbsky_m88dc2800_config* config,
+					    struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* dvbsky_m88dc2800_attach(const struct dvbsky_m88dc2800_config* config,
+					    struct i2c_adapter* i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_DVBSKY_M88DC2800 */
+#endif /* dvbsky_m88dc2800_H */
diff --git a/drivers/media/dvb-frontends/dvbsky_m88ds3103.c b/drivers/media/dvb-frontends/dvbsky_m88ds3103.c
new file mode 100644
index 0000000..99985a7
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvbsky_m88ds3103.c
@@ -0,0 +1,1707 @@
+/*
+    Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner driver
+
+    Copyright (C) 2011 Max nibble<nibble.max@gmail.com>
+    Copyright (C) 2010 Montage Technology<www.montage-tech.com>
+    Copyright (C) 2009 Konstantin Dimitrov.
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "dvbsky_m88ds3103.h"
+#include "dvbsky_m88ds3103_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+#define dprintk(args...) \
+	do { \
+		if (debug) \
+			printk(KERN_INFO "m88ds3103: " args); \
+	} while (0)
+
+/*demod register operations.*/
+static int dvbsky_m88ds3103_writereg(struct dvbsky_m88ds3103_state *state, int reg, int data)
+{
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address,
+		.flags = 0, .buf = buf, .len = 2 };
+	int err;
+
+	if (debug > 1)
+		printk("m88ds3103: %s: write reg 0x%02x, value 0x%02x\n",
+			__func__, reg, data);
+
+	err = i2c_transfer(state->i2c, &msg, 1);
+	if (err != 1) {
+		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x,"
+			 " value == 0x%02x)\n", __func__, err, reg, data);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int dvbsky_m88ds3103_readreg(struct dvbsky_m88ds3103_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {
+		{ .addr = state->config->demod_address, .flags = 0,
+			.buf = b0, .len = 1 },
+		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
+			.buf = b1, .len = 1 }
+	};
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
+			__func__, reg, ret);
+		return ret;
+	}
+
+	if (debug > 1)
+		printk(KERN_INFO "m88ds3103: read reg 0x%02x, value 0x%02x\n",
+			reg, b1[0]);
+
+	return b1[0];
+}
+
+/*tuner register operations.*/
+static int dvbsky_m88ds3103_tuner_writereg(struct dvbsky_m88ds3103_state *state, int reg, int data)
+{
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = 0x60,
+		.flags = 0, .buf = buf, .len = 2 };
+	int err;
+
+	dvbsky_m88ds3103_writereg(state, 0x03, 0x11);
+	err = i2c_transfer(state->i2c, &msg, 1);
+	
+	if (err != 1) {
+		printk("%s: writereg error(err == %i, reg == 0x%02x,"
+			 " value == 0x%02x)\n", __func__, err, reg, data);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int dvbsky_m88ds3103_tuner_readreg(struct dvbsky_m88ds3103_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {
+		{ .addr = 0x60, .flags = 0,
+			.buf = b0, .len = 1 },
+		{ .addr = 0x60, .flags = I2C_M_RD,
+			.buf = b1, .len = 1 }
+	};
+
+	dvbsky_m88ds3103_writereg(state, 0x03, 0x11);	
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
+		return ret;
+	}
+
+	return b1[0];
+}
+
+/* Bulk demod I2C write, for firmware download. */
+static int dvbsky_m88ds3103_writeregN(struct dvbsky_m88ds3103_state *state, int reg,
+				const u8 *data, u16 len)
+{
+	int ret = -EREMOTEIO;
+	struct i2c_msg msg;
+	u8 *buf;
+
+	buf = kmalloc(len + 1, GFP_KERNEL);
+	if (buf == NULL) {
+		printk("Unable to kmalloc\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	*(buf) = reg;
+	memcpy(buf + 1, data, len);
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = len + 1;
+
+	if (debug > 1)
+		printk(KERN_INFO "m88ds3103: %s:  write regN 0x%02x, len = %d\n",
+			__func__, reg, len);
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+	if (ret != 1) {
+		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n",
+			 __func__, ret, reg);
+		ret = -EREMOTEIO;
+	}
+	
+error:
+	kfree(buf);
+
+	return ret;
+}
+
+static int dvbsky_m88ds3103_load_firmware(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	const struct firmware *fw;
+	int i, ret = 0;
+
+	dprintk("%s()\n", __func__);
+		
+	if (state->skip_fw_load)
+		return 0;
+	/* Load firmware */
+	/* request the firmware, this will block until someone uploads it */	
+	if(state->demod_id == DS3000_ID){
+		printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
+				DS3000_DEFAULT_FIRMWARE);		
+		ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE,
+					state->i2c->dev.parent);
+	}else if(state->demod_id == DS3103_ID){
+		printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
+				DS3103_DEFAULT_FIRMWARE);
+		ret = request_firmware(&fw, DS3103_DEFAULT_FIRMWARE,
+					state->i2c->dev.parent);
+	}
+	
+	printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__);
+	if (ret) {
+		printk(KERN_ERR "%s: No firmware uploaded (timeout or file not "
+				"found?)\n", __func__);
+		return ret;
+	}
+
+	/* Make sure we don't recurse back through here during loading */
+	state->skip_fw_load = 1;
+
+	dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
+			fw->size,
+			fw->data[0],
+			fw->data[1],
+			fw->data[fw->size - 2],
+			fw->data[fw->size - 1]);
+			
+	/* stop internal mcu. */
+	dvbsky_m88ds3103_writereg(state, 0xb2, 0x01);
+	/* split firmware to download.*/
+	for(i = 0; i < FW_DOWN_LOOP; i++){
+		ret = dvbsky_m88ds3103_writeregN(state, 0xb0, &(fw->data[FW_DOWN_SIZE*i]), FW_DOWN_SIZE);
+		if(ret != 1) break;		
+	}
+	/* start internal mcu. */
+	if(ret == 1)
+		dvbsky_m88ds3103_writereg(state, 0xb2, 0x00);
+		
+	release_firmware(fw);
+
+	dprintk("%s: Firmware upload %s\n", __func__,
+			ret == 1 ? "complete" : "failed");
+
+	if(ret == 1) ret = 0;
+	
+	/* Ensure firmware is always loaded if required */
+	state->skip_fw_load = 0;
+
+	return ret;
+}
+
+
+static int dvbsky_m88ds3103_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u8 data;
+
+	dprintk("%s(%d)\n", __func__, voltage);
+
+	dprintk("m88ds3103:pin_ctrl = (%02x)\n", state->config->pin_ctrl);
+	
+	if(state->config->set_voltage)
+		state->config->set_voltage(fe, voltage);
+	
+	data = dvbsky_m88ds3103_readreg(state, 0xa2);
+	
+        if(state->config->pin_ctrl & 0x80){ /*If control pin is assigned.*/
+	        data &= ~0x03; /* bit0 V/H, bit1 off/on */
+	        if(state->config->pin_ctrl & 0x02)
+		     data |= 0x02;
+
+	        switch (voltage) {
+	        case SEC_VOLTAGE_18:
+		     if((state->config->pin_ctrl & 0x01) == 0)
+			  data |= 0x01;
+		     break;
+	        case SEC_VOLTAGE_13:
+		     if(state->config->pin_ctrl & 0x01)
+			  data |= 0x01;
+		     break;
+	        case SEC_VOLTAGE_OFF:
+		     if(state->config->pin_ctrl & 0x02)
+			   data &= ~0x02;			
+		     else
+			   data |= 0x02;
+		     break;
+	         }
+        }
+
+	dvbsky_m88ds3103_writereg(state, 0xa2, data);
+
+	return 0;
+}
+
+static int dvbsky_m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t* status)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	int lock = 0;
+	
+	*status = 0;
+	
+	switch (state->delivery_system){
+	case SYS_DVBS:
+		lock = dvbsky_m88ds3103_readreg(state, 0xd1);
+		dprintk("%s: SYS_DVBS status=%x.\n", __func__, lock);
+		
+		if ((lock & 0x07) == 0x07){
+			/*if((dvbsky_m88ds3103_readreg(state, 0x0d) & 0x07) == 0x07)*/
+				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER 
+					| FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+			
+		}
+		break;
+	case SYS_DVBS2:
+		lock = dvbsky_m88ds3103_readreg(state, 0x0d);
+		dprintk("%s: SYS_DVBS2 status=%x.\n", __func__, lock);
+
+		if ((lock & 0x8f) == 0x8f)
+			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER 
+				| FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+			
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int dvbsky_m88ds3103_read_ber(struct dvb_frontend *fe, u32* ber)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u8 tmp1, tmp2, tmp3;
+	u32 ldpc_frame_cnt, pre_err_packags, code_rate_fac = 0;
+
+	dprintk("%s()\n", __func__);
+
+	switch (state->delivery_system) {
+	case SYS_DVBS:
+		dvbsky_m88ds3103_writereg(state, 0xf9, 0x04);
+		tmp3 = dvbsky_m88ds3103_readreg(state, 0xf8);
+		if ((tmp3&0x10) == 0){
+			tmp1 = dvbsky_m88ds3103_readreg(state, 0xf7);
+			tmp2 = dvbsky_m88ds3103_readreg(state, 0xf6);
+			tmp3 |= 0x10;
+			dvbsky_m88ds3103_writereg(state, 0xf8, tmp3);
+			state->preBer = (tmp1<<8) | tmp2;
+		}
+		break;
+	case SYS_DVBS2:
+		tmp1 = dvbsky_m88ds3103_readreg(state, 0x7e) & 0x0f;
+		switch(tmp1){
+		case 0:	code_rate_fac = 16008 - 80; break;
+		case 1:	code_rate_fac = 21408 - 80; break;
+		case 2:	code_rate_fac = 25728 - 80; break;
+		case 3:	code_rate_fac = 32208 - 80; break;
+		case 4:	code_rate_fac = 38688 - 80; break;
+		case 5:	code_rate_fac = 43040 - 80; break;
+		case 6:	code_rate_fac = 48408 - 80; break;
+		case 7:	code_rate_fac = 51648 - 80; break;
+		case 8:	code_rate_fac = 53840 - 80; break;
+		case 9:	code_rate_fac = 57472 - 80; break;
+		case 10: code_rate_fac = 58192 - 80; break;
+		}
+		
+		tmp1 = dvbsky_m88ds3103_readreg(state, 0xd7) & 0xff;
+		tmp2 = dvbsky_m88ds3103_readreg(state, 0xd6) & 0xff;
+		tmp3 = dvbsky_m88ds3103_readreg(state, 0xd5) & 0xff;		
+		ldpc_frame_cnt = (tmp1 << 16) | (tmp2 << 8) | tmp3;
+
+		tmp1 = dvbsky_m88ds3103_readreg(state, 0xf8) & 0xff;
+		tmp2 = dvbsky_m88ds3103_readreg(state, 0xf7) & 0xff;
+		pre_err_packags = tmp1<<8 | tmp2;
+		
+		if (ldpc_frame_cnt > 1000){
+			dvbsky_m88ds3103_writereg(state, 0xd1, 0x01);
+			dvbsky_m88ds3103_writereg(state, 0xf9, 0x01);
+			dvbsky_m88ds3103_writereg(state, 0xf9, 0x00);
+			dvbsky_m88ds3103_writereg(state, 0xd1, 0x00);
+			state->preBer = pre_err_packags;
+		} 				
+		break;
+	default:
+		break;
+	}
+	*ber = state->preBer;
+	
+	return 0;
+}
+
+static int dvbsky_m88ds3103_read_signal_strength(struct dvb_frontend *fe,
+						u16 *signal_strength)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u16 gain;
+	u8 gain1, gain2, gain3 = 0;
+
+	dprintk("%s()\n", __func__);
+
+	gain1 = dvbsky_m88ds3103_tuner_readreg(state, 0x3d) & 0x1f;
+	dprintk("%s: gain1 = 0x%02x \n", __func__, gain1);
+	
+	if (gain1 > 15) gain1 = 15;
+	gain2 = dvbsky_m88ds3103_tuner_readreg(state, 0x21) & 0x1f;
+	dprintk("%s: gain2 = 0x%02x \n", __func__, gain2);
+	
+	if(state->tuner_id == TS2022_ID){
+		gain3 = (dvbsky_m88ds3103_tuner_readreg(state, 0x66)>>3) & 0x07;
+		dprintk("%s: gain3 = 0x%02x \n", __func__, gain3);
+		
+		if (gain2 > 16) gain2 = 16;
+		if (gain2 < 2) gain2 = 2;			
+		if (gain3 > 6) gain3 = 6;
+	}else{
+		if (gain2 > 13) gain2 = 13;
+		gain3 = 0;
+	}
+
+	gain = gain1*23 + gain2*35 + gain3*29;
+	*signal_strength = 60000 - gain*55;
+
+	return 0;
+}
+
+
+static int dvbsky_m88ds3103_read_snr(struct dvb_frontend *fe, u16 *p_snr)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u8 val, npow1, npow2, spow1, cnt;
+	u16 tmp, snr;
+	u32 npow, spow, snr_total;	
+	static const u16 mes_log10[] ={
+		0,	3010,	4771,	6021, 	6990,	7781,	8451,	9031,	9542,	10000,
+		10414,	10792,	11139,	11461,	11761,	12041,	12304,	12553,	12788,	13010,
+		13222,	13424,	13617,	13802,	13979,	14150,	14314,	14472,	14624,	14771,
+		14914,	15052,	15185,	15315,	15441,	15563,	15682,	15798,	15911,	16021,
+		16128,	16232,	16335,	16435,	16532,	16628,	16721,	16812,	16902,	16990,
+		17076,	17160,	17243,	17324,	17404,	17482,	17559,	17634,	17709,	17782,
+		17853,	17924,	17993,	18062,	18129,	18195,	18261,	18325,	18388,	18451,
+		18513,	18573,	18633,	18692,	18751,	18808,	18865,	18921,	18976,	19031
+	};
+	static const u16 mes_loge[] ={
+		0,	6931,	10986,	13863, 	16094,	17918,	19459,	20794,	21972,	23026,
+		23979,	24849,	25649,	26391,	27081,	27726,	28332,	28904,	29444,	29957,
+		30445,	30910,	31355,	31781,	32189,	32581,	32958,	33322,	33673,	34012,
+		34340,	34657,
+	};
+
+	dprintk("%s()\n", __func__);
+
+	snr = 0;
+	
+	switch (state->delivery_system){
+	case SYS_DVBS:
+		cnt = 10; snr_total = 0;
+		while(cnt > 0){
+			val = dvbsky_m88ds3103_readreg(state, 0xff);
+			snr_total += val;
+			cnt--;
+		}
+		tmp = (u16)(snr_total/80);
+		if(tmp > 0){
+			if (tmp > 32) tmp = 32;
+			snr = (mes_loge[tmp - 1] * 100) / 45;
+		}else{
+			snr = 0;
+		}
+		break;
+	case SYS_DVBS2:
+		cnt  = 10; npow = 0; spow = 0;
+		while(cnt >0){
+			npow1 = dvbsky_m88ds3103_readreg(state, 0x8c) & 0xff;
+			npow2 = dvbsky_m88ds3103_readreg(state, 0x8d) & 0xff;
+			npow += (((npow1 & 0x3f) + (u16)(npow2 << 6)) >> 2);
+
+			spow1 = dvbsky_m88ds3103_readreg(state, 0x8e) & 0xff;
+			spow += ((spow1 * spow1) >> 1);
+			cnt--;
+		}
+		npow /= 10; spow /= 10;
+		if(spow == 0){
+			snr = 0;
+		}else if(npow == 0){
+			snr = 19;
+		}else{
+			if(spow > npow){
+				tmp = (u16)(spow / npow);
+				if (tmp > 80) tmp = 80;
+				snr = mes_log10[tmp - 1]*3;
+			}else{
+				tmp = (u16)(npow / spow);
+				if (tmp > 80) tmp = 80;
+				snr = -(mes_log10[tmp - 1] / 1000);
+			}
+		}			
+		break;
+	default:
+		break;
+	}
+	*p_snr = snr;
+
+	return 0;
+}
+
+
+static int dvbsky_m88ds3103_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u8 tmp1, tmp2, tmp3, data;
+
+	dprintk("%s()\n", __func__);
+
+	switch (state->delivery_system) {
+	case SYS_DVBS:
+		data = dvbsky_m88ds3103_readreg(state, 0xf8);
+		data |= 0x40;
+		dvbsky_m88ds3103_writereg(state, 0xf8, data);		
+		tmp1 = dvbsky_m88ds3103_readreg(state, 0xf5);
+		tmp2 = dvbsky_m88ds3103_readreg(state, 0xf4);
+		*ucblocks = (tmp1 <<8) | tmp2;		
+		data &= ~0x20;
+		dvbsky_m88ds3103_writereg(state, 0xf8, data);
+		data |= 0x20;
+		dvbsky_m88ds3103_writereg(state, 0xf8, data);
+		data &= ~0x40;
+		dvbsky_m88ds3103_writereg(state, 0xf8, data);
+		break;
+	case SYS_DVBS2:
+		tmp1 = dvbsky_m88ds3103_readreg(state, 0xda);
+		tmp2 = dvbsky_m88ds3103_readreg(state, 0xd9);
+		tmp3 = dvbsky_m88ds3103_readreg(state, 0xd8);
+		*ucblocks = (tmp1 <<16)|(tmp2 <<8)|tmp3;
+		data = dvbsky_m88ds3103_readreg(state, 0xd1);
+		data |= 0x01;
+		dvbsky_m88ds3103_writereg(state, 0xd1, data);
+		data &= ~0x01;
+		dvbsky_m88ds3103_writereg(state, 0xd1, data);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int dvbsky_m88ds3103_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u8 data_a1, data_a2;
+
+	dprintk("%s(%d)\n", __func__, tone);
+	if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+		printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
+		return -EINVAL;
+	}
+
+	data_a1 = dvbsky_m88ds3103_readreg(state, 0xa1);
+	data_a2 = dvbsky_m88ds3103_readreg(state, 0xa2);
+	if(state->demod_id == DS3103_ID)
+		data_a2 &= 0xdf; /* Normal mode */
+	switch (tone) {
+	case SEC_TONE_ON:
+		dprintk("%s: SEC_TONE_ON\n", __func__);
+		data_a1 |= 0x04;
+		data_a1 &= ~0x03;
+		data_a1 &= ~0x40;
+		data_a2 &= ~0xc0;
+		break;
+	case SEC_TONE_OFF:
+		dprintk("%s: SEC_TONE_OFF\n", __func__);
+		data_a2 &= ~0xc0;
+		data_a2 |= 0x80;
+		break;
+	}
+	dvbsky_m88ds3103_writereg(state, 0xa2, data_a2);
+	dvbsky_m88ds3103_writereg(state, 0xa1, data_a1);
+	return 0;
+}
+
+static int dvbsky_m88ds3103_send_diseqc_msg(struct dvb_frontend *fe,
+				struct dvb_diseqc_master_cmd *d)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	int i, ret = 0;
+	u8 tmp, time_out;
+
+	/* Dump DiSEqC message */
+	if (debug) {
+		printk(KERN_INFO "m88ds3103: %s(", __func__);
+		for (i = 0 ; i < d->msg_len ;) {
+			printk(KERN_INFO "0x%02x", d->msg[i]);
+			if (++i < d->msg_len)
+				printk(KERN_INFO ", ");
+		}
+	}
+
+	tmp = dvbsky_m88ds3103_readreg(state, 0xa2);
+	tmp &= ~0xc0;
+	if(state->demod_id == DS3103_ID)
+		tmp &= ~0x20;
+	dvbsky_m88ds3103_writereg(state, 0xa2, tmp);
+	
+	for (i = 0; i < d->msg_len; i ++)
+		dvbsky_m88ds3103_writereg(state, (0xa3+i), d->msg[i]);
+
+	tmp = dvbsky_m88ds3103_readreg(state, 0xa1);	
+	tmp &= ~0x38;
+	tmp &= ~0x40;
+	tmp |= ((d->msg_len-1) << 3) | 0x07;
+	tmp &= ~0x80;
+	dvbsky_m88ds3103_writereg(state, 0xa1, tmp);
+	/*	1.5 * 9 * 8	= 108ms	*/
+	time_out = 150;
+	while (time_out > 0){
+		msleep(10);
+		time_out -= 10;
+		tmp = dvbsky_m88ds3103_readreg(state, 0xa1);		
+		if ((tmp & 0x40) == 0)
+			break;
+	}
+	if (time_out == 0){
+		tmp = dvbsky_m88ds3103_readreg(state, 0xa1);
+		tmp &= ~0x80;
+		tmp |= 0x40;
+		dvbsky_m88ds3103_writereg(state, 0xa1, tmp);
+		ret = 1;
+	}
+	tmp = dvbsky_m88ds3103_readreg(state, 0xa2);
+	tmp &= ~0xc0;
+	tmp |= 0x80;
+	dvbsky_m88ds3103_writereg(state, 0xa2, tmp);	
+	return ret;
+}
+
+
+static int dvbsky_m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
+					fe_sec_mini_cmd_t burst)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u8	val, time_out;
+	
+	dprintk("%s()\n", __func__);
+
+	val = dvbsky_m88ds3103_readreg(state, 0xa2);
+	val &= ~0xc0;
+	if(state->demod_id == DS3103_ID)
+		val &= 0xdf; /* Normal mode */
+	dvbsky_m88ds3103_writereg(state, 0xa2, val);
+	/* DiSEqC burst */
+	if (burst == SEC_MINI_B)
+		dvbsky_m88ds3103_writereg(state, 0xa1, 0x01);
+	else
+		dvbsky_m88ds3103_writereg(state, 0xa1, 0x02);
+
+	msleep(13);
+
+	time_out = 5;
+	do{
+		val = dvbsky_m88ds3103_readreg(state, 0xa1);
+		if ((val & 0x40) == 0)
+			break;
+		msleep(1);
+		time_out --;
+	} while (time_out > 0);
+
+	val = dvbsky_m88ds3103_readreg(state, 0xa2);
+	val &= ~0xc0;
+	val |= 0x80;
+	dvbsky_m88ds3103_writereg(state, 0xa2, val);
+	
+	return 0;
+}
+
+static void dvbsky_m88ds3103_release(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+
+	dprintk("%s\n", __func__);
+	kfree(state);
+}
+
+static int dvbsky_m88ds3103_check_id(struct dvbsky_m88ds3103_state *state)
+{
+	int val_00, val_01;
+	
+	/*check demod id*/
+	val_01 = dvbsky_m88ds3103_readreg(state, 0x01);
+	printk(KERN_INFO "DS3000 chip version: %x attached.\n", val_01);
+			
+	if(val_01 == 0xD0)
+		state->demod_id = DS3103_ID;
+	else if(val_01 == 0xC0)
+		state->demod_id = DS3000_ID;
+	else
+		state->demod_id = UNKNOW_ID;
+		
+	/*check tuner id*/
+	val_00 = dvbsky_m88ds3103_tuner_readreg(state, 0x00);
+	printk(KERN_INFO "TS202x chip version[1]: %x attached.\n", val_00);
+	val_00 &= 0x03;
+	if(val_00 == 0)
+	{
+		dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x01);
+		msleep(3);		
+	}
+	dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x03);
+	msleep(5);
+	
+	val_00 = dvbsky_m88ds3103_tuner_readreg(state, 0x00);
+	printk(KERN_INFO "TS202x chip version[2]: %x attached.\n", val_00);
+	val_00 &= 0xff;
+	if((val_00 == 0x01) || (val_00 == 0x41) || (val_00 == 0x81))
+		state->tuner_id = TS2020_ID;
+	else if(((val_00 & 0xc0)== 0xc0) || (val_00 == 0x83))
+		state->tuner_id = TS2022_ID;
+	else
+		state->tuner_id = UNKNOW_ID;
+			
+	return state->demod_id;	
+}
+
+static struct dvb_frontend_ops dvbsky_m88ds3103_ops;
+static int dvbsky_m88ds3103_initilaze(struct dvb_frontend *fe);
+
+struct dvb_frontend *dvbsky_m88ds3103_attach(const struct dvbsky_m88ds3103_config *config,
+				    struct i2c_adapter *i2c)
+{
+	struct dvbsky_m88ds3103_state *state = NULL;
+
+	dprintk("%s\n", __func__);
+
+	/* allocate memory for the internal state */
+	state = kzalloc(sizeof(struct dvbsky_m88ds3103_state), GFP_KERNEL);
+	if (state == NULL) {
+		printk(KERN_ERR "Unable to kmalloc\n");
+		goto error2;
+	}
+
+	state->config = config;
+	state->i2c = i2c;
+	state->preBer = 0xffff;
+	state->delivery_system = SYS_DVBS; /*Default to DVB-S.*/
+	
+	/* check demod id */
+	if(dvbsky_m88ds3103_check_id(state) == UNKNOW_ID){
+		printk(KERN_ERR "Unable to find Montage chip\n");
+		goto error3;
+	}
+
+	memcpy(&state->frontend.ops, &dvbsky_m88ds3103_ops,
+			sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	
+	dvbsky_m88ds3103_initilaze(&state->frontend);
+	
+	return &state->frontend;
+
+error3:
+	kfree(state);
+error2:
+	return NULL;
+}
+EXPORT_SYMBOL(dvbsky_m88ds3103_attach);
+
+static int dvbsky_m88ds3103_set_carrier_offset(struct dvb_frontend *fe,
+					s32 carrier_offset_khz)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	s32 tmp;
+
+	tmp = carrier_offset_khz;
+	tmp *= 65536;
+	
+	tmp = (2*tmp + MT_FE_MCLK_KHZ) / (2*MT_FE_MCLK_KHZ);
+
+	if (tmp < 0)
+		tmp += 65536;
+
+	dvbsky_m88ds3103_writereg(state, 0x5f, tmp >> 8);
+	dvbsky_m88ds3103_writereg(state, 0x5e, tmp & 0xff);
+
+	return 0;
+}
+
+static int dvbsky_m88ds3103_set_symrate(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u16 value;
+	
+	value = (((c->symbol_rate / 1000) << 15) + (MT_FE_MCLK_KHZ / 4)) / (MT_FE_MCLK_KHZ / 2);
+	dvbsky_m88ds3103_writereg(state, 0x61, value & 0x00ff);
+	dvbsky_m88ds3103_writereg(state, 0x62, (value & 0xff00) >> 8);
+
+	return 0;
+}
+
+static int dvbsky_m88ds3103_set_CCI(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u8 tmp;
+
+	tmp = dvbsky_m88ds3103_readreg(state, 0x56);
+	tmp &= ~0x01;
+	dvbsky_m88ds3103_writereg(state, 0x56, tmp);
+
+	tmp = dvbsky_m88ds3103_readreg(state, 0x76);
+	tmp &= ~0x80;
+	dvbsky_m88ds3103_writereg(state, 0x76, tmp);
+
+	return 0;
+}
+
+static int dvbsky_m88ds3103_init_reg(struct dvbsky_m88ds3103_state *state, const u8 *p_reg_tab, u32 size)
+{
+	u32 i;
+	
+	for(i = 0; i < size; i+=2)
+		dvbsky_m88ds3103_writereg(state, p_reg_tab[i], p_reg_tab[i+1]);
+		
+	return 0;
+}
+
+static int dvbsky_m88ds3103_get_locked_sym_rate(struct dvbsky_m88ds3103_state *state, u32 *sym_rate_KSs)
+{
+	u16	tmp;
+	u32	sym_rate_tmp;
+	u8	val_0x6d, val_0x6e;
+
+	val_0x6d = dvbsky_m88ds3103_readreg(state, 0x6d);
+	val_0x6e = dvbsky_m88ds3103_readreg(state, 0x6e);
+
+	tmp = (u16)((val_0x6e<<8) | val_0x6d);
+
+	sym_rate_tmp = (u32)(tmp * MT_FE_MCLK_KHZ);
+	sym_rate_tmp = (u32)(sym_rate_tmp / (1<<16));
+	*sym_rate_KSs = sym_rate_tmp;
+
+	return 0;
+}
+
+static int dvbsky_m88ds3103_get_channel_info(struct dvbsky_m88ds3103_state *state, u8 *p_mode, u8 *p_coderate)
+{
+	u8	tmp, val_0x7E;
+
+	if(state->delivery_system == SYS_DVBS2){
+		val_0x7E = dvbsky_m88ds3103_readreg(state, 0x7e);
+		tmp = (u8)((val_0x7E&0xC0) >> 6);
+		*p_mode = tmp;
+		tmp = (u8)(val_0x7E & 0x0f);
+		*p_coderate = tmp;
+	} else {
+		*p_mode = 0;
+		tmp = dvbsky_m88ds3103_readreg(state, 0xe6);		
+		tmp = (u8)(tmp >> 5);
+		*p_coderate = tmp;
+	}
+	
+	return 0;
+}
+
+static int dvbsky_m88ds3103_set_clock_ratio(struct dvbsky_m88ds3103_state *state)
+{
+	u8	val, mod_fac, tmp1, tmp2;
+	u32	input_datarate, locked_sym_rate_KSs;
+	u32 MClk_KHz = 96000;
+	u8 mod_mode, code_rate, divid_ratio = 0;
+
+	locked_sym_rate_KSs = 0;
+	dvbsky_m88ds3103_get_locked_sym_rate(state, &locked_sym_rate_KSs);
+	if(locked_sym_rate_KSs == 0)
+		return 0;
+
+	dvbsky_m88ds3103_get_channel_info(state, &mod_mode, &code_rate);
+
+	if (state->delivery_system == SYS_DVBS2)
+	{
+		switch(mod_mode) {
+			case 1: mod_fac = 3; break;
+			case 2:	mod_fac = 4; break;
+			case 3:	mod_fac = 5; break;
+			default: mod_fac = 2; break;
+		}
+
+		switch(code_rate) {
+			case 0: input_datarate = locked_sym_rate_KSs*mod_fac/8/4; break;
+			case 1: input_datarate = locked_sym_rate_KSs*mod_fac/8/3;  	break;
+			case 2: input_datarate = locked_sym_rate_KSs*mod_fac*2/8/5;	break;
+			case 3:	input_datarate = locked_sym_rate_KSs*mod_fac/8/2;	break;
+			case 4:	input_datarate = locked_sym_rate_KSs*mod_fac*3/8/5;	break;
+			case 5:	input_datarate = locked_sym_rate_KSs*mod_fac*2/8/3;	break;
+			case 6:	input_datarate = locked_sym_rate_KSs*mod_fac*3/8/4;	break;
+			case 7:	input_datarate = locked_sym_rate_KSs*mod_fac*4/8/5;	break;
+			case 8:	input_datarate = locked_sym_rate_KSs*mod_fac*5/8/6;	break;
+			case 9:	input_datarate = locked_sym_rate_KSs*mod_fac*8/8/9;	break;
+			case 10: input_datarate = locked_sym_rate_KSs*mod_fac*9/8/10; break;
+			default: input_datarate = locked_sym_rate_KSs*mod_fac*2/8/3; break;
+		}
+
+		if(state->demod_id == DS3000_ID)
+			input_datarate = input_datarate * 115 / 100;
+
+		if(input_datarate < 4800)  {tmp1 = 15;tmp2 = 15;} //4.8MHz         TS clock
+		else if(input_datarate < 4966)  {tmp1 = 14;tmp2 = 15;} //4.966MHz  TS clock
+		else if(input_datarate < 5143)  {tmp1 = 14;tmp2 = 14;} //5.143MHz  TS clock
+		else if(input_datarate < 5333)  {tmp1 = 13;tmp2 = 14;} //5.333MHz  TS clock
+		else if(input_datarate < 5538)  {tmp1 = 13;tmp2 = 13;} //5.538MHz  TS clock
+		else if(input_datarate < 5760)  {tmp1 = 12;tmp2 = 13;} //5.76MHz   TS clock       allan 0809
+		else if(input_datarate < 6000)  {tmp1 = 12;tmp2 = 12;} //6MHz      TS clock
+		else if(input_datarate < 6260)  {tmp1 = 11;tmp2 = 12;} //6.26MHz   TS clock
+		else if(input_datarate < 6545)  {tmp1 = 11;tmp2 = 11;} //6.545MHz  TS clock
+		else if(input_datarate < 6857)  {tmp1 = 10;tmp2 = 11;} //6.857MHz  TS clock
+		else if(input_datarate < 7200)  {tmp1 = 10;tmp2 = 10;} //7.2MHz    TS clock
+		else if(input_datarate < 7578)  {tmp1 = 9;tmp2 = 10;}  //7.578MHz  TS clock
+		else if(input_datarate < 8000)	{tmp1 = 9;tmp2 = 9;}   //8MHz      TS clock
+		else if(input_datarate < 8470)	{tmp1 = 8;tmp2 = 9;}   //8.47MHz   TS clock
+		else if(input_datarate < 9000)	{tmp1 = 8;tmp2 = 8;}   //9MHz      TS clock
+		else if(input_datarate < 9600)	{tmp1 = 7;tmp2 = 8;}   //9.6MHz    TS clock
+		else if(input_datarate < 10285)	{tmp1 = 7;tmp2 = 7;}   //10.285MHz TS clock
+		else if(input_datarate < 12000)	{tmp1 = 6;tmp2 = 6;}   //12MHz     TS clock
+		else if(input_datarate < 14400)	{tmp1 = 5;tmp2 = 5;}   //14.4MHz   TS clock
+		else if(input_datarate < 18000)	{tmp1 = 4;tmp2 = 4;}   //18MHz     TS clock
+		else							{tmp1 = 3;tmp2 = 3;}   //24MHz     TS clock
+
+		if(state->demod_id == DS3000_ID) {
+			val = (u8)((tmp1<<4) + tmp2);
+			dvbsky_m88ds3103_writereg(state, 0xfe, val);
+		} else {
+			tmp1 = dvbsky_m88ds3103_readreg(state, 0x22);
+			tmp2 = dvbsky_m88ds3103_readreg(state, 0x24);
+
+			tmp1 >>= 6;
+			tmp1 &= 0x03;
+			tmp2 >>= 6;
+			tmp2 &= 0x03;
+
+			if((tmp1 == 0x00) && (tmp2 == 0x01))
+				MClk_KHz = 144000;
+			else if((tmp1 == 0x00) && (tmp2 == 0x03))
+				MClk_KHz = 72000;
+			else if((tmp1 == 0x01) && (tmp2 == 0x01))
+				MClk_KHz = 115200;
+			else if((tmp1 == 0x02) && (tmp2 == 0x01))
+				MClk_KHz = 96000;
+			else if((tmp1 == 0x03) && (tmp2 == 0x00))
+				MClk_KHz = 192000;
+			else
+				return 0;
+
+			if(input_datarate < 5200) /*Max. 2011-12-23 11:55*/
+				input_datarate = 5200;
+				
+			if(input_datarate != 0)
+				divid_ratio = (u8)(MClk_KHz / input_datarate);
+			else
+				divid_ratio = 0xFF;
+
+			if(divid_ratio > 128)
+				divid_ratio = 128;
+
+			if(divid_ratio < 2)
+				divid_ratio = 2;
+
+			tmp1 = (u8)(divid_ratio / 2);
+			tmp2 = (u8)(divid_ratio / 2);
+
+			if((divid_ratio % 2) != 0)
+				tmp2 += 1;
+
+			tmp1 -= 1;
+			tmp2 -= 1;
+
+			tmp1 &= 0x3f;
+			tmp2 &= 0x3f;
+
+			val = dvbsky_m88ds3103_readreg(state, 0xfe);
+			val &= 0xF0;
+			val |= (tmp2 >> 2) & 0x0f;
+			dvbsky_m88ds3103_writereg(state, 0xfe, val);
+
+			val = (u8)((tmp2 & 0x03) << 6);	
+			val |= tmp1;
+			dvbsky_m88ds3103_writereg(state, 0xea, val);
+		}
+	} else {
+		mod_fac = 2;
+
+		switch(code_rate) {
+			case 4:	input_datarate = locked_sym_rate_KSs*mod_fac/2/8;	break;
+			case 3:	input_datarate = locked_sym_rate_KSs*mod_fac*2/3/8;	break;
+			case 2:	input_datarate = locked_sym_rate_KSs*mod_fac*3/4/8;	break;
+			case 1:	input_datarate = locked_sym_rate_KSs*mod_fac*5/6/8;	break;
+			case 0:	input_datarate = locked_sym_rate_KSs*mod_fac*7/8/8;	break;
+			default: input_datarate = locked_sym_rate_KSs*mod_fac*3/4/8;	break;
+		}
+
+		if(state->demod_id == DS3000_ID)
+			input_datarate = input_datarate * 115 / 100;
+
+		if(input_datarate < 6857)		{tmp1 = 7;tmp2 = 7;} //6.857MHz     TS clock
+		else if(input_datarate < 7384)	{tmp1 = 6;tmp2 = 7;} //7.384MHz     TS clock
+		else if(input_datarate < 8000)	{tmp1 = 6;tmp2 = 6;} //8MHz     	TS clock
+		else if(input_datarate < 8727)	{tmp1 = 5;tmp2 = 6;} //8.727MHz 	TS clock
+		else if(input_datarate < 9600)	{tmp1 = 5;tmp2 = 5;} //9.6MHz    	TS clock
+		else if(input_datarate < 10666)	{tmp1 = 4;tmp2 = 5;} //10.666MHz 	TS clock
+		else if(input_datarate < 12000)	{tmp1 = 4;tmp2 = 4;} //12MHz 	 	TS clock
+		else if(input_datarate < 13714)	{tmp1 = 3;tmp2 = 4;} //13.714MHz 	TS clock
+		else if(input_datarate < 16000)	{tmp1 = 3;tmp2 = 3;} //16MHz     	TS clock
+		else if(input_datarate < 19200)	{tmp1 = 2;tmp2 = 3;} //19.2MHz   	TS clock
+		else 							{tmp1 = 2;tmp2 = 2;} //24MHz     	TS clock
+
+		if(state->demod_id == DS3000_ID) {
+			val = dvbsky_m88ds3103_readreg(state, 0xfe);
+			val &= 0xc0;
+			val |= ((u8)((tmp1<<3) + tmp2));
+			dvbsky_m88ds3103_writereg(state, 0xfe, val);
+		} else {
+			if(input_datarate < 5200) /*Max. 2011-12-23 11:55*/
+				input_datarate = 5200;
+			
+			if(input_datarate != 0)
+				divid_ratio = (u8)(MClk_KHz / input_datarate);
+			else
+				divid_ratio = 0xFF;
+
+			if(divid_ratio > 128)
+				divid_ratio = 128;
+
+			if(divid_ratio < 2)
+				divid_ratio = 2;
+
+			tmp1 = (u8)(divid_ratio / 2);
+			tmp2 = (u8)(divid_ratio / 2);
+
+			if((divid_ratio % 2) != 0)
+				tmp2 += 1;
+
+			tmp1 -= 1;
+			tmp2 -= 1;
+
+			tmp1 &= 0x3f;
+			tmp2 &= 0x3f;
+
+			val = dvbsky_m88ds3103_readreg(state, 0xfe);
+			val &= 0xF0;
+			val |= (tmp2 >> 2) & 0x0f;
+			dvbsky_m88ds3103_writereg(state, 0xfe, val);
+			
+			val = (u8)((tmp2 & 0x03) << 6);
+			val |= tmp1;
+			dvbsky_m88ds3103_writereg(state, 0xea, val);
+		}
+	}
+	return 0;
+}
+
+static int dvbsky_m88ds3103_demod_connect(struct dvb_frontend *fe, s32 carrier_offset_khz) 
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u16 value;
+	u8 val1,val2,data;
+	
+	dprintk("connect delivery system = %d\n", state->delivery_system);
+	
+	/* ds3000 global reset */
+	dvbsky_m88ds3103_writereg(state, 0x07, 0x80);
+	dvbsky_m88ds3103_writereg(state, 0x07, 0x00);
+	/* ds3000 build-in uC reset */
+	dvbsky_m88ds3103_writereg(state, 0xb2, 0x01);
+	/* ds3000 software reset */
+	dvbsky_m88ds3103_writereg(state, 0x00, 0x01);
+
+	switch (state->delivery_system) {
+	case SYS_DVBS:
+		/* initialise the demod in DVB-S mode */
+		if(state->demod_id == DS3000_ID){
+			dvbsky_m88ds3103_init_reg(state, ds3000_dvbs_init_tab, sizeof(ds3000_dvbs_init_tab));
+			
+			value = dvbsky_m88ds3103_readreg(state, 0xfe);
+			value &= 0xc0;
+			value |= 0x1b;
+			dvbsky_m88ds3103_writereg(state, 0xfe, value);
+			
+			if(state->config->ci_mode)
+				val1 = 0x80;
+			else if(state->config->ts_mode)
+				val1 = 0x60;
+			else
+				val1 = 0x20;
+			dvbsky_m88ds3103_writereg(state, 0xfd, val1);
+			
+		}else if(state->demod_id == DS3103_ID){
+			dvbsky_m88ds3103_init_reg(state, ds3103_dvbs_init_tab, sizeof(ds3103_dvbs_init_tab));
+			
+			/* set ts clock */
+			if(state->config->ci_mode == 2){
+				val1 = 6; val2 = 6;
+			}else if(state->config->ts_mode == 0)	{
+				val1 = 3; val2 = 3;
+			}else{
+				val1 = 0; val2 = 0;
+			}
+			val1 -= 1; val2 -= 1;
+			val1 &= 0x3f; val2 &= 0x3f;
+			data = dvbsky_m88ds3103_readreg(state, 0xfe);
+			data &= 0xf0;
+			data |= (val2 >> 2) & 0x0f;
+			dvbsky_m88ds3103_writereg(state, 0xfe, data);
+			data = (val2 & 0x03) << 6;
+			data |= val1;
+			dvbsky_m88ds3103_writereg(state, 0xea, data);
+			
+			dvbsky_m88ds3103_writereg(state, 0x4d, 0xfd & dvbsky_m88ds3103_readreg(state, 0x4d));
+			dvbsky_m88ds3103_writereg(state, 0x30, 0xef & dvbsky_m88ds3103_readreg(state, 0x30));
+			
+			/* set master clock */
+			val1 = dvbsky_m88ds3103_readreg(state, 0x22);
+			val2 = dvbsky_m88ds3103_readreg(state, 0x24);
+			
+			val1 &= 0x3f;
+			val2 &= 0x3f;
+			val1 |= 0x80;
+			val2 |= 0x40;
+
+			dvbsky_m88ds3103_writereg(state, 0x22, val1);
+			dvbsky_m88ds3103_writereg(state, 0x24, val2);	
+			
+			if(state->config->ci_mode){
+				if(state->config->ci_mode == 2)
+					val1 = 0x43;
+				else
+					val1 = 0x03;
+			}
+			else if(state->config->ts_mode)
+				val1 = 0x06;
+			else
+				val1 = 0x42;
+			dvbsky_m88ds3103_writereg(state, 0xfd, val1);		
+		}
+		break;
+	case SYS_DVBS2:
+		/* initialise the demod in DVB-S2 mode */
+		if(state->demod_id == DS3000_ID){
+			dvbsky_m88ds3103_init_reg(state, ds3000_dvbs2_init_tab, sizeof(ds3000_dvbs2_init_tab));
+		
+			if (c->symbol_rate >= 30000000)
+				dvbsky_m88ds3103_writereg(state, 0xfe, 0x54);
+			else
+				dvbsky_m88ds3103_writereg(state, 0xfe, 0x98);
+								
+		}else if(state->demod_id == DS3103_ID){
+			dvbsky_m88ds3103_init_reg(state, ds3103_dvbs2_init_tab, sizeof(ds3103_dvbs2_init_tab));
+
+			/* set ts clock */
+			if(state->config->ci_mode == 2){
+				val1 = 6; val2 = 6;
+			}else if(state->config->ts_mode == 0){
+				val1 = 5; val2 = 4;
+			}else{
+				val1 = 0; val2 = 0;
+			}
+			val1 -= 1; val2 -= 1;
+			val1 &= 0x3f; val2 &= 0x3f;
+			data = dvbsky_m88ds3103_readreg(state, 0xfe);
+			data &= 0xf0;
+			data |= (val2 >> 2) & 0x0f;
+			dvbsky_m88ds3103_writereg(state, 0xfe, data);
+			data = (val2 & 0x03) << 6;
+			data |= val1;
+			dvbsky_m88ds3103_writereg(state, 0xea, data);
+			
+			dvbsky_m88ds3103_writereg(state, 0x4d, 0xfd & dvbsky_m88ds3103_readreg(state, 0x4d));
+			dvbsky_m88ds3103_writereg(state, 0x30, 0xef & dvbsky_m88ds3103_readreg(state, 0x30));
+			
+			/* set master clock */
+			val1 = dvbsky_m88ds3103_readreg(state, 0x22);
+			val2 = dvbsky_m88ds3103_readreg(state, 0x24);
+			
+			val1 &= 0x3f;
+			val2 &= 0x3f;
+			if((state->config->ci_mode == 2) || (state->config->ts_mode == 1)){
+				val1 |= 0x80;
+				val2 |= 0x40;
+			}else{
+				if (c->symbol_rate >= 28000000){
+					val1 |= 0xc0;
+				}else if (c->symbol_rate >= 18000000){
+					val2 |= 0x40;
+				}else{
+					val1 |= 0x80;
+					val2 |= 0x40;
+				}				
+			}
+			dvbsky_m88ds3103_writereg(state, 0x22, val1);
+			dvbsky_m88ds3103_writereg(state, 0x24, val2);					
+		}
+		
+		if(state->config->ci_mode){
+			if(state->config->ci_mode == 2)
+				val1 = 0x43;
+			else
+				val1 = 0x03;
+		}
+		else if(state->config->ts_mode)
+			val1 = 0x06;
+		else
+			val1 = 0x42;
+		dvbsky_m88ds3103_writereg(state, 0xfd, val1);
+		
+		break;
+	default:
+		return 1;
+	}
+	/* disable 27MHz clock output */
+	dvbsky_m88ds3103_writereg(state, 0x29, 0x80);
+	/* enable ac coupling */
+	dvbsky_m88ds3103_writereg(state, 0x25, 0x8a);
+
+	if ((c->symbol_rate / 1000) <= 3000){
+		dvbsky_m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 32 * 100 / 64 = 400*/
+		dvbsky_m88ds3103_writereg(state, 0xc8, 0x20);
+		dvbsky_m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
+		dvbsky_m88ds3103_writereg(state, 0xc7, 0x00);
+	}else if((c->symbol_rate / 1000) <= 10000){
+		dvbsky_m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 16 * 100 / 64 = 200*/
+		dvbsky_m88ds3103_writereg(state, 0xc8, 0x10);
+		dvbsky_m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
+		dvbsky_m88ds3103_writereg(state, 0xc7, 0x00);
+	}else{
+		dvbsky_m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 6 * 100 / 64 = 75*/
+		dvbsky_m88ds3103_writereg(state, 0xc8, 0x06);
+		dvbsky_m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
+		dvbsky_m88ds3103_writereg(state, 0xc7, 0x00);
+	}
+
+	dvbsky_m88ds3103_set_symrate(fe);
+	
+	dvbsky_m88ds3103_set_CCI(fe);
+
+	dvbsky_m88ds3103_set_carrier_offset(fe, carrier_offset_khz);
+		
+	/* ds3000 out of software reset */
+	dvbsky_m88ds3103_writereg(state, 0x00, 0x00);
+	/* start ds3000 build-in uC */
+	dvbsky_m88ds3103_writereg(state, 0xb2, 0x00);	
+	
+	return 0;
+}
+
+static int dvbsky_m88ds3103_set_frontend(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+	int i;
+	fe_status_t status;
+	u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf, div4, capCode, changePLL;
+	s32 offset_khz, lpf_offset_KHz;
+	u16 value, ndiv, lpf_coeff;
+	u32 f3db, gdiv28, realFreq;
+	u8 RFgain;
+
+	dprintk("%s() ", __func__);
+	dprintk("c frequency = %d\n", c->frequency);
+	dprintk("symbol rate = %d\n", c->symbol_rate);
+	dprintk("delivery system = %d\n", c->delivery_system);
+	
+	state->delivery_system = c->delivery_system;
+	
+	realFreq = c->frequency;
+	lpf_offset_KHz = 0;
+	if(c->symbol_rate < 5000000){
+		lpf_offset_KHz = FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz;
+		realFreq += FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz;
+	}
+	
+	if (state->config->set_ts_params)
+		state->config->set_ts_params(fe, 0);
+
+	div4 = 0;
+	RFgain = 0;
+	if(state->tuner_id == TS2022_ID){
+		dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x0a);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x11, 0x40);
+		if (realFreq < 1103000) {
+			dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x1b);
+			div4 = 1;
+			ndiv = (realFreq * (6 + 8) * 4)/MT_FE_CRYSTAL_KHZ;				
+		}else {
+			ndiv = (realFreq * (6 + 8) * 2)/MT_FE_CRYSTAL_KHZ;
+		}
+		ndiv = ndiv + ndiv%2;
+		if(ndiv < 4095)
+			ndiv = ndiv - 1024;
+		else if (ndiv < 6143)
+			ndiv = ndiv + 1024;
+		else
+			ndiv = ndiv + 3072;	
+		
+		dvbsky_m88ds3103_tuner_writereg(state, 0x01, (ndiv & 0x3f00) >> 8);											
+	}else{
+		dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x00);			
+		if (realFreq < 1146000){
+			dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x11);
+			div4 = 1;
+			ndiv = (realFreq * (6 + 8) * 4) / MT_FE_CRYSTAL_KHZ;
+		}else{
+			dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x01);
+			ndiv = (realFreq * (6 + 8) * 2) / MT_FE_CRYSTAL_KHZ;
+		}
+		ndiv = ndiv + ndiv%2;
+		ndiv = ndiv - 1024;
+		dvbsky_m88ds3103_tuner_writereg(state, 0x01, (ndiv>>8)&0x0f);
+	}
+	/* set pll */
+	dvbsky_m88ds3103_tuner_writereg(state, 0x02, ndiv & 0x00ff);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x03, 0x06);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x0f);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x10);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);	
+
+	if(state->tuner_id == TS2022_ID){
+		if(( realFreq >= 1650000 ) && (realFreq <= 1850000)){
+			msleep(5);
+			value = dvbsky_m88ds3103_tuner_readreg(state, 0x14);
+			value &= 0x7f;
+			if(value < 64){
+				dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x82);
+				dvbsky_m88ds3103_tuner_writereg(state, 0x11, 0x6f);
+
+				dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x0f);
+				dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+				dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x10);
+				dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+			}
+		}
+		msleep(5);
+		value = dvbsky_m88ds3103_tuner_readreg(state, 0x14);
+		value &= 0x1f;
+
+		if(value > 19){
+			value = dvbsky_m88ds3103_tuner_readreg(state, 0x10);
+			value &= 0x1d;
+			dvbsky_m88ds3103_tuner_writereg(state, 0x10, value);
+		}				
+	}else{
+		msleep(5);
+		value = dvbsky_m88ds3103_tuner_readreg(state, 0x66);
+		changePLL = (((value & 0x80) >> 7) != div4);
+
+		if(changePLL){
+			dvbsky_m88ds3103_tuner_writereg(state, 0x10, 0x11);
+			div4 = 1;
+			ndiv = (realFreq * (6 + 8) * 4)/MT_FE_CRYSTAL_KHZ;
+			ndiv = ndiv + ndiv%2;
+			ndiv = ndiv - 1024;
+					
+			dvbsky_m88ds3103_tuner_writereg(state, 0x01, (ndiv>>8) & 0x0f);
+			dvbsky_m88ds3103_tuner_writereg(state, 0x02, ndiv & 0xff);
+			
+			dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x0f);
+			dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+			dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x10);
+			dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+		}		
+	}
+	/*set the RF gain*/
+	if(state->tuner_id == TS2020_ID)
+		dvbsky_m88ds3103_tuner_writereg(state, 0x60, 0x79);
+			
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x17);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x08);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+	msleep(5);
+
+	if(state->tuner_id == TS2020_ID){
+		RFgain = dvbsky_m88ds3103_tuner_readreg(state, 0x3d);
+		RFgain &= 0x0f;
+		if(RFgain < 15){
+			if(RFgain < 4) 
+				RFgain = 0;
+			else
+				RFgain = RFgain -3;
+			value = ((RFgain << 3) | 0x01) & 0x79;
+			dvbsky_m88ds3103_tuner_writereg(state, 0x60, value);
+			dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x17);
+			dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+			dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x08);
+			dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+		}
+	}
+	
+	/* set the LPF */
+	if(state->tuner_id == TS2022_ID){
+		dvbsky_m88ds3103_tuner_writereg(state, 0x25, 0x00);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x27, 0x70);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x41, 0x09);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x08, 0x0b);
+	}
+
+	f3db = ((c->symbol_rate / 1000) *135) / 200 + 2000;
+	f3db += lpf_offset_KHz;
+	if (f3db < 7000)
+		f3db = 7000;
+	if (f3db > 40000)
+		f3db = 40000;
+			
+	gdiv28 = (MT_FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000;
+	dvbsky_m88ds3103_tuner_writereg(state, 0x04, gdiv28 & 0xff);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1b);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x04);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+	msleep(5);
+
+	value = dvbsky_m88ds3103_tuner_readreg(state, 0x26);
+	capCode = value & 0x3f;
+	if(state->tuner_id == TS2022_ID){
+		dvbsky_m88ds3103_tuner_writereg(state, 0x41, 0x0d);
+
+		dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1b);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x04);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+
+		msleep(2);
+
+		value = dvbsky_m88ds3103_tuner_readreg(state, 0x26);
+		value &= 0x3f;
+		value = (capCode + value) / 2;		
+	}
+	else
+		value = capCode;
+		
+	gdiv28 = gdiv28 * 207 / (value * 2 + 151);	
+	mlpf_max = gdiv28 * 135 / 100;
+	mlpf_min = gdiv28 * 78 / 100;
+	if (mlpf_max > 63)
+		mlpf_max = 63;
+
+	if(state->tuner_id == TS2022_ID)
+		lpf_coeff = 3200;
+	else
+		lpf_coeff = 2766;
+		
+	nlpf = (f3db * gdiv28 * 2 / lpf_coeff / (MT_FE_CRYSTAL_KHZ / 1000)  + 1) / 2 ;	
+	if (nlpf > 23) nlpf = 23;
+	if (nlpf < 1) nlpf = 1;
+
+	lpf_mxdiv = (nlpf * (MT_FE_CRYSTAL_KHZ / 1000) * lpf_coeff * 2 / f3db + 1) / 2;
+
+	if (lpf_mxdiv < mlpf_min){
+		nlpf++;
+		lpf_mxdiv = (nlpf * (MT_FE_CRYSTAL_KHZ / 1000) * lpf_coeff * 2  / f3db + 1) / 2;
+	}
+
+	if (lpf_mxdiv > mlpf_max)
+		lpf_mxdiv = mlpf_max;
+
+	dvbsky_m88ds3103_tuner_writereg(state, 0x04, lpf_mxdiv);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x06, nlpf);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1b);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x04);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+	msleep(5);
+	
+	if(state->tuner_id == TS2022_ID){
+		msleep(2);
+		value = dvbsky_m88ds3103_tuner_readreg(state, 0x26);
+		capCode = value & 0x3f;
+
+		dvbsky_m88ds3103_tuner_writereg(state, 0x41, 0x09);
+
+		dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1b);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x04);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+
+		msleep(2);
+		value = dvbsky_m88ds3103_tuner_readreg(state, 0x26);
+		value &= 0x3f;
+		value = (capCode + value) / 2;
+
+		value = value | 0x80;
+		dvbsky_m88ds3103_tuner_writereg(state, 0x25, value);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x27, 0x30);
+
+		dvbsky_m88ds3103_tuner_writereg(state, 0x08, 0x09);		
+	}
+
+	/* Set the BB gain */
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1e);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x01);
+	dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+	if(state->tuner_id == TS2020_ID){
+		if(RFgain == 15){
+			msleep(40);
+			value = dvbsky_m88ds3103_tuner_readreg(state, 0x21);
+			value &= 0x0f;
+			if(value < 3){
+				dvbsky_m88ds3103_tuner_writereg(state, 0x60, 0x61);
+				dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x17);
+				dvbsky_m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+				dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x08);
+				dvbsky_m88ds3103_tuner_writereg(state, 0x50, 0x00);
+			}			
+		}
+	}
+	msleep(60);
+	
+	offset_khz = (ndiv - ndiv % 2 + 1024) * MT_FE_CRYSTAL_KHZ
+		/ (6 + 8) / (div4 + 1) / 2 - realFreq;
+
+	dvbsky_m88ds3103_demod_connect(fe, offset_khz+lpf_offset_KHz);
+
+	for (i = 0; i < 30 ; i++) {
+		dvbsky_m88ds3103_read_status(fe, &status);
+		if (status & FE_HAS_LOCK){
+			break;
+                }
+		msleep(20);
+	}
+	
+	if (status & FE_HAS_LOCK){
+		if(state->config->ci_mode == 2)
+			dvbsky_m88ds3103_set_clock_ratio(state);
+		if(state->config->start_ctrl){
+			if(state->first_lock == 0){
+				state->config->start_ctrl(fe);
+				state->first_lock = 1;	
+			}
+		}		
+	}
+		
+	return 0;
+}
+
+static int dvbsky_m88ds3103_tune(struct dvb_frontend *fe,
+			bool re_tune,
+			unsigned int mode_flags,
+			unsigned int *delay,
+			fe_status_t *status)
+{	
+	*delay = HZ / 5;
+	
+	dprintk("%s() ", __func__);
+	dprintk("re_tune = %d\n", re_tune);
+	
+	if (re_tune) {
+		int ret = dvbsky_m88ds3103_set_frontend(fe);
+		if (ret)
+			return ret;
+	}
+	
+	return dvbsky_m88ds3103_read_status(fe, status);
+}
+
+static enum dvbfe_algo dvbsky_m88ds3103_get_algo(struct dvb_frontend *fe)
+{
+	return DVBFE_ALGO_HW;
+}
+ 
+ /*
+ * Power config will reset and load initial firmware if required
+ */
+static int dvbsky_m88ds3103_initilaze(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	int ret;
+
+	dprintk("%s()\n", __func__);
+	/* hard reset */
+	dvbsky_m88ds3103_writereg(state, 0x07, 0x80);
+	dvbsky_m88ds3103_writereg(state, 0x07, 0x00);
+	msleep(1);
+	
+	dvbsky_m88ds3103_writereg(state, 0x08, 0x01 | dvbsky_m88ds3103_readreg(state, 0x08));
+	msleep(1);
+
+	if(state->tuner_id == TS2020_ID){
+		/* TS2020 init */
+		dvbsky_m88ds3103_tuner_writereg(state, 0x42, 0x73);
+		msleep(2);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x05, 0x01);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x62, 0xb5);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x07, 0x02);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x08, 0x01);
+	}
+	else if(state->tuner_id == TS2022_ID){
+		/* TS2022 init */
+		dvbsky_m88ds3103_tuner_writereg(state, 0x62, 0x6c);
+		msleep(2);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x42, 0x6c);
+		msleep(2);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x7d, 0x9d);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x7c, 0x9a);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x7a, 0x76);
+
+		dvbsky_m88ds3103_tuner_writereg(state, 0x3b, 0x01);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x63, 0x88);
+
+		dvbsky_m88ds3103_tuner_writereg(state, 0x61, 0x85);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x22, 0x30);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x30, 0x40);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x20, 0x23);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x24, 0x02);
+		dvbsky_m88ds3103_tuner_writereg(state, 0x12, 0xa0);	
+	}
+		
+	if(state->demod_id == DS3103_ID){
+		dvbsky_m88ds3103_writereg(state, 0x07, 0xe0);
+		dvbsky_m88ds3103_writereg(state, 0x07, 0x00);
+		msleep(1);		
+	}
+	dvbsky_m88ds3103_writereg(state, 0xb2, 0x01);
+	
+	/* Load the firmware if required */
+	ret = dvbsky_m88ds3103_load_firmware(fe);
+	if (ret != 0){
+		printk(KERN_ERR "%s: Unable initialize firmware\n", __func__);
+		return ret;
+	}
+	if(state->demod_id == DS3103_ID){
+		dvbsky_m88ds3103_writereg(state, 0x4d, 0xfd & dvbsky_m88ds3103_readreg(state, 0x4d));
+		dvbsky_m88ds3103_writereg(state, 0x30, 0xef & dvbsky_m88ds3103_readreg(state, 0x30));		
+	}
+
+	return 0;
+}
+
+/*
+ * Initialise or wake up device
+ */
+static int dvbsky_m88ds3103_initfe(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+	u8 val;
+
+	dprintk("%s()\n", __func__);
+
+	/* 1st step to wake up demod */
+	dvbsky_m88ds3103_writereg(state, 0x08, 0x01 | dvbsky_m88ds3103_readreg(state, 0x08));
+	dvbsky_m88ds3103_writereg(state, 0x04, 0xfe & dvbsky_m88ds3103_readreg(state, 0x04));
+	dvbsky_m88ds3103_writereg(state, 0x23, 0xef & dvbsky_m88ds3103_readreg(state, 0x23));
+	
+	/* 2nd step to wake up tuner */
+	val = dvbsky_m88ds3103_tuner_readreg(state, 0x00) & 0xff;
+	if((val & 0x01) == 0){
+		dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x01);
+		msleep(50);
+	}
+	dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x03);
+	msleep(50);
+	
+	return 0;	
+}
+
+/* Put device to sleep */
+static int dvbsky_m88ds3103_sleep(struct dvb_frontend *fe)
+{
+	struct dvbsky_m88ds3103_state *state = fe->demodulator_priv;
+
+	dprintk("%s()\n", __func__);
+	
+	/* 1st step to sleep tuner */
+	dvbsky_m88ds3103_tuner_writereg(state, 0x00, 0x00);
+	
+	/* 2nd step to sleep demod */
+	dvbsky_m88ds3103_writereg(state, 0x08, 0xfe & dvbsky_m88ds3103_readreg(state, 0x08));
+	dvbsky_m88ds3103_writereg(state, 0x04, 0x01 | dvbsky_m88ds3103_readreg(state, 0x04));
+	dvbsky_m88ds3103_writereg(state, 0x23, 0x10 | dvbsky_m88ds3103_readreg(state, 0x23));
+	
+
+	return 0;
+}
+
+static struct dvb_frontend_ops dvbsky_m88ds3103_ops = {
+	.delsys = { SYS_DVBS, SYS_DVBS2},
+	.info = {
+		.name = "Montage DS3103/TS2022",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
+		.frequency_tolerance = 5000,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_2G_MODULATION |
+			FE_CAN_QPSK | FE_CAN_RECOVER
+	},
+
+	.release = dvbsky_m88ds3103_release,
+
+	.init = dvbsky_m88ds3103_initfe,
+	.sleep = dvbsky_m88ds3103_sleep,
+	.read_status = dvbsky_m88ds3103_read_status,
+	.read_ber = dvbsky_m88ds3103_read_ber,
+	.read_signal_strength = dvbsky_m88ds3103_read_signal_strength,
+	.read_snr = dvbsky_m88ds3103_read_snr,
+	.read_ucblocks = dvbsky_m88ds3103_read_ucblocks,
+	.set_tone = dvbsky_m88ds3103_set_tone,
+	.set_voltage = dvbsky_m88ds3103_set_voltage,
+	.diseqc_send_master_cmd = dvbsky_m88ds3103_send_diseqc_msg,
+	.diseqc_send_burst = dvbsky_m88ds3103_diseqc_send_burst,
+	.get_frontend_algo = dvbsky_m88ds3103_get_algo,
+	.tune = dvbsky_m88ds3103_tune,
+	.set_frontend = dvbsky_m88ds3103_set_frontend,
+};
+
+MODULE_DESCRIPTION("DVB Frontend module for Montage DS3103/TS2022 hardware");
+MODULE_AUTHOR("Max nibble");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/dvbsky_m88ds3103.h b/drivers/media/dvb-frontends/dvbsky_m88ds3103.h
new file mode 100644
index 0000000..df95253
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvbsky_m88ds3103.h
@@ -0,0 +1,53 @@
+/*
+    Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner 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.
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef dvbsky_m88ds3103_H
+#define dvbsky_m88ds3103_H
+
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+
+struct dvbsky_m88ds3103_config {
+	/* the demodulator's i2c address */
+	u8 demod_address;
+	u8 ci_mode;
+	u8 pin_ctrl;
+	u8 ts_mode; /* 0: Parallel, 1: Serial */
+
+	/* Set device param to start dma */
+	int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+    /* Start to transfer data */
+    int (*start_ctrl)(struct dvb_frontend *fe);
+    /* Set LNB voltage */
+    int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+};
+
+#if IS_ENABLED(CONFIG_DVB_DVBSKY_M88DS3103)
+extern struct dvb_frontend *dvbsky_m88ds3103_attach(
+       const struct dvbsky_m88ds3103_config *config,
+       struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *dvbsky_m88ds3103_attach(
+       const struct dvbsky_m88ds3103_config *config,
+       struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_DVBSKY_M88DS3103 */
+#endif /* dvbsky_m88ds3103_H */
diff --git a/drivers/media/dvb-frontends/dvbsky_m88ds3103_priv.h b/drivers/media/dvb-frontends/dvbsky_m88ds3103_priv.h
new file mode 100644
index 0000000..dfb3f8b
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvbsky_m88ds3103_priv.h
@@ -0,0 +1,403 @@
+/*
+    Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner 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.
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef dvbsky_m88ds3103_PRIV_H
+#define dvbsky_m88ds3103_PRIV_H
+
+#define FW_DOWN_SIZE 32
+#define FW_DOWN_LOOP (8192/FW_DOWN_SIZE)
+#define DS3103_DEFAULT_FIRMWARE "dvb-fe-ds3103.fw"
+#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds300x.fw"
+#define MT_FE_MCLK_KHZ 96000 /* in kHz */
+#define MT_FE_CRYSTAL_KHZ   27000 /* in kHz */
+#define FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz 3000
+#define DS3000_ID	0x3000
+#define DS3103_ID	0x3103
+#define TS2020_ID	0x2020
+#define TS2022_ID	0x2022
+#define UNKNOW_ID	0x0000
+
+struct dvbsky_m88ds3103_state {
+	struct i2c_adapter *i2c;
+	const struct dvbsky_m88ds3103_config *config;
+	
+	struct dvb_frontend frontend;
+	
+	u32 preBer;
+	u8 skip_fw_load;	
+	u8 first_lock; /* The first time of signal lock */
+	u16 demod_id; /* demod chip type */
+	u16 tuner_id; /* tuner chip type */
+	fe_delivery_system_t delivery_system;
+};
+
+/* For M88DS3103 demod dvbs mode.*/
+static u8 ds3103_dvbs_init_tab[] = {
+	0x23, 0x07,
+	0x08, 0x03,
+	0x0c, 0x02,
+	0x21, 0x54,
+	0x25, 0x82,
+	0x27, 0x31,
+	0x30, 0x08,
+	0x31, 0x40,
+	0x32, 0x32,
+	0x33, 0x35,
+	0x35, 0xff,
+	0x3a, 0x00,
+	0x37, 0x10,
+	0x38, 0x10,
+	0x39, 0x02,
+	0x42, 0x60,
+	0x4a, 0x80,
+	0x4b, 0x04,
+	0x4d, 0x91,
+	0x5d, 0xc8,
+	0x50, 0x36,
+	0x51, 0x36,
+	0x52, 0x36,
+	0x53, 0x36,
+	0x63, 0x0f,
+	0x64, 0x30,
+	0x65, 0x40,
+	0x68, 0x26,
+	0x69, 0x4c,
+	0x70, 0x20,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x40,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x60,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x80,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0xa0,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x1f,
+	0x76, 0x38,
+	0x77, 0xa6,
+	0x78, 0x0c,
+	0x79, 0x80,
+	0x7f, 0x14,
+	0x7c, 0x00,
+	0xae, 0x82,
+	0x80, 0x64,
+	0x81, 0x66,
+	0x82, 0x44,
+	0x85, 0x04,
+	0xcd, 0xf4,
+	0x90, 0x33,
+	0xa0, 0x44,
+	0xc0, 0x08,
+	0xc3, 0x10,
+	0xc4, 0x08,
+	0xc5, 0xf0,
+	0xc6, 0xff,
+	0xc7, 0x00,
+	0xc8, 0x1a,
+	0xc9, 0x80,
+	0xe0, 0xf8,
+	0xe6, 0x8b,
+	0xd0, 0x40,
+	0xf8, 0x20,
+	0xfa, 0x0f,
+	0x00, 0x00,
+	0xbd, 0x01,
+	0xb8, 0x00,
+};
+/* For M88DS3103 demod dvbs2 mode.*/
+static u8 ds3103_dvbs2_init_tab[] = {
+	0x23, 0x07,
+	0x08, 0x07,
+	0x0c, 0x02,
+	0x21, 0x54,
+	0x25, 0x82,
+	0x27, 0x31,
+	0x30, 0x08,
+	0x32, 0x32,
+	0x33, 0x35,
+	0x35, 0xff,
+	0x3a, 0x00,
+	0x37, 0x10,
+	0x38, 0x10,
+	0x39, 0x02,
+	0x42, 0x60,
+	0x4a, 0x80,
+	0x4b, 0x04,
+	0x4d, 0x91,
+	0x5d, 0xc8,
+	0x50, 0x36,
+	0x51, 0x36,
+	0x52, 0x36,
+	0x53, 0x36,
+	0x63, 0x0f,
+	0x64, 0x10,
+	0x65, 0x20,
+	0x68, 0x46,
+	0x69, 0xcd,
+	0x70, 0x20,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x40,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x60,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x80,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0xa0,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x1f,
+	0x76, 0x38,
+	0x77, 0xa6,
+	0x78, 0x0c,
+	0x79, 0x80,
+	0x7f, 0x14,
+	0x85, 0x08,
+	0xcd, 0xf4,
+	0x90, 0x33,
+	0x86, 0x00,
+	0x87, 0x0f,
+	0x89, 0x00,
+	0x8b, 0x44,
+	0x8c, 0x66,
+	0x9d, 0xc1,
+	0x8a, 0x10,
+	0xad, 0x40,
+	0xa0, 0x44,
+	0xc0, 0x08,
+	0xc1, 0x10,
+	0xc2, 0x08,
+	0xc3, 0x10,
+	0xc4, 0x08,
+	0xc5, 0xf0,
+	0xc6, 0xff,
+	0xc7, 0x00,
+	0xc8, 0x1a,
+	0xc9, 0x80,
+	0xca, 0x23,
+	0xcb, 0x24,
+	0xcc, 0xf4,
+	0xce, 0x74,
+	0x00, 0x00,
+	0xbd, 0x01,
+	0xb8, 0x00,
+};
+
+/* For M88DS3000 demod dvbs mode.*/
+static u8 ds3000_dvbs_init_tab[] = {
+	0x23, 0x05,
+	0x08, 0x03,
+	0x0c, 0x02,
+	0x21, 0x54,
+	0x25, 0x82,
+	0x27, 0x31,
+	0x30, 0x08,
+	0x31, 0x40,
+	0x32, 0x32,
+	0x33, 0x35,
+	0x35, 0xff,
+	0x3a, 0x00,
+	0x37, 0x10,
+	0x38, 0x10,
+	0x39, 0x02,
+	0x42, 0x60,
+	0x4a, 0x40,
+	0x4b, 0x04,
+	0x4d, 0x91,
+	0x5d, 0xc8,
+	0x50, 0x77,
+	0x51, 0x77,
+	0x52, 0x36,
+	0x53, 0x36,
+	0x56, 0x01,
+	0x63, 0x47,
+	0x64, 0x30,
+	0x65, 0x40,
+	0x68, 0x26,
+	0x69, 0x4c,
+	0x70, 0x20,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x40,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x60,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x80,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0xa0,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x1f,
+	0x76, 0x00,
+	0x77, 0xd1,
+	0x78, 0x0c,
+	0x79, 0x80,
+	0x7f, 0x04,
+	0x7c, 0x00,
+	0x80, 0x86,
+	0x81, 0xa6,
+	0x85, 0x04,
+	0xcd, 0xf4,
+	0x90, 0x33,
+	0xa0, 0x44,
+	0xc0, 0x18,
+	0xc3, 0x10,
+	0xc4, 0x08,
+	0xc5, 0x80,
+	0xc6, 0x80,
+	0xc7, 0x0a,
+	0xc8, 0x1a,
+	0xc9, 0x80,
+	0xfe, 0xb6,
+	0xe0, 0xf8,
+	0xe6, 0x8b,
+	0xd0, 0x40,
+	0xf8, 0x20,
+	0xfa, 0x0f,
+	0xad, 0x20,
+	0xae, 0x07,
+	0xb8, 0x00,
+};
+
+/* For M88DS3000 demod dvbs2 mode.*/
+static u8 ds3000_dvbs2_init_tab[] = {
+	0x23, 0x0f,
+	0x08, 0x07,
+	0x0c, 0x02,
+	0x21, 0x54,
+	0x25, 0x82,
+	0x27, 0x31,
+	0x30, 0x08,
+	0x31, 0x32,
+	0x32, 0x32,
+	0x33, 0x35,
+	0x35, 0xff,
+	0x3a, 0x00,
+	0x37, 0x10,
+	0x38, 0x10,
+	0x39, 0x02,
+	0x42, 0x60,
+	0x4a, 0x80,
+	0x4b, 0x04,
+	0x4d, 0x91,
+	0x5d, 0x88,
+	0x50, 0x36,
+	0x51, 0x36,
+	0x52, 0x36,
+	0x53, 0x36,
+	0x63, 0x60,
+	0x64, 0x10,
+	0x65, 0x10,
+	0x68, 0x04,
+	0x69, 0x29,
+	0x70, 0x20,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x40,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x60,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x80,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0xa0,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x1f,
+	0xa0, 0x44,
+	0xc0, 0x08,
+	0xc1, 0x10,
+	0xc2, 0x08,
+	0xc3, 0x10,
+	0xc4, 0x08,
+	0xc5, 0xf0,
+	0xc6, 0xf0,
+	0xc7, 0x0a,
+	0xc8, 0x1a,
+	0xc9, 0x80,
+	0xca, 0x23,
+	0xcb, 0x24,
+	0xce, 0x74,
+	0x56, 0x01,
+	0x90, 0x03,
+	0x76, 0x80,
+	0x77, 0x42,
+	0x78, 0x0a,
+	0x79, 0x80,
+	0xad, 0x40,
+	0xae, 0x07,
+	0x7f, 0xd4,
+	0x7c, 0x00,
+	0x80, 0xa8,
+	0x81, 0xda,
+	0x7c, 0x01,
+	0x80, 0xda,
+	0x81, 0xec,
+	0x7c, 0x02,
+	0x80, 0xca,
+	0x81, 0xeb,
+	0x7c, 0x03,
+	0x80, 0xba,
+	0x81, 0xdb,
+	0x85, 0x08,
+	0x86, 0x00,
+	0x87, 0x02,
+	0x89, 0x80,
+	0x8b, 0x44,
+	0x8c, 0xaa,
+	0x8a, 0x10,
+	0xba, 0x00,
+	0xf5, 0x04,
+	0xd2, 0x32,
+	0xb8, 0x00,
+};
+
+#endif /* dvbsky_m88ds3103_PRIV_H */
diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig
index d1dcb1d..a5fbc89 100644
--- a/drivers/media/pci/cx23885/Kconfig
+++ b/drivers/media/pci/cx23885/Kconfig
@@ -23,6 +23,8 @@ config VIDEO_CX23885
 	select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_DVBSKY_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_DVBSKY_M88DC2800 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_CX24117 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c
index 16fa7ea..2b63f78 100644
--- a/drivers/media/pci/cx23885/cimax2.c
+++ b/drivers/media/pci/cx23885/cimax2.c
@@ -426,7 +426,7 @@ int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
 	return state->status;
 }
 
-int netup_ci_init(struct cx23885_tsport *port)
+int netup_ci_init(struct cx23885_tsport *port, bool isDVBSky)
 {
 	struct netup_ci_state *state;
 	u8 cimax_init[34] = {
@@ -475,6 +475,11 @@ int netup_ci_init(struct cx23885_tsport *port)
 		goto err;
 	}
 
+	if(isDVBSky) {
+		cimax_init[32] = 0x22;
+		cimax_init[33] = 0x00;
+	}
+
 	port->port_priv = state;
 
 	switch (port->nr) {
@@ -548,3 +553,19 @@ void netup_ci_exit(struct cx23885_tsport *port)
 	dvb_ca_en50221_release(&state->ca);
 	kfree(state);
 }
+
+/* CI irq handler for DVBSky board*/
+int dvbsky_ci_slot_status(struct cx23885_dev *dev)
+{
+	struct cx23885_tsport *port = NULL;
+	struct netup_ci_state *state = NULL;
+
+	ci_dbg_print("%s:\n", __func__);
+
+	port = &dev->ts1;
+	state = port->port_priv;
+	schedule_work(&state->work);
+	ci_dbg_print("%s: Wakeup CI0\n", __func__);
+
+	return 1;
+}
diff --git a/drivers/media/pci/cx23885/cimax2.h b/drivers/media/pci/cx23885/cimax2.h
index 518744a..39f3db7 100644
--- a/drivers/media/pci/cx23885/cimax2.h
+++ b/drivers/media/pci/cx23885/cimax2.h
@@ -41,7 +41,9 @@ extern int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot);
 extern int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status);
 extern int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
 						int slot, int open);
-extern int netup_ci_init(struct cx23885_tsport *port);
+extern int netup_ci_init(struct cx23885_tsport *port, bool isDVBSky);
 extern void netup_ci_exit(struct cx23885_tsport *port);
 
+extern int dvbsky_ci_slot_status(struct cx23885_dev *dev);
+
 #endif
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index 79f20c8..cb00710 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -613,6 +613,34 @@ struct cx23885_board cx23885_boards[] = {
 		.name		= "TeVii S471",
 		.portb		= CX23885_MPEG_DVB,
 	},
+	[CX23885_BOARD_BST_PS8512] = {
+		.name		= "Bestunar PS8512",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_S950] = {
+		.name		= "DVBSKY S950",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_S952] = {
+		.name		= "DVBSKY S952",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_S950_CI] = {
+		.ci_type	= 3,
+		.name		= "DVBSKY S950CI DVB-S2 CI",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_C2800E_CI] = {
+		.ci_type	= 3,
+		.name		= "DVBSKY C2800E DVB-C CI",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_T9580] = {
+		.name		= "DVBSKY T9580",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
 	[CX23885_BOARD_PROF_8000] = {
 		.name		= "Prof Revolution DVB-S2 8000",
 		.portb		= CX23885_MPEG_DVB,
@@ -874,6 +902,30 @@ struct cx23885_subid cx23885_subids[] = {
 		.subdevice = 0x9022,
 		.card      = CX23885_BOARD_TEVII_S471,
 	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8512,
+		.card      = CX23885_BOARD_BST_PS8512,
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x0950,
+		.card      = CX23885_BOARD_DVBSKY_S950,		
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x0952,
+		.card      = CX23885_BOARD_DVBSKY_S952,
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x950C,
+		.card      = CX23885_BOARD_DVBSKY_S950_CI,
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x2800,
+		.card      = CX23885_BOARD_DVBSKY_C2800E_CI,
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x9580,
+		.card      = CX23885_BOARD_DVBSKY_T9580,
+	}, {
 		.subvendor = 0x8000,
 		.subdevice = 0x3034,
 		.card      = CX23885_BOARD_PROF_8000,
@@ -1483,9 +1535,84 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
 		cx_set(GP0_IO, 0x00040004);
 		mdelay(60);
 		break;
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_BST_PS8512:			
+		cx23885_gpio_enable(dev, GPIO_2, 1);
+		cx23885_gpio_clear(dev, GPIO_2);
+		msleep(100);		
+		cx23885_gpio_set(dev, GPIO_2);
+		break;
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_T9580:
+		cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
+		
+		cx23885_gpio_enable(dev, GPIO_2, 1);
+		cx23885_gpio_enable(dev, GPIO_11, 1);
+		
+		cx23885_gpio_clear(dev, GPIO_2);
+		cx23885_gpio_clear(dev, GPIO_11);
+		msleep(100);		
+		cx23885_gpio_set(dev, GPIO_2);
+		cx23885_gpio_set(dev, GPIO_11);	
+		break;
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+		/* GPIO-0 INTA from CiMax, input
+		   GPIO-1 reset CiMax, output, high active
+		   GPIO-2 reset demod, output, low active
+		   GPIO-3 to GPIO-10 data/addr for CAM
+		   GPIO-11 ~CS0 to CiMax1
+		   GPIO-12 ~CS1 to CiMax2
+		   GPIO-13 ADL0 load LSB addr
+		   GPIO-14 ADL1 load MSB addr
+		   GPIO-15 ~RDY from CiMax
+		   GPIO-17 ~RD to CiMax
+		   GPIO-18 ~WR to CiMax
+		 */
+		cx_set(GP0_IO, 0x00060002); /* GPIO 1/2 as output */
+		cx_clear(GP0_IO, 0x00010004); /*GPIO 0 as input*/
+		mdelay(100);/* reset delay */
+		cx_set(GP0_IO, 0x00060004); /* GPIO as out, reset high */
+		cx_clear(GP0_IO, 0x00010002);
+		cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
+		/* GPIO-15 IN as ~ACK, rest as OUT */
+		cx_write(MC417_OEN, 0x00001000);
+		/* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
+		cx_write(MC417_RWD, 0x0000c300);
+		/* enable irq */
+		cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+		break;
 	}
 }
 
+static int cx23885_ir_patch(struct i2c_adapter *i2c, u8 reg, u8 mask)
+{
+	struct i2c_msg msgs[2];
+	u8 tx_buf[2], rx_buf[1];
+	/* Write register address */
+	tx_buf[0] = reg;
+	msgs[0].addr = 0x4c;
+	msgs[0].flags = 0;
+	msgs[0].len = 1;
+	msgs[0].buf = (char *) tx_buf;
+	/* Read data from register */
+	msgs[1].addr = 0x4c;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = 1;
+	msgs[1].buf = (char *) rx_buf;	
+	
+	i2c_transfer(i2c, msgs, 2);
+
+	tx_buf[0] = reg;
+	tx_buf[1] = rx_buf[0] | mask;
+	msgs[0].addr = 0x4c;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (char *) tx_buf;
+	
+	return i2c_transfer(i2c, msgs, 1);
+}
+
 int cx23885_ir_init(struct cx23885_dev *dev)
 {
 	static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
@@ -1573,6 +1700,23 @@ int cx23885_ir_init(struct cx23885_dev *dev)
 		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
 				 ir_rx_pin_cfg_count, ir_rx_pin_cfg);
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+	case CX23885_BOARD_DVBSKY_T9580:
+		dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+		if (dev->sd_ir == NULL) {
+			ret = -ENODEV;
+			break;
+		}
+		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+				 ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+				 
+		cx23885_ir_patch(&(dev->i2c_bus[2].i2c_adap),0x1f,0x80);
+		cx23885_ir_patch(&(dev->i2c_bus[2].i2c_adap),0x23,0x80);
+		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 		if (!enable_885_ir)
 			break;
@@ -1602,6 +1746,12 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
 		cx23888_ir_remove(dev);
 		dev->sd_ir = NULL;
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+	case CX23885_BOARD_DVBSKY_T9580:		
 	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
 	case CX23885_BOARD_TEVII_S470:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
@@ -1649,6 +1799,12 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
 		if (dev->sd_ir)
 			cx23885_irq_add_enable(dev, PCI_MSK_IR);
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+	case CX23885_BOARD_DVBSKY_T9580:		
 	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
 	case CX23885_BOARD_TEVII_S470:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
@@ -1752,6 +1908,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
 		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
 	case CX23885_BOARD_TEVII_S470:
 	case CX23885_BOARD_TEVII_S471:
 	case CX23885_BOARD_DVBWORLD_2005:
@@ -1800,6 +1960,22 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
 		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
 		break;
+	case CX23885_BOARD_DVBSKY_S952:
+		ts1->gen_ctrl_val  = 0x5; /* Parallel */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		ts2->gen_ctrl_val  = 0xe; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
+	case CX23885_BOARD_DVBSKY_T9580:
+		ts1->gen_ctrl_val  = 0x5; /* Parallel */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		ts2->gen_ctrl_val  = 0x8; /* Serial bus */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;		
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
@@ -1857,6 +2033,12 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 	case CX23885_BOARD_MPX885:
 	case CX23885_BOARD_MYGICA_X8507:
 	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+	case CX23885_BOARD_DVBSKY_T9580:
 	case CX23885_BOARD_AVERMEDIA_HC81R:
 	case CX23885_BOARD_TBS_6980:
 	case CX23885_BOARD_TBS_6981:
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index edcd79d..4b57eef 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -1909,6 +1909,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
 			(pci_status & PCI_MSK_GPIO0))
 		handled += altera_ci_irq(dev);
 
+	if (cx23885_boards[dev->board].ci_type == 3 &&
+			(pci_status & PCI_MSK_GPIO0))
+		handled += dvbsky_ci_slot_status(dev);
+		
 	if (ts1_status) {
 		if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
 			handled += cx23885_irq_ts(ts1, ts1_status);
@@ -2141,6 +2145,8 @@ static int cx23885_initdev(struct pci_dev *pci_dev,
 		cx23885_irq_add_enable(dev, PCI_MSK_GPIO1 | PCI_MSK_GPIO0);
 		break;
 	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
 		cx23885_irq_add_enable(dev, PCI_MSK_GPIO0);
 		break;
 	}
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index 0549205..201523a 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -52,6 +52,8 @@
 #include "lnbh24.h"
 #include "cx24116.h"
 #include "cx24117.h"
+#include "dvbsky_m88ds3103.h"
+#include "dvbsky_m88dc2800.h"
 #include "cimax2.h"
 #include "lgs8gxx.h"
 #include "netup-eeprom.h"
@@ -507,6 +509,93 @@ static struct xc5000_config mygica_x8507_xc5000_config = {
 	.if_khz = 4000,
 };
 
+/* bst control */
+int bst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct cx23885_tsport *port = fe->dvb->priv;
+	struct cx23885_dev *dev = port->dev;
+	
+	cx23885_gpio_enable(dev, GPIO_1, 1);
+	cx23885_gpio_enable(dev, GPIO_0, 1);
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		cx23885_gpio_set(dev, GPIO_1);
+		cx23885_gpio_clear(dev, GPIO_0);
+		break;
+	case SEC_VOLTAGE_18:
+		cx23885_gpio_set(dev, GPIO_1);
+		cx23885_gpio_set(dev, GPIO_0);
+		break;
+	case SEC_VOLTAGE_OFF:
+		cx23885_gpio_clear(dev, GPIO_1);
+		cx23885_gpio_clear(dev, GPIO_0);
+		break;
+	}
+	return 0;
+}
+
+int dvbsky_set_voltage_sec(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct cx23885_tsport *port = fe->dvb->priv;
+	struct cx23885_dev *dev = port->dev;
+	
+	cx23885_gpio_enable(dev, GPIO_12, 1);
+	cx23885_gpio_enable(dev, GPIO_13, 1);
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		cx23885_gpio_set(dev, GPIO_13);
+		cx23885_gpio_clear(dev, GPIO_12);
+		break;
+	case SEC_VOLTAGE_18:
+		cx23885_gpio_set(dev, GPIO_13);
+		cx23885_gpio_set(dev, GPIO_12);
+		break;
+	case SEC_VOLTAGE_OFF:
+		cx23885_gpio_clear(dev, GPIO_13);
+		cx23885_gpio_clear(dev, GPIO_12);
+		break;
+	}
+	return 0;
+}
+
+/* bestunar single dvb-s2 */
+static struct dvbsky_m88ds3103_config bst_ds3103_config = {
+	.demod_address = 0x68,
+	.ci_mode = 0,
+	.pin_ctrl = 0x82,
+	.ts_mode = 0,
+	.set_voltage = bst_set_voltage,
+};
+/* DVBSKY dual dvb-s2 */
+static struct dvbsky_m88ds3103_config dvbsky_ds3103_config_pri = {
+	.demod_address = 0x68,
+	.ci_mode = 0,
+	.pin_ctrl = 0x82,
+	.ts_mode = 0,
+	.set_voltage = bst_set_voltage,	
+};
+static struct dvbsky_m88ds3103_config dvbsky_ds3103_config_sec = {
+	.demod_address = 0x68,
+	.ci_mode = 0,
+	.pin_ctrl = 0x82,
+	.ts_mode = 1,
+	.set_voltage = dvbsky_set_voltage_sec,	
+};
+
+static struct dvbsky_m88ds3103_config dvbsky_ds3103_ci_config = {
+	.demod_address = 0x68,
+	.ci_mode = 2,
+	.pin_ctrl = 0x82,
+	.ts_mode = 0,
+};
+
+static struct dvbsky_m88dc2800_config dvbsky_dc2800_config = {
+	.demod_address = 0x1c,
+	.ts_mode = 3,	
+};
+
 static struct stv090x_config prof_8000_stv090x_config = {
 	.device                 = STV0903,
 	.demod_mode             = STV090x_SINGLE,
@@ -1311,6 +1400,57 @@ static int dvb_register(struct cx23885_tsport *port)
 				&tevii_ts2020_config, &i2c_bus->i2c_adap);
 		}
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+		i2c_bus = &dev->i2c_bus[1];	
+		fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach,
+					&bst_ds3103_config,
+					&i2c_bus->i2c_adap);
+		break;				
+	case CX23885_BOARD_DVBSKY_S952:
+		switch (port->nr) {
+		/* port B */
+		case 1:
+			i2c_bus = &dev->i2c_bus[1];
+			fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach,
+						&dvbsky_ds3103_config_pri,
+						&i2c_bus->i2c_adap);
+			break;
+		/* port C */
+		case 2:
+			i2c_bus = &dev->i2c_bus[0];
+			fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach,
+						&dvbsky_ds3103_config_sec,
+						&i2c_bus->i2c_adap);	
+			break;
+		}
+		break;
+	case CX23885_BOARD_DVBSKY_S950_CI:
+		i2c_bus = &dev->i2c_bus[1];	
+		fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach,
+					&dvbsky_ds3103_ci_config,
+					&i2c_bus->i2c_adap);
+		break;				
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+		i2c_bus = &dev->i2c_bus[1];	
+		fe0->dvb.frontend = dvb_attach(dvbsky_m88dc2800_attach,
+					&dvbsky_dc2800_config,
+					&i2c_bus->i2c_adap);
+		break;
+	case CX23885_BOARD_DVBSKY_T9580:
+		switch (port->nr) {
+		/* port B */
+		case 1:
+			i2c_bus = &dev->i2c_bus[1];
+			fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach,
+						&dvbsky_ds3103_config_pri,
+						&i2c_bus->i2c_adap);
+			break;
+		/* port C */
+		case 2:
+			break;
+		}
+		break;				
 	case CX23885_BOARD_PROF_8000:
 		i2c_bus = &dev->i2c_bus[0];
 
@@ -1386,7 +1526,7 @@ static int dvb_register(struct cx23885_tsport *port)
 		printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
 			port->nr, port->frontends.adapter.proposed_mac);
 
-		netup_ci_init(port);
+		netup_ci_init(port, false);
 		break;
 		}
 	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
@@ -1413,6 +1553,41 @@ static int dvb_register(struct cx23885_tsport *port)
 		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
 		break;
 		}
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_T9580:{
+		u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+		if(port->nr > 2)
+			break;
+
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
+		printk(KERN_INFO "DVBSKY PCIe MAC= %pM\n", eeprom + 0xc0+(port->nr-1)*8);
+		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + 
+			(port->nr-1)*8, 6);
+		break;
+		}
+	case CX23885_BOARD_DVBSKY_S950_CI: {
+		u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+		if(port->nr > 2)
+			break;
+
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
+		printk(KERN_INFO "DVBSKY PCIe MAC= %pM\n", eeprom + 0xc0+(port->nr-1)*8);
+		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + 
+			(port->nr-1)*8, 6);
+			
+		netup_ci_init(port, true);
+		break;
+		}
+	case CX23885_BOARD_DVBSKY_C2800E_CI: {
+		netup_ci_init(port, true);
+		break;
+		}		
 	}
 
 	return ret;
@@ -1495,6 +1670,8 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port)
 
 	switch (port->dev->board) {
 	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
 		netup_ci_exit(port);
 		break;
 	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
index 8a49e7c..a5e4639 100644
--- a/drivers/media/pci/cx23885/cx23885-input.c
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -89,6 +89,12 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
 	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
 	case CX23885_BOARD_TEVII_S470:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+	case CX23885_BOARD_DVBSKY_T9580:
 	case CX23885_BOARD_MYGICA_X8507:
 	case CX23885_BOARD_TBS_6980:
 	case CX23885_BOARD_TBS_6981:
@@ -143,6 +149,12 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+	case CX23885_BOARD_DVBSKY_T9580:
 	case CX23885_BOARD_MYGICA_X8507:
 		/*
 		 * The IR controller on this board only returns pulse widths.
@@ -295,6 +307,18 @@ int cx23885_input_init(struct cx23885_dev *dev)
 		/* A guess at the remote */
 		rc_map = RC_MAP_TEVII_NEC;
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+	case CX23885_BOARD_DVBSKY_T9580:
+		/* Integrated CX2388[58] IR controller */
+		driver_type = RC_DRIVER_IR_RAW;
+		allowed_protos = RC_BIT_ALL;
+		/* A guess at the remote */
+		rc_map = RC_MAP_DVBSKY;
+		break;
 	case CX23885_BOARD_MYGICA_X8507:
 		/* Integrated CX23885 IR controller */
 		driver_type = RC_DRIVER_IR_RAW;
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
index 0fa4048..7084a4e 100644
--- a/drivers/media/pci/cx23885/cx23885.h
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -97,6 +97,14 @@
 #define CX23885_BOARD_TBS_6980                 41
 #define CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200 42
 
+#define CX23885_BOARD_BASE_INDEX	       43
+#define CX23885_BOARD_BST_PS8512	       (CX23885_BOARD_BASE_INDEX)
+#define CX23885_BOARD_DVBSKY_S952	       (CX23885_BOARD_BASE_INDEX+1)
+#define CX23885_BOARD_DVBSKY_S950	       (CX23885_BOARD_BASE_INDEX+2)
+#define CX23885_BOARD_DVBSKY_S950_CI	       (CX23885_BOARD_BASE_INDEX+3)
+#define CX23885_BOARD_DVBSKY_C2800E_CI	       (CX23885_BOARD_BASE_INDEX+4)
+#define CX23885_BOARD_DVBSKY_T9580	       (CX23885_BOARD_BASE_INDEX+5)
+
 #define GPIO_0 0x00000001
 #define GPIO_1 0x00000002
 #define GPIO_2 0x00000004
@@ -234,7 +242,7 @@ struct cx23885_board {
 	 */
 	u32			clk_freq;
 	struct cx23885_input    input[MAX_CX23885_INPUT];
-	int			ci_type; /* for NetUP */
+	int			ci_type; /* 1 and 2 for NetUP, 3 for DVBSky. */
 	/* Force bottom field first during DMA (888 workaround) */
 	u32                     force_bff;
 };
diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig
index a63a9ad..7deb300 100644
--- a/drivers/media/pci/cx88/Kconfig
+++ b/drivers/media/pci/cx88/Kconfig
@@ -57,6 +57,7 @@ config VIDEO_CX88_DVB
 	select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_DVBSKY_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c
index e18a7ac..317511d 100644
--- a/drivers/media/pci/cx88/cx88-cards.c
+++ b/drivers/media/pci/cx88/cx88-cards.c
@@ -2314,6 +2314,18 @@ static const struct cx88_board cx88_boards[] = {
 		} },
 		.mpeg           = CX88_MPEG_DVB,
 	},
+	[CX88_BOARD_BST_PS8312] = {
+		.name           = "Bestunar PS8312 DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
 };
 
 /* ------------------------------------------------------------------ */
@@ -2818,6 +2830,10 @@ static const struct cx88_subid cx88_subids[] = {
 		.subvendor = 0x1822,
 		.subdevice = 0x0023,
 		.card      = CX88_BOARD_TWINHAN_VP1027_DVBS,
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8312,
+		.card      = CX88_BOARD_BST_PS8312,
 	},
 };
 
@@ -3551,6 +3567,12 @@ static void cx88_card_setup(struct cx88_core *core)
 		cx_write(MO_SRST_IO, 1);
 		msleep(100);
 		break;
+	case  CX88_BOARD_BST_PS8312:
+		cx_write(MO_GP1_IO, 0x808000);
+		msleep(100);
+		cx_write(MO_GP1_IO, 0x808080);
+		msleep(100);
+		break;
 	} /*end switch() */
 
 
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
index 053ed1b..955b916 100644
--- a/drivers/media/pci/cx88/cx88-dvb.c
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -54,6 +54,7 @@
 #include "stv0288.h"
 #include "stb6000.h"
 #include "cx24116.h"
+#include "dvbsky_m88ds3103.h"
 #include "stv0900.h"
 #include "stb6100.h"
 #include "stb6100_proc.h"
@@ -459,6 +460,56 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
 		return core->prev_set_voltage(fe, voltage);
 	return 0;
 }
+/*CX88_BOARD_BST_PS8312*/
+static int bst_dvbs_set_voltage(struct dvb_frontend *fe,
+				      fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	cx_write(MO_GP1_IO, 0x111111);
+	switch (voltage) {
+		case SEC_VOLTAGE_13:
+			cx_write(MO_GP1_IO, 0x020200);
+			break;
+		case SEC_VOLTAGE_18:
+			cx_write(MO_GP1_IO, 0x020202);
+			break;
+		case SEC_VOLTAGE_OFF:
+			cx_write(MO_GP1_IO, 0x111100);
+			break;
+	}
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
+
+static int bst_dvbs_set_voltage_v2(struct dvb_frontend *fe,
+				      fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	cx_write(MO_GP1_IO, 0x111101);
+	switch (voltage) {
+		case SEC_VOLTAGE_13:
+			cx_write(MO_GP1_IO, 0x020200);
+			break;
+		case SEC_VOLTAGE_18:
+
+			cx_write(MO_GP1_IO, 0x020202);
+			break;
+		case SEC_VOLTAGE_OFF:
+
+			cx_write(MO_GP1_IO, 0x111110);
+			break;
+	}
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
 
 static int vp1027_set_voltage(struct dvb_frontend *fe,
 				    fe_sec_voltage_t voltage)
@@ -706,6 +757,11 @@ static struct ts2020_config tevii_ts2020_config  = {
 	.clk_out_div = 1,
 };
 
+static struct dvbsky_m88ds3103_config dvbsky_ds3103_config = {
+	.demod_address = 0x68,
+	.set_ts_params = ds3000_set_ts_param,
+};
+
 static const struct stv0900_config prof_7301_stv0900_config = {
 	.demod_address = 0x6a,
 /*	demod_mode = 0,*/
@@ -1487,6 +1543,35 @@ static int dvb_register(struct cx8802_dev *dev)
 							tevii_dvbs_set_voltage;
 		}
 		break;
+	case CX88_BOARD_BST_PS8312:
+		fe0->dvb.frontend = dvb_attach(dvbsky_m88ds3103_attach,
+						&dvbsky_ds3103_config,
+						&core->i2c_adap);
+		if (fe0->dvb.frontend != NULL){
+			int ret;
+			u8 b0[] = { 0x60 };
+			u8 b1[2] = { 0 };
+			struct i2c_msg msg[] = {
+				{
+				.addr = 0x50,
+				.flags = 0,
+				.buf = b0,
+				.len = 1
+				}, {
+				.addr = 0x50,
+				.flags = I2C_M_RD,
+				.buf = b1,
+				.len = 2
+				}
+			};
+			ret = i2c_transfer(&core->i2c_adap, msg, 2);
+			printk("PS8312: config = %02x, %02x", b1[0],b1[1]);
+			if(b1[0] == 0xaa)
+				fe0->dvb.frontend->ops.set_voltage = bst_dvbs_set_voltage_v2;
+			else			
+				fe0->dvb.frontend->ops.set_voltage = bst_dvbs_set_voltage;
+		}
+		break;
 	case CX88_BOARD_OMICOM_SS4_PCI:
 	case CX88_BOARD_TBS_8920:
 	case CX88_BOARD_PROF_7300:
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index f29e18c..42a911c 100644
--- a/drivers/media/pci/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -419,6 +419,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
 		rc_type          = RC_BIT_NEC;
 		ir->sampling     = 0xff00; /* address */
 		break;
+	case CX88_BOARD_BST_PS8312:
+		ir_codes         = RC_MAP_DVBSKY;
+		ir->sampling     = 0xff00; /* address */
+		break;
 	}
 
 	if (!ir_codes) {
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
index 28893a6..5fa5f48 100644
--- a/drivers/media/pci/cx88/cx88.h
+++ b/drivers/media/pci/cx88/cx88.h
@@ -237,6 +237,7 @@ extern const struct sram_channel cx88_sram_channels[];
 #define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
 #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89
 #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90
+#define CX88_BOARD_BST_PS8312              91
 
 enum cx88_itype {
 	CX88_VMUX_COMPOSITE1 = 1,
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 0b8c549..abf6079 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
 			rc-dm1105-nec.o \
 			rc-dntv-live-dvb-t.o \
 			rc-dntv-live-dvbt-pro.o \
+			rc-dvbsky.o \
 			rc-em-terratec.o \
 			rc-encore-enltv2.o \
 			rc-encore-enltv.o \
diff --git a/drivers/media/rc/keymaps/rc-dvbsky.c b/drivers/media/rc/keymaps/rc-dvbsky.c
new file mode 100644
index 0000000..bfc41fb
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-dvbsky.c
@@ -0,0 +1,78 @@
+/* rc-dvbsky.c - Keytable for Dvbsky Remote Controllers
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ *
+ * Copyright (c) 2010-2012 by Nibble Max <nibble.max@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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+/*
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+
+static struct rc_map_table rc5_dvbsky[] = {
+	{ 0x0000, KEY_0 },
+	{ 0x0001, KEY_1 },
+	{ 0x0002, KEY_2 },
+	{ 0x0003, KEY_3 },
+	{ 0x0004, KEY_4 },
+	{ 0x0005, KEY_5 },
+	{ 0x0006, KEY_6 },
+	{ 0x0007, KEY_7 },
+	{ 0x0008, KEY_8 },
+	{ 0x0009, KEY_9 },
+	{ 0x000a, KEY_MUTE },
+	{ 0x000d, KEY_OK },
+	{ 0x000b, KEY_STOP },
+	{ 0x000c, KEY_EXIT },
+	{ 0x000e, KEY_CAMERA }, /*Snap shot*/
+	{ 0x000f, KEY_SUBTITLE }, /*PIP*/
+	{ 0x0010, KEY_VOLUMEUP },
+	{ 0x0011, KEY_VOLUMEDOWN },
+	{ 0x0012, KEY_FAVORITES },
+	{ 0x0013, KEY_LIST }, /*Info*/
+	{ 0x0016, KEY_PAUSE },
+	{ 0x0017, KEY_PLAY },
+	{ 0x001f, KEY_RECORD },
+	{ 0x0020, KEY_CHANNELDOWN },
+	{ 0x0021, KEY_CHANNELUP },
+	{ 0x0025, KEY_POWER2 },
+	{ 0x0026, KEY_REWIND },
+	{ 0x0027, KEY_FASTFORWARD },
+	{ 0x0029, KEY_LAST },
+	{ 0x002b, KEY_MENU },
+	{ 0x002c, KEY_EPG },
+	{ 0x002d, KEY_ZOOM },
+};
+
+static struct rc_map_list rc5_dvbsky_map = {
+	.map = {
+		.scan    = rc5_dvbsky,
+		.size    = ARRAY_SIZE(rc5_dvbsky),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_DVBSKY,
+	}
+};
+
+static int __init init_rc_map_rc5_dvbsky(void)
+{
+	return rc_map_register(&rc5_dvbsky_map);
+}
+
+static void __exit exit_rc_map_rc5_dvbsky(void)
+{
+	rc_map_unregister(&rc5_dvbsky_map);
+}
+
+module_init(init_rc_map_rc5_dvbsky)
+module_exit(exit_rc_map_rc5_dvbsky)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nibble Max <nibble.max@gmail.com>");
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 2059d0c..24a4ec2 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -147,3 +147,9 @@ config DVB_USB_RTL28XXU
 	help
 	  Say Y here to support the Realtek RTL28xxU DVB USB receiver.
 
+config DVB_USB_DVBSKY
+	tristate "DVBSky USB2.0 support"
+	depends on DVB_USB_V2
+	select DVB_DVBSKY_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y here to support the USB receivers from DVBSky.
diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile
index 2c06714..926f12d 100644
--- a/drivers/media/usb/dvb-usb-v2/Makefile
+++ b/drivers/media/usb/dvb-usb-v2/Makefile
@@ -40,6 +40,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
 dvb-usb-rtl28xxu-objs := rtl28xxu.o
 obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
 
+dvb-usb-dvbsky-objs := dvbsky.o
+obj-$(CONFIG_DVB_USB_DVBSKY) += dvb-usb-dvbsky.o
+
 ccflags-y += -I$(srctree)/drivers/media/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
 ccflags-y += -I$(srctree)/drivers/media/tuners
diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c
new file mode 100644
index 0000000..9033d92
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c
@@ -0,0 +1,739 @@
+/*
+ * Driver for DVBSky USB2.0 receiver
+ *
+ * Copyright (C) 2013 Max nibble <nibble.max@gmail.com>
+ *
+ * CIMax code is copied and modified from:
+ * CIMax2(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dvb_ca_en50221.h"
+#include "dvb_usb.h"
+#include "dvbsky_m88ds3103.h"
+
+static int dvbsky_debug;
+module_param(dvbsky_debug, int, 0644);
+MODULE_PARM_DESC(dvbsky_debug, "Activates dvbsky usb debugging (default:0)");
+
+#define DVBSKY_CI_CTL		0x04
+#define DVBSKY_CI_RD		1
+
+#define dprintk(args...) \
+	do { \
+		if (dvbsky_debug) \
+			printk(KERN_INFO "dvbsky_usb: " args); \
+	} while (0)
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct dvbsky_state {
+	struct mutex stream_mutex;
+	u8 has_ci;
+	u8 ci_attached;
+	struct dvb_ca_en50221 ci;
+	unsigned long next_status_checked_time;
+	u8 ci_i2c_addr;
+	u8 current_ci_flag;
+	int ci_status;
+};
+
+static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff)
+{
+	struct dvbsky_state *state = d_to_priv(d);
+	int ret;
+	u8 obuf_pre[3] = { 0x37, 0, 0 };
+	u8 obuf_post[3] = { 0x36, 3, 0 };
+	dprintk("%s() -off \n", __func__);
+	mutex_lock(&state->stream_mutex);
+	ret = dvb_usbv2_generic_write(d, obuf_pre, 3);
+	if (!ret && onoff) {
+		msleep(10);
+		ret = dvb_usbv2_generic_write(d, obuf_post, 3);
+		dprintk("%s() -on \n", __func__);
+	}
+	mutex_unlock(&state->stream_mutex);
+	return ret;
+}
+
+/* CI opertaions */
+static int dvbsky_ci_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
+						u8 *buf, int len)
+{
+	int ret;
+	struct i2c_msg msg[] = {
+		{
+			.addr	= addr,
+			.flags	= 0,
+			.buf	= &reg,
+			.len	= 1
+		}, {
+			.addr	= addr,
+			.flags	= I2C_M_RD,
+			.buf	= buf,
+			.len	= len
+		}
+	};
+	
+	ret = i2c_transfer(i2c_adap, msg, 2);
+	
+	if (ret != 2) {
+		dprintk("%s: error, Reg = 0x%02x, Status = %d\n", __func__, reg, ret);
+		return -1;
+	}
+	return 0;
+}
+
+static int dvbsky_ci_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
+						u8 *buf, int len)
+{
+	int ret;
+	u8 buffer[len + 1];
+
+	struct i2c_msg msg = {
+		.addr	= addr,
+		.flags	= 0,
+		.buf	= &buffer[0],
+		.len	= len + 1
+	};
+
+	buffer[0] = reg;
+	memcpy(&buffer[1], buf, len);
+
+	ret = i2c_transfer(i2c_adap, &msg, 1);
+
+	if (ret != 1) {
+		dprintk("%s: error, Reg=[0x%02x], Status=%d\n", __func__, reg, ret);
+		return -1;
+	}
+	return 0;
+}
+
+static int dvbsky_ci_op_cam(struct dvb_ca_en50221 *ci, int slot,
+				u8 flag, u8 read, int addr, u8 data)
+{
+	struct dvb_usb_device *d = ci->data;
+	struct dvbsky_state *state = d_to_priv(d);	
+	u8 store;
+	int ret;
+	u8 command[4], respond[2], command_size, respond_size;
+
+	/*dprintk("%s()\n", __func__);*/
+	if (0 != slot)
+		return -EINVAL;
+
+	if (state->current_ci_flag != flag) {
+		ret = dvbsky_ci_read_i2c(&d->i2c_adap, state->ci_i2c_addr,
+				0, &store, 1);
+		if (ret != 0)
+			return ret;
+
+		store &= ~0x0c;
+		store |= flag;
+
+		ret = dvbsky_ci_write_i2c(&d->i2c_adap, state->ci_i2c_addr,
+				0, &store, 1);
+		if (ret != 0)
+			return ret;
+	}
+	state->current_ci_flag = flag;
+
+	command[1] = (u8)((addr >> 8) & 0xff); /*high part of address*/
+	command[2] = (u8)(addr & 0xff); /*low part of address*/
+	if (read) {
+		command[0] = 0x71;
+		command_size = 3;
+		respond_size = 2;
+	} else {
+		command[0] = 0x70;
+		command[3] = data;
+		command_size = 4;
+		respond_size = 1;
+	}
+	ret = dvb_usbv2_generic_rw(d, command, command_size, respond, respond_size);
+
+	return (read) ? respond[1] : 0;
+}
+
+static int dvbsky_ci_read_attribute_mem(struct dvb_ca_en50221 *ci,
+						int slot, int addr)
+{
+	return dvbsky_ci_op_cam(ci, slot, 0, DVBSKY_CI_RD, addr, 0);
+}
+
+static int dvbsky_ci_write_attribute_mem(struct dvb_ca_en50221 *ci,
+						int slot, int addr, u8 data)
+{
+	return dvbsky_ci_op_cam(ci, slot, 0, 0, addr, data);
+}
+
+static int dvbsky_ci_read_cam_ctl(struct dvb_ca_en50221 *ci, int slot, u8 addr)
+{
+	return dvbsky_ci_op_cam(ci, slot, DVBSKY_CI_CTL, DVBSKY_CI_RD, addr, 0);
+}
+
+static int dvbsky_ci_write_cam_ctl(struct dvb_ca_en50221 *ci, int slot,
+							u8 addr, u8 data)
+{
+	return dvbsky_ci_op_cam(ci, slot, DVBSKY_CI_CTL, 0, addr, data);
+}
+
+static int dvbsky_ci_slot_reset(struct dvb_ca_en50221 *ci, int slot)
+{
+	struct dvb_usb_device *d = ci->data;
+	struct dvbsky_state *state = d_to_priv(d);
+	u8 buf =  0x80;
+	int ret;
+	dprintk("%s() slot=%d\n", __func__, slot);
+
+	if (0 != slot)
+		return -EINVAL;
+
+	udelay(500);
+	ret = dvbsky_ci_write_i2c(&d->i2c_adap, state->ci_i2c_addr,
+							0, &buf, 1);
+
+	if (ret != 0)
+		return ret;
+
+	udelay(500);
+
+	buf = 0x00;
+	ret = dvbsky_ci_write_i2c(&d->i2c_adap, state->ci_i2c_addr,
+							0, &buf, 1);
+	msleep(1000);
+	dprintk("%s() slot=%d complete\n", __func__, slot);
+	return 0;
+
+}
+
+static int dvbsky_ci_slot_shutdown(struct dvb_ca_en50221 *ci, int slot)
+{
+	/* not implemented */
+	dprintk("%s()\n", __func__);
+	return 0;
+}
+
+static int dvbsky_ci_slot_ts_enable(struct dvb_ca_en50221 *ci, int slot)
+{
+	struct dvb_usb_device *d = ci->data;
+	struct dvbsky_state *state = d_to_priv(d);
+	u8 buf;
+	int ret;
+
+	dprintk("%s()\n", __func__);
+	if (0 != slot)
+		return -EINVAL;
+
+	dvbsky_ci_read_i2c(&d->i2c_adap, state->ci_i2c_addr,
+			0, &buf, 1);
+	buf |= 0x60;
+
+	ret = dvbsky_ci_write_i2c(&d->i2c_adap, state->ci_i2c_addr,
+							0, &buf, 1);
+	return ret;
+}
+
+static int dvbsky_ci_poll_slot_status(struct dvb_ca_en50221 *ci, int slot,
+	int open)
+{
+	struct dvb_usb_device *d = ci->data;
+	struct dvbsky_state *state = d_to_priv(d);
+	int ret = 0;
+	u8 buf = 0;
+	/*dprintk("%s()\n", __func__);*/
+
+	/* CAM module INSERT/REMOVE processing. slow operation because of i2c
+	 * transfers */
+	if (time_after(jiffies, state->next_status_checked_time)) {
+		ret = dvbsky_ci_read_i2c(&d->i2c_adap, state->ci_i2c_addr,
+				0, &buf, 1);
+
+		/*dprintk("%s() status=%x\n", __func__, buf);*/
+		
+		state->next_status_checked_time = jiffies
+			+ msecs_to_jiffies(1000);
+
+		if (ret != 0)
+			return 0;
+
+		if (buf & 1) {
+			state->ci_status = DVB_CA_EN50221_POLL_CAM_PRESENT |
+				DVB_CA_EN50221_POLL_CAM_READY;
+		}
+		else
+			state->ci_status = 0;
+	}
+	/*dprintk("%s() ret=%x\n", __func__, state->ci_status);*/
+	return state->ci_status;
+}
+
+static int dvbsky_ci_init(struct dvb_usb_device *d)
+{
+	struct dvbsky_state *state = d_to_priv(d);
+	int ret;
+	u8 cimax_init[34] = {
+		0x00, /* module A control*/
+		0x00, /* auto select mask high A */
+		0x00, /* auto select mask low A */
+		0x00, /* auto select pattern high A */
+		0x00, /* auto select pattern low A */
+		0x44, /* memory access time A */
+		0x00, /* invert input A */
+		0x00, /* RFU */
+		0x00, /* RFU */
+		0x00, /* module B control*/
+		0x00, /* auto select mask high B */
+		0x00, /* auto select mask low B */
+		0x00, /* auto select pattern high B */
+		0x00, /* auto select pattern low B */
+		0x44, /* memory access time B */
+		0x00, /* invert input B */
+		0x00, /* RFU */
+		0x00, /* RFU */
+		0x00, /* auto select mask high Ext */
+		0x00, /* auto select mask low Ext */
+		0x00, /* auto select pattern high Ext */
+		0x00, /* auto select pattern low Ext */
+		0x00, /* RFU */
+		0x02, /* destination - module A */
+		0x01, /* power on (use it like store place) */
+		0x00, /* RFU */
+		0x00, /* int status read only */
+		0x00, /* Max: Disable the interrupt in USB solution.*/
+		0x05, /* EXTINT=active-high, INT=push-pull */
+		0x00, /* USCG1 */
+		0x04, /* ack active low */
+		0x00, /* LOCK = 0 */
+		0x22, /* serial mode, rising in, rising out, MSB first*/
+		0x00  /* synchronization */
+	};	
+	dprintk("%s()\n", __func__);
+	state->current_ci_flag = 0xff;
+	state->ci_status = 0;
+	state->next_status_checked_time = jiffies + msecs_to_jiffies(1000);
+	state->ci_i2c_addr = 0x40;
+
+	state->ci.owner               = THIS_MODULE;
+	state->ci.read_attribute_mem  = dvbsky_ci_read_attribute_mem;
+	state->ci.write_attribute_mem = dvbsky_ci_write_attribute_mem;
+	state->ci.read_cam_control    = dvbsky_ci_read_cam_ctl;
+	state->ci.write_cam_control   = dvbsky_ci_write_cam_ctl;
+	state->ci.slot_reset          = dvbsky_ci_slot_reset;
+	state->ci.slot_shutdown       = dvbsky_ci_slot_shutdown;
+	state->ci.slot_ts_enable      = dvbsky_ci_slot_ts_enable;
+	state->ci.poll_slot_status    = dvbsky_ci_poll_slot_status;
+	state->ci.data                = d;
+
+	ret = dvbsky_ci_write_i2c(&d->i2c_adap, state->ci_i2c_addr,
+						0, &cimax_init[0], 34);
+	/* lock registers */
+	ret |= dvbsky_ci_write_i2c(&d->i2c_adap, state->ci_i2c_addr,
+						0x1f, &cimax_init[0x18], 1);
+	/* power on slots */
+	ret |= dvbsky_ci_write_i2c(&d->i2c_adap, state->ci_i2c_addr,
+						0x18, &cimax_init[0x18], 1);
+	if (0 != ret)
+		return ret;
+		
+	ret = dvb_ca_en50221_init(&d->adapter[0].dvb_adap, &state->ci, 0, 1);
+	if (ret)
+		return ret;
+	state->ci_attached = 1;
+	dprintk("%s() complete.\n", __func__);
+	return 0;
+}
+
+static void dvbsky_ci_release(struct dvb_usb_device *d)
+{
+	struct dvbsky_state *state = d_to_priv(d);
+
+	/* detach CI */
+	if (state->ci_attached)
+		dvb_ca_en50221_release(&state->ci);
+
+	return;
+}
+
+static int dvbsky_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+	struct dvb_usb_device *d = fe_to_d(fe);
+	/*dprintk("%s() %d\n", __func__, onoff);*/
+	return dvbsky_stream_ctrl(d, (onoff == 0) ? 0 : 1);
+}
+
+/* GPIO */
+static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value)
+{
+	u8 obuf[64], ibuf[64];
+	obuf[0] = 0x0e;
+	obuf[1] = gport;
+	obuf[2] = value;
+	return dvb_usbv2_generic_rw(d, obuf, 3, ibuf, 1);
+}
+
+/* I2C */
+static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+	int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	int ret = 0;
+	u8 ibuf[64], obuf[64]; 
+
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	if (num > 2) {
+		printk(KERN_ERR "dvbsky_usb: too many i2c messages[%d] than 2.", num);
+		ret = -EOPNOTSUPP;
+		goto i2c_error;
+	}
+		
+	if(num == 1) {
+		if (msg[0].len > 60) {
+			printk(KERN_ERR "dvbsky_usb: too many i2c bytes[%d] than 60.", msg[0].len);
+			ret = -EOPNOTSUPP;
+			goto i2c_error;
+		}
+		if (msg[0].flags & I2C_M_RD) {
+			/* single read */
+			obuf[0] = 0x09;
+			obuf[1] = 0;
+			obuf[2] = msg[0].len;
+			obuf[3] = msg[0].addr;
+			ret = dvb_usbv2_generic_rw(d, obuf, 4, ibuf, msg[0].len + 1);
+			/*dprintk("%s(): read status = %d\n", __func__, ibuf[0]);*/
+			if (!ret)
+				memcpy(msg[0].buf, &ibuf[1], msg[0].len);
+		} else {
+			/* write */
+			obuf[0] = 0x08;
+			obuf[1] = msg[0].addr;
+			obuf[2] = msg[0].len;
+			memcpy(&obuf[3], msg[0].buf, msg[0].len);
+			ret = dvb_usbv2_generic_rw(d, obuf, msg[0].len + 3, ibuf, 1);
+			/*dprintk("%s(): write status = %d\n", __func__, ibuf[0]);*/
+		}
+	} else {
+		if ((msg[0].len > 60) || (msg[1].len > 60)) {
+			printk(KERN_ERR "dvbsky_usb: too many i2c bytes[w-%d][r-%d] than 60.", msg[0].len, msg[1].len);
+			ret = -EOPNOTSUPP;
+			goto i2c_error;
+		}
+		/* write then read */
+		obuf[0] = 0x09;
+		obuf[1] = msg[0].len;
+		obuf[2] = msg[1].len;
+		obuf[3] = msg[0].addr;
+		memcpy(&obuf[4], msg[0].buf, msg[0].len);
+		ret = dvb_usbv2_generic_rw(d, obuf, msg[0].len + 4, ibuf, msg[1].len + 1);
+		/*dprintk("%s(): write then read status = %d\n", __func__, ibuf[0]);*/
+		if (!ret)
+			memcpy(msg[1].buf, &ibuf[1], msg[1].len);
+	}
+i2c_error:
+	mutex_unlock(&d->i2c_mutex);
+	return (ret) ? ret : num;
+}
+
+static u32 dvbsky_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dvbsky_i2c_algo = {
+	.master_xfer   = dvbsky_i2c_xfer,
+	.functionality = dvbsky_i2c_func,
+};
+
+#if IS_ENABLED(CONFIG_RC_CORE)
+static int dvbsky_rc_query(struct dvb_usb_device *d)
+{
+	u32 code = 0xffff;
+	u8 obuf[2], ibuf[2], toggle;
+	int ret;
+	obuf[0] = 0x10;
+	ret = dvb_usbv2_generic_rw(d, obuf, 1, ibuf, 2);
+	if(ret == 0)
+		code = (ibuf[0] << 8) | ibuf[1];
+
+	if (code != 0xffff) {
+		dprintk("rc code: %x", code);
+		toggle = (code & 0x800) ? 1 : 0;
+		code &= 0x3f;
+		rc_keydown(d->rc_dev, code, toggle);
+	}
+	return 0;
+}
+
+static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
+{
+	rc->allowed_protos = RC_BIT_RC5;
+	rc->query          = dvbsky_rc_query;
+	rc->interval       = 300;
+	return 0;
+}
+#else
+	#define dvbsky_get_rc_config NULL
+#endif
+
+static int dvbsky_sync_ctrl(struct dvb_frontend *fe)
+{
+	struct dvb_usb_device *d = fe_to_d(fe);
+	return dvbsky_stream_ctrl(d, 1);
+}
+
+static int dvbsky_usb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct dvb_usb_device *d = fe_to_d(fe);
+	u8 value;
+
+	if (voltage == SEC_VOLTAGE_OFF)
+		value = 0;
+	else
+		value = 1;
+	return dvbsky_gpio_ctrl(d, 0x80, value);
+}
+
+static int dvbsky_usb_ci_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct dvb_usb_device *d = fe_to_d(fe);
+	u8 value;
+
+	if (voltage == SEC_VOLTAGE_OFF)
+		value = 0;
+	else
+		value = 1;
+	return dvbsky_gpio_ctrl(d, 0x00, value);
+}
+
+static int dvbsky_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6])
+{
+	struct dvb_usb_device *d = adap_to_d(adap);
+	u8 obuf[] = { 0x1e, 0x00 };
+	u8 ibuf[6] = { 0 };
+	struct i2c_msg msg[] = {
+		{
+			.addr = 0x51,
+			.flags = 0,
+			.buf = obuf,
+			.len = 2,
+		}, {
+			.addr = 0x51,
+			.flags = I2C_M_RD,
+			.buf = ibuf,
+			.len = 6,
+
+		}
+	};
+	
+	if (i2c_transfer(&d->i2c_adap, msg, 2) == 2)
+		memcpy(mac, ibuf, 6);
+
+	printk(KERN_INFO "dvbsky_usb MAC address=%pM\n", mac);
+	
+	return 0;
+}
+
+static struct dvbsky_m88ds3103_config dvbsky_usb_ds3103_config = {
+	.demod_address = 0x68,
+	.ci_mode = 1,
+	.pin_ctrl = 0x83,
+	.ts_mode = 0,
+	.start_ctrl = dvbsky_sync_ctrl,
+	.set_voltage = dvbsky_usb_set_voltage,
+};
+
+static int dvbsky_s960_attach(struct dvb_usb_adapter *adap)
+{
+	struct dvbsky_state *state = adap_to_priv(adap);
+	struct dvb_usb_device *d = adap_to_d(adap);
+	int ret = 0;
+	
+	dprintk("%s()\n", __func__);
+
+	dvbsky_gpio_ctrl(d, 0x04, 1);
+		
+	dvbsky_gpio_ctrl(d, 0x83, 0);
+	msleep(50);
+	dvbsky_gpio_ctrl(d, 0x83, 1);
+	msleep(20);
+	
+	adap->fe[0] = dvb_attach(dvbsky_m88ds3103_attach,
+				&dvbsky_usb_ds3103_config,
+				&d->i2c_adap);
+	if (!adap->fe[0]) {
+		printk(KERN_ERR "dvbsky_s960_attach fail.");
+		ret = -ENODEV;
+	}
+	
+	state->has_ci = 0;
+
+	return ret;
+}
+
+static struct dvbsky_m88ds3103_config dvbsky_usb_ds3103_ci_config = {
+	.demod_address = 0x68,
+	.ci_mode = 2,
+	.pin_ctrl = 0x82,
+	.ts_mode = 0,
+	.start_ctrl = dvbsky_sync_ctrl,
+	.set_voltage = dvbsky_usb_ci_set_voltage,
+};
+
+static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap)
+{
+	struct dvbsky_state *state = adap_to_priv(adap);
+	struct dvb_usb_device *d = adap_to_d(adap);
+	int ret = 0;
+	
+	dvbsky_gpio_ctrl(d, 0x04, 1);
+	
+	dvbsky_gpio_ctrl(d, 0x83, 0);
+	msleep(50);
+	dvbsky_gpio_ctrl(d, 0x83, 1);
+	msleep(20);
+	
+	adap->fe[0] = dvb_attach(dvbsky_m88ds3103_attach,
+				&dvbsky_usb_ds3103_ci_config,
+				&d->i2c_adap);
+	if (!adap->fe[0]) {
+		printk(KERN_ERR "dvbsky_s960c_attach fail.");
+		ret = -ENODEV;
+	}
+	
+	state->has_ci = 1;
+
+	return ret;
+}
+
+static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name)
+{
+	return WARM;
+}
+
+static int dvbsky_init(struct dvb_usb_device *d)
+{
+	struct dvbsky_state *state = d_to_priv(d);
+	int ret;
+
+	/* use default interface */
+	ret = usb_set_interface(d->udev, 0, 0);
+	if (ret)
+		return ret;
+
+	mutex_init(&state->stream_mutex);
+	
+	/* attach CI */
+	if (state->has_ci) {
+		dvbsky_gpio_ctrl(d, 0xc0, 1);
+		msleep(100);
+		dvbsky_gpio_ctrl(d, 0xc0, 0);
+		msleep(50);
+		state->ci_attached = 0;
+		ret = dvbsky_ci_init(d);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static void dvbsky_exit(struct dvb_usb_device *d)
+{
+	return dvbsky_ci_release(d);
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties dvbsky_s960c_props = {
+	.driver_name = KBUILD_MODNAME,
+	.owner = THIS_MODULE,
+	.adapter_nr = adapter_nr,
+	.size_of_priv = sizeof(struct dvbsky_state),
+
+	.generic_bulk_ctrl_endpoint = 0x01,
+	.generic_bulk_ctrl_endpoint_response = 0x81,
+
+	.i2c_algo         = &dvbsky_i2c_algo,
+	.frontend_attach  = dvbsky_s960c_attach,
+	.init             = dvbsky_init,
+	.get_rc_config    = dvbsky_get_rc_config,
+	.streaming_ctrl   = dvbsky_streaming_ctrl,
+	.identify_state	  = dvbsky_identify_state,
+	.exit             = dvbsky_exit,
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+		}
+	}
+};
+
+static struct dvb_usb_device_properties dvbsky_s960_props = {
+	.driver_name = KBUILD_MODNAME,
+	.owner = THIS_MODULE,
+	.adapter_nr = adapter_nr,
+	.size_of_priv = sizeof(struct dvbsky_state),
+
+	.generic_bulk_ctrl_endpoint = 0x01,
+	.generic_bulk_ctrl_endpoint_response = 0x81,
+
+	.i2c_algo         = &dvbsky_i2c_algo,
+	.frontend_attach  = dvbsky_s960_attach,
+	.init             = dvbsky_init,
+	.get_rc_config    = dvbsky_get_rc_config,
+	.streaming_ctrl   = dvbsky_streaming_ctrl,
+	.identify_state	  = dvbsky_identify_state,
+	.exit             = dvbsky_exit,
+	.read_mac_address = dvbsky_read_mac_addr,
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+		}
+	}
+};
+
+static const struct usb_device_id dvbsky_id_table[] = {
+	{ DVB_USB_DEVICE(0x0572, 0x960c,
+		&dvbsky_s960c_props, "DVBSky S960CI", RC_MAP_DVBSKY) },
+	{ DVB_USB_DEVICE(0x0572, 0x6831,
+		&dvbsky_s960_props, "DVBSky S960/S860", RC_MAP_DVBSKY) },
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, dvbsky_id_table);
+
+static struct usb_driver dvbsky_usb_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = dvbsky_id_table,
+	.probe = dvb_usbv2_probe,
+	.disconnect = dvb_usbv2_disconnect,
+	.suspend = dvb_usbv2_suspend,
+	.resume = dvb_usbv2_resume,
+	.reset_resume = dvb_usbv2_reset_resume,
+	.no_dynamic_id = 1,
+	.soft_unbind = 1,
+};
+
+module_usb_driver(dvbsky_usb_driver);
+
+MODULE_AUTHOR("Max nibble <nibble.max@gmail.com>");
+MODULE_DESCRIPTION("Driver for DVBSky USB2.0");
+MODULE_LICENSE("GPL");
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index a20ed97..73f8c92 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -119,6 +119,7 @@ void rc_map_init(void);
 #define RC_MAP_DM1105_NEC                "rc-dm1105-nec"
 #define RC_MAP_DNTV_LIVE_DVBT_PRO        "rc-dntv-live-dvbt-pro"
 #define RC_MAP_DNTV_LIVE_DVB_T           "rc-dntv-live-dvb-t"
+#define RC_MAP_DVBSKY                    "rc-dvbsky"
 #define RC_MAP_EMPTY                     "rc-empty"
 #define RC_MAP_EM_TERRATEC               "rc-em-terratec"
 #define RC_MAP_ENCORE_ENLTV2             "rc-encore-enltv2"
