linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V2 0/5] [media] NetUP Universal DVB PCIe card support
@ 2015-04-15 10:07 serjk
  2015-04-15 10:07 ` [PATCH V2 1/5] [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver serjk
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: serjk @ 2015-04-15 10:07 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab, aospan1, Kozlov Sergey

From: Kozlov Sergey <serjk@netup.ru>

Add support for NetUP Universal Dual DVB-CI PCIe board.
The board has:

    - two CI slots

    - Altera FPGA-based PCIe bridge

    - two independent multistandard DTV demodulators based on
      Sony CXD2841ER chip

    - two Sony Horus3a DVB-S/S2 tuner chips

    - two Sony Ascot2e DVB-T/T2/C/C2 tuner chips

    - two LNBH25 SEC controller chips

DVB-C2 is supported by hardware but not yet implemented in the driver.
Product webpages are
http://www.netup.tv/en-EN/netup-universal-dual-dvb-ci (official)
http://linuxtv.org/wiki/index.php/NetUP_Dual_Universal_CI (LinuxTV WIKI)

Kozlov Sergey (5):
  [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver
  [media] ascot2e: Sony Ascot2e DVB-C/T/T2 tuner driver
  [media] lnbh25: LNBH25 SEC controller driver
  [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver
  [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card
    driver

 MAINTAINERS                                        |   45 +
 drivers/media/dvb-frontends/Kconfig                |   29 +
 drivers/media/dvb-frontends/Makefile               |    4 +
 drivers/media/dvb-frontends/ascot2e.c              |  543 ++++
 drivers/media/dvb-frontends/ascot2e.h              |   58 +
 drivers/media/dvb-frontends/cxd2841er.c            | 2721 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2841er.h            |   65 +
 drivers/media/dvb-frontends/cxd2841er_priv.h       |   43 +
 drivers/media/dvb-frontends/horus3a.c              |  413 +++
 drivers/media/dvb-frontends/horus3a.h              |   58 +
 drivers/media/dvb-frontends/lnbh25.c               |  192 ++
 drivers/media/dvb-frontends/lnbh25.h               |   56 +
 drivers/media/pci/Kconfig                          |    1 +
 drivers/media/pci/Makefile                         |    3 +-
 drivers/media/pci/netup_unidvb/Kconfig             |   12 +
 drivers/media/pci/netup_unidvb/Makefile            |    9 +
 drivers/media/pci/netup_unidvb/netup_unidvb.h      |  133 +
 drivers/media/pci/netup_unidvb/netup_unidvb_ci.c   |  248 ++
 drivers/media/pci/netup_unidvb/netup_unidvb_core.c | 1002 +++++++
 drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c  |  381 +++
 drivers/media/pci/netup_unidvb/netup_unidvb_spi.c  |  252 ++
 21 files changed, 6267 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/dvb-frontends/ascot2e.c
 create mode 100644 drivers/media/dvb-frontends/ascot2e.h
 create mode 100644 drivers/media/dvb-frontends/cxd2841er.c
 create mode 100644 drivers/media/dvb-frontends/cxd2841er.h
 create mode 100644 drivers/media/dvb-frontends/cxd2841er_priv.h
 create mode 100644 drivers/media/dvb-frontends/horus3a.c
 create mode 100644 drivers/media/dvb-frontends/horus3a.h
 create mode 100644 drivers/media/dvb-frontends/lnbh25.c
 create mode 100644 drivers/media/dvb-frontends/lnbh25.h
 create mode 100644 drivers/media/pci/netup_unidvb/Kconfig
 create mode 100644 drivers/media/pci/netup_unidvb/Makefile
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb.h
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_core.c
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_spi.c

-- 
1.7.10.4


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH V2 1/5] [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver
  2015-04-15 10:07 [PATCH V2 0/5] [media] NetUP Universal DVB PCIe card support serjk
@ 2015-04-15 10:07 ` serjk
  2015-05-14 13:58   ` Mauro Carvalho Chehab
  2015-04-15 10:07 ` [PATCH V2 2/5] [media] ascot2e: Sony Ascot2e DVB-C/T/T2 " serjk
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: serjk @ 2015-04-15 10:07 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab, aospan1, Kozlov Sergey

From: Kozlov Sergey <serjk@netup.ru>

Add DVB-S/S2 frontend driver for Sony Horus3A (CXD2832AER) chip

Changes in version 2:
    - rename MAINTAINERS entry
    - fix coding style
    - use dynamic debug instead of module-specifig debug parameter
    - fix I2C bus error handling

Signed-off-by: Kozlov Sergey <serjk@netup.ru>
---
 MAINTAINERS                           |    9 +
 drivers/media/dvb-frontends/Kconfig   |    7 +
 drivers/media/dvb-frontends/Makefile  |    1 +
 drivers/media/dvb-frontends/horus3a.c |  413 +++++++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/horus3a.h |   58 +++++
 5 files changed, 488 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/horus3a.c
 create mode 100644 drivers/media/dvb-frontends/horus3a.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eaf9996..7ba61b2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6269,6 +6269,15 @@ W:	http://linuxtv.org
 S:	Maintained
 F:	drivers/media/radio/radio-maxiradio*
 
+MEDIA DRIVERS FOR HORUS3A
+M:	Sergey Kozlov <serjk@netup.ru>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+W:	http://netup.tv/
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	drivers/media/dvb-frontends/horus3a*
+
 MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
 M:	Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 P:	LinuxTV.org Project
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index bb76727..bd5ab69 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -798,6 +798,13 @@ config DVB_AF9033
 	depends on DVB_CORE && I2C
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 
+config DVB_HORUS3A
+	tristate "Sony Horus3A tuner"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.
+
 comment "Tools to develop new frontends"
 
 config DVB_DUMMY_FE
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index ba59df6..3aa05f3 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -116,3 +116,4 @@ obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
 obj-$(CONFIG_DVB_AF9033) += af9033.o
 obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
 obj-$(CONFIG_DVB_TC90522) += tc90522.o
+obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c
new file mode 100644
index 0000000..54cf112
--- /dev/null
+++ b/drivers/media/dvb-frontends/horus3a.c
@@ -0,0 +1,413 @@
+/*
+ * horus3a.h
+ *
+ * Sony Horus3A DVB-S/S2 tuner driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <linux/types.h>
+#include "horus3a.h"
+#include "dvb_frontend.h"
+
+enum horus3a_state {
+	STATE_UNKNOWN,
+	STATE_SLEEP,
+	STATE_ACTIVE
+};
+
+struct horus3a_priv {
+	u32			frequency;
+	u8			i2c_address;
+	struct i2c_adapter	*i2c;
+	enum horus3a_state	state;
+	void			*set_tuner_data;
+	int			(*set_tuner)(void *, int);
+};
+
+static void horus3a_i2c_debug(struct horus3a_priv *priv,
+			      u8 reg, u8 write, const u8 *data, u32 len)
+{
+	dev_dbg(&priv->i2c->dev, "horus3a: I2C %s reg 0x%02x size %d\n",
+		(write == 0 ? "read" : "write"), reg, len);
+#if defined(CONFIG_DYNAMIC_DEBUG)
+	dynamic_hex_dump("horus3a: I2C data: ",
+		DUMP_PREFIX_OFFSET, 16, 1, data, len, false);
+#endif
+}
+
+static int horus3a_write_regs(struct horus3a_priv *priv,
+			      u8 reg, const u8 *data, u32 len)
+{
+	int ret;
+	u8 buf[len+1];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = priv->i2c_address,
+			.flags = 0,
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
+
+	horus3a_i2c_debug(priv, reg, 1, data, len);
+
+	buf[0] = reg;
+	memcpy(&buf[1], data, len);
+
+	ret = i2c_transfer(priv->i2c, msg, 1);
+	if (ret >= 0 && ret != 1)
+		ret = -EREMOTEIO;
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: i2c wr failed=%d reg=%02x len=%d\n",
+			KBUILD_MODNAME, ret, reg, len);
+		return ret;
+	}
+	return 0;
+}
+
+static int horus3a_write_reg(struct horus3a_priv *priv, u8 reg, u8 val)
+{
+	return horus3a_write_regs(priv, reg, &val, 1);
+}
+
+static int horus3a_enter_power_save(struct horus3a_priv *priv)
+{
+	u8 data[2];
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state == STATE_SLEEP)
+		return 0;
+	/* IQ Generator disable */
+	horus3a_write_reg(priv, 0x2a, 0x79);
+	/* MDIV_EN = 0 */
+	horus3a_write_reg(priv, 0x29, 0x70);
+	/* VCO disable preparation */
+	horus3a_write_reg(priv, 0x28, 0x3e);
+	/* VCO buffer disable */
+	horus3a_write_reg(priv, 0x2a, 0x19);
+	/* VCO calibration disable */
+	horus3a_write_reg(priv, 0x1c, 0x00);
+	/* Power save setting (xtal is not stopped) */
+	data[0] = 0xC0;
+	/* LNA is Disabled */
+	data[1] = 0xA7;
+	/* 0x11 - 0x12 */
+	horus3a_write_regs(priv, 0x11, data, sizeof(data));
+	priv->state = STATE_SLEEP;
+	return 0;
+}
+
+static int horus3a_leave_power_save(struct horus3a_priv *priv)
+{
+	u8 data[2];
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state == STATE_ACTIVE)
+		return 0;
+	/* Leave power save */
+	data[0] = 0x00;
+	/* LNA is Disabled */
+	data[1] = 0xa7;
+	/* 0x11 - 0x12 */
+	horus3a_write_regs(priv, 0x11, data, sizeof(data));
+	/* VCO buffer enable */
+	horus3a_write_reg(priv, 0x2a, 0x79);
+	/* VCO calibration enable */
+	horus3a_write_reg(priv, 0x1c, 0xc0);
+	/* MDIV_EN = 1 */
+	horus3a_write_reg(priv, 0x29, 0x71);
+	usleep_range(5000, 7000);
+	priv->state = STATE_ACTIVE;
+	return 0;
+}
+
+static int horus3a_init(struct dvb_frontend *fe)
+{
+	struct horus3a_priv *priv = fe->tuner_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	return 0;
+}
+
+static int horus3a_release(struct dvb_frontend *fe)
+{
+	struct horus3a_priv *priv = fe->tuner_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static int horus3a_sleep(struct dvb_frontend *fe)
+{
+	struct horus3a_priv *priv = fe->tuner_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	horus3a_enter_power_save(priv);
+	return 0;
+}
+
+static int horus3a_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct horus3a_priv *priv = fe->tuner_priv;
+	u32 frequency = p->frequency;
+	u32 symbol_rate = p->symbol_rate/1000;
+	u8 mixdiv = 0;
+	u8 mdiv = 0;
+	u32 ms = 0;
+	u8 f_ctl = 0;
+	u8 g_ctl = 0;
+	u8 fc_lpf = 0;
+	u8 data[5];
+
+	dev_dbg(&priv->i2c->dev, "%s(): frequency %dkHz symbol_rate %dksps\n",
+		__func__, frequency, symbol_rate);
+	if (priv->set_tuner)
+		priv->set_tuner(priv->set_tuner_data, 0);
+	if (priv->state == STATE_SLEEP)
+		horus3a_leave_power_save(priv);
+
+	/* frequency should be X MHz (X : integer) */
+	frequency = DIV_ROUND_CLOSEST(frequency, 1000) * 1000;
+	if (frequency <= 1155000) {
+		mixdiv = 4;
+		mdiv = 1;
+	} else {
+		mixdiv = 2;
+		mdiv = 0;
+	}
+	/* Assumed that fREF == 1MHz (1000kHz) */
+	ms = DIV_ROUND_CLOSEST((frequency * mixdiv) / 2, 1000);
+	if (ms > 0x7FFF) { /* 15 bit */
+		dev_err(&priv->i2c->dev, "horus3a: invalid frequency %d\n",
+			frequency);
+		return -EINVAL;
+	}
+	if (frequency < 975000) {
+		/* F_CTL=11100 G_CTL=001 */
+		f_ctl = 0x1C;
+		g_ctl = 0x01;
+	} else if (frequency < 1050000) {
+		/* F_CTL=11000 G_CTL=010 */
+		f_ctl = 0x18;
+		g_ctl = 0x02;
+	} else if (frequency < 1150000) {
+		/* F_CTL=10100 G_CTL=010 */
+		f_ctl = 0x14;
+		g_ctl = 0x02;
+	} else if (frequency < 1250000) {
+		/* F_CTL=10000 G_CTL=011 */
+		f_ctl = 0x10;
+		g_ctl = 0x03;
+	} else if (frequency < 1350000) {
+		/* F_CTL=01100 G_CTL=100 */
+		f_ctl = 0x0C;
+		g_ctl = 0x04;
+	} else if (frequency < 1450000) {
+		/* F_CTL=01010 G_CTL=100 */
+		f_ctl = 0x0A;
+		g_ctl = 0x04;
+	} else if (frequency < 1600000) {
+		/* F_CTL=00111 G_CTL=101 */
+		f_ctl = 0x07;
+		g_ctl = 0x05;
+	} else if (frequency < 1800000) {
+		/* F_CTL=00100 G_CTL=010 */
+		f_ctl = 0x04;
+		g_ctl = 0x02;
+	} else if (frequency < 2000000) {
+		/* F_CTL=00010 G_CTL=001 */
+		f_ctl = 0x02;
+		g_ctl = 0x01;
+	} else {
+		/* F_CTL=00000 G_CTL=000 */
+		f_ctl = 0x00;
+		g_ctl = 0x00;
+	}
+	/* LPF cutoff frequency setting */
+	switch (p->delivery_system) {
+	case SYS_DVBS:
+		/*
+		 * rolloff = 0.35
+		 * SR <= 4.3
+		 * fc_lpf = 5
+		 * 4.3 < SR <= 10
+		 * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 =
+		 *	SR * 1.175 = SR * (47/40)
+		 * 10 < SR
+		 * fc_lpf = SR * (1 + rolloff) / 2 + 5 =
+		 *	SR * 0.675 + 5 = SR * (27/40) + 5
+		 * NOTE: The result should be round up.
+		 */
+		if (symbol_rate <= 4300)
+			fc_lpf = 5;
+		else if (symbol_rate <= 10000)
+			fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 47, 40000);
+		else
+			fc_lpf = (u8)(DIV_ROUND_UP(symbol_rate * 27,
+					40000) + 5);
+		/* 5 <= fc_lpf <= 36 */
+		if (fc_lpf > 36)
+			fc_lpf = 36;
+		break;
+	case SYS_DVBS2:
+		/*
+		 * rolloff = 0.2
+		 * SR <= 4.5
+		 * fc_lpf = 5
+		 * 4.5 < SR <= 10
+		 * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 =
+		 *	SR * 1.1 = SR * (11/10)
+		 * 10 < SR
+		 * fc_lpf = SR * (1 + rolloff) / 2 + 5 =
+		 *	SR * 0.6 + 5 = SR * (3/5) + 5
+		 * NOTE: The result should be round up.
+		 */
+		if (symbol_rate <= 4500)
+			fc_lpf = 5;
+		else if (symbol_rate <= 10000)
+			fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 11, 10000);
+		else
+			fc_lpf = (u8)(DIV_ROUND_UP(symbol_rate * 3,
+					5000) + 5);
+		/* 5 <= fc_lpf <= 36 is valid */
+		if (fc_lpf > 36)
+			fc_lpf = 36;
+		break;
+	default:
+		dev_err(&priv->i2c->dev,
+			"horus3a: invalid delivery system %d\n",
+			p->delivery_system);
+		return -EINVAL;
+	}
+	/* 0x00 - 0x04 */
+	data[0] = (u8)((ms >> 7) & 0xFF);
+	data[1] = (u8)((ms << 1) & 0xFF);
+	data[2] = 0x00;
+	data[3] = 0x00;
+	data[4] = (u8)(mdiv << 7);
+	horus3a_write_regs(priv, 0x00, data, sizeof(data));
+	/* Write G_CTL, F_CTL */
+	horus3a_write_reg(priv, 0x09, (u8)((g_ctl << 5) | f_ctl));
+	/* Write LPF cutoff frequency */
+	horus3a_write_reg(priv, 0x37, (u8)(0x80 | (fc_lpf << 1)));
+	/* Start Calibration */
+	horus3a_write_reg(priv, 0x05, 0x80);
+	/* IQ Generator enable */
+	horus3a_write_reg(priv, 0x2a, 0x7b);
+	/* tuner stabilization time */
+	msleep(60);
+	/* Store tuned frequency to the struct */
+	priv->frequency = ms * 2 * 1000 / mixdiv;
+	return 0;
+}
+
+static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct horus3a_priv *priv = fe->tuner_priv;
+	*frequency = priv->frequency;
+	return 0;
+}
+
+static struct dvb_tuner_ops horus3a_tuner_ops = {
+	.info = {
+		.name = "Sony Horus3a",
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_step = 1000,
+	},
+	.init = horus3a_init,
+	.release = horus3a_release,
+	.sleep = horus3a_sleep,
+	.set_params = horus3a_set_params,
+	.get_frequency = horus3a_get_frequency,
+};
+
+struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
+				    const struct horus3a_config *config,
+				    struct i2c_adapter *i2c)
+{
+	u8 buf[3], val;
+	struct horus3a_priv *priv = NULL;
+
+	priv = kzalloc(sizeof(struct horus3a_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+	priv->i2c_address = (config->i2c_address >> 1);
+	priv->i2c = i2c;
+	priv->set_tuner_data = config->set_tuner_priv;
+	priv->set_tuner = config->set_tuner_callback;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	/* wait 4ms after power on */
+	usleep_range(4000, 6000);
+	/* IQ Generator disable */
+	horus3a_write_reg(priv, 0x2a, 0x79);
+	/* REF_R = Xtal Frequency */
+	buf[0] = config->xtal_freq_mhz;
+	buf[1] = config->xtal_freq_mhz;
+	buf[2] = 0;
+	/* 0x6 - 0x8 */
+	horus3a_write_regs(priv, 0x6, buf, 3);
+	/* IQ Out = Single Ended */
+	horus3a_write_reg(priv, 0x0a, 0x40);
+	switch (config->xtal_freq_mhz) {
+	case 27:
+		val = 0x1f;
+		break;
+	case 24:
+		val = 0x10;
+		break;
+	case 16:
+		val = 0xc;
+		break;
+	default:
+		val = 0;
+		dev_warn(&priv->i2c->dev,
+			"horus3a: invalid xtal frequency %dMHz\n",
+			config->xtal_freq_mhz);
+		break;
+	}
+	val <<= 2;
+	horus3a_write_reg(priv, 0x0e, val);
+	horus3a_enter_power_save(priv);
+	usleep_range(3000, 5000);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	memcpy(&fe->ops.tuner_ops, &horus3a_tuner_ops,
+				sizeof(struct dvb_tuner_ops));
+	fe->tuner_priv = priv;
+	dev_info(&priv->i2c->dev,
+		"Sony HORUS3A attached on addr=%x at I2C adapter %p\n",
+		priv->i2c_address, priv->i2c);
+	return fe;
+}
+EXPORT_SYMBOL(horus3a_attach);
+
+MODULE_DESCRIPTION("Sony HORUS3A sattelite tuner driver");
+MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/horus3a.h b/drivers/media/dvb-frontends/horus3a.h
new file mode 100644
index 0000000..c38f946
--- /dev/null
+++ b/drivers/media/dvb-frontends/horus3a.h
@@ -0,0 +1,58 @@
+/*
+ * horus3a.h
+ *
+ * Sony Horus3A DVB-S/S2 tuner driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+  */
+
+#ifndef __DVB_HORUS3A_H__
+#define __DVB_HORUS3A_H__
+
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+/**
+ * struct horus3a_config - the configuration of Horus3A tuner driver
+ * @i2c_address:    I2C address of the tuner
+ * @xtal_freq_mhz:  Oscillator frequency, MHz
+ * @set_tuner_priv: Callback function private context
+ * @set_tuner_callback: Callback function that notifies the parent driver
+ *          which tuner is active now
+ */
+struct horus3a_config {
+	u8	i2c_address;
+	u8	xtal_freq_mhz;
+	void	*set_tuner_priv;
+	int	(*set_tuner_callback)(void *, int);
+};
+
+#if IS_ENABLED(CONFIG_DVB_HORUS3A)
+extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
+					const struct horus3a_config *config,
+					struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *horus3a_attach(
+					const struct cxd2820r_config *config,
+					struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 2/5] [media] ascot2e: Sony Ascot2e DVB-C/T/T2 tuner driver
  2015-04-15 10:07 [PATCH V2 0/5] [media] NetUP Universal DVB PCIe card support serjk
  2015-04-15 10:07 ` [PATCH V2 1/5] [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver serjk
@ 2015-04-15 10:07 ` serjk
  2015-05-14 14:07   ` Mauro Carvalho Chehab
  2015-04-15 10:07 ` [PATCH V2 3/5] [media] lnbh25: LNBH25 SEC controller driver serjk
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: serjk @ 2015-04-15 10:07 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab, aospan1, Kozlov Sergey

From: Kozlov Sergey <serjk@netup.ru>

Add DVB-T/T2/C frontend driver for Sony Ascot2e (CXD2861ER) chip.

Changes in version 2:
    - rename MAINTAINERS entry
    - fix coding style
    - use dynamic debug instead of module-specifig debug parameter
    - fix I2C bus error handling

Signed-off-by: Kozlov Sergey <serjk@netup.ru>
---
 MAINTAINERS                           |    9 +
 drivers/media/dvb-frontends/Kconfig   |    7 +
 drivers/media/dvb-frontends/Makefile  |    1 +
 drivers/media/dvb-frontends/ascot2e.c |  543 +++++++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/ascot2e.h |   58 ++++
 5 files changed, 618 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/ascot2e.c
 create mode 100644 drivers/media/dvb-frontends/ascot2e.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 7ba61b2..9950fbe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6269,6 +6269,15 @@ W:	http://linuxtv.org
 S:	Maintained
 F:	drivers/media/radio/radio-maxiradio*
 
+MEDIA DRIVERS FOR ASCOT2E
+M:	Sergey Kozlov <serjk@netup.ru>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org
+W:	http://netup.tv/
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	drivers/media/dvb-frontends/ascot2e*
+
 MEDIA DRIVERS FOR HORUS3A
 M:	Sergey Kozlov <serjk@netup.ru>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index bd5ab69..d178aca 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -805,6 +805,13 @@ config DVB_HORUS3A
 	help
 	  Say Y when you want to support this frontend.
 
+config DVB_ASCOT2E
+	tristate "Sony Ascot2E tuner"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.
+
 comment "Tools to develop new frontends"
 
 config DVB_DUMMY_FE
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 3aa05f3..0b19c10 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -117,3 +117,4 @@ obj-$(CONFIG_DVB_AF9033) += af9033.o
 obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
 obj-$(CONFIG_DVB_TC90522) += tc90522.o
 obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
+obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c
new file mode 100644
index 0000000..a87cfee
--- /dev/null
+++ b/drivers/media/dvb-frontends/ascot2e.c
@@ -0,0 +1,543 @@
+/*
+ * ascot2e.c
+ *
+ * Sony Ascot3E DVB-T/T2/C/C2 tuner driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+  */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <linux/types.h>
+#include "ascot2e.h"
+#include "dvb_frontend.h"
+
+enum ascot2e_state {
+	STATE_UNKNOWN,
+	STATE_SLEEP,
+	STATE_ACTIVE
+};
+
+struct ascot2e_priv {
+	u32			frequency;
+	u8			i2c_address;
+	struct i2c_adapter	*i2c;
+	enum ascot2e_state	state;
+	void			*set_tuner_data;
+	int			(*set_tuner)(void *, int);
+};
+
+enum ascot2e_tv_system_t {
+	ASCOT2E_DTV_DVBT_5,
+	ASCOT2E_DTV_DVBT_6,
+	ASCOT2E_DTV_DVBT_7,
+	ASCOT2E_DTV_DVBT_8,
+	ASCOT2E_DTV_DVBT2_1_7,
+	ASCOT2E_DTV_DVBT2_5,
+	ASCOT2E_DTV_DVBT2_6,
+	ASCOT2E_DTV_DVBT2_7,
+	ASCOT2E_DTV_DVBT2_8,
+	ASCOT2E_DTV_DVBC_6,
+	ASCOT2E_DTV_DVBC_8,
+	ASCOT2E_DTV_DVBC2_6,
+	ASCOT2E_DTV_DVBC2_8,
+	ASCOT2E_DTV_UNKNOWN
+};
+
+struct ascot2e_band_sett {
+	u8	if_out_sel;
+	u8	agc_sel;
+	u8	mix_oll;
+	u8	rf_gain;
+	u8	if_bpf_gc;
+	u8	fif_offset;
+	u8	bw_offset;
+	u8	bw;
+	u8	rf_oldet;
+	u8	if_bpf_f0;
+};
+
+#define ASCOT2E_AUTO		0xff
+#define ASCOT2E_OFFSET(ofs)	((u8)(ofs) & 0x1F)
+#define ASCOT2E_BW_6		0x00
+#define ASCOT2E_BW_7		0x01
+#define ASCOT2E_BW_8		0x02
+#define ASCOT2E_BW_1_7		0x03
+
+static struct ascot2e_band_sett ascot2e_sett[] = {
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	  ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6,  0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	  ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6,  0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	  ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7,  0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	  ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8,  0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	ASCOT2E_OFFSET(-10), ASCOT2E_OFFSET(-16), ASCOT2E_BW_1_7, 0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	  ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6,  0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	  ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6,  0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	  ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7,  0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+	  ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8,  0x0B, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03,
+	  ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-8), ASCOT2E_BW_6,  0x09, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03,
+	  ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(-1), ASCOT2E_BW_8,  0x09, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01,
+	  ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_6,  0x09, 0x00 },
+	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01,
+	  ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(2),  ASCOT2E_BW_8,  0x09, 0x00 }
+};
+
+static void ascot2e_i2c_debug(struct ascot2e_priv *priv,
+			      u8 reg, u8 write, const u8 *data, u32 len)
+{
+	dev_dbg(&priv->i2c->dev, "ascot2e: I2C %s reg 0x%02x size %d\n",
+		(write == 0 ? "read" : "write"), reg, len);
+#if defined(CONFIG_DYNAMIC_DEBUG)
+	dynamic_hex_dump("ascot2e: I2C data: ",
+		DUMP_PREFIX_OFFSET, 16, 1, data, len, false);
+#endif
+}
+
+static int ascot2e_write_regs(struct ascot2e_priv *priv,
+			      u8 reg, const u8 *data, u32 len)
+{
+	int ret;
+	u8 buf[len+1];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = priv->i2c_address,
+			.flags = 0,
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
+
+	ascot2e_i2c_debug(priv, reg, 1, data, len);
+	buf[0] = reg;
+	memcpy(&buf[1], data, len);
+
+	ret = i2c_transfer(priv->i2c, msg, 1);
+	if (ret >= 0 && ret != 1)
+		ret = -EREMOTEIO;
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: i2c wr failed=%d reg=%02x len=%d\n",
+			KBUILD_MODNAME, ret, reg, len);
+		return ret;
+	}
+	return 0;
+}
+
+static int ascot2e_write_reg(struct ascot2e_priv *priv, u8 reg, u8 val)
+{
+	return ascot2e_write_regs(priv, reg, &val, 1);
+}
+
+static int ascot2e_read_regs(struct ascot2e_priv *priv,
+			     u8 reg, u8 *val, u32 len)
+{
+	int ret;
+	struct i2c_msg msg[2] = {
+		{
+			.addr = priv->i2c_address,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		}, {
+			.addr = priv->i2c_address,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = val,
+		}
+	};
+
+	ret = i2c_transfer(priv->i2c, &msg[0], 1);
+	if (ret >= 0 && ret != 1)
+		ret = -EREMOTEIO;
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: I2C rw failed=%d addr=%02x reg=%02x\n",
+			KBUILD_MODNAME, ret, priv->i2c_address, reg);
+		return ret;
+	}
+
+	ret = i2c_transfer(priv->i2c, &msg[1], 1);
+	if (ret >= 0 && ret != 1)
+		ret = -EREMOTEIO;
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: i2c rd failed=%d addr=%02x reg=%02x\n",
+			KBUILD_MODNAME, ret, priv->i2c_address, reg);
+		return ret;
+	}
+	ascot2e_i2c_debug(priv, reg, 0, val, len);
+	return 0;
+}
+
+static int ascot2e_read_reg(struct ascot2e_priv *priv, u8 reg, u8 *val)
+{
+	return ascot2e_read_regs(priv, reg, val, 1);
+}
+
+static int ascot2e_set_reg_bits(struct ascot2e_priv *priv,
+				u8 reg, u8 data, u8 mask)
+{
+	int res;
+	u8 rdata;
+
+	if (mask != 0xff) {
+		res = ascot2e_read_reg(priv, reg, &rdata);
+		if (res != 0)
+			return res;
+		data = ((data & mask) | (rdata & (mask ^ 0xFF)));
+	}
+	return ascot2e_write_reg(priv, reg, data);
+}
+
+static int ascot2e_enter_power_save(struct ascot2e_priv *priv)
+{
+	u8 data[2];
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state == STATE_SLEEP)
+		return 0;
+	data[0] = 0x00;
+	data[1] = 0x04;
+	ascot2e_write_regs(priv, 0x14, data, 2);
+	ascot2e_write_reg(priv, 0x50, 0x01);
+	priv->state = STATE_SLEEP;
+	return 0;
+}
+
+static int ascot2e_leave_power_save(struct ascot2e_priv *priv)
+{
+	u8 data[2] = { 0xFB, 0x0F };
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state == STATE_ACTIVE)
+		return 0;
+	ascot2e_write_regs(priv, 0x14, data, 2);
+	ascot2e_write_reg(priv, 0x50, 0x00);
+	priv->state = STATE_ACTIVE;
+	return 0;
+}
+
+static int ascot2e_init(struct dvb_frontend *fe)
+{
+	struct ascot2e_priv *priv = fe->tuner_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	return ascot2e_leave_power_save(priv);
+}
+
+static int ascot2e_release(struct dvb_frontend *fe)
+{
+	struct ascot2e_priv *priv = fe->tuner_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static int ascot2e_sleep(struct dvb_frontend *fe)
+{
+	struct ascot2e_priv *priv = fe->tuner_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	ascot2e_enter_power_save(priv);
+	return 0;
+}
+
+static enum ascot2e_tv_system_t ascot2e_get_tv_system(struct dvb_frontend *fe)
+{
+	enum ascot2e_tv_system_t system = ASCOT2E_DTV_UNKNOWN;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct ascot2e_priv *priv = fe->tuner_priv;
+
+	if (p->delivery_system == SYS_DVBT) {
+		if (p->bandwidth_hz <= 5000000)
+			system = ASCOT2E_DTV_DVBT_5;
+		else if (p->bandwidth_hz <= 6000000)
+			system = ASCOT2E_DTV_DVBT_6;
+		else if (p->bandwidth_hz <= 7000000)
+			system = ASCOT2E_DTV_DVBT_7;
+		else if (p->bandwidth_hz <= 8000000)
+			system = ASCOT2E_DTV_DVBT_8;
+		else {
+			system = ASCOT2E_DTV_DVBT_8;
+			p->bandwidth_hz = 8000000;
+		}
+	} else if (p->delivery_system == SYS_DVBT2) {
+		if (p->bandwidth_hz <= 5000000)
+			system = ASCOT2E_DTV_DVBT2_5;
+		else if (p->bandwidth_hz <= 6000000)
+			system = ASCOT2E_DTV_DVBT2_6;
+		else if (p->bandwidth_hz <= 7000000)
+			system = ASCOT2E_DTV_DVBT2_7;
+		else if (p->bandwidth_hz <= 8000000)
+			system = ASCOT2E_DTV_DVBT2_8;
+		else {
+			system = ASCOT2E_DTV_DVBT2_8;
+			p->bandwidth_hz = 8000000;
+		}
+	} else if (p->delivery_system == SYS_DVBC_ANNEX_A) {
+		/* only 8MHz bandwidth supported now */
+		system = ASCOT2E_DTV_DVBC_8;
+		p->bandwidth_hz = 8000000;
+	}
+	dev_dbg(&priv->i2c->dev,
+		"%s(): ASCOT2E DTV system %d (delsys %d, bandwidth %d)\n",
+		__func__, (int)system, p->delivery_system, p->bandwidth_hz);
+	return system;
+}
+
+static int ascot2e_set_params(struct dvb_frontend *fe)
+{
+	u8 data[10];
+	u32 frequency;
+	enum ascot2e_tv_system_t tv_system;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct ascot2e_priv *priv = fe->tuner_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n",
+		__func__, p->frequency / 1000);
+	tv_system = ascot2e_get_tv_system(fe);
+
+	if (tv_system == ASCOT2E_DTV_UNKNOWN) {
+		dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n",
+			__func__);
+		return -EINVAL;
+	}
+	if (priv->set_tuner)
+		priv->set_tuner(priv->set_tuner_data, 1);
+	frequency = roundup(p->frequency / 1000, 25);
+	if (priv->state == STATE_SLEEP)
+		ascot2e_leave_power_save(priv);
+
+	/* IF_OUT_SEL / AGC_SEL setting */
+	data[0] = 0x00;
+	if (ascot2e_sett[tv_system].agc_sel != ASCOT2E_AUTO) {
+		/* AGC pin setting from parameter table */
+		data[0] |= (u8)(
+			(ascot2e_sett[tv_system].agc_sel & 0x03) << 3);
+	}
+	if (ascot2e_sett[tv_system].if_out_sel != ASCOT2E_AUTO) {
+		/* IFOUT pin setting from parameter table */
+		data[0] |= (u8)(
+			(ascot2e_sett[tv_system].if_out_sel & 0x01) << 2);
+	}
+	/* Set bit[4:2] only */
+	ascot2e_set_reg_bits(priv, 0x05, data[0], 0x1c);
+	/* 0x06 - 0x0F */
+	/* REF_R setting (0x06) */
+	if (tv_system == ASCOT2E_DTV_DVBC_6 ||
+			tv_system == ASCOT2E_DTV_DVBC_8) {
+		/* xtal, xtal*2 */
+		data[0] = (frequency > 500000) ? 16 : 32;
+	} else {
+		/* xtal/8, xtal/4 */
+		data[0] = (frequency > 500000) ? 2 : 4;
+	}
+	/* XOSC_SEL=100uA */
+	data[1] = 0x04;
+	/* KBW setting (0x08), KC0 setting (0x09), KC1 setting (0x0A) */
+	if (tv_system == ASCOT2E_DTV_DVBC_6 ||
+			tv_system == ASCOT2E_DTV_DVBC_8) {
+		data[2] = 18;
+		data[3] = 120;
+		data[4] = 20;
+	} else {
+		data[2] = 48;
+		data[3] = 10;
+		data[4] = 30;
+	}
+	/* ORDER/R2_RANGE/R2_BANK/C2_BANK setting (0x0B) */
+	if (tv_system == ASCOT2E_DTV_DVBC_6 ||
+			tv_system == ASCOT2E_DTV_DVBC_8)
+		data[5] = (frequency > 500000) ? 0x08 : 0x0c;
+	else
+		data[5] = (frequency > 500000) ? 0x30 : 0x38;
+	/* Set MIX_OLL (0x0C) value from parameter table */
+	data[6] = ascot2e_sett[tv_system].mix_oll;
+	/* Set RF_GAIN (0x0D) setting from parameter table */
+	if (ascot2e_sett[tv_system].rf_gain == ASCOT2E_AUTO) {
+		/* RF_GAIN auto control enable */
+		ascot2e_write_reg(priv, 0x4E, 0x01);
+		/* RF_GAIN Default value */
+		data[7] = 0x00;
+	} else {
+		/* RF_GAIN auto control disable */
+		ascot2e_write_reg(priv, 0x4E, 0x00);
+		data[7] = ascot2e_sett[tv_system].rf_gain;
+	}
+	/* Set IF_BPF_GC/FIF_OFFSET (0x0E) value from parameter table */
+	data[8] = (u8)((ascot2e_sett[tv_system].fif_offset << 3) |
+		(ascot2e_sett[tv_system].if_bpf_gc & 0x07));
+	/* Set BW_OFFSET (0x0F) value from parameter table */
+	data[9] = ascot2e_sett[tv_system].bw_offset;
+	ascot2e_write_regs(priv, 0x06, data, 10);
+	/*
+	 * 0x45 - 0x47
+	 * LNA optimization setting
+	 * RF_LNA_DIST1-5, RF_LNA_CM
+	 */
+	if (tv_system == ASCOT2E_DTV_DVBC_6 ||
+			tv_system == ASCOT2E_DTV_DVBC_8) {
+		data[0] = 0x0F;
+		data[1] = 0x00;
+		data[2] = 0x01;
+	} else {
+		data[0] = 0x0F;
+		data[1] = 0x00;
+		data[2] = 0x03;
+	}
+	ascot2e_write_regs(priv, 0x45, data, 3);
+	/* 0x49 - 0x4A
+	 Set RF_OLDET_ENX/RF_OLDET_OLL value from parameter table */
+	data[0] = ascot2e_sett[tv_system].rf_oldet;
+	/* Set IF_BPF_F0 value from parameter table */
+	data[1] = ascot2e_sett[tv_system].if_bpf_f0;
+	ascot2e_write_regs(priv, 0x49, data, 2);
+	/*
+	 * Tune now
+	 * RFAGC fast mode / RFAGC auto control enable
+	 * (set bit[7], bit[5:4] only)
+	 * vco_cal = 1, set MIX_OL_CPU_EN
+	 */
+	ascot2e_set_reg_bits(priv, 0x0c, 0x90, 0xb0);
+	/* Logic wake up, CPU wake up */
+	data[0] = 0xc4;
+	data[1] = 0x40;
+	ascot2e_write_regs(priv, 0x03, data, 2);
+	/* 0x10 - 0x14 */
+	data[0] = (u8)(frequency & 0xFF);         /* 0x10: FRF_L */
+	data[1] = (u8)((frequency >> 8) & 0xFF);  /* 0x11: FRF_M */
+	data[2] = (u8)((frequency >> 16) & 0x0F); /* 0x12: FRF_H (bit[3:0]) */
+	/* 0x12: BW (bit[5:4]) */
+	data[2] |= (u8)(ascot2e_sett[tv_system].bw << 4);
+	data[3] = 0xFF; /* 0x13: VCO calibration enable */
+	data[4] = 0xFF; /* 0x14: Analog block enable */
+	/* Tune (Burst write) */
+	ascot2e_write_regs(priv, 0x10, data, 5);
+	msleep(50);
+	/* CPU deep sleep */
+	ascot2e_write_reg(priv, 0x04, 0x00);
+	/* Logic sleep */
+	ascot2e_write_reg(priv, 0x03, 0xC0);
+	/* RFAGC normal mode (set bit[5:4] only) */
+	ascot2e_set_reg_bits(priv, 0x0C, 0x00, 0x30);
+	priv->frequency = frequency;
+	return 0;
+}
+
+static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct ascot2e_priv *priv = fe->tuner_priv;
+
+	*frequency = priv->frequency * 1000;
+	return 0;
+}
+
+static struct dvb_tuner_ops ascot2e_tuner_ops = {
+	.info = {
+		.name = "Sony ASCOT2E",
+		.frequency_min = 1000000,
+		.frequency_max = 1200000000,
+		.frequency_step = 25000,
+	},
+	.init = ascot2e_init,
+	.release = ascot2e_release,
+	.sleep = ascot2e_sleep,
+	.set_params = ascot2e_set_params,
+	.get_frequency = ascot2e_get_frequency,
+};
+
+struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
+				    const struct ascot2e_config *config,
+				    struct i2c_adapter *i2c)
+{
+	u8 data[4];
+	struct ascot2e_priv *priv = NULL;
+
+	priv = kzalloc(sizeof(struct ascot2e_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+	priv->i2c_address = (config->i2c_address >> 1);
+	priv->i2c = i2c;
+	priv->set_tuner_data = config->set_tuner_priv;
+	priv->set_tuner = config->set_tuner_callback;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	/* 16 MHz xTal frequency */
+	data[0] = 16;
+	/* VCO current setting */
+	data[1] = 0x06;
+	/* Logic wake up, CPU boot */
+	data[2] = 0xC4;
+	data[3] = 0x40;
+	ascot2e_write_regs(priv, 0x01, data, 4);
+	/* RFVGA optimization setting (RF_DIST0 - RF_DIST2) */
+	data[0] = 0x10;
+	data[1] = 0x3F;
+	data[2] = 0x25;
+	ascot2e_write_regs(priv, 0x22, data, 3);
+	/* PLL mode setting */
+	ascot2e_write_reg(priv, 0x28, 0x1e);
+	/* RSSI setting */
+	ascot2e_write_reg(priv, 0x59, 0x04);
+	/* TODO check CPU HW error state here */
+	msleep(80);
+	/* Xtal oscillator current control setting */
+	ascot2e_write_reg(priv, 0x4c, 0x01);
+	/* XOSC_SEL=100uA */
+	ascot2e_write_reg(priv, 0x07, 0x04);
+	/* CPU deep sleep */
+	ascot2e_write_reg(priv, 0x04, 0x00);
+	/* Logic sleep */
+	ascot2e_write_reg(priv, 0x03, 0xc0);
+	/* Power save setting */
+	data[0] = 0x00;
+	data[1] = 0x04;
+	ascot2e_write_regs(priv, 0x14, data, 2);
+	ascot2e_write_reg(priv, 0x50, 0x01);
+	priv->state = STATE_SLEEP;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	memcpy(&fe->ops.tuner_ops, &ascot2e_tuner_ops,
+				sizeof(struct dvb_tuner_ops));
+	fe->tuner_priv = priv;
+	dev_info(&priv->i2c->dev,
+		"Sony ASCOT2E attached on addr=%x at I2C adapter %p\n",
+		priv->i2c_address, priv->i2c);
+	return fe;
+}
+EXPORT_SYMBOL(ascot2e_attach);
+
+MODULE_DESCRIPTION("Sony ASCOT2E terr/cab tuner driver");
+MODULE_AUTHOR("info@netup.ru");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/ascot2e.h b/drivers/media/dvb-frontends/ascot2e.h
new file mode 100644
index 0000000..b9e12a3
--- /dev/null
+++ b/drivers/media/dvb-frontends/ascot2e.h
@@ -0,0 +1,58 @@
+/*
+ * ascot2e.h
+ *
+ * Sony Ascot3E DVB-T/T2/C/C2 tuner driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+  */
+
+#ifndef __DVB_ASCOT2E_H__
+#define __DVB_ASCOT2E_H__
+
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+/**
+ * struct ascot2e_config - the configuration of Ascot2E tuner driver
+ * @i2c_address:	I2C address of the tuner
+ * @xtal_freq_mhz:	Oscillator frequency, MHz
+ * @set_tuner_priv:	Callback function private context
+ * @set_tuner_callback:	Callback function that notifies the parent driver
+ *			which tuner is active now
+ */
+struct ascot2e_config {
+	u8	i2c_address;
+	u8	xtal_freq_mhz;
+	void	*set_tuner_priv;
+	int	(*set_tuner_callback)(void *, int);
+};
+
+#if IS_ENABLED(CONFIG_DVB_ASCOT2E)
+extern struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
+					const struct ascot2e_config *config,
+					struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
+					const struct ascot2e_config *config,
+					struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 3/5] [media] lnbh25: LNBH25 SEC controller driver
  2015-04-15 10:07 [PATCH V2 0/5] [media] NetUP Universal DVB PCIe card support serjk
  2015-04-15 10:07 ` [PATCH V2 1/5] [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver serjk
  2015-04-15 10:07 ` [PATCH V2 2/5] [media] ascot2e: Sony Ascot2e DVB-C/T/T2 " serjk
@ 2015-04-15 10:07 ` serjk
  2015-05-14 14:05   ` Mauro Carvalho Chehab
  2015-04-15 10:07 ` [PATCH V2 4/5] [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver serjk
  2015-04-15 10:07 ` [PATCH V2 5/5] [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver serjk
  4 siblings, 1 reply; 12+ messages in thread
From: serjk @ 2015-04-15 10:07 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab, aospan1, Kozlov Sergey

From: Kozlov Sergey <serjk@netup.ru>

Add DVB SEC frontend driver for STM LNBH25PQR chip.

Changes in version 2:
    - rename MAINTAINERS entry
    - fix coding style
    - use dynamic debug instead of module-specifig debug parameter
    - fix I2C bus error handling

Signed-off-by: Kozlov Sergey <serjk@netup.ru>
---
 MAINTAINERS                          |    9 ++
 drivers/media/dvb-frontends/Kconfig  |    8 ++
 drivers/media/dvb-frontends/Makefile |    1 +
 drivers/media/dvb-frontends/lnbh25.c |  192 ++++++++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/lnbh25.h |   56 ++++++++++
 5 files changed, 266 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/lnbh25.c
 create mode 100644 drivers/media/dvb-frontends/lnbh25.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 9950fbe..4695cdc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6287,6 +6287,15 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Supported
 F:	drivers/media/dvb-frontends/horus3a*
 
+MEDIA DRIVERS FOR LNBH25
+M:	Sergey Kozlov <serjk@netup.ru>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+W:	http://netup.tv/
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	drivers/media/dvb-frontends/lnbh25*
+
 MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
 M:	Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 P:	LinuxTV.org Project
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index d178aca..eec0405 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -695,6 +695,14 @@ comment "SEC control devices for DVB-S"
 
 source "drivers/media/dvb-frontends/drx39xyj/Kconfig"
 
+config DVB_LNBH25
+	tristate "LNBH25 SEC controller"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  An SEC control chip.
+	  Say Y when you want to support this chip.
+
 config DVB_LNBP21
 	tristate "LNBP21/LNBH24 SEC controllers"
 	depends on DVB_CORE && I2C
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 0b19c10..06a0d21 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
 obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
 obj-$(CONFIG_DVB_LG2160) += lg2160.o
 obj-$(CONFIG_DVB_CX24123) += cx24123.o
+obj-$(CONFIG_DVB_LNBH25) += lnbh25.o
 obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
 obj-$(CONFIG_DVB_LNBP22) += lnbp22.o
 obj-$(CONFIG_DVB_ISL6405) += isl6405.o
diff --git a/drivers/media/dvb-frontends/lnbh25.c b/drivers/media/dvb-frontends/lnbh25.c
new file mode 100644
index 0000000..e44a088
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbh25.c
@@ -0,0 +1,192 @@
+/*
+ * lnbh25.c
+ *
+ * Driver for LNB supply and control IC LNBH25
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "lnbh25.h"
+
+/**
+ * struct lnbh25_priv - LNBH25 driver private data
+ * @i2c:		pointer to the I2C adapter structure
+ * @i2c_address:	I2C address of LNBH25 SEC chip
+ * @config:		Registers configuration:
+ *			offset 0: 1st register address, always 0x02 (DATA1)
+ *			offset 1: DATA1 register value
+ *			offset 2: DATA2 register value
+ */
+struct lnbh25_priv {
+	struct i2c_adapter	*i2c;
+	u8			i2c_address;
+	u8			config[3];
+};
+
+#define LNBH25_STATUS_OFL	0x1
+#define LNBH25_STATUS_VMON	0x4
+#define LNBH25_VSEL_13		0x03
+#define LNBH25_VSEL_18		0x0a
+
+static int lnbh25_read_vmon(struct lnbh25_priv *priv)
+{
+	int i, ret;
+	u8 addr = 0x00;
+	u8 status[6];
+	struct i2c_msg msg[2] = {
+		{
+			.addr = priv->i2c_address,
+			.flags = 0,
+			.len = 1,
+			.buf = &addr
+		}, {
+			.addr = priv->i2c_address,
+			.flags = I2C_M_RD,
+			.len = sizeof(status),
+			.buf = status
+		}
+	};
+
+	for (i = 0; i < 2; i++) {
+		ret = i2c_transfer(priv->i2c, &msg[i], 1);
+		if (ret >= 0 && ret != 1)
+			ret = -EIO;
+		if (ret < 0) {
+			dev_dbg(&priv->i2c->dev,
+				"%s(): I2C transfer %d failed (%d)\n",
+				__func__, i, ret);
+			return ret;
+		}
+	}
+#if defined(CONFIG_DYNAMIC_DEBUG)
+	dynamic_hex_dump("lnbh25_read_vmon: ", DUMP_PREFIX_OFFSET,
+		16, 1, status, sizeof(status), false);
+#endif
+
+	if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) {
+		dev_err(&priv->i2c->dev,
+			"%s(): voltage in failure state, status reg 0x%x\n",
+			__func__, status[0]);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int lnbh25_set_voltage(struct dvb_frontend *fe,
+			      fe_sec_voltage_t voltage)
+{
+	int ret;
+	u8 data1_reg;
+	const char *vsel;
+	struct lnbh25_priv *priv = fe->sec_priv;
+	struct i2c_msg msg = {
+		.addr = priv->i2c_address,
+		.flags = 0,
+		.len = sizeof(priv->config),
+		.buf = priv->config
+	};
+
+	switch (voltage) {
+	case SEC_VOLTAGE_OFF:
+		data1_reg = 0x00;
+		vsel = "Off";
+		break;
+	case SEC_VOLTAGE_13:
+		data1_reg = LNBH25_VSEL_13;
+		vsel = "13V";
+		break;
+	case SEC_VOLTAGE_18:
+		data1_reg = LNBH25_VSEL_18;
+		vsel = "18V";
+		break;
+	default:
+		return -EINVAL;
+	}
+	priv->config[1] = data1_reg;
+	dev_dbg(&priv->i2c->dev,
+		"%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n",
+		__func__, vsel, priv->i2c_address,
+		priv->config[0], priv->config[1], priv->config[2]);
+	ret = i2c_transfer(priv->i2c, &msg, 1);
+	if (ret >= 0 && ret != 1)
+		ret = -EIO;
+	if (ret < 0) {
+		dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n",
+			__func__, ret);
+		return ret;
+	}
+	if (voltage != SEC_VOLTAGE_OFF) {
+		msleep(120);
+		ret = lnbh25_read_vmon(priv);
+	} else {
+		msleep(20);
+		ret = 0;
+	}
+	return ret;
+}
+
+static void lnbh25_release(struct dvb_frontend *fe)
+{
+	struct lnbh25_priv *priv = fe->sec_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF);
+	kfree(fe->sec_priv);
+	fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe,
+				   struct lnbh25_config *cfg,
+				   struct i2c_adapter *i2c)
+{
+	struct lnbh25_priv *priv;
+
+	dev_dbg(&i2c->dev, "%s()\n", __func__);
+	priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+	priv->i2c_address = (cfg->i2c_address >> 1);
+	priv->i2c = i2c;
+	priv->config[0] = 0x02;
+	priv->config[1] = 0x00;
+	priv->config[2] = cfg->data2_config;
+	fe->sec_priv = priv;
+	if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) {
+		dev_err(&i2c->dev,
+			"%s(): no LNBH25 found at I2C addr 0x%02x\n",
+			__func__, priv->i2c_address);
+		kfree(priv);
+		fe->sec_priv = NULL;
+		return NULL;
+	}
+
+	fe->ops.release_sec = lnbh25_release;
+	fe->ops.set_voltage = lnbh25_set_voltage;
+
+	dev_err(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n",
+		__func__, priv->i2c_address);
+	return fe;
+}
+EXPORT_SYMBOL(lnbh25_attach);
+
+MODULE_DESCRIPTION("ST LNBH25 driver");
+MODULE_AUTHOR("info@netup.ru");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/lnbh25.h b/drivers/media/dvb-frontends/lnbh25.h
new file mode 100644
index 0000000..7fc5123
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbh25.h
@@ -0,0 +1,56 @@
+/*
+ * lnbh25.c
+ *
+ * Driver for LNB supply and control IC LNBH25
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef LNBH25_H
+#define LNBH25_H
+
+#include <linux/i2c.h>
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+
+/* 22 kHz tone enabled. Tone output controlled by DSQIN pin */
+#define	LNBH25_TEN	0x01
+/* Low power mode activated (used only with 22 kHz tone output disabled) */
+#define LNBH25_LPM	0x02
+/* DSQIN input pin is set to receive external 22 kHz TTL signal source */
+#define LNBH25_EXTM	0x04
+
+struct lnbh25_config {
+	u8	i2c_address;
+	u8	data2_config;
+};
+
+#if IS_ENABLED(CONFIG_DVB_LNBH25)
+struct dvb_frontend *lnbh25_attach(
+	struct dvb_frontend *fe,
+	struct lnbh25_config *cfg,
+	struct i2c_adapter *i2c);
+#else
+static inline dvb_frontend *lnbh25_attach(
+	struct dvb_frontend *fe,
+	struct lnbh25_config *cfg,
+	struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 4/5] [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver
  2015-04-15 10:07 [PATCH V2 0/5] [media] NetUP Universal DVB PCIe card support serjk
                   ` (2 preceding siblings ...)
  2015-04-15 10:07 ` [PATCH V2 3/5] [media] lnbh25: LNBH25 SEC controller driver serjk
@ 2015-04-15 10:07 ` serjk
  2015-05-14 14:15   ` Mauro Carvalho Chehab
  2015-04-15 10:07 ` [PATCH V2 5/5] [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver serjk
  4 siblings, 1 reply; 12+ messages in thread
From: serjk @ 2015-04-15 10:07 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab, aospan1, Kozlov Sergey

From: Kozlov Sergey <serjk@netup.ru>

Add DVB-C/T/T2/S/S2 demodulator frontend driver Sony CXD2841ER chip.

Changes in version 2:
    - rename MAINTAINERS entry
    - fix coding style
    - use dynamic debug instead of module-specifig debug parameter
    - fix I2C bus error handling
    - provide signal statistics via DVBv5 API instead of v3

Signed-off-by: Kozlov Sergey <serjk@netup.ru>
---
 MAINTAINERS                                  |    9 +
 drivers/media/dvb-frontends/Kconfig          |    7 +
 drivers/media/dvb-frontends/Makefile         |    1 +
 drivers/media/dvb-frontends/cxd2841er.c      | 2721 ++++++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2841er.h      |   65 +
 drivers/media/dvb-frontends/cxd2841er_priv.h |   43 +
 6 files changed, 2846 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2841er.c
 create mode 100644 drivers/media/dvb-frontends/cxd2841er.h
 create mode 100644 drivers/media/dvb-frontends/cxd2841er_priv.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 4695cdc..968a044 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6278,6 +6278,15 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Supported
 F:	drivers/media/dvb-frontends/ascot2e*
 
+MEDIA DRIVERS FOR CXD2841ER
+M:	Sergey Kozlov <serjk@netup.ru>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+W:	http://netup.tv/
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	drivers/media/dvb-frontends/cxd2841er*
+
 MEDIA DRIVERS FOR HORUS3A
 M:	Sergey Kozlov <serjk@netup.ru>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index eec0405..1428fdd 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -441,6 +441,13 @@ config DVB_CXD2820R
 	help
 	  Say Y when you want to support this frontend.
 
+config DVB_CXD2841ER
+	tristate "Sony CXD2841ER"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.
+
 config DVB_RTL2830
 	tristate "Realtek RTL2830 DVB-T"
 	depends on DVB_CORE && I2C && I2C_MUX
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 06a0d21..f67cb8c 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o
 obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
 obj-$(CONFIG_DVB_STV0367) += stv0367.o
 obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
+obj-$(CONFIG_DVB_CXD2841ER) += cxd2841er.o
 obj-$(CONFIG_DVB_DRXK) += drxk.o
 obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
 obj-$(CONFIG_DVB_SI2165) += si2165.o
diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c
new file mode 100644
index 0000000..dc9498b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2841er.c
@@ -0,0 +1,2721 @@
+/*
+ * cxd2841er.c
+ *
+ * Sony CXD2441ER digital demodulator driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+  */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/math64.h>
+#include <linux/log2.h>
+#include <linux/dynamic_debug.h>
+
+#include "dvb_math.h"
+#include "dvb_frontend.h"
+#include "cxd2841er.h"
+#include "cxd2841er_priv.h"
+
+enum cxd2841er_state {
+	STATE_SHUTDOWN = 0,
+	STATE_SLEEP_S,
+	STATE_ACTIVE_S,
+	STATE_SLEEP_TC,
+	STATE_ACTIVE_TC
+};
+
+struct cxd2841er_priv {
+	struct dvb_frontend		frontend;
+	struct i2c_adapter		*i2c;
+	u8				i2c_addr_slvx;
+	u8				i2c_addr_slvt;
+	const struct cxd2841er_config	*config;
+	enum cxd2841er_state		state;
+	u8				system;
+};
+
+static const struct cxd2841er_cnr_data s_cn_data[] = {
+	{ 0x033e, 0 }, { 0x0339, 100 }, { 0x0333, 200 },
+	{ 0x032e, 300 }, { 0x0329, 400 }, { 0x0324, 500 },
+	{ 0x031e, 600 }, { 0x0319, 700 }, { 0x0314, 800 },
+	{ 0x030f, 900 }, { 0x030a, 1000 }, { 0x02ff, 1100 },
+	{ 0x02f4, 1200 }, { 0x02e9, 1300 }, { 0x02de, 1400 },
+	{ 0x02d4, 1500 }, { 0x02c9, 1600 }, { 0x02bf, 1700 },
+	{ 0x02b5, 1800 }, { 0x02ab, 1900 }, { 0x02a1, 2000 },
+	{ 0x029b, 2100 }, { 0x0295, 2200 }, { 0x0290, 2300 },
+	{ 0x028a, 2400 }, { 0x0284, 2500 }, { 0x027f, 2600 },
+	{ 0x0279, 2700 }, { 0x0274, 2800 }, { 0x026e, 2900 },
+	{ 0x0269, 3000 }, { 0x0262, 3100 }, { 0x025c, 3200 },
+	{ 0x0255, 3300 }, { 0x024f, 3400 }, { 0x0249, 3500 },
+	{ 0x0242, 3600 }, { 0x023c, 3700 }, { 0x0236, 3800 },
+	{ 0x0230, 3900 }, { 0x022a, 4000 }, { 0x0223, 4100 },
+	{ 0x021c, 4200 }, { 0x0215, 4300 }, { 0x020e, 4400 },
+	{ 0x0207, 4500 }, { 0x0201, 4600 }, { 0x01fa, 4700 },
+	{ 0x01f4, 4800 }, { 0x01ed, 4900 }, { 0x01e7, 5000 },
+	{ 0x01e0, 5100 }, { 0x01d9, 5200 }, { 0x01d2, 5300 },
+	{ 0x01cb, 5400 }, { 0x01c4, 5500 }, { 0x01be, 5600 },
+	{ 0x01b7, 5700 }, { 0x01b1, 5800 }, { 0x01aa, 5900 },
+	{ 0x01a4, 6000 }, { 0x019d, 6100 }, { 0x0196, 6200 },
+	{ 0x018f, 6300 }, { 0x0189, 6400 }, { 0x0182, 6500 },
+	{ 0x017c, 6600 }, { 0x0175, 6700 }, { 0x016f, 6800 },
+	{ 0x0169, 6900 }, { 0x0163, 7000 }, { 0x015c, 7100 },
+	{ 0x0156, 7200 }, { 0x0150, 7300 }, { 0x014a, 7400 },
+	{ 0x0144, 7500 }, { 0x013e, 7600 }, { 0x0138, 7700 },
+	{ 0x0132, 7800 }, { 0x012d, 7900 }, { 0x0127, 8000 },
+	{ 0x0121, 8100 }, { 0x011c, 8200 }, { 0x0116, 8300 },
+	{ 0x0111, 8400 }, { 0x010b, 8500 }, { 0x0106, 8600 },
+	{ 0x0101, 8700 }, { 0x00fc, 8800 }, { 0x00f7, 8900 },
+	{ 0x00f2, 9000 }, { 0x00ee, 9100 }, { 0x00ea, 9200 },
+	{ 0x00e6, 9300 }, { 0x00e2, 9400 }, { 0x00de, 9500 },
+	{ 0x00da, 9600 }, { 0x00d7, 9700 }, { 0x00d3, 9800 },
+	{ 0x00d0, 9900 }, { 0x00cc, 10000 }, { 0x00c7, 10100 },
+	{ 0x00c3, 10200 }, { 0x00bf, 10300 }, { 0x00ba, 10400 },
+	{ 0x00b6, 10500 }, { 0x00b2, 10600 }, { 0x00ae, 10700 },
+	{ 0x00aa, 10800 }, { 0x00a7, 10900 }, { 0x00a3, 11000 },
+	{ 0x009f, 11100 }, { 0x009c, 11200 }, { 0x0098, 11300 },
+	{ 0x0094, 11400 }, { 0x0091, 11500 }, { 0x008e, 11600 },
+	{ 0x008a, 11700 }, { 0x0087, 11800 }, { 0x0084, 11900 },
+	{ 0x0081, 12000 }, { 0x007e, 12100 }, { 0x007b, 12200 },
+	{ 0x0079, 12300 }, { 0x0076, 12400 }, { 0x0073, 12500 },
+	{ 0x0071, 12600 }, { 0x006e, 12700 }, { 0x006c, 12800 },
+	{ 0x0069, 12900 }, { 0x0067, 13000 }, { 0x0065, 13100 },
+	{ 0x0062, 13200 }, { 0x0060, 13300 }, { 0x005e, 13400 },
+	{ 0x005c, 13500 }, { 0x005a, 13600 }, { 0x0058, 13700 },
+	{ 0x0056, 13800 }, { 0x0054, 13900 }, { 0x0052, 14000 },
+	{ 0x0050, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 },
+	{ 0x004b, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 },
+	{ 0x0046, 14700 }, { 0x0044, 14800 }, { 0x0043, 14900 },
+	{ 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 },
+	{ 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 },
+	{ 0x0037, 15700 }, { 0x0036, 15800 }, { 0x0034, 15900 },
+	{ 0x0033, 16000 }, { 0x0032, 16100 }, { 0x0031, 16200 },
+	{ 0x0030, 16300 }, { 0x002f, 16400 }, { 0x002e, 16500 },
+	{ 0x002d, 16600 }, { 0x002c, 16700 }, { 0x002b, 16800 },
+	{ 0x002a, 16900 }, { 0x0029, 17000 }, { 0x0028, 17100 },
+	{ 0x0027, 17200 }, { 0x0026, 17300 }, { 0x0025, 17400 },
+	{ 0x0024, 17500 }, { 0x0023, 17600 }, { 0x0022, 17800 },
+	{ 0x0021, 17900 }, { 0x0020, 18000 }, { 0x001f, 18200 },
+	{ 0x001e, 18300 }, { 0x001d, 18500 }, { 0x001c, 18700 },
+	{ 0x001b, 18900 }, { 0x001a, 19000 }, { 0x0019, 19200 },
+	{ 0x0018, 19300 }, { 0x0017, 19500 }, { 0x0016, 19700 },
+	{ 0x0015, 19900 }, { 0x0014, 20000 },
+};
+
+static const struct cxd2841er_cnr_data s2_cn_data[] = {
+	{ 0x05af, 0 }, { 0x0597, 100 }, { 0x057e, 200 },
+	{ 0x0567, 300 }, { 0x0550, 400 }, { 0x0539, 500 },
+	{ 0x0522, 600 }, { 0x050c, 700 }, { 0x04f6, 800 },
+	{ 0x04e1, 900 }, { 0x04cc, 1000 }, { 0x04b6, 1100 },
+	{ 0x04a1, 1200 }, { 0x048c, 1300 }, { 0x0477, 1400 },
+	{ 0x0463, 1500 }, { 0x044f, 1600 }, { 0x043c, 1700 },
+	{ 0x0428, 1800 }, { 0x0416, 1900 }, { 0x0403, 2000 },
+	{ 0x03ef, 2100 }, { 0x03dc, 2200 }, { 0x03c9, 2300 },
+	{ 0x03b6, 2400 }, { 0x03a4, 2500 }, { 0x0392, 2600 },
+	{ 0x0381, 2700 }, { 0x036f, 2800 }, { 0x035f, 2900 },
+	{ 0x034e, 3000 }, { 0x033d, 3100 }, { 0x032d, 3200 },
+	{ 0x031d, 3300 }, { 0x030d, 3400 }, { 0x02fd, 3500 },
+	{ 0x02ee, 3600 }, { 0x02df, 3700 }, { 0x02d0, 3800 },
+	{ 0x02c2, 3900 }, { 0x02b4, 4000 }, { 0x02a6, 4100 },
+	{ 0x0299, 4200 }, { 0x028c, 4300 }, { 0x027f, 4400 },
+	{ 0x0272, 4500 }, { 0x0265, 4600 }, { 0x0259, 4700 },
+	{ 0x024d, 4800 }, { 0x0241, 4900 }, { 0x0236, 5000 },
+	{ 0x022b, 5100 }, { 0x0220, 5200 }, { 0x0215, 5300 },
+	{ 0x020a, 5400 }, { 0x0200, 5500 }, { 0x01f6, 5600 },
+	{ 0x01ec, 5700 }, { 0x01e2, 5800 }, { 0x01d8, 5900 },
+	{ 0x01cf, 6000 }, { 0x01c6, 6100 }, { 0x01bc, 6200 },
+	{ 0x01b3, 6300 }, { 0x01aa, 6400 }, { 0x01a2, 6500 },
+	{ 0x0199, 6600 }, { 0x0191, 6700 }, { 0x0189, 6800 },
+	{ 0x0181, 6900 }, { 0x0179, 7000 }, { 0x0171, 7100 },
+	{ 0x0169, 7200 }, { 0x0161, 7300 }, { 0x015a, 7400 },
+	{ 0x0153, 7500 }, { 0x014b, 7600 }, { 0x0144, 7700 },
+	{ 0x013d, 7800 }, { 0x0137, 7900 }, { 0x0130, 8000 },
+	{ 0x012a, 8100 }, { 0x0124, 8200 }, { 0x011e, 8300 },
+	{ 0x0118, 8400 }, { 0x0112, 8500 }, { 0x010c, 8600 },
+	{ 0x0107, 8700 }, { 0x0101, 8800 }, { 0x00fc, 8900 },
+	{ 0x00f7, 9000 }, { 0x00f2, 9100 }, { 0x00ec, 9200 },
+	{ 0x00e7, 9300 }, { 0x00e2, 9400 }, { 0x00dd, 9500 },
+	{ 0x00d8, 9600 }, { 0x00d4, 9700 }, { 0x00cf, 9800 },
+	{ 0x00ca, 9900 }, { 0x00c6, 10000 }, { 0x00c2, 10100 },
+	{ 0x00be, 10200 }, { 0x00b9, 10300 }, { 0x00b5, 10400 },
+	{ 0x00b1, 10500 }, { 0x00ae, 10600 }, { 0x00aa, 10700 },
+	{ 0x00a6, 10800 }, { 0x00a3, 10900 }, { 0x009f, 11000 },
+	{ 0x009b, 11100 }, { 0x0098, 11200 }, { 0x0095, 11300 },
+	{ 0x0091, 11400 }, { 0x008e, 11500 }, { 0x008b, 11600 },
+	{ 0x0088, 11700 }, { 0x0085, 11800 }, { 0x0082, 11900 },
+	{ 0x007f, 12000 }, { 0x007c, 12100 }, { 0x007a, 12200 },
+	{ 0x0077, 12300 }, { 0x0074, 12400 }, { 0x0072, 12500 },
+	{ 0x006f, 12600 }, { 0x006d, 12700 }, { 0x006b, 12800 },
+	{ 0x0068, 12900 }, { 0x0066, 13000 }, { 0x0064, 13100 },
+	{ 0x0061, 13200 }, { 0x005f, 13300 }, { 0x005d, 13400 },
+	{ 0x005b, 13500 }, { 0x0059, 13600 }, { 0x0057, 13700 },
+	{ 0x0055, 13800 }, { 0x0053, 13900 }, { 0x0051, 14000 },
+	{ 0x004f, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 },
+	{ 0x004a, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 },
+	{ 0x0045, 14700 }, { 0x0044, 14800 }, { 0x0042, 14900 },
+	{ 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 },
+	{ 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 },
+	{ 0x0038, 15600 }, { 0x0037, 15700 }, { 0x0036, 15800 },
+	{ 0x0034, 15900 }, { 0x0033, 16000 }, { 0x0032, 16100 },
+	{ 0x0031, 16200 }, { 0x0030, 16300 }, { 0x002f, 16400 },
+	{ 0x002e, 16500 }, { 0x002d, 16600 }, { 0x002c, 16700 },
+	{ 0x002b, 16800 }, { 0x002a, 16900 }, { 0x0029, 17000 },
+	{ 0x0028, 17100 }, { 0x0027, 17200 }, { 0x0026, 17300 },
+	{ 0x0025, 17400 }, { 0x0024, 17500 }, { 0x0023, 17600 },
+	{ 0x0022, 17800 }, { 0x0021, 17900 }, { 0x0020, 18000 },
+	{ 0x001f, 18200 }, { 0x001e, 18300 }, { 0x001d, 18500 },
+	{ 0x001c, 18700 }, { 0x001b, 18900 }, { 0x001a, 19000 },
+	{ 0x0019, 19200 }, { 0x0018, 19300 }, { 0x0017, 19500 },
+	{ 0x0016, 19700 }, { 0x0015, 19900 }, { 0x0014, 20000 },
+};
+
+#define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5))
+
+static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv,
+				u8 addr, u8 reg, u8 write,
+				const u8 *data, u32 len)
+{
+	dev_dbg(&priv->i2c->dev,
+		"cxd2841er: I2C %s addr %02x reg 0x%02x size %d\n",
+		(write == 0 ? "read" : "write"), addr, reg, len);
+#if defined(CONFIG_DYNAMIC_DEBUG)
+	dynamic_hex_dump("cxd2841er: I2C data: ",
+		DUMP_PREFIX_OFFSET, 16, 1, data, len, false);
+#endif
+}
+
+static int cxd2841er_write_regs(struct cxd2841er_priv *priv,
+				u8 addr, u8 reg, const u8 *data, u32 len)
+{
+	int ret;
+	u8 buf[len+1];
+	u8 i2c_addr = (addr == I2C_SLVX ?
+		priv->i2c_addr_slvx : priv->i2c_addr_slvt);
+	struct i2c_msg msg[1] = {
+		{
+			.addr = i2c_addr,
+			.flags = 0,
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
+
+	cxd2841er_i2c_debug(priv, i2c_addr, reg, 1, data, len);
+	buf[0] = reg;
+	memcpy(&buf[1], data, len);
+
+	ret = i2c_transfer(priv->i2c, msg, 1);
+	if (ret >= 0 && ret != 1)
+		ret = -EIO;
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: i2c wr failed=%d addr=%02x reg=%02x len=%d\n",
+			KBUILD_MODNAME, ret, i2c_addr, reg, len);
+		return ret;
+	}
+	return 0;
+}
+
+static int cxd2841er_write_reg(struct cxd2841er_priv *priv,
+			       u8 addr, u8 reg, u8 val)
+{
+	return cxd2841er_write_regs(priv, addr, reg, &val, 1);
+}
+
+static int cxd2841er_read_regs(struct cxd2841er_priv *priv,
+			       u8 addr, u8 reg, u8 *val, u32 len)
+{
+	int ret;
+	u8 i2c_addr = (addr == I2C_SLVX ?
+		priv->i2c_addr_slvx : priv->i2c_addr_slvt);
+	struct i2c_msg msg[2] = {
+		{
+			.addr = i2c_addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		}, {
+			.addr = i2c_addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = val,
+		}
+	};
+
+	ret = i2c_transfer(priv->i2c, &msg[0], 1);
+	if (ret >= 0 && ret != 1)
+		ret = -EIO;
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: i2c rw failed=%d addr=%02x reg=%02x\n",
+			KBUILD_MODNAME, ret, i2c_addr, reg);
+		return ret;
+	}
+	ret = i2c_transfer(priv->i2c, &msg[1], 1);
+	if (ret >= 0 && ret != 1)
+		ret = -EIO;
+	if (ret < 0) {
+		dev_warn(&priv->i2c->dev,
+			"%s: i2c rd failed=%d addr=%02x reg=%02x\n",
+			KBUILD_MODNAME, ret, i2c_addr, reg);
+		return ret;
+	}
+	return 0;
+}
+
+static int cxd2841er_read_reg(struct cxd2841er_priv *priv,
+			      u8 addr, u8 reg, u8 *val)
+{
+	return cxd2841er_read_regs(priv, addr, reg, val, 1);
+}
+
+static int cxd2841er_set_reg_bits(struct cxd2841er_priv *priv,
+				  u8 addr, u8 reg, u8 data, u8 mask)
+{
+	int res;
+	u8 rdata;
+
+	if (mask != 0xff) {
+		res = cxd2841er_read_reg(priv, addr, reg, &rdata);
+		if (res)
+			return res;
+		data = ((data & mask) | (rdata & (mask ^ 0xFF)));
+	}
+	return cxd2841er_write_reg(priv, addr, reg, data);
+}
+
+static int cxd2841er_dvbs2_set_symbol_rate(struct cxd2841er_priv *priv,
+					   u32 symbol_rate)
+{
+	u32 reg_value = 0;
+	u8 data[3] = {0, 0, 0};
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	/*
+	 * regValue = (symbolRateKSps * 2^14 / 1000) + 0.5
+	 *          = ((symbolRateKSps * 2^14) + 500) / 1000
+	 *          = ((symbolRateKSps * 16384) + 500) / 1000
+	 */
+	reg_value = DIV_ROUND_CLOSEST(symbol_rate * 16384, 1000);
+	if ((reg_value == 0) || (reg_value > 0xFFFFF)) {
+		dev_err(&priv->i2c->dev,
+			"%s(): reg_value is out of range\n", __func__);
+		return -EINVAL;
+	}
+	data[0] = (u8)((reg_value >> 16) & 0x0F);
+	data[1] = (u8)((reg_value >>  8) & 0xFF);
+	data[2] = (u8)(reg_value & 0xFF);
+	/* Set SLV-T Bank : 0xAE */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
+	cxd2841er_write_regs(priv, I2C_SLVT, 0x20, data, 3);
+	return 0;
+}
+
+static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
+					u8 system);
+
+static int cxd2841er_sleep_s_to_active_s(struct cxd2841er_priv *priv,
+					 u8 system, u32 symbol_rate)
+{
+	int ret;
+	u8 data[4] = { 0, 0, 0, 0 };
+
+	if (priv->state != STATE_SLEEP_S) {
+		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, (int)priv->state);
+		return -EINVAL;
+	}
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_set_ts_clock_mode(priv, SYS_DVBS);
+	/* Set demod mode */
+	if (system == SYS_DVBS) {
+		data[0] = 0x0A;
+	} else if (system == SYS_DVBS2) {
+		data[0] = 0x0B;
+	} else {
+		dev_err(&priv->i2c->dev, "%s(): invalid delsys %d\n",
+			__func__, system);
+		return -EINVAL;
+	}
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, data[0]);
+	/* DVB-S/S2 */
+	data[0] = 0x00;
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Enable S/S2 auto detection 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, data[0]);
+	/* Set SLV-T Bank : 0xAE */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
+	/* Enable S/S2 auto detection 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, data[0]);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Enable demod clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+	/* Enable ADC clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x01);
+	/* Enable ADC 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
+	/* Enable ADC 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x3f);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Enable ADC 3 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+	/* Set SLV-T Bank : 0xA3 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa3);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xac, 0x00);
+	data[0] = 0x07;
+	data[1] = 0x3B;
+	data[2] = 0x08;
+	data[3] = 0xC5;
+	/* Set SLV-T Bank : 0xAB */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xab);
+	cxd2841er_write_regs(priv, I2C_SLVT, 0x98, data, 4);
+	data[0] = 0x05;
+	data[1] = 0x80;
+	data[2] = 0x0A;
+	data[3] = 0x80;
+	cxd2841er_write_regs(priv, I2C_SLVT, 0xa8, data, 4);
+	data[0] = 0x0C;
+	data[1] = 0xCC;
+	cxd2841er_write_regs(priv, I2C_SLVT, 0xc3, data, 2);
+	/* Set demod parameter */
+	ret = cxd2841er_dvbs2_set_symbol_rate(priv, symbol_rate);
+	if (ret != 0)
+		return ret;
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* disable Hi-Z setting 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x10);
+	/* disable Hi-Z setting 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+	priv->state = STATE_ACTIVE_S;
+	return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t_band(struct cxd2841er_priv *priv,
+					       u32 bandwidth);
+
+static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
+						u32 bandwidth);
+
+static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
+					       u32 bandwidth);
+
+static int cxd2841er_retune_active(struct cxd2841er_priv *priv,
+				   struct dtv_frontend_properties *p)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_S &&
+			priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* disable TS output */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+	if (priv->state == STATE_ACTIVE_S)
+		return cxd2841er_dvbs2_set_symbol_rate(
+				priv, p->symbol_rate / 1000);
+	else if (priv->state == STATE_ACTIVE_TC) {
+		switch (priv->system) {
+		case SYS_DVBT:
+			return cxd2841er_sleep_tc_to_active_t_band(
+					priv, p->bandwidth_hz);
+		case SYS_DVBT2:
+			return cxd2841er_sleep_tc_to_active_t2_band(
+					priv, p->bandwidth_hz);
+		case SYS_DVBC_ANNEX_A:
+			return cxd2841er_sleep_tc_to_active_c_band(
+					priv, 8000000);
+		}
+	}
+	dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+		__func__, priv->system);
+	return -EINVAL;
+}
+
+static int cxd2841er_active_s_to_sleep_s(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_S) {
+		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* disable TS output */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+	/* enable Hi-Z setting 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1f);
+	/* enable Hi-Z setting 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* disable ADC 1 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* disable ADC clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x00);
+	/* disable ADC 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
+	/* disable ADC 3 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
+	/* SADC Bias ON */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
+	/* disable demod clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+	/* Set SLV-T Bank : 0xAE */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
+	/* disable S/S2 auto detection1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* disable S/S2 auto detection2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, 0x00);
+	priv->state = STATE_SLEEP_S;
+	return 0;
+}
+
+static int cxd2841er_sleep_s_to_shutdown(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_SLEEP_S) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Disable DSQOUT */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+	/* Disable DSQIN */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x9c, 0x00);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Disable oscillator */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01);
+	/* Set demod mode */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
+	priv->state = STATE_SHUTDOWN;
+	return 0;
+}
+
+static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_SLEEP_TC) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Disable oscillator */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01);
+	/* Set demod mode */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
+	priv->state = STATE_SHUTDOWN;
+	return 0;
+}
+
+static int cxd2841er_active_t_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* disable TS output */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+	/* enable Hi-Z setting 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+	/* enable Hi-Z setting 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* disable ADC 1 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Disable ADC 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+	/* Disable ADC 3 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+	/* Disable ADC clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+	/* Disable RF level monitor */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+	/* Disable demod clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+	priv->state = STATE_SLEEP_TC;
+	return 0;
+}
+
+static int cxd2841er_active_t2_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* disable TS output */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+	/* enable Hi-Z setting 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+	/* enable Hi-Z setting 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+	/* Cancel DVB-T2 setting */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x40);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x21);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xfb);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x00, 0x0f);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x00, 0x3f);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* disable ADC 1 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Disable ADC 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+	/* Disable ADC 3 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+	/* Disable ADC clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+	/* Disable RF level monitor */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+	/* Disable demod clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+	priv->state = STATE_SLEEP_TC;
+	return 0;
+}
+
+static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* disable TS output */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+	/* enable Hi-Z setting 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+	/* enable Hi-Z setting 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+	/* Cancel DVB-C setting */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa3, 0x00, 0x1f);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* disable ADC 1 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Disable ADC 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+	/* Disable ADC 3 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+	/* Disable ADC clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+	/* Disable RF level monitor */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+	/* Disable demod clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+	priv->state = STATE_SLEEP_TC;
+	return 0;
+}
+
+static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_SHUTDOWN) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Clear all demodulator registers */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00);
+	usleep_range(3000, 5000);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Set demod SW reset */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01);
+	/* Set X'tal clock to 20.5Mhz */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00);
+	/* Set demod mode */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a);
+	/* Clear demod SW reset */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00);
+	usleep_range(1000, 2000);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* enable DSQOUT */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1F);
+	/* enable DSQIN */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x9C, 0x40);
+	/* TADC Bias On */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+	/* SADC Bias On */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
+	priv->state = STATE_SLEEP_S;
+	return 0;
+}
+
+static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_SHUTDOWN) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Clear all demodulator registers */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00);
+	usleep_range(3000, 5000);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Set demod SW reset */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01);
+	/* Set X'tal clock to 20.5Mhz */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00);
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00);
+	/* Clear demod SW reset */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00);
+	usleep_range(1000, 2000);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* TADC Bias On */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+	/* SADC Bias On */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
+	priv->state = STATE_SLEEP_TC;
+	return 0;
+}
+
+static int cxd2841er_tune_done(struct cxd2841er_priv *priv)
+{
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0, 0);
+	/* SW Reset */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xfe, 0x01);
+	/* Enable TS output */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x00);
+	return 0;
+}
+
+/* Set TS parallel mode */
+static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
+					u8 system)
+{
+	u8 serial_ts, ts_rate_ctrl_off, ts_in_off;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	cxd2841er_read_reg(priv, I2C_SLVT, 0xc4, &serial_ts);
+	cxd2841er_read_reg(priv, I2C_SLVT, 0xd3, &ts_rate_ctrl_off);
+	cxd2841er_read_reg(priv, I2C_SLVT, 0xde, &ts_in_off);
+	dev_dbg(&priv->i2c->dev, "%s(): ser_ts=0x%02x rate_ctrl_off=0x%02x in_off=0x%02x\n",
+		__func__, serial_ts, ts_rate_ctrl_off, ts_in_off);
+
+	/*
+	 * slave    Bank    Addr    Bit    default    Name
+	 * <SLV-T>  00h     D9h     [7:0]  8'h08      OTSCKPERIOD
+	 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xd9, 0x08);
+	/*
+	 * Disable TS IF Clock
+	 * slave    Bank    Addr    Bit    default    Name
+	 * <SLV-T>  00h     32h     [0]    1'b1       OREG_CK_TSIF_EN
+	 */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x00, 0x01);
+	/*
+	 * slave    Bank    Addr    Bit    default    Name
+	 * <SLV-T>  00h     33h     [1:0]  2'b01      OREG_CKSEL_TSIF
+	 */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x33, 0x00, 0x03);
+	/*
+	 * Enable TS IF Clock
+	 * slave    Bank    Addr    Bit    default    Name
+	 * <SLV-T>  00h     32h     [0]    1'b1       OREG_CK_TSIF_EN
+	 */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x01, 0x01);
+
+	if (system == SYS_DVBT) {
+		/* Enable parity period for DVB-T */
+		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+		cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01);
+	} else if (system == SYS_DVBC_ANNEX_A) {
+		/* Enable parity period for DVB-C */
+		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+		cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01);
+	}
+}
+
+static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv)
+{
+	u8 chip_id;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0, 0);
+	cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id);
+	return chip_id;
+}
+
+static int cxd2841er_read_status_s(struct dvb_frontend *fe,
+				   fe_status_t *status)
+{
+	u8 reg = 0;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	*status = 0;
+	if (priv->state != STATE_ACTIVE_S) {
+		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0xA0 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+	/*
+	 *  slave     Bank      Addr      Bit      Signal name
+	 * <SLV-T>    A0h       11h       [2]      ITSLOCK
+	 */
+	cxd2841er_read_reg(priv, I2C_SLVT, 0x11, &reg);
+	if (reg & 0x04) {
+		*status = FE_HAS_SIGNAL
+			| FE_HAS_CARRIER
+			| FE_HAS_VITERBI
+			| FE_HAS_SYNC
+			| FE_HAS_LOCK;
+	}
+	dev_dbg(&priv->i2c->dev, "%s(): result 0x%x\n", __func__, *status);
+	return 0;
+}
+
+static int cxd2841er_read_status_t_t2(struct cxd2841er_priv *priv,
+				      u8 *sync, u8 *tslock, u8 *unlock)
+{
+	u8 data = 0;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_TC)
+		return -EINVAL;
+	if (priv->system == SYS_DVBT) {
+		/* Set SLV-T Bank : 0x10 */
+		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	} else {
+		/* Set SLV-T Bank : 0x20 */
+		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+	}
+	cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data);
+	if ((data & 0x07) == 0x07) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): invalid hardware state detected\n", __func__);
+		*sync = 0;
+		*tslock = 0;
+		*unlock = 0;
+	} else {
+		*sync = ((data & 0x07) == 0x6 ? 1 : 0);
+		*tslock = ((data & 0x20) ? 1 : 0);
+		*unlock = ((data & 0x10) ? 1 : 0);
+	}
+	return 0;
+}
+
+static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock)
+{
+	u8 data;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_TC)
+		return -EINVAL;
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+	cxd2841er_read_reg(priv, I2C_SLVT, 0x88, &data);
+	if ((data & 0x01) == 0) {
+		*tslock = 0;
+	} else {
+		cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data);
+		*tslock = ((data & 0x20) ? 1 : 0);
+	}
+	return 0;
+}
+
+static int cxd2841er_read_status_tc(struct dvb_frontend *fe,
+				    fe_status_t *status)
+{
+	int ret = 0;
+	u8 sync = 0;
+	u8 tslock = 0;
+	u8 unlock = 0;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	*status = 0;
+	if (priv->state == STATE_ACTIVE_TC) {
+		if (priv->system == SYS_DVBT || priv->system == SYS_DVBT2) {
+			ret = cxd2841er_read_status_t_t2(
+				priv, &sync, &tslock, &unlock);
+			if (ret)
+				goto done;
+			if (unlock)
+				goto done;
+			if (sync)
+				*status = FE_HAS_SIGNAL |
+					FE_HAS_CARRIER |
+					FE_HAS_VITERBI |
+					FE_HAS_SYNC;
+			if (tslock)
+				*status |= FE_HAS_LOCK;
+		} else if (priv->system == SYS_DVBC_ANNEX_A) {
+			ret = cxd2841er_read_status_c(priv, &tslock);
+			if (ret)
+				goto done;
+			if (tslock)
+				*status = FE_HAS_SIGNAL |
+					FE_HAS_CARRIER |
+					FE_HAS_VITERBI |
+					FE_HAS_SYNC |
+					FE_HAS_LOCK;
+		}
+	}
+done:
+	dev_dbg(&priv->i2c->dev, "%s(): status 0x%x\n", __func__, *status);
+	return ret;
+}
+
+static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv,
+					     int *offset)
+{
+	u8 data[3];
+	u8 is_hs_mode;
+	s32 cfrl_ctrlval;
+	s32 temp_div, temp_q, temp_r;
+
+	if (priv->state != STATE_ACTIVE_S) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	/*
+	 * Get High Sampling Rate mode
+	 *  slave     Bank      Addr      Bit      Signal name
+	 * <SLV-T>    A0h       10h       [0]      ITRL_LOCK
+	 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+	cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data[0]);
+	if (data[0] & 0x01) {
+		/*
+		 *  slave     Bank      Addr      Bit      Signal name
+		 * <SLV-T>    A0h       50h       [4]      IHSMODE
+		 */
+		cxd2841er_read_reg(priv, I2C_SLVT, 0x50, &data[0]);
+		is_hs_mode = (data[0] & 0x10 ? 1 : 0);
+	} else {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): unable to detect sampling rate mode\n",
+			__func__);
+		return -EINVAL;
+	}
+	/*
+	 *  slave     Bank      Addr      Bit      Signal name
+	 * <SLV-T>    A0h       45h       [4:0]    ICFRL_CTRLVAL[20:16]
+	 * <SLV-T>    A0h       46h       [7:0]    ICFRL_CTRLVAL[15:8]
+	 * <SLV-T>    A0h       47h       [7:0]    ICFRL_CTRLVAL[7:0]
+	 */
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x45, data, 3);
+	cfrl_ctrlval = sign_extend32((((u32)data[0] & 0x1F) << 16) |
+				(((u32)data[1] & 0xFF) <<  8) |
+				((u32)data[2] & 0xFF), 20);
+	temp_div = (is_hs_mode ? 1048576 : 1572864);
+	if (cfrl_ctrlval > 0) {
+		temp_q = div_s64_rem(97375LL * cfrl_ctrlval,
+			temp_div, &temp_r);
+	} else {
+		temp_q = div_s64_rem(-97375LL * cfrl_ctrlval,
+			temp_div, &temp_r);
+	}
+	if (temp_r >= temp_div / 2)
+		temp_q++;
+	if (cfrl_ctrlval > 0)
+		temp_q *= -1;
+	*offset = temp_q;
+	return 0;
+}
+
+int cxd2841er_get_carrier_offset_t2(
+	struct cxd2841er_priv *priv, u32 bandwidth, int *offset)
+{
+	u8 data[4];
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	if (priv->system != SYS_DVBT2) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+			__func__, priv->system);
+		return -EINVAL;
+	}
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data));
+	*offset = -1 * sign_extend32(
+		((u32)(data[0] & 0x0F) << 24) | ((u32)data[1] << 16) |
+		((u32)data[2] << 8) | (u32)data[3], 27);
+	switch (bandwidth) {
+	case 1712000:
+		*offset /= 582;
+		break;
+	case 5000000:
+	case 6000000:
+	case 7000000:
+	case 8000000:
+		*offset *= (bandwidth / 1000000);
+		*offset /= 940;
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
+			__func__, bandwidth);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int cxd2841er_get_carrier_offset_c(
+	struct cxd2841er_priv *priv, int *offset)
+{
+	u8 data[2];
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	if (priv->system != SYS_DVBC_ANNEX_A) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+			__func__, priv->system);
+		return -EINVAL;
+	}
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x15, data, sizeof(data));
+	*offset = div_s64(41000LL * sign_extend32((((u32)data[0] & 0x3f) << 8)
+						| (u32)data[1], 13), 16384);
+	return 0;
+}
+
+static int cxd2841er_read_packet_errors_t(
+		struct cxd2841er_priv *priv, u32 *penum)
+{
+	u8 data[3];
+
+	*penum = 0;
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data));
+	if (data[2] & 0x01)
+		*penum = ((u32)data[0] << 8) | (u32)data[1];
+	return 0;
+}
+
+static int cxd2841er_read_packet_errors_t2(
+		struct cxd2841er_priv *priv, u32 *penum)
+{
+	u8 data[3];
+
+	*penum = 0;
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24);
+	cxd2841er_read_regs(priv, I2C_SLVT, 0xfd, data, sizeof(data));
+	if (data[0] & 0x01)
+		*penum = ((u32)data[1] << 8) | (u32)data[2];
+	return 0;
+}
+
+static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv)
+{
+	u8 data[11];
+	u32 bit_error, bit_count;
+	u32 temp_q, temp_r;
+
+	/* Set SLV-T Bank : 0xA0 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+	/*
+	 *  slave     Bank      Addr      Bit      Signal name
+	 * <SLV-T>    A0h       35h       [0]      IFVBER_VALID
+	 * <SLV-T>    A0h       36h       [5:0]    IFVBER_BITERR[21:16]
+	 * <SLV-T>    A0h       37h       [7:0]    IFVBER_BITERR[15:8]
+	 * <SLV-T>    A0h       38h       [7:0]    IFVBER_BITERR[7:0]
+	 * <SLV-T>    A0h       3Dh       [5:0]    IFVBER_BITNUM[21:16]
+	 * <SLV-T>    A0h       3Eh       [7:0]    IFVBER_BITNUM[15:8]
+	 * <SLV-T>    A0h       3Fh       [7:0]    IFVBER_BITNUM[7:0]
+	 */
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11);
+	if (data[0] & 0x01) {
+		bit_error = ((u32)(data[1]  & 0x3F) << 16) |
+			((u32)(data[2]  & 0xFF) <<  8) |
+			(u32)(data[3]  & 0xFF);
+		bit_count = ((u32)(data[8]  & 0x3F) << 16) |
+			((u32)(data[9]  & 0xFF) <<  8) |
+			(u32)(data[10] & 0xFF);
+		/*
+		 *	BER = bitError / bitCount
+		 *	= (bitError * 10^7) / bitCount
+		 *	= ((bitError * 625 * 125 * 128) / bitCount
+		 */
+		if ((bit_count == 0) || (bit_error > bit_count)) {
+			dev_dbg(&priv->i2c->dev,
+				"%s(): invalid bit_error %d, bit_count %d\n",
+				__func__, bit_error, bit_count);
+			return 0;
+		}
+		temp_q = div_u64_rem(10000000ULL * bit_error,
+						bit_count, &temp_r);
+		if (bit_count != 1 && temp_r >= bit_count / 2)
+			temp_q++;
+		return temp_q;
+	}
+	dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__);
+	return 0;
+}
+
+
+static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv)
+{
+	u8 data[5];
+	u32 bit_error, period;
+	u32 temp_q, temp_r;
+	u32 result = 0;
+
+	/* Set SLV-T Bank : 0xB2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2);
+	/*
+	 *  slave     Bank      Addr      Bit      Signal name
+	 * <SLV-T>    B2h       30h       [0]      IFLBER_VALID
+	 * <SLV-T>    B2h       31h       [3:0]    IFLBER_BITERR[27:24]
+	 * <SLV-T>    B2h       32h       [7:0]    IFLBER_BITERR[23:16]
+	 * <SLV-T>    B2h       33h       [7:0]    IFLBER_BITERR[15:8]
+	 * <SLV-T>    B2h       34h       [7:0]    IFLBER_BITERR[7:0]
+	 */
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5);
+	if (data[0] & 0x01) {
+		/* Bit error count */
+		bit_error = ((u32)(data[1] & 0x0F) << 24) |
+			((u32)(data[2] & 0xFF) << 16) |
+			((u32)(data[3] & 0xFF) <<  8) |
+			(u32)(data[4] & 0xFF);
+
+		/* Set SLV-T Bank : 0xA0 */
+		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+		cxd2841er_read_reg(priv, I2C_SLVT, 0x7a, data);
+		/* Measurement period */
+		period = (u32)(1 << (data[0] & 0x0F));
+		if (period == 0) {
+			dev_dbg(&priv->i2c->dev,
+				"%s(): period is 0\n", __func__);
+			return 0;
+		}
+		if (bit_error > (period * 64800)) {
+			dev_dbg(&priv->i2c->dev,
+				"%s(): invalid bit_err 0x%x period 0x%x\n",
+				__func__, bit_error, period);
+			return 0;
+		}
+		/*
+		 * BER = bitError / (period * 64800)
+		 *	= (bitError * 10^7) / (period * 64800)
+		 *	= (bitError * 10^5) / (period * 648)
+		 *	= (bitError * 12500) / (period * 81)
+		 *	= (bitError * 10) * 1250 / (period * 81)
+		 */
+		temp_q = div_u64_rem(12500ULL * bit_error,
+					period * 81, &temp_r);
+		if (temp_r >= period * 40)
+			temp_q++;
+		result = temp_q;
+	} else {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): no data available\n", __func__);
+	}
+	return result;
+}
+
+static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber)
+{
+	u8 data[4];
+	u32 div, q, r;
+	u32 bit_err, period_exp, n_ldpc;
+
+	*ber = 0;
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): invalid state %d\n", __func__, priv->state);
+		return -EINVAL;
+	}
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x39, data, sizeof(data));
+	if (!(data[0] & 0x10)) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): no valid BER data\n", __func__);
+		return 0;
+	}
+	bit_err = ((u32)(data[0] & 0x0f) << 24) |
+		((u32)data[1] << 16) |
+		((u32)data[2] << 8) |
+		(u32)data[3];
+	cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
+	period_exp = data[0] & 0x0f;
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22);
+	cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data);
+	n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800);
+	if (bit_err > ((1U << period_exp) * n_ldpc)) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): invalid BER value\n", __func__);
+		return -EINVAL;
+	}
+	if (period_exp >= 4) {
+		div = (1U << (period_exp - 4)) * (n_ldpc / 200);
+		q = div_u64_rem(3125ULL * bit_err, div, &r);
+	} else {
+		div = (1U << period_exp) * (n_ldpc / 200);
+		q = div_u64_rem(50000ULL * bit_err, div, &r);
+	}
+	*ber = (r >= div / 2) ? q + 1 : q;
+	return 0;
+}
+
+static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber)
+{
+	u8 data[2];
+	u32 div, q, r;
+	u32 bit_err, period;
+
+	*ber = 0;
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): invalid state %d\n", __func__, priv->state);
+		return -EINVAL;
+	}
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	cxd2841er_read_reg(priv, I2C_SLVT, 0x39, data);
+	if (!(data[0] & 0x01)) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): no valid BER data\n", __func__);
+		return 0;
+	}
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data));
+	bit_err = ((u32)data[0] << 8) | (u32)data[1];
+	cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
+	period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07));
+	div = period / 128;
+	q = div_u64_rem(78125ULL * bit_err, div, &r);
+	*ber = (r >= div / 2) ? q + 1 : q;
+	return 0;
+}
+
+static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys)
+{
+	u8 data[3];
+	u32 res = 0, value;
+	int min_index, max_index, index;
+	static const struct cxd2841er_cnr_data *cn_data;
+
+	/* Set SLV-T Bank : 0xA1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa1);
+	/*
+	 *  slave     Bank      Addr      Bit     Signal name
+	 * <SLV-T>    A1h       10h       [0]     ICPM_QUICKRDY
+	 * <SLV-T>    A1h       11h       [4:0]   ICPM_QUICKCNDT[12:8]
+	 * <SLV-T>    A1h       12h       [7:0]   ICPM_QUICKCNDT[7:0]
+	 */
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x10, data, 3);
+	if (data[0] & 0x01) {
+		value = ((u32)(data[1] & 0x1F) << 8) | (u32)(data[2] & 0xFF);
+		min_index = 0;
+		if (delsys == SYS_DVBS) {
+			cn_data = s_cn_data;
+			max_index = sizeof(s_cn_data) /
+				sizeof(s_cn_data[0]) - 1;
+		} else {
+			cn_data = s2_cn_data;
+			max_index = sizeof(s2_cn_data) /
+				sizeof(s2_cn_data[0]) - 1;
+		}
+		if (value >= cn_data[min_index].value) {
+			res = cn_data[min_index].cnr_x1000;
+			goto done;
+		}
+		if (value <= cn_data[max_index].value) {
+			res = cn_data[max_index].cnr_x1000;
+			goto done;
+		}
+		while ((max_index - min_index) > 1) {
+			index = (max_index + min_index) / 2;
+			if (value == cn_data[index].value) {
+				res = cn_data[index].cnr_x1000;
+				goto done;
+			} else if (value > cn_data[index].value)
+				max_index = index;
+			else
+				min_index = index;
+			if ((max_index - min_index) <= 1) {
+				if (value == cn_data[max_index].value) {
+					res = cn_data[max_index].cnr_x1000;
+					goto done;
+				} else {
+					res = cn_data[min_index].cnr_x1000;
+					goto done;
+				}
+			}
+		}
+	} else {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): no data available\n", __func__);
+	}
+done:
+	return res;
+}
+
+static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr)
+{
+	u32 reg;
+	u8 data[2];
+
+	*snr = 0;
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): invalid state %d\n", __func__, priv->state);
+		return -EINVAL;
+	}
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+	reg = ((u32)data[0] << 8) | (u32)data[1];
+	if (reg == 0) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): reg value out of range\n", __func__);
+		return 0;
+	}
+	if (reg > 4996)
+		reg = 4996;
+	*snr = 10000 * ((intlog10(reg) - intlog10(5350 - reg)) >> 24) + 28500;
+	return 0;
+}
+
+int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr)
+{
+	u32 reg;
+	u8 data[2];
+
+	*snr = 0;
+	if (priv->state != STATE_ACTIVE_TC) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): invalid state %d\n", __func__, priv->state);
+		return -EINVAL;
+	}
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+	reg = ((u32)data[0] << 8) | (u32)data[1];
+	if (reg == 0) {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): reg value out of range\n", __func__);
+		return 0;
+	}
+	if (reg > 10876)
+		reg = 10876;
+	*snr = 10000 * ((intlog10(reg) -
+		intlog10(12600 - reg)) >> 24) + 32000;
+	return 0;
+}
+
+static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv,
+					u8 delsys)
+{
+	u8 data[2];
+
+	cxd2841er_write_reg(
+		priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20));
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2);
+	return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4;
+}
+
+static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv)
+{
+	u8 data[2];
+
+	/* Set SLV-T Bank : 0xA0 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+	/*
+	 *  slave     Bank      Addr      Bit       Signal name
+	 * <SLV-T>    A0h       1Fh       [4:0]     IRFAGC_GAIN[12:8]
+	 * <SLV-T>    A0h       20h       [7:0]     IRFAGC_GAIN[7:0]
+	 */
+	cxd2841er_read_regs(priv, I2C_SLVT, 0x1f, data, 2);
+	return ((((u16)data[0] & 0x1F) << 8) | (u16)(data[1] & 0xFF)) << 3;
+}
+
+static int cxd2841er_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	*ber = 0;
+	switch (p->delivery_system) {
+	case SYS_DVBS:
+		*ber = cxd2841er_mon_read_ber_s(priv);
+		break;
+	case SYS_DVBS2:
+		*ber = cxd2841er_mon_read_ber_s2(priv);
+		break;
+	case SYS_DVBT:
+		return cxd2841er_read_ber_t(priv, ber);
+	case SYS_DVBT2:
+		return cxd2841er_read_ber_t2(priv, ber);
+	default:
+		*ber = 0;
+		break;
+	}
+	return 0;
+}
+
+static int cxd2841er_read_signal_strength(struct dvb_frontend *fe,
+					  u16 *strength)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	switch (p->delivery_system) {
+	case SYS_DVBT:
+	case SYS_DVBT2:
+		*strength = 65535 - cxd2841er_read_agc_gain_t_t2(
+			priv, p->delivery_system);
+		break;
+	case SYS_DVBS:
+	case SYS_DVBS2:
+		*strength = 65535 - cxd2841er_read_agc_gain_s(priv);
+		break;
+	default:
+		*strength = 0;
+		break;
+	}
+	return 0;
+}
+
+static int cxd2841er_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	u32 tmp = 0;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	switch (p->delivery_system) {
+	case SYS_DVBT:
+		cxd2841er_read_snr_t(priv, &tmp);
+		break;
+	case SYS_DVBT2:
+		cxd2841er_read_snr_t2(priv, &tmp);
+		break;
+	case SYS_DVBS:
+	case SYS_DVBS2:
+		tmp = cxd2841er_dvbs_read_snr(priv, p->delivery_system);
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s(): unknown delivery system %d\n",
+			__func__, p->delivery_system);
+		break;
+	}
+	*snr = tmp & 0xffff;
+	return 0;
+}
+
+static int cxd2841er_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	switch (p->delivery_system) {
+	case SYS_DVBT:
+		cxd2841er_read_packet_errors_t(priv, ucblocks);
+		break;
+	case SYS_DVBT2:
+		cxd2841er_read_packet_errors_t2(priv, ucblocks);
+		break;
+	default:
+		*ucblocks = 0;
+		break;
+	}
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	return 0;
+}
+
+static int cxd2841er_dvbt2_set_profile(
+	struct cxd2841er_priv *priv, enum cxd2841er_dvbt2_profile_t profile)
+{
+	u8 tune_mode;
+	u8 seq_not2d_time;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	switch (profile) {
+	case DVBT2_PROFILE_BASE:
+		tune_mode = 0x01;
+		seq_not2d_time = 12;
+		break;
+	case DVBT2_PROFILE_LITE:
+		tune_mode = 0x05;
+		seq_not2d_time = 40;
+		break;
+	case DVBT2_PROFILE_ANY:
+		tune_mode = 0x00;
+		seq_not2d_time = 40;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0x2E */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2e);
+	/* Set profile and tune mode */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x10, tune_mode, 0x07);
+	/* Set SLV-T Bank : 0x2B */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
+	/* Set early unlock detection time */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x9d, seq_not2d_time);
+	return 0;
+}
+
+static int cxd2841er_dvbt2_set_plp_config(struct cxd2841er_priv *priv,
+					  u8 is_auto, u8 plp_id)
+{
+	if (is_auto) {
+		dev_dbg(&priv->i2c->dev,
+			"%s() using auto PLP selection\n", __func__);
+	} else {
+		dev_dbg(&priv->i2c->dev,
+			"%s() using manual PLP selection, ID %d\n",
+			__func__, plp_id);
+	}
+	/* Set SLV-T Bank : 0x23 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23);
+	if (!is_auto) {
+		/* Manual PLP selection mode. Set the data PLP Id. */
+		cxd2841er_write_reg(priv, I2C_SLVT, 0xaf, plp_id);
+	}
+	/* Auto PLP select (Scanning mode = 0x00). Data PLP select = 0x01. */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xad, (is_auto ? 0x00 : 0x01));
+	return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
+						u32 bandwidth)
+{
+	u32 iffreq;
+	u8 b20_9f[5];
+	u8 b10_a6[14];
+	u8 b10_b6[3];
+	u8 b10_d7;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	switch (bandwidth) {
+	case 8000000:
+		/* bank 0x20, reg 0x9f */
+		b20_9f[0] = 0x11;
+		b20_9f[1] = 0xf0;
+		b20_9f[2] = 0x00;
+		b20_9f[3] = 0x00;
+		b20_9f[4] = 0x00;
+		/* bank 0x10, reg 0xa6 */
+		b10_a6[0] = 0x26;
+		b10_a6[1] = 0xaf;
+		b10_a6[2] = 0x06;
+		b10_a6[3] = 0xcd;
+		b10_a6[4] = 0x13;
+		b10_a6[5] = 0xbb;
+		b10_a6[6] = 0x28;
+		b10_a6[7] = 0xba;
+		b10_a6[8] = 0x23;
+		b10_a6[9] = 0xa9;
+		b10_a6[10] = 0x1f;
+		b10_a6[11] = 0xa8;
+		b10_a6[12] = 0x2c;
+		b10_a6[13] = 0xc8;
+		iffreq = MAKE_IFFREQ_CONFIG(4.80);
+		b10_d7 = 0x00;
+		break;
+	case 7000000:
+		/* bank 0x20, reg 0x9f */
+		b20_9f[0] = 0x14;
+		b20_9f[1] = 0x80;
+		b20_9f[2] = 0x00;
+		b20_9f[3] = 0x00;
+		b20_9f[4] = 0x00;
+		/* bank 0x10, reg 0xa6 */
+		b10_a6[0] = 0x2C;
+		b10_a6[1] = 0xBD;
+		b10_a6[2] = 0x02;
+		b10_a6[3] = 0xCF;
+		b10_a6[4] = 0x04;
+		b10_a6[5] = 0xF8;
+		b10_a6[6] = 0x23;
+		b10_a6[7] = 0xA6;
+		b10_a6[8] = 0x29;
+		b10_a6[9] = 0xB0;
+		b10_a6[10] = 0x26;
+		b10_a6[11] = 0xA9;
+		b10_a6[12] = 0x21;
+		b10_a6[13] = 0xA5;
+		iffreq = MAKE_IFFREQ_CONFIG(4.2);
+		b10_d7 = 0x02;
+		break;
+	case 6000000:
+		/* bank 0x20, reg 0x9f */
+		b20_9f[0] = 0x17;
+		b20_9f[1] = 0xEA;
+		b20_9f[2] = 0xAA;
+		b20_9f[3] = 0xAA;
+		b20_9f[4] = 0xAA;
+		/* bank 0x10, reg 0xa6 */
+		b10_a6[0] = 0x27;
+		b10_a6[1] = 0xA7;
+		b10_a6[2] = 0x28;
+		b10_a6[3] = 0xB3;
+		b10_a6[4] = 0x02;
+		b10_a6[5] = 0xF0;
+		b10_a6[6] = 0x01;
+		b10_a6[7] = 0xE8;
+		b10_a6[8] = 0x00;
+		b10_a6[9] = 0xCF;
+		b10_a6[10] = 0x00;
+		b10_a6[11] = 0xE6;
+		b10_a6[12] = 0x23;
+		b10_a6[13] = 0xA4;
+		iffreq = MAKE_IFFREQ_CONFIG(3.6);
+		b10_d7 = 0x04;
+		break;
+	case 5000000:
+		/* bank 0x20, reg 0x9f */
+		b20_9f[0] = 0x1C;
+		b20_9f[1] = 0xB3;
+		b20_9f[2] = 0x33;
+		b20_9f[3] = 0x33;
+		b20_9f[4] = 0x33;
+		/* bank 0x10, reg 0xa6 */
+		b10_a6[0] = 0x27;
+		b10_a6[1] = 0xA7;
+		b10_a6[2] = 0x28;
+		b10_a6[3] = 0xB3;
+		b10_a6[4] = 0x02;
+		b10_a6[5] = 0xF0;
+		b10_a6[6] = 0x01;
+		b10_a6[7] = 0xE8;
+		b10_a6[8] = 0x00;
+		b10_a6[9] = 0xCF;
+		b10_a6[10] = 0x00;
+		b10_a6[11] = 0xE6;
+		b10_a6[12] = 0x23;
+		b10_a6[13] = 0xA4;
+		iffreq = MAKE_IFFREQ_CONFIG(3.6);
+		b10_d7 = 0x06;
+		break;
+	case 1712000:
+		/* bank 0x20, reg 0x9f */
+		b20_9f[0] = 0x58;
+		b20_9f[1] = 0xE2;
+		b20_9f[2] = 0xAF;
+		b20_9f[3] = 0xE0;
+		b20_9f[4] = 0xBC;
+		/* bank 0x10, reg 0xa6 */
+		b10_a6[0] = 0x25;
+		b10_a6[1] = 0xA0;
+		b10_a6[2] = 0x36;
+		b10_a6[3] = 0x8D;
+		b10_a6[4] = 0x2E;
+		b10_a6[5] = 0x94;
+		b10_a6[6] = 0x28;
+		b10_a6[7] = 0x9B;
+		b10_a6[8] = 0x32;
+		b10_a6[9] = 0x90;
+		b10_a6[10] = 0x2C;
+		b10_a6[11] = 0x9D;
+		b10_a6[12] = 0x29;
+		b10_a6[13] = 0x99;
+		iffreq = MAKE_IFFREQ_CONFIG(3.5);
+		b10_d7 = 0x03;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Set SLV-T Bank : 0x20 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x20);
+	cxd2841er_write_regs(priv, I2C_SLVT, 0x9f, b20_9f, sizeof(b20_9f));
+	/* Set SLV-T Bank : 0x27 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+	cxd2841er_set_reg_bits(
+		priv, I2C_SLVT, 0x7a,
+		(bandwidth == 1712000 ? 0x03 : 0x00), 0x0f);
+	/* Set SLV-T Bank : 0x10 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	/* Group delay equaliser sett. for ASCOT2E */
+	cxd2841er_write_regs(priv, I2C_SLVT, 0xa6, b10_a6, sizeof(b10_a6));
+	/* <IF freq setting> */
+	b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
+	b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
+	b10_b6[2] = (u8)(iffreq & 0xff);
+	cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
+	/* System bandwidth setting */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, b10_d7, 0x07);
+	return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t_band(
+		struct cxd2841er_priv *priv, u32 bandwidth)
+{
+	u8 b13_9c[2] = { 0x01, 0x14 };
+	u8 bw8mhz_b10_9f[] = { 0x11, 0xF0, 0x00, 0x00, 0x00 };
+	u8 bw8mhz_b10_a6[] = { 0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB,
+			0x28, 0xBA, 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8 };
+	u8 bw8mhz_b10_d9[] = { 0x01, 0xE0 };
+	u8 bw8mhz_b17_38[] = { 0x01, 0x02 };
+	u8 bw7mhz_b10_9f[] = { 0x14, 0x80, 0x00, 0x00, 0x00 };
+	u8 bw7mhz_b10_a6[] = { 0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8,
+			0x23, 0xA6, 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5 };
+	u8 bw7mhz_b10_d9[] = { 0x12, 0xF8 };
+	u8 bw7mhz_b17_38[] = { 0x00, 0x03 };
+	u8 bw6mhz_b10_9f[] = { 0x17, 0xEA, 0xAA, 0xAA, 0xAA };
+	u8 bw6mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0,
+			0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
+	u8 bw6mhz_b10_d9[] = { 0x1F, 0xDC };
+	u8 bw6mhz_b17_38[] = { 0x00, 0x03 };
+	u8 bw5mhz_b10_9f[] = { 0x1C, 0xB3, 0x33, 0x33, 0x33 };
+	u8 bw5mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0,
+			0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
+	u8 bw5mhz_b10_d9[] = { 0x26, 0x3C };
+	u8 bw5mhz_b17_38[] = { 0x00, 0x03 };
+	u8 b10_b6[3];
+	u8 d7val;
+	u32 iffreq;
+	u8 *b10_9f;
+	u8 *b10_a6;
+	u8 *b10_d9;
+	u8 *b17_38;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
+	/* Echo performance optimization setting */
+	cxd2841er_write_regs(priv, I2C_SLVT, 0x9c, b13_9c, sizeof(b13_9c));
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+	switch (bandwidth) {
+	case 8000000:
+		b10_9f = bw8mhz_b10_9f;
+		b10_a6 = bw8mhz_b10_a6;
+		b10_d9 = bw8mhz_b10_d9;
+		b17_38 = bw8mhz_b17_38;
+		d7val = 0;
+		iffreq = MAKE_IFFREQ_CONFIG(4.80);
+		break;
+	case 7000000:
+		b10_9f = bw7mhz_b10_9f;
+		b10_a6 = bw7mhz_b10_a6;
+		b10_d9 = bw7mhz_b10_d9;
+		b17_38 = bw7mhz_b17_38;
+		d7val = 2;
+		iffreq = MAKE_IFFREQ_CONFIG(4.20);
+		break;
+	case 6000000:
+		b10_9f = bw6mhz_b10_9f;
+		b10_a6 = bw6mhz_b10_a6;
+		b10_d9 = bw6mhz_b10_d9;
+		b17_38 = bw6mhz_b17_38;
+		d7val = 4;
+		iffreq = MAKE_IFFREQ_CONFIG(3.60);
+		break;
+	case 5000000:
+		b10_9f = bw5mhz_b10_9f;
+		b10_a6 = bw5mhz_b10_a6;
+		b10_d9 = bw5mhz_b10_d9;
+		b17_38 = bw5mhz_b17_38;
+		d7val = 6;
+		iffreq = MAKE_IFFREQ_CONFIG(3.60);
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
+			__func__, bandwidth);
+		return -EINVAL;
+	}
+	/* <IF freq setting> */
+	b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
+	b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
+	b10_b6[2] = (u8)(iffreq & 0xff);
+	cxd2841er_write_regs(
+		priv, I2C_SLVT, 0x9f, b10_9f, sizeof(bw8mhz_b10_9f));
+	cxd2841er_write_regs(
+		priv, I2C_SLVT, 0xa6, b10_a6, sizeof(bw8mhz_b10_a6));
+	cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, d7val, 0x7);
+	cxd2841er_write_regs(
+		priv, I2C_SLVT, 0xd9, b10_d9, sizeof(bw8mhz_b10_d9));
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+	cxd2841er_write_regs(
+		priv, I2C_SLVT, 0x38, b17_38, sizeof(bw8mhz_b17_38));
+	return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
+					       u32 bandwidth)
+{
+	u8 bw7_8mhz_b10_a6[] = {
+		0x2D, 0xC7, 0x04, 0xF4, 0x07, 0xC5, 0x2A, 0xB8,
+		0x27, 0x9E, 0x27, 0xA4, 0x29, 0xAB };
+	u8 bw6mhz_b10_a6[] = {
+		0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+		0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
+	u8 b10_b6[3];
+	u32 iffreq;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	switch (bandwidth) {
+	case 8000000:
+	case 7000000:
+		cxd2841er_write_regs(
+			priv, I2C_SLVT, 0xa6,
+			bw7_8mhz_b10_a6, sizeof(bw7_8mhz_b10_a6));
+		iffreq = MAKE_IFFREQ_CONFIG(4.9);
+		break;
+	case 6000000:
+		cxd2841er_write_regs(
+			priv, I2C_SLVT, 0xa6,
+			bw6mhz_b10_a6, sizeof(bw6mhz_b10_a6));
+		iffreq = MAKE_IFFREQ_CONFIG(3.7);
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n",
+			__func__, bandwidth);
+		return -EINVAL;
+	}
+	/* <IF freq setting> */
+	b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
+	b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
+	b10_b6[2] = (u8)(iffreq & 0xff);
+	cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
+	/* Set SLV-T Bank : 0x11 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+	switch (bandwidth) {
+	case 8000000:
+	case 7000000:
+		cxd2841er_set_reg_bits(
+			priv, I2C_SLVT, 0xa3, 0x00, 0x1f);
+		break;
+	case 6000000:
+		cxd2841er_set_reg_bits(
+			priv, I2C_SLVT, 0xa3, 0x14, 0x1f);
+		break;
+	}
+	/* Set SLV-T Bank : 0x40 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+	switch (bandwidth) {
+	case 8000000:
+		cxd2841er_set_reg_bits(
+			priv, I2C_SLVT, 0x26, 0x0b, 0x0f);
+		cxd2841er_write_reg(priv, I2C_SLVT,  0x27, 0x3e);
+		break;
+	case 7000000:
+		cxd2841er_set_reg_bits(
+			priv, I2C_SLVT, 0x26, 0x09, 0x0f);
+		cxd2841er_write_reg(priv, I2C_SLVT,  0x27, 0xd6);
+		break;
+	case 6000000:
+		cxd2841er_set_reg_bits(
+			priv, I2C_SLVT, 0x26, 0x08, 0x0f);
+		cxd2841er_write_reg(priv, I2C_SLVT,  0x27, 0x6e);
+		break;
+	}
+	return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
+					  u32 bandwidth)
+{
+	u8 data[2] = { 0x09, 0x54 };
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_set_ts_clock_mode(priv, SYS_DVBT);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Set demod mode */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Enable demod clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+	/* Disable RF level monitor */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+	/* Enable ADC clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+	/* Enable ADC 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
+	/* xtal freq 20.5MHz */
+	cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
+	/* Enable ADC 4 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+	/* Set SLV-T Bank : 0x10 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	/* IFAGC gain settings */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f);
+	/* Set SLV-T Bank : 0x11 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+	/* BBAGC TARGET level setting */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50);
+	/* Set SLV-T Bank : 0x10 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	/* ASCOT setting ON */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
+	/* Set SLV-T Bank : 0x18 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18);
+	/* Pre-RS BER moniter setting */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x36, 0x40, 0x07);
+	/* FEC Auto Recovery setting */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x01, 0x01);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* TSIF setting */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
+	cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Disable HiZ Setting 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
+	/* Disable HiZ Setting 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+	priv->state = STATE_ACTIVE_TC;
+	return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
+					   u32 bandwidth)
+{
+	u8 data[2] = { 0x09, 0x54 };
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Set demod mode */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x02);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Enable demod clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+	/* Disable RF level monitor */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+	/* Enable ADC clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+	/* Enable ADC 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
+	/* xtal freq 20.5MHz */
+	cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
+	/* Enable ADC 4 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+	/* Set SLV-T Bank : 0x10 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	/* IFAGC gain settings */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f);
+	/* Set SLV-T Bank : 0x11 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+	/* BBAGC TARGET level setting */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50);
+	/* Set SLV-T Bank : 0x10 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	/* ASCOT setting ON */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
+	/* Set SLV-T Bank : 0x20 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+	/* Acquisition optimization setting */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x8b, 0x3c);
+	/* Set SLV-T Bank : 0x2b */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* TSIF setting */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
+	/* DVB-T2 initial setting */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x10);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x34);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xd8);
+	/* Set SLV-T Bank : 0x2a */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x04, 0x0f);
+	/* Set SLV-T Bank : 0x2b */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f);
+
+	cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth);
+
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Disable HiZ Setting 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
+	/* Disable HiZ Setting 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+	priv->state = STATE_ACTIVE_TC;
+	return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv,
+					  u32 bandwidth)
+{
+	u8 data[2] = { 0x09, 0x54 };
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_set_ts_clock_mode(priv, SYS_DVBC_ANNEX_A);
+	/* Set SLV-X Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+	/* Set demod mode */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x04);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Enable demod clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+	/* Disable RF level monitor */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+	/* Enable ADC clock */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+	/* Enable ADC 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
+	/* xtal freq 20.5MHz */
+	cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
+	/* Enable ADC 4 */
+	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+	/* Set SLV-T Bank : 0x10 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	/* IFAGC gain settings */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x09, 0x1f);
+	/* Set SLV-T Bank : 0x11 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+	/* BBAGC TARGET level setting */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x48);
+	/* Set SLV-T Bank : 0x10 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	/* ASCOT setting ON */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
+	/* Set SLV-T Bank : 0x40 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+	/* Demod setting */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc3, 0x00, 0x04);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* TSIF setting */
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
+
+	cxd2841er_sleep_tc_to_active_c_band(priv, 8000000);
+	/* Set SLV-T Bank : 0x00 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	/* Disable HiZ Setting 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
+	/* Disable HiZ Setting 2 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+	priv->state = STATE_ACTIVE_TC;
+	return 0;
+}
+
+static int cxd2841er_get_frontend(struct dvb_frontend *fe)
+{
+	fe_status_t status = 0;
+	u16 strength = 0, snr = 0;
+	u32 errors = 0, ber = 0;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state == STATE_ACTIVE_S)
+		cxd2841er_read_status_s(fe, &status);
+	else if (priv->state == STATE_ACTIVE_TC)
+		cxd2841er_read_status_tc(fe, &status);
+
+	if (status & FE_HAS_LOCK) {
+		cxd2841er_read_signal_strength(fe, &strength);
+		p->strength.len = 1;
+		p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+		p->strength.stat[0].uvalue = strength;
+		cxd2841er_read_snr(fe, &snr);
+		p->cnr.len = 1;
+		p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		p->cnr.stat[0].svalue = snr;
+		cxd2841er_read_ucblocks(fe, &errors);
+		p->block_error.len = 1;
+		p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+		p->block_error.stat[0].uvalue = errors;
+		cxd2841er_read_ber(fe, &ber);
+		p->post_bit_error.len = 1;
+		p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+		p->post_bit_error.stat[0].uvalue = ber;
+	} else {
+		p->strength.len = 1;
+		p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		p->cnr.len = 1;
+		p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		p->block_error.len = 1;
+		p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		p->post_bit_error.len = 1;
+		p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+	}
+	return 0;
+}
+
+static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
+{
+	int ret = 0, i, timeout, carr_offset;
+	fe_status_t status;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	u32 symbol_rate = p->symbol_rate/1000;
+
+	dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n",
+		__func__,
+		(p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"),
+		 p->frequency, symbol_rate);
+	switch (priv->state) {
+	case STATE_SLEEP_S:
+		ret = cxd2841er_sleep_s_to_active_s(
+			priv, p->delivery_system, symbol_rate);
+		break;
+	case STATE_ACTIVE_S:
+		ret = cxd2841er_retune_active(priv, p);
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		ret = -EINVAL;
+		goto done;
+	}
+	if (ret) {
+		dev_dbg(&priv->i2c->dev, "%s(): tune failed\n", __func__);
+		goto done;
+	}
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+	cxd2841er_tune_done(priv);
+	timeout = ((3000000 + (symbol_rate - 1)) / symbol_rate) + 150;
+	for (i = 0; i < timeout / CXD2841ER_DVBS_POLLING_INVL; i++) {
+		usleep_range(CXD2841ER_DVBS_POLLING_INVL*1000,
+			(CXD2841ER_DVBS_POLLING_INVL + 2) * 1000);
+		cxd2841er_read_status_s(fe, &status);
+		if (status & FE_HAS_LOCK)
+			break;
+	}
+	if (status & FE_HAS_LOCK) {
+		if (cxd2841er_get_carrier_offset_s_s2(
+				priv, &carr_offset)) {
+			ret = -EINVAL;
+			goto done;
+		}
+		dev_dbg(&priv->i2c->dev, "%s(): carrier_offset=%d\n",
+			__func__, carr_offset);
+	}
+done:
+	return ret;
+}
+
+static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe)
+{
+	int ret = 0, timeout;
+	fe_status_t status;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (p->delivery_system == SYS_DVBT) {
+		priv->system = SYS_DVBT;
+		switch (priv->state) {
+		case STATE_SLEEP_TC:
+			ret = cxd2841er_sleep_tc_to_active_t(
+				priv, p->bandwidth_hz);
+			break;
+		case STATE_ACTIVE_TC:
+			ret = cxd2841er_retune_active(priv, p);
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+				__func__, priv->state);
+			ret = -EINVAL;
+		}
+	} else if (p->delivery_system == SYS_DVBT2) {
+		priv->system = SYS_DVBT2;
+		cxd2841er_dvbt2_set_plp_config(priv,
+			(int)(p->stream_id > 255), p->stream_id);
+		cxd2841er_dvbt2_set_profile(priv, DVBT2_PROFILE_BASE);
+		switch (priv->state) {
+		case STATE_SLEEP_TC:
+			ret = cxd2841er_sleep_tc_to_active_t2(priv,
+				p->bandwidth_hz);
+			break;
+		case STATE_ACTIVE_TC:
+			ret = cxd2841er_retune_active(priv, p);
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+				__func__, priv->state);
+			ret = -EINVAL;
+		}
+	} else if (p->delivery_system == SYS_DVBC_ANNEX_A ||
+			p->delivery_system == SYS_DVBC_ANNEX_C) {
+		priv->system = SYS_DVBC_ANNEX_A;
+		switch (priv->state) {
+		case STATE_SLEEP_TC:
+			ret = cxd2841er_sleep_tc_to_active_c(
+				priv, p->bandwidth_hz);
+			break;
+		case STATE_ACTIVE_TC:
+			ret = cxd2841er_retune_active(priv, p);
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+				__func__, priv->state);
+			ret = -EINVAL;
+		}
+	} else {
+		dev_dbg(&priv->i2c->dev,
+			"%s(): invalid delivery system %d\n",
+			__func__, p->delivery_system);
+		ret = -EINVAL;
+	}
+	if (ret)
+		goto done;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+	cxd2841er_tune_done(priv);
+	timeout = 2500;
+	while (timeout > 0) {
+		ret = cxd2841er_read_status_tc(fe, &status);
+		if (ret)
+			goto done;
+		if (status & FE_HAS_LOCK)
+			break;
+		msleep(20);
+		timeout -= 20;
+	}
+	if (timeout < 0)
+		dev_dbg(&priv->i2c->dev,
+			"%s(): LOCK wait timeout\n", __func__);
+done:
+	return ret;
+}
+
+static int cxd2841er_tune_s(struct dvb_frontend *fe,
+			    bool re_tune,
+			    unsigned int mode_flags,
+			    unsigned int *delay,
+			    fe_status_t *status)
+{
+	int ret, carrier_offset;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+	dev_dbg(&priv->i2c->dev, "%s() re_tune=%d\n", __func__, re_tune);
+	if (re_tune) {
+		ret = cxd2841er_set_frontend_s(fe);
+		if (ret)
+			return ret;
+		cxd2841er_read_status_s(fe, status);
+		if (*status & FE_HAS_LOCK) {
+			if (cxd2841er_get_carrier_offset_s_s2(
+					priv, &carrier_offset))
+				return -EINVAL;
+			p->frequency += carrier_offset;
+			ret = cxd2841er_set_frontend_s(fe);
+			if (ret)
+				return ret;
+		}
+	}
+	*delay = HZ / 5;
+	return cxd2841er_read_status_s(fe, status);
+}
+
+static int cxd2841er_tune_tc(struct dvb_frontend *fe,
+			     bool re_tune,
+			     unsigned int mode_flags,
+			     unsigned int *delay,
+			     fe_status_t *status)
+{
+	int ret, carrier_offset;
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+	dev_dbg(&priv->i2c->dev, "%s(): re_tune %d\n", __func__, re_tune);
+	if (re_tune) {
+		ret = cxd2841er_set_frontend_tc(fe);
+		if (ret)
+			return ret;
+		cxd2841er_read_status_tc(fe, status);
+		if (*status & FE_HAS_LOCK) {
+			switch (priv->system) {
+			case SYS_DVBT:
+			case SYS_DVBT2:
+				ret = cxd2841er_get_carrier_offset_t2(
+					priv, p->bandwidth_hz,
+					&carrier_offset);
+				break;
+			case SYS_DVBC_ANNEX_A:
+				ret = cxd2841er_get_carrier_offset_c(
+					priv, &carrier_offset);
+				break;
+			default:
+				dev_dbg(&priv->i2c->dev,
+					"%s(): invalid delivery system %d\n",
+					__func__, priv->system);
+				return -EINVAL;
+			}
+			if (ret)
+				return ret;
+			dev_dbg(&priv->i2c->dev, "%s(): carrier offset %d\n",
+				__func__, carrier_offset);
+			p->frequency += carrier_offset;
+			ret = cxd2841er_set_frontend_tc(fe);
+			if (ret)
+				return ret;
+		}
+	}
+	*delay = HZ / 5;
+	return cxd2841er_read_status_tc(fe, status);
+}
+
+static int cxd2841er_sleep_s(struct dvb_frontend *fe)
+{
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_active_s_to_sleep_s(fe->demodulator_priv);
+	cxd2841er_sleep_s_to_shutdown(fe->demodulator_priv);
+	return 0;
+}
+
+static int cxd2841er_sleep_tc(struct dvb_frontend *fe)
+{
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	if (priv->state == STATE_ACTIVE_TC) {
+		switch (priv->system) {
+		case SYS_DVBT:
+			cxd2841er_active_t_to_sleep_tc(priv);
+			break;
+		case SYS_DVBT2:
+			cxd2841er_active_t2_to_sleep_tc(priv);
+			break;
+		case SYS_DVBC_ANNEX_A:
+			cxd2841er_active_c_to_sleep_tc(priv);
+			break;
+		default:
+			dev_warn(&priv->i2c->dev,
+				"%s(): unknown delivery system %d\n",
+				__func__, priv->system);
+		}
+	}
+	if (priv->state != STATE_SLEEP_TC) {
+		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	cxd2841er_sleep_tc_to_shutdown(priv);
+	return 0;
+}
+
+static int cxd2841er_send_burst(struct dvb_frontend *fe,
+				fe_sec_mini_cmd_t burst)
+{
+	u8 data;
+	struct cxd2841er_priv *priv  = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s(): burst mode %s\n", __func__,
+		(burst == SEC_MINI_A ? "A" : "B"));
+	if (priv->state != STATE_SLEEP_S &&
+			priv->state != STATE_ACTIVE_S) {
+		dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	data = (burst == SEC_MINI_A ? 0 : 1);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x34, 0x01);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x35, data);
+	return 0;
+}
+
+static int cxd2841er_set_tone(struct dvb_frontend *fe,
+			      fe_sec_tone_mode_t tone)
+{
+	u8 data;
+	struct cxd2841er_priv *priv  = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s(): tone %s\n", __func__,
+		(tone == SEC_TONE_ON ? "On" : "Off"));
+	if (priv->state != STATE_SLEEP_S &&
+			priv->state != STATE_ACTIVE_S) {
+		dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	data = (tone == SEC_TONE_ON ? 1 : 0);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x36, data);
+	return 0;
+}
+
+static int cxd2841er_send_diseqc_msg(struct dvb_frontend *fe,
+				     struct dvb_diseqc_master_cmd *cmd)
+{
+	int i;
+	u8 data[12];
+	struct cxd2841er_priv *priv  = fe->demodulator_priv;
+
+	if (priv->state != STATE_SLEEP_S &&
+			priv->state != STATE_ACTIVE_S) {
+		dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+			__func__, priv->state);
+		return -EINVAL;
+	}
+	dev_dbg(&priv->i2c->dev,
+		"%s(): cmd->len %d\n", __func__, cmd->msg_len);
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
+	/* DiDEqC enable */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x33, 0x01);
+	/* cmd1 length & data */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x3d, cmd->msg_len);
+	memset(data, 0, sizeof(data));
+	for (i = 0; i < cmd->msg_len && i < sizeof(data); i++)
+		data[i] = cmd->msg[i];
+	cxd2841er_write_regs(priv, I2C_SLVT, 0x3e, data, sizeof(data));
+	/* repeat count for cmd1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x37, 1);
+	/* repeat count for cmd2: always 0 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x38, 0);
+	/* start transmit */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x32, 0x01);
+	/* wait for 1 sec timeout */
+	for (i = 0; i < 50; i++) {
+		cxd2841er_read_reg(priv, I2C_SLVT, 0x10, data);
+		if (!data[0]) {
+			dev_dbg(&priv->i2c->dev,
+				"%s(): DiSEqC cmd has been sent\n", __func__);
+			return 0;
+		}
+		msleep(20);
+	}
+	dev_dbg(&priv->i2c->dev,
+		"%s(): DiSEqC cmd transmit timeout\n", __func__);
+	return -ETIMEDOUT;
+}
+
+static void cxd2841er_release(struct dvb_frontend *fe)
+{
+	struct cxd2841er_priv *priv  = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	kfree(priv);
+}
+
+static int cxd2841er_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s(): enable=%d\n", __func__, enable);
+	cxd2841er_set_reg_bits(
+		priv, I2C_SLVX, 0x8, (enable ? 0x01 : 0x00), 0x01);
+	return 0;
+}
+
+static enum dvbfe_algo cxd2841er_get_algo(struct dvb_frontend *fe)
+{
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	return DVBFE_ALGO_HW;
+}
+
+static int cxd2841er_init_s(struct dvb_frontend *fe)
+{
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_shutdown_to_sleep_s(priv);
+	/* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xb9, 0x01, 0x01);
+	return 0;
+}
+
+static int cxd2841er_init_tc(struct dvb_frontend *fe)
+{
+	struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+	cxd2841er_shutdown_to_sleep_tc(priv);
+	/* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcb, 0x40, 0x40);
+	/* SONY_DEMOD_CONFIG_IFAGC_ADC_FS = 0 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0xcd, 0x50);
+	/* SONY_DEMOD_CONFIG_PARALLEL_SEL = 1 */
+	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x80);
+	return 0;
+}
+
+static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops;
+static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops;
+static struct dvb_frontend_ops cxd2841er_dvbc_ops;
+
+static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
+					     struct i2c_adapter *i2c,
+					     u8 system)
+{
+	u8 chip_id = 0;
+	const char *type;
+	struct cxd2841er_priv *priv = NULL;
+
+	/* allocate memory for the internal state */
+	priv = kzalloc(sizeof(struct cxd2841er_priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+	priv->i2c = i2c;
+	priv->config = cfg;
+	priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1;
+	priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1;
+	/* create dvb_frontend */
+	switch (system) {
+	case SYS_DVBS:
+		memcpy(&priv->frontend.ops,
+			&cxd2841er_dvbs_s2_ops,
+			sizeof(struct dvb_frontend_ops));
+		type = "S/S2";
+		break;
+	case SYS_DVBT:
+		memcpy(&priv->frontend.ops,
+			&cxd2841er_dvbt_t2_ops,
+			sizeof(struct dvb_frontend_ops));
+		type = "T/T2";
+		break;
+	case SYS_DVBC_ANNEX_A:
+		memcpy(&priv->frontend.ops,
+			&cxd2841er_dvbc_ops,
+			sizeof(struct dvb_frontend_ops));
+		type = "C/C2";
+		break;
+	default:
+		kfree(priv);
+		return NULL;
+	}
+	priv->frontend.demodulator_priv = priv;
+	dev_info(&priv->i2c->dev,
+		"%s(): attaching CXD2841ER DVB-%s frontend\n",
+		__func__, type);
+	dev_info(&priv->i2c->dev,
+		"%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n",
+		__func__, priv->i2c,
+		priv->i2c_addr_slvx, priv->i2c_addr_slvt);
+	chip_id = cxd2841er_chip_id(priv);
+	if (chip_id != CXD2841ER_CHIP_ID) {
+		dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n",
+			__func__, chip_id);
+		priv->frontend.demodulator_priv = NULL;
+		kfree(priv);
+		return NULL;
+	}
+	dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n",
+		__func__, chip_id);
+	return &priv->frontend;
+}
+
+struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
+					struct i2c_adapter *i2c)
+{
+	return cxd2841er_attach(cfg, i2c, SYS_DVBS);
+}
+EXPORT_SYMBOL(cxd2841er_attach_s);
+
+struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg,
+					struct i2c_adapter *i2c)
+{
+	return cxd2841er_attach(cfg, i2c, SYS_DVBT);
+}
+EXPORT_SYMBOL(cxd2841er_attach_t);
+
+struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg,
+					struct i2c_adapter *i2c)
+{
+	return cxd2841er_attach(cfg, i2c, SYS_DVBC_ANNEX_A);
+}
+EXPORT_SYMBOL(cxd2841er_attach_c);
+
+static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = {
+	.delsys = { SYS_DVBS, SYS_DVBS2 },
+	.info = {
+		.name		= "Sony CXD2841ER DVB-S/S2 demodulator",
+		.frequency_min	= 500000,
+		.frequency_max	= 2500000,
+		.frequency_stepsize	= 0,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.symbol_rate_tolerance = 500,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK,
+	},
+	.init = cxd2841er_init_s,
+	.sleep = cxd2841er_sleep_s,
+	.release = cxd2841er_release,
+	.set_frontend = cxd2841er_set_frontend_s,
+	.get_frontend = cxd2841er_get_frontend,
+	.read_status = cxd2841er_read_status_s,
+	.i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
+	.get_frontend_algo = cxd2841er_get_algo,
+	.set_tone = cxd2841er_set_tone,
+	.diseqc_send_burst = cxd2841er_send_burst,
+	.diseqc_send_master_cmd = cxd2841er_send_diseqc_msg,
+	.tune = cxd2841er_tune_s
+};
+
+static struct  dvb_frontend_ops cxd2841er_dvbt_t2_ops = {
+	.delsys = { SYS_DVBT, SYS_DVBT2 },
+	.info = {
+		.name	= "Sony CXD2841ER DVB-T/T2 demodulator",
+		.caps = FE_CAN_FEC_1_2 |
+			FE_CAN_FEC_2_3 |
+			FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 |
+			FE_CAN_FEC_7_8 |
+			FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK |
+			FE_CAN_QAM_16 |
+			FE_CAN_QAM_32 |
+			FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 |
+			FE_CAN_QAM_256 |
+			FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_HIERARCHY_AUTO |
+			FE_CAN_MUTE_TS |
+			FE_CAN_2G_MODULATION,
+		.frequency_min = 42000000,
+		.frequency_max = 1002000000
+	},
+	.init = cxd2841er_init_tc,
+	.sleep = cxd2841er_sleep_tc,
+	.release = cxd2841er_release,
+	.set_frontend = cxd2841er_set_frontend_tc,
+	.get_frontend = cxd2841er_get_frontend,
+	.read_status = cxd2841er_read_status_tc,
+	.tune = cxd2841er_tune_tc,
+	.i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
+	.get_frontend_algo = cxd2841er_get_algo
+};
+
+static struct  dvb_frontend_ops cxd2841er_dvbc_ops = {
+	.delsys = { SYS_DVBC_ANNEX_A },
+	.info = {
+		.name	= "Sony CXD2841ER DVB-C demodulator",
+		.caps = FE_CAN_FEC_1_2 |
+			FE_CAN_FEC_2_3 |
+			FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 |
+			FE_CAN_FEC_7_8 |
+			FE_CAN_FEC_AUTO |
+			FE_CAN_QAM_16 |
+			FE_CAN_QAM_32 |
+			FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 |
+			FE_CAN_QAM_256 |
+			FE_CAN_QAM_AUTO |
+			FE_CAN_INVERSION_AUTO,
+		.frequency_min = 42000000,
+		.frequency_max = 1002000000
+	},
+	.init = cxd2841er_init_tc,
+	.sleep = cxd2841er_sleep_tc,
+	.release = cxd2841er_release,
+	.set_frontend = cxd2841er_set_frontend_tc,
+	.get_frontend = cxd2841er_get_frontend,
+	.read_status = cxd2841er_read_status_tc,
+	.tune = cxd2841er_tune_tc,
+	.i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
+	.get_frontend_algo = cxd2841er_get_algo,
+};
+
+MODULE_DESCRIPTION("Sony CXD2841ER DVB-C/C2/T/T2/S/S2 demodulator driver");
+MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h
new file mode 100644
index 0000000..5322d37
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2841er.h
@@ -0,0 +1,65 @@
+/*
+ * cxd2841er.h
+ *
+ * Sony CXD2441ER digital demodulator driver public definitions
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+  */
+
+#ifndef CXD2841ER_H
+#define CXD2841ER_H
+
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+
+struct cxd2841er_config {
+	u8	i2c_addr;
+};
+
+#if IS_ENABLED(CONFIG_DVB_CXD2841ER)
+extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
+					       struct i2c_adapter *i2c);
+
+extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg,
+					       struct i2c_adapter *i2c);
+
+extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg,
+					       struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *cxd2841er_attach_s(
+					struct cxd2841er_config *cfg,
+					struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+
+static inline struct dvb_frontend *cxd2841er_attach_t(
+		struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+
+static inline struct dvb_frontend *cxd2841er_attach_c(
+		struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h
new file mode 100644
index 0000000..33e2f49
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2841er_priv.h
@@ -0,0 +1,43 @@
+/*
+ * cxd2841er_priv.h
+ *
+ * Sony CXD2441ER digital demodulator driver internal definitions
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CXD2841ER_PRIV_H
+#define CXD2841ER_PRIV_H
+
+#define I2C_SLVX			0
+#define I2C_SLVT			1
+
+#define CXD2841ER_CHIP_ID		0xa7
+
+#define CXD2841ER_DVBS_POLLING_INVL	10
+
+struct cxd2841er_cnr_data {
+	u32 value;
+	int cnr_x1000;
+};
+
+enum cxd2841er_dvbt2_profile_t {
+	DVBT2_PROFILE_ANY = 0,
+	DVBT2_PROFILE_BASE = 1,
+	DVBT2_PROFILE_LITE = 2
+};
+
+#endif
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V2 5/5] [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver
  2015-04-15 10:07 [PATCH V2 0/5] [media] NetUP Universal DVB PCIe card support serjk
                   ` (3 preceding siblings ...)
  2015-04-15 10:07 ` [PATCH V2 4/5] [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver serjk
@ 2015-04-15 10:07 ` serjk
  2015-05-14 14:20   ` Mauro Carvalho Chehab
  4 siblings, 1 reply; 12+ messages in thread
From: serjk @ 2015-04-15 10:07 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab, aospan1, Kozlov Sergey

From: Kozlov Sergey <serjk@netup.ru>

Add NetUP Dual Universal CI PCIe board driver.
The board has
    - two CI slots
    - two I2C adapters
    - SPI master bus for accessing flash memory containing
      FPGA firmware

Changes in version 2:
    - rename driver directory from netup to netup_unidvb
    - rename MAINTAINERS entry
    - fix coding style
    - use dynamic debug instead of module-specifig debug parameters
    - remove unneccecary msleep in the I2C driver code
    - use videobuf2 API instead of videobuf

Signed-off-by: Kozlov Sergey <serjk@netup.ru>
---
 MAINTAINERS                                        |    9 +
 drivers/media/pci/Kconfig                          |    1 +
 drivers/media/pci/Makefile                         |    3 +-
 drivers/media/pci/netup_unidvb/Kconfig             |   12 +
 drivers/media/pci/netup_unidvb/Makefile            |    9 +
 drivers/media/pci/netup_unidvb/netup_unidvb.h      |  133 +++
 drivers/media/pci/netup_unidvb/netup_unidvb_ci.c   |  248 +++++
 drivers/media/pci/netup_unidvb/netup_unidvb_core.c | 1002 ++++++++++++++++++++
 drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c  |  381 ++++++++
 drivers/media/pci/netup_unidvb/netup_unidvb_spi.c  |  252 +++++
 10 files changed, 2049 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/pci/netup_unidvb/Kconfig
 create mode 100644 drivers/media/pci/netup_unidvb/Makefile
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb.h
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_core.c
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
 create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_spi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 968a044..bf6f3cb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6305,6 +6305,15 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Supported
 F:	drivers/media/dvb-frontends/lnbh25*
 
+MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices
+M:	Sergey Kozlov <serjk@netup.ru>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+W:	http://netup.tv/
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	drivers/media/pci/netup_unidvb/*
+
 MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
 M:	Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 P:	LinuxTV.org Project
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index 218144a..4bc9188 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -47,6 +47,7 @@ source "drivers/media/pci/mantis/Kconfig"
 source "drivers/media/pci/ngene/Kconfig"
 source "drivers/media/pci/ddbridge/Kconfig"
 source "drivers/media/pci/smipcie/Kconfig"
+source "drivers/media/pci/netup_unidvb/Kconfig"
 endif
 
 endif #MEDIA_PCI_SUPPORT
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index 0baf0d2..515cf0f 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -12,7 +12,8 @@ obj-y        +=	ttpci/		\
 		ngene/		\
 		ddbridge/	\
 		saa7146/	\
-		smipcie/
+		smipcie/	\
+		netup_unidvb/
 
 obj-$(CONFIG_VIDEO_IVTV) += ivtv/
 obj-$(CONFIG_VIDEO_ZORAN) += zoran/
diff --git a/drivers/media/pci/netup_unidvb/Kconfig b/drivers/media/pci/netup_unidvb/Kconfig
new file mode 100644
index 0000000..f277b0b
--- /dev/null
+++ b/drivers/media/pci/netup_unidvb/Kconfig
@@ -0,0 +1,12 @@
+config DVB_NETUP_UNIDVB
+	tristate "NetUP Universal DVB card support"
+	depends on DVB_CORE && VIDEO_DEV && PCI && I2C && SPI_MASTER
+    select VIDEOBUF2_DVB
+    select VIDEOBUF2_VMALLOC
+	select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT
+	---help---
+	  Support for NetUP PCI express Universal DVB card.
+
diff --git a/drivers/media/pci/netup_unidvb/Makefile b/drivers/media/pci/netup_unidvb/Makefile
new file mode 100644
index 0000000..ee6ae05
--- /dev/null
+++ b/drivers/media/pci/netup_unidvb/Makefile
@@ -0,0 +1,9 @@
+netup-unidvb-objs += netup_unidvb_core.o
+netup-unidvb-objs += netup_unidvb_i2c.o
+netup-unidvb-objs += netup_unidvb_ci.o
+netup-unidvb-objs += netup_unidvb_spi.o
+
+obj-$(CONFIG_DVB_NETUP_UNIDVB) += netup-unidvb.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb.h b/drivers/media/pci/netup_unidvb/netup_unidvb.h
new file mode 100644
index 0000000..fa95110
--- /dev/null
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb.h
@@ -0,0 +1,133 @@
+/*
+ * netup_unidvb.h
+ *
+ * Data type definitions for NetUP Universal Dual DVB-CI
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dvb.h>
+#include <dvb_ca_en50221.h>
+
+#define NETUP_UNIDVB_NAME	"netup_unidvb"
+#define NETUP_UNIDVB_VERSION	"0.0.1"
+#define NETUP_VENDOR_ID		0x1b55
+#define NETUP_PCI_DEV_REVISION  0x2
+
+/* IRQ-related regisers */
+#define REG_ISR			0x4890
+#define REG_ISR_MASKED		0x4892
+#define REG_IMASK_SET		0x4894
+#define REG_IMASK_CLEAR		0x4896
+/* REG_ISR register bits */
+#define NETUP_UNIDVB_IRQ_SPI	(1 << 0)
+#define NETUP_UNIDVB_IRQ_I2C0	(1 << 1)
+#define NETUP_UNIDVB_IRQ_I2C1	(1 << 2)
+#define NETUP_UNIDVB_IRQ_FRA0	(1 << 4)
+#define NETUP_UNIDVB_IRQ_FRA1	(1 << 5)
+#define NETUP_UNIDVB_IRQ_FRB0	(1 << 6)
+#define NETUP_UNIDVB_IRQ_FRB1	(1 << 7)
+#define NETUP_UNIDVB_IRQ_DMA1	(1 << 8)
+#define NETUP_UNIDVB_IRQ_DMA2	(1 << 9)
+#define NETUP_UNIDVB_IRQ_CI	(1 << 10)
+#define NETUP_UNIDVB_IRQ_CAM0	(1 << 11)
+#define NETUP_UNIDVB_IRQ_CAM1	(1 << 12)
+
+struct netup_dma {
+	u8			num;
+	spinlock_t		lock;
+	struct netup_unidvb_dev	*ndev;
+	struct netup_dma_regs	*regs;
+	u32			ring_buffer_size;
+	u8			*addr_virt;
+	dma_addr_t		addr_phys;
+	u64			addr_last;
+	u32			high_addr;
+	u32			data_offset;
+	u32			data_size;
+	struct list_head	free_buffers;
+	struct work_struct	work;
+	struct timer_list	timeout;
+};
+
+enum netup_i2c_state {
+	STATE_DONE,
+	STATE_WAIT,
+	STATE_WANT_READ,
+	STATE_WANT_WRITE,
+	STATE_ERROR
+};
+
+struct netup_i2c_regs;
+
+struct netup_i2c {
+	spinlock_t			lock;
+	wait_queue_head_t		wq;
+	struct i2c_adapter		adap;
+	struct netup_unidvb_dev		*dev;
+	struct netup_i2c_regs		*regs;
+	struct i2c_msg			*msg;
+	enum netup_i2c_state		state;
+	u32				xmit_size;
+};
+
+struct netup_ci_state {
+	struct dvb_ca_en50221		ca;
+	u8 __iomem			*membase8_config;
+	u8 __iomem			*membase8_io;
+	struct netup_unidvb_dev		*dev;
+	int status;
+	int nr;
+};
+
+struct netup_spi;
+
+struct netup_unidvb_dev {
+	struct pci_dev			*pci_dev;
+	int				pci_bus;
+	int				pci_slot;
+	int				pci_func;
+	int				board_num;
+	int				old_fw;
+	u32 __iomem			*lmmio0;
+	u8 __iomem			*bmmio0;
+	u32 __iomem			*lmmio1;
+	u8 __iomem			*bmmio1;
+	u8				*dma_virt;
+	dma_addr_t			dma_phys;
+	u32				dma_size;
+	struct vb2_dvb_frontends	frontends[2];
+	struct netup_i2c		i2c[2];
+	struct workqueue_struct		*wq;
+	struct netup_dma		dma[2];
+	struct netup_ci_state		ci[2];
+	struct netup_spi		*spi;
+};
+
+int netup_i2c_register(struct netup_unidvb_dev *ndev);
+void netup_i2c_unregister(struct netup_unidvb_dev *ndev);
+irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev);
+irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c);
+irqreturn_t netup_spi_interrupt(struct netup_spi *spi);
+int netup_unidvb_ci_register(struct netup_unidvb_dev *dev,
+			     int num, struct pci_dev *pci_dev);
+void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num);
+int netup_spi_init(struct netup_unidvb_dev *ndev);
+void netup_spi_release(struct netup_unidvb_dev *ndev);
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
new file mode 100644
index 0000000..751b51b
--- /dev/null
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
@@ -0,0 +1,248 @@
+/*
+ * netup_unidvb_ci.c
+ *
+ * DVB CAM support for NetUP Universal Dual DVB-CI
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "netup_unidvb.h"
+
+/* CI slot 0 base address */
+#define CAM0_CONFIG		0x0
+#define CAM0_IO			0x8000
+#define CAM0_MEM		0x10000
+#define CAM0_SZ			32
+/* CI slot 1 base address */
+#define CAM1_CONFIG		0x20000
+#define CAM1_IO			0x28000
+#define CAM1_MEM		0x30000
+#define CAM1_SZ			32
+/* ctrlstat registers */
+#define CAM_CTRLSTAT_READ_SET	0x4980
+#define CAM_CTRLSTAT_CLR	0x4982
+/* register bits */
+#define BIT_CAM_STCHG		(1<<0)
+#define BIT_CAM_PRESENT		(1<<1)
+#define BIT_CAM_RESET		(1<<2)
+#define BIT_CAM_BYPASS		(1<<3)
+#define BIT_CAM_READY		(1<<4)
+#define BIT_CAM_ERROR		(1<<5)
+#define BIT_CAM_OVERCURR	(1<<6)
+/* BIT_CAM_BYPASS bit shift for SLOT 1 */
+#define CAM1_SHIFT 8
+
+irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev)
+{
+	writew(0x101, ndev->bmmio0 + CAM_CTRLSTAT_CLR);
+	return IRQ_HANDLED;
+}
+
+static int netup_unidvb_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221,
+				       int slot)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct netup_unidvb_dev *dev = state->dev;
+	u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
+
+	dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x\n",
+		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
+	if (slot != 0)
+		return -EINVAL;
+	/* pass data to CAM module */
+	writew(BIT_CAM_BYPASS << shift, dev->bmmio0 + CAM_CTRLSTAT_CLR);
+	dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x done\n",
+		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
+	return 0;
+}
+
+static int netup_unidvb_ci_slot_shutdown(struct dvb_ca_en50221 *en50221,
+					 int slot)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct netup_unidvb_dev *dev = state->dev;
+
+	dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__);
+	return 0;
+}
+
+static int netup_unidvb_ci_slot_reset(struct dvb_ca_en50221 *en50221,
+				      int slot)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct netup_unidvb_dev *dev = state->dev;
+	unsigned long timeout = 0;
+	u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
+	u16 ci_stat = 0;
+	int reset_counter = 3;
+
+	dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n",
+		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
+reset:
+	timeout = jiffies + msecs_to_jiffies(5000);
+	/* start reset */
+	writew(BIT_CAM_RESET << shift, dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
+	dev_dbg(&dev->pci_dev->dev, "%s(): waiting for reset\n", __func__);
+	/* wait until reset done */
+	while (time_before(jiffies, timeout)) {
+		ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
+		if (ci_stat & (BIT_CAM_READY << shift))
+			break;
+		udelay(1000);
+	}
+	if (!(ci_stat & (BIT_CAM_READY << shift)) && reset_counter > 0) {
+		dev_dbg(&dev->pci_dev->dev,
+			"%s(): CAMP reset timeout! Will try again..\n",
+			 __func__);
+		reset_counter--;
+		goto reset;
+	}
+	return 0;
+}
+
+static int netup_unidvb_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
+					    int slot, int open)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct netup_unidvb_dev *dev = state->dev;
+	u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
+	u16 ci_stat = 0;
+
+	dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n",
+		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
+	ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
+	if (ci_stat & (BIT_CAM_READY << shift)) {
+		state->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
+			DVB_CA_EN50221_POLL_CAM_READY;
+	} else if (ci_stat & (BIT_CAM_PRESENT << shift)) {
+		state->status = DVB_CA_EN50221_POLL_CAM_PRESENT;
+	} else {
+		state->status = 0;
+	}
+	return state->status;
+}
+
+static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+					      int slot, int addr)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct netup_unidvb_dev *dev = state->dev;
+	u8 val = state->membase8_config[addr];
+
+	dev_dbg(&dev->pci_dev->dev,
+		"%s(): addr=0x%x val=0x%x\n", __func__, addr, val);
+	return val;
+}
+
+static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+					       int slot, int addr, u8 data)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct netup_unidvb_dev *dev = state->dev;
+
+	dev_dbg(&dev->pci_dev->dev,
+		"%s(): addr=0x%x data=0x%x\n", __func__, addr, data);
+	state->membase8_config[addr] = data;
+	return 0;
+}
+
+static int netup_unidvb_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
+					int slot, u8 addr)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct netup_unidvb_dev *dev = state->dev;
+	u8 val = state->membase8_io[addr];
+
+	dev_dbg(&dev->pci_dev->dev,
+		"%s(): addr=0x%x val=0x%x\n", __func__, addr, val);
+	return val;
+}
+
+static int netup_unidvb_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221,
+					 int slot, u8 addr, u8 data)
+{
+	struct netup_ci_state *state = en50221->data;
+	struct netup_unidvb_dev *dev = state->dev;
+
+	dev_dbg(&dev->pci_dev->dev,
+		"%s(): addr=0x%x data=0x%x\n", __func__, addr, data);
+	state->membase8_io[addr] = data;
+	return 0;
+}
+
+int netup_unidvb_ci_register(struct netup_unidvb_dev *dev,
+			     int num, struct pci_dev *pci_dev)
+{
+	int result;
+	struct netup_ci_state *state;
+
+	if (num < 0 || num > 1) {
+		dev_err(&pci_dev->dev, "%s(): invalid CI adapter %d\n",
+			__func__, num);
+		return -EINVAL;
+	}
+	state = &dev->ci[num];
+	state->nr = num;
+	state->membase8_config = dev->bmmio1 +
+		((num == 0) ? CAM0_CONFIG : CAM1_CONFIG);
+	state->membase8_io = dev->bmmio1 +
+		((num == 0) ? CAM0_IO : CAM1_IO);
+	state->dev = dev;
+	state->ca.owner = THIS_MODULE;
+	state->ca.read_attribute_mem = netup_unidvb_ci_read_attribute_mem;
+	state->ca.write_attribute_mem = netup_unidvb_ci_write_attribute_mem;
+	state->ca.read_cam_control = netup_unidvb_ci_read_cam_ctl;
+	state->ca.write_cam_control = netup_unidvb_ci_write_cam_ctl;
+	state->ca.slot_reset = netup_unidvb_ci_slot_reset;
+	state->ca.slot_shutdown = netup_unidvb_ci_slot_shutdown;
+	state->ca.slot_ts_enable = netup_unidvb_ci_slot_ts_ctl;
+	state->ca.poll_slot_status = netup_unidvb_poll_ci_slot_status;
+	state->ca.data = state;
+	result = dvb_ca_en50221_init(&dev->frontends[num].adapter,
+		&state->ca, 0, 1);
+	if (result < 0) {
+		dev_err(&pci_dev->dev,
+			"%s(): dvb_ca_en50221_init result %d\n",
+			__func__, result);
+		return result;
+	}
+	writew(NETUP_UNIDVB_IRQ_CI, (u16 *)(dev->bmmio0 + REG_IMASK_SET));
+	dev_info(&pci_dev->dev,
+		"%s(): CI adapter %d init done\n", __func__, num);
+	return 0;
+}
+
+void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num)
+{
+	struct netup_ci_state *state;
+
+	dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__);
+	if (num < 0 || num > 1) {
+		dev_err(&dev->pci_dev->dev, "%s(): invalid CI adapter %d\n",
+				__func__, num);
+		return;
+	}
+	state = &dev->ci[num];
+	dvb_ca_en50221_release(&state->ca);
+}
+
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
new file mode 100644
index 0000000..181dd6a
--- /dev/null
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
@@ -0,0 +1,1002 @@
+/*
+ * netup_unidvb_core.c
+ *
+ * Main module for NetUP Universal Dual DVB-CI
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "netup_unidvb.h"
+#include "cxd2841er.h"
+#include "horus3a.h"
+#include "ascot2e.h"
+#include "lnbh25.h"
+
+static int spi_enable;
+module_param(spi_enable, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+MODULE_DESCRIPTION("Driver for NetUP Dual Universal DVB CI PCIe card");
+MODULE_AUTHOR("info@netup.ru");
+MODULE_VERSION(NETUP_UNIDVB_VERSION);
+MODULE_LICENSE("GPL");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* Avalon-MM PCI-E registers */
+#define	AVL_PCIE_IENR		0x50
+#define AVL_PCIE_ISR		0x40
+#define AVL_IRQ_ENABLE		0x80
+#define AVL_IRQ_ASSERTED	0x80
+/* GPIO registers */
+#define GPIO_REG_IO		0x4880
+#define GPIO_REG_IO_TOGGLE	0x4882
+#define GPIO_REG_IO_SET		0x4884
+#define GPIO_REG_IO_CLEAR	0x4886
+/* GPIO bits */
+#define GPIO_FEA_RESET		(1 << 0)
+#define GPIO_FEB_RESET		(1 << 1)
+#define GPIO_RFA_CTL		(1 << 2)
+#define GPIO_RFB_CTL		(1 << 3)
+#define GPIO_FEA_TU_RESET	(1 << 4)
+#define GPIO_FEB_TU_RESET	(1 << 5)
+/* DMA base address */
+#define NETUP_DMA0_ADDR		0x4900
+#define NETUP_DMA1_ADDR		0x4940
+/* 8 DMA blocks * 128 packets * 188 bytes*/
+#define NETUP_DMA_BLOCKS_COUNT	8
+#define NETUP_DMA_PACKETS_COUNT	128
+/* DMA status bits */
+#define BIT_DMA_RUN		1
+#define BIT_DMA_ERROR		2
+#define BIT_DMA_IRQ		0x200
+
+/**
+ * struct netup_dma_regs - the map of DMA module registers
+ * @ctrlstat_set:	Control register, write to set control bits
+ * @ctrlstat_clear:	Control register, write to clear control bits
+ * @start_addr_lo:	DMA ring buffer start address, lower part
+ * @start_addr_hi:	DMA ring buffer start address, higher part
+ * @size:		DMA ring buffer size register
+			Bits [0-7]:	DMA packet size, 188 bytes
+			Bits [16-23]:	packets count in block, 128 packets
+			Bits [24-31]:	blocks count, 8 blocks
+ * @timeout:		DMA timeout in units of 8ns
+			For example, value of 375000000 equals to 3 sec
+ * @curr_addr_lo:	Current ring buffer head address, lower part
+ * @curr_addr_hi:	Current ring buffer head address, higher part
+ * @stat_pkt_received:	Statistic register, not tested
+ * @stat_pkt_accepted:	Statistic register, not tested
+ * @stat_pkt_overruns:	Statistic register, not tested
+ * @stat_pkt_underruns:	Statistic register, not tested
+ * @stat_fifo_overruns:	Statistic register, not tested
+ */
+struct netup_dma_regs {
+	__le32	ctrlstat_set;
+	__le32	ctrlstat_clear;
+	__le32	start_addr_lo;
+	__le32	start_addr_hi;
+	__le32	size;
+	__le32	timeout;
+	__le32	curr_addr_lo;
+	__le32	curr_addr_hi;
+	__le32	stat_pkt_received;
+	__le32	stat_pkt_accepted;
+	__le32	stat_pkt_overruns;
+	__le32	stat_pkt_underruns;
+	__le32	stat_fifo_overruns;
+} __packed __aligned(1);
+
+struct netup_unidvb_buffer {
+	struct vb2_buffer	vb;
+	struct list_head	list;
+	u32			size;
+};
+
+static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc);
+static void netup_unidvb_queue_cleanup(struct netup_dma *dma);
+
+static struct cxd2841er_config demod_config = {
+	.i2c_addr = 0xc8
+};
+
+static struct horus3a_config horus3a_conf = {
+	.i2c_address = 0xc0,
+	.xtal_freq_mhz = 16,
+	.set_tuner_callback = netup_unidvb_tuner_ctrl
+};
+
+static struct ascot2e_config ascot2e_conf = {
+	.i2c_address = 0xc2,
+	.set_tuner_callback = netup_unidvb_tuner_ctrl
+};
+
+static struct lnbh25_config lnbh25_conf = {
+	.i2c_address = 0x10,
+	.data2_config = LNBH25_TEN | LNBH25_EXTM
+};
+
+static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc)
+{
+	u8 reg, mask;
+	struct netup_dma *dma = priv;
+	struct netup_unidvb_dev *ndev;
+
+	if (!priv)
+		return -EINVAL;
+	ndev = dma->ndev;
+	dev_dbg(&ndev->pci_dev->dev, "%s(): num %d is_dvb_tc %d\n",
+		__func__, dma->num, is_dvb_tc);
+	reg = readb(ndev->bmmio0 + GPIO_REG_IO);
+	mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL;
+	if (!is_dvb_tc)
+		reg |= mask;
+	else
+		reg &= ~mask;
+	writeb(reg, ndev->bmmio0 + GPIO_REG_IO);
+	return 0;
+}
+
+static void netup_unidvb_dev_enable(struct netup_unidvb_dev *ndev)
+{
+	u16 gpio_reg;
+
+	/* enable PCI-E interrupts */
+	writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR);
+	/* unreset frontends bits[0:1] */
+	writeb(0x00, ndev->bmmio0 + GPIO_REG_IO);
+	msleep(100);
+	gpio_reg =
+		GPIO_FEA_RESET | GPIO_FEB_RESET |
+		GPIO_FEA_TU_RESET | GPIO_FEB_TU_RESET |
+		GPIO_RFA_CTL | GPIO_RFB_CTL;
+	writeb(gpio_reg, ndev->bmmio0 + GPIO_REG_IO);
+	dev_dbg(&ndev->pci_dev->dev,
+		"%s(): AVL_PCIE_IENR 0x%x GPIO_REG_IO 0x%x\n",
+		__func__, readl(ndev->bmmio0 + AVL_PCIE_IENR),
+		(int)readb(ndev->bmmio0 + GPIO_REG_IO));
+
+}
+
+static void netup_unidvb_dma_enable(struct netup_dma *dma, int enable)
+{
+	u32 irq_mask = (dma->num == 0 ?
+		NETUP_UNIDVB_IRQ_DMA1 : NETUP_UNIDVB_IRQ_DMA2);
+
+	dev_dbg(&dma->ndev->pci_dev->dev,
+		"%s(): DMA%d enable %d\n", __func__, dma->num, enable);
+	if (enable) {
+		writel(BIT_DMA_RUN, &dma->regs->ctrlstat_set);
+		writew(irq_mask,
+			(u16 *)(dma->ndev->bmmio0 + REG_IMASK_SET));
+	} else {
+		writel(BIT_DMA_RUN, &dma->regs->ctrlstat_clear);
+		writew(irq_mask,
+			(u16 *)(dma->ndev->bmmio0 + REG_IMASK_CLEAR));
+	}
+}
+
+static irqreturn_t netup_dma_interrupt(struct netup_dma *dma)
+{
+	u64 addr_curr;
+	u32 size;
+	unsigned long flags;
+	struct device *dev = &dma->ndev->pci_dev->dev;
+
+	spin_lock_irqsave(&dma->lock, flags);
+	addr_curr = ((u64)readl(&dma->regs->curr_addr_hi) << 32) |
+		(u64)readl(&dma->regs->curr_addr_lo) | dma->high_addr;
+	/* clear IRQ */
+	writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear);
+	/* sanity check */
+	if (addr_curr < dma->addr_phys ||
+			addr_curr > dma->addr_phys +  dma->ring_buffer_size) {
+		if (addr_curr != 0) {
+			dev_err(dev,
+				"%s(): addr 0x%llx not from 0x%llx:0x%llx\n",
+				__func__, addr_curr, (u64)dma->addr_phys,
+				(u64)(dma->addr_phys + dma->ring_buffer_size));
+		}
+		goto irq_handled;
+	}
+	size = (addr_curr >= dma->addr_last) ?
+		(u32)(addr_curr - dma->addr_last) :
+		(u32)(dma->ring_buffer_size - (dma->addr_last - addr_curr));
+	if (dma->data_size != 0) {
+		printk_ratelimited("%s(): lost interrupt, data size %d\n",
+			__func__, dma->data_size);
+		dma->data_size += size;
+	}
+	if (dma->data_size == 0 || dma->data_size > dma->ring_buffer_size) {
+		dma->data_size = size;
+		dma->data_offset = (u32)(dma->addr_last - dma->addr_phys);
+	}
+	dma->addr_last = addr_curr;
+	queue_work(dma->ndev->wq, &dma->work);
+irq_handled:
+	spin_unlock_irqrestore(&dma->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t netup_unidvb_isr(int irq, void *dev_id)
+{
+	struct pci_dev *pci_dev = (struct pci_dev *)dev_id;
+	struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev);
+	u32 reg40, reg_isr;
+	irqreturn_t iret = IRQ_NONE;
+
+	/* disable interrupts */
+	writel(0, ndev->bmmio0 + AVL_PCIE_IENR);
+	/* check IRQ source */
+	reg40 = readl(ndev->bmmio0 + AVL_PCIE_ISR);
+	if ((reg40 & AVL_IRQ_ASSERTED) != 0) {
+		/* IRQ is being signaled */
+		reg_isr = readw(ndev->bmmio0 + REG_ISR);
+		if (reg_isr & NETUP_UNIDVB_IRQ_I2C0) {
+			iret = netup_i2c_interrupt(&ndev->i2c[0]);
+		} else if (reg_isr & NETUP_UNIDVB_IRQ_I2C1) {
+			iret = netup_i2c_interrupt(&ndev->i2c[1]);
+		} else if (reg_isr & NETUP_UNIDVB_IRQ_SPI) {
+			iret = netup_spi_interrupt(ndev->spi);
+		} else if (reg_isr & NETUP_UNIDVB_IRQ_DMA1) {
+			iret = netup_dma_interrupt(&ndev->dma[0]);
+		} else if (reg_isr & NETUP_UNIDVB_IRQ_DMA2) {
+			iret = netup_dma_interrupt(&ndev->dma[1]);
+		} else if (reg_isr & NETUP_UNIDVB_IRQ_CI) {
+			iret = netup_ci_interrupt(ndev);
+		} else {
+			dev_err(&pci_dev->dev,
+				"%s(): unknown interrupt 0x%x\n",
+				__func__, reg_isr);
+		}
+	}
+	/* re-enable interrupts */
+	writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR);
+	return iret;
+}
+
+static int netup_unidvb_queue_setup(struct vb2_queue *vq,
+				    const struct v4l2_format *fmt,
+				    unsigned int *nbuffers,
+				    unsigned int *nplanes,
+				    unsigned int sizes[],
+				    void *alloc_ctxs[])
+{
+	struct netup_dma *dma = vb2_get_drv_priv(vq);
+
+	dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__);
+
+	*nplanes = 1;
+	if (vq->num_buffers + *nbuffers < VIDEO_MAX_FRAME)
+		*nbuffers = VIDEO_MAX_FRAME - vq->num_buffers;
+	sizes[0] = PAGE_ALIGN(NETUP_DMA_PACKETS_COUNT * 188);
+	dev_dbg(&dma->ndev->pci_dev->dev, "%s() nbuffers=%d sizes[0]=%d\n",
+		__func__, *nbuffers, sizes[0]);
+	return 0;
+}
+
+static int netup_unidvb_buf_prepare(struct vb2_buffer *vb)
+{
+	struct netup_dma *dma = vb2_get_drv_priv(vb->vb2_queue);
+	struct netup_unidvb_buffer *buf = container_of(vb,
+				struct netup_unidvb_buffer, vb);
+
+	dev_dbg(&dma->ndev->pci_dev->dev, "%s(): buf 0x%p\n", __func__, buf);
+	buf->size = 0;
+	return 0;
+}
+
+static void netup_unidvb_buf_queue(struct vb2_buffer *vb)
+{
+	unsigned long flags;
+	struct netup_dma *dma = vb2_get_drv_priv(vb->vb2_queue);
+	struct netup_unidvb_buffer *buf = container_of(vb,
+				struct netup_unidvb_buffer, vb);
+
+	dev_dbg(&dma->ndev->pci_dev->dev, "%s(): %p\n", __func__, buf);
+	spin_lock_irqsave(&dma->lock, flags);
+	list_add_tail(&buf->list, &dma->free_buffers);
+	spin_unlock_irqrestore(&dma->lock, flags);
+	mod_timer(&dma->timeout, jiffies + msecs_to_jiffies(1000));
+}
+
+static int netup_unidvb_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct netup_dma *dma = vb2_get_drv_priv(q);
+
+	dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__);
+	netup_unidvb_dma_enable(dma, 1);
+	return 0;
+}
+
+static void netup_unidvb_stop_streaming(struct vb2_queue *q)
+{
+	struct netup_dma *dma = vb2_get_drv_priv(q);
+
+	dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__);
+	netup_unidvb_dma_enable(dma, 0);
+	netup_unidvb_queue_cleanup(dma);
+}
+
+static struct vb2_ops dvb_qops = {
+	.queue_setup		= netup_unidvb_queue_setup,
+	.buf_prepare		= netup_unidvb_buf_prepare,
+	.buf_queue		= netup_unidvb_buf_queue,
+	.start_streaming	= netup_unidvb_start_streaming,
+	.stop_streaming		= netup_unidvb_stop_streaming,
+};
+
+static int netup_unidvb_queue_init(struct netup_dma *dma,
+				   struct vb2_queue *vb_queue)
+{
+	int res;
+
+	/* Init videobuf2 queue structure */
+	vb_queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vb_queue->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+	vb_queue->drv_priv = dma;
+	vb_queue->buf_struct_size = sizeof(struct netup_unidvb_buffer);
+	vb_queue->ops = &dvb_qops;
+	vb_queue->mem_ops = &vb2_vmalloc_memops;
+	vb_queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	res = vb2_queue_init(vb_queue);
+	if (res != 0) {
+		dev_err(&dma->ndev->pci_dev->dev,
+			"%s(): vb2_queue_init failed (%d)\n", __func__, res);
+	}
+	return res;
+}
+
+static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev,
+				 int num)
+{
+	struct vb2_dvb_frontend *fe0, *fe1, *fe2;
+
+	if (num < 0 || num > 1) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to init DVB bus %d\n", __func__, num);
+		return -ENODEV;
+	}
+	mutex_init(&ndev->frontends[num].lock);
+	INIT_LIST_HEAD(&ndev->frontends[num].felist);
+	if (vb2_dvb_alloc_frontend(&ndev->frontends[num], 1) == NULL ||
+		vb2_dvb_alloc_frontend(
+			&ndev->frontends[num], 2) == NULL ||
+		vb2_dvb_alloc_frontend(
+			&ndev->frontends[num], 3) == NULL) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to to alllocate vb2_dvb_frontend\n",
+			__func__);
+		return -ENOMEM;
+	}
+	fe0 = vb2_dvb_get_frontend(&ndev->frontends[num], 1);
+	fe1 = vb2_dvb_get_frontend(&ndev->frontends[num], 2);
+	fe2 = vb2_dvb_get_frontend(&ndev->frontends[num], 3);
+	if (fe0 == NULL || fe1 == NULL || fe2 == NULL) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): frontends has not been allocated\n", __func__);
+		return -EINVAL;
+	}
+	netup_unidvb_queue_init(&ndev->dma[num], &fe0->dvb.dvbq);
+	netup_unidvb_queue_init(&ndev->dma[num], &fe1->dvb.dvbq);
+	netup_unidvb_queue_init(&ndev->dma[num], &fe2->dvb.dvbq);
+	fe0->dvb.name = "netup_fe0";
+	fe1->dvb.name = "netup_fe1";
+	fe2->dvb.name = "netup_fe2";
+	fe0->dvb.frontend = dvb_attach(cxd2841er_attach_s,
+		&demod_config, &ndev->i2c[num].adap);
+	if (fe0->dvb.frontend == NULL) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to attach DVB-S/S2 frontend\n",
+			__func__);
+		goto frontend_detach;
+	}
+	horus3a_conf.set_tuner_priv = &ndev->dma[num];
+	if (!dvb_attach(horus3a_attach, fe0->dvb.frontend,
+			&horus3a_conf, &ndev->i2c[num].adap)) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to attach DVB-S/S2 tuner frontend\n",
+			__func__);
+		goto frontend_detach;
+	}
+	if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend,
+			&lnbh25_conf, &ndev->i2c[num].adap)) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to attach SEC frontend\n", __func__);
+		goto frontend_detach;
+	}
+	/* DVB-T/T2 frontend */
+	fe1->dvb.frontend = dvb_attach(cxd2841er_attach_t,
+		&demod_config, &ndev->i2c[num].adap);
+	if (fe1->dvb.frontend == NULL) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to attach DVB-T frontend\n", __func__);
+		goto frontend_detach;
+	}
+	fe1->dvb.frontend->id = 1;
+	ascot2e_conf.set_tuner_priv = &ndev->dma[num];
+	if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend,
+			&ascot2e_conf, &ndev->i2c[num].adap)) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to attach DVB-T tuner frontend\n",
+			__func__);
+		goto frontend_detach;
+	}
+	/* DVB-C/C2 frontend */
+	fe2->dvb.frontend = dvb_attach(cxd2841er_attach_c,
+				&demod_config, &ndev->i2c[num].adap);
+	if (fe2->dvb.frontend == NULL) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to attach DVB-C frontend\n", __func__);
+		goto frontend_detach;
+	}
+	fe2->dvb.frontend->id = 2;
+	if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend,
+			&ascot2e_conf, &ndev->i2c[num].adap)) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to attach DVB-T/C tuner frontend\n",
+			__func__);
+		goto frontend_detach;
+	}
+
+	if (vb2_dvb_register_bus(&ndev->frontends[num],
+			THIS_MODULE, NULL,
+			&ndev->pci_dev->dev, adapter_nr, 1)) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): unable to register DVB bus %d\n",
+			__func__, num);
+		goto frontend_detach;
+	}
+	dev_info(&ndev->pci_dev->dev, "DVB init done, num=%d\n", num);
+	return 0;
+frontend_detach:
+	vb2_dvb_dealloc_frontends(&ndev->frontends[num]);
+	return -EINVAL;
+}
+
+static void netup_unidvb_dvb_fini(struct netup_unidvb_dev *ndev, int num)
+{
+	if (num < 0 || num > 1) {
+		dev_err(&ndev->pci_dev->dev,
+			"%s(): unable to unregister DVB bus %d\n",
+			__func__, num);
+		return;
+	}
+	vb2_dvb_unregister_bus(&ndev->frontends[num]);
+	dev_info(&ndev->pci_dev->dev,
+		"%s(): DVB bus %d unregistered\n", __func__, num);
+}
+
+static int netup_unidvb_dvb_setup(struct netup_unidvb_dev *ndev)
+{
+	int res;
+
+	res = netup_unidvb_dvb_init(ndev, 0);
+	if (res)
+		return res;
+	res = netup_unidvb_dvb_init(ndev, 1);
+	if (res) {
+		netup_unidvb_dvb_fini(ndev, 0);
+		return res;
+	}
+	return 0;
+}
+
+static int netup_unidvb_ring_copy(struct netup_dma *dma,
+				  struct netup_unidvb_buffer *buf)
+{
+	u32 copy_bytes, ring_bytes;
+	u32 buff_bytes = NETUP_DMA_PACKETS_COUNT * 188 - buf->size;
+	u8 *p = vb2_plane_vaddr(&buf->vb, 0);
+	struct netup_unidvb_dev *ndev = dma->ndev;
+
+	if (p == NULL) {
+		dev_err(&ndev->pci_dev->dev,
+			"%s(): buffer is NULL\n", __func__);
+		return -EINVAL;
+	}
+	p += buf->size;
+	if (dma->data_offset + dma->data_size > dma->ring_buffer_size) {
+		ring_bytes = dma->ring_buffer_size - dma->data_offset;
+		copy_bytes = (ring_bytes > buff_bytes) ?
+			buff_bytes : ring_bytes;
+		memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes);
+		p += copy_bytes;
+		buf->size += copy_bytes;
+		buff_bytes -= copy_bytes;
+		dma->data_size -= copy_bytes;
+		dma->data_offset += copy_bytes;
+		if (dma->data_offset == dma->ring_buffer_size)
+			dma->data_offset = 0;
+	}
+	if (buff_bytes > 0) {
+		ring_bytes = dma->data_size;
+		copy_bytes = (ring_bytes > buff_bytes) ?
+				buff_bytes : ring_bytes;
+		memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes);
+		buf->size += copy_bytes;
+		dma->data_size -= copy_bytes;
+		dma->data_offset += copy_bytes;
+		if (dma->data_offset == dma->ring_buffer_size)
+			dma->data_offset = 0;
+	}
+	return 0;
+}
+
+static void netup_unidvb_dma_worker(struct work_struct *work)
+{
+	struct netup_dma *dma = container_of(work, struct netup_dma, work);
+	struct netup_unidvb_dev *ndev = dma->ndev;
+	struct netup_unidvb_buffer *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dma->lock, flags);
+	if (dma->data_size == 0) {
+		dev_dbg(&ndev->pci_dev->dev,
+			"%s(): data_size == 0\n", __func__);
+		goto work_done;
+	}
+	while (dma->data_size > 0) {
+		if (list_empty(&dma->free_buffers)) {
+			dev_dbg(&ndev->pci_dev->dev,
+				"%s(): no free buffers\n", __func__);
+			goto work_done;
+		}
+		buf = list_first_entry(&dma->free_buffers,
+			struct netup_unidvb_buffer, list);
+		if (buf->size >= NETUP_DMA_PACKETS_COUNT * 188) {
+			dev_dbg(&ndev->pci_dev->dev,
+				"%s(): buffer overflow, size %d\n",
+				__func__, buf->size);
+			goto work_done;
+		}
+		if (netup_unidvb_ring_copy(dma, buf))
+			goto work_done;
+		if (buf->size == NETUP_DMA_PACKETS_COUNT * 188) {
+			list_del(&buf->list);
+			dev_dbg(&ndev->pci_dev->dev,
+				"%s(): buffer %p done, size %d\n",
+				__func__, buf, buf->size);
+			v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+			vb2_set_plane_payload(&buf->vb, 0, buf->size);
+			vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+		}
+	}
+work_done:
+	dma->data_size = 0;
+	spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void netup_unidvb_queue_cleanup(struct netup_dma *dma)
+{
+	struct netup_unidvb_buffer *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dma->lock, flags);
+	while (!list_empty(&dma->free_buffers)) {
+		buf = list_first_entry(&dma->free_buffers,
+			struct netup_unidvb_buffer, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void netup_unidvb_dma_timeout(unsigned long data)
+{
+	struct netup_dma *dma = (struct netup_dma *)data;
+	struct netup_unidvb_dev *ndev = dma->ndev;
+
+	dev_dbg(&ndev->pci_dev->dev, "%s()\n", __func__);
+	netup_unidvb_queue_cleanup(dma);
+}
+
+static int netup_unidvb_dma_init(struct netup_unidvb_dev *ndev, int num)
+{
+	struct netup_dma *dma;
+	struct device *dev = &ndev->pci_dev->dev;
+
+	if (num < 0 || num > 1) {
+		dev_err(dev, "%s(): unable to register DMA%d\n",
+			__func__, num);
+		return -ENODEV;
+	}
+	dma = &ndev->dma[num];
+	dev_info(dev, "%s(): starting DMA%d\n", __func__, num);
+	dma->num = num;
+	dma->ndev = ndev;
+	spin_lock_init(&dma->lock);
+	INIT_WORK(&dma->work, netup_unidvb_dma_worker);
+	INIT_LIST_HEAD(&dma->free_buffers);
+	dma->timeout.function = netup_unidvb_dma_timeout;
+	dma->timeout.data = (unsigned long)dma;
+	init_timer(&dma->timeout);
+	dma->ring_buffer_size = ndev->dma_size / 2;
+	dma->addr_virt = ndev->dma_virt + dma->ring_buffer_size * num;
+	dma->addr_phys = (dma_addr_t)((u64)ndev->dma_phys +
+		dma->ring_buffer_size * num);
+	dev_info(dev, "%s(): DMA%d buffer virt/phys 0x%p/0x%llx size %d\n",
+		__func__, num, dma->addr_virt,
+		(unsigned long long)dma->addr_phys,
+		dma->ring_buffer_size);
+	memset_io(dma->addr_virt, 0, dma->ring_buffer_size);
+	dma->addr_last = dma->addr_phys;
+	dma->high_addr = (u32)(dma->addr_phys & 0xC0000000);
+	dma->regs = (struct netup_dma_regs *)(num == 0 ?
+		ndev->bmmio0 + NETUP_DMA0_ADDR :
+		ndev->bmmio0 + NETUP_DMA1_ADDR);
+	writel((NETUP_DMA_BLOCKS_COUNT << 24) |
+		(NETUP_DMA_PACKETS_COUNT << 8) | 188, &dma->regs->size);
+	writel((u32)(dma->addr_phys & 0x3FFFFFFF), &dma->regs->start_addr_lo);
+	writel(0, &dma->regs->start_addr_hi);
+	writel(dma->high_addr, ndev->bmmio0 + 0x1000);
+	writel(375000000, &dma->regs->timeout);
+	msleep(1000);
+	writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear);
+	return 0;
+}
+
+static void netup_unidvb_dma_fini(struct netup_unidvb_dev *ndev, int num)
+{
+	struct netup_dma *dma;
+
+	if (num < 0 || num > 1)
+		return;
+	dev_dbg(&ndev->pci_dev->dev, "%s(): num %d\n", __func__, num);
+	dma = &ndev->dma[num];
+	netup_unidvb_dma_enable(dma, 0);
+	msleep(50);
+	cancel_work_sync(&dma->work);
+	del_timer(&dma->timeout);
+}
+
+static int netup_unidvb_dma_setup(struct netup_unidvb_dev *ndev)
+{
+	int res;
+
+	res = netup_unidvb_dma_init(ndev, 0);
+	if (res)
+		return res;
+	res = netup_unidvb_dma_init(ndev, 1);
+	if (res) {
+		netup_unidvb_dma_fini(ndev, 0);
+		return res;
+	}
+	netup_unidvb_dma_enable(&ndev->dma[0], 0);
+	netup_unidvb_dma_enable(&ndev->dma[1], 0);
+	return 0;
+}
+
+static int netup_unidvb_ci_setup(struct netup_unidvb_dev *ndev,
+				 struct pci_dev *pci_dev)
+{
+	int res;
+
+	writew(NETUP_UNIDVB_IRQ_CI, ndev->bmmio0 + REG_IMASK_SET);
+	res = netup_unidvb_ci_register(ndev, 0, pci_dev);
+	if (res)
+		return res;
+	res = netup_unidvb_ci_register(ndev, 1, pci_dev);
+	if (res)
+		netup_unidvb_ci_unregister(ndev, 0);
+	return res;
+}
+
+static int netup_unidvb_request_mmio(struct pci_dev *pci_dev)
+{
+	if (!request_mem_region(pci_resource_start(pci_dev, 0),
+			pci_resource_len(pci_dev, 0), NETUP_UNIDVB_NAME)) {
+		dev_err(&pci_dev->dev,
+			"%s(): unable to request MMIO bar 0 at 0x%llx\n",
+			__func__,
+			(unsigned long long)pci_resource_start(pci_dev, 0));
+		return -EBUSY;
+	}
+	if (!request_mem_region(pci_resource_start(pci_dev, 1),
+			pci_resource_len(pci_dev, 1), NETUP_UNIDVB_NAME)) {
+		dev_err(&pci_dev->dev,
+			"%s(): unable to request MMIO bar 1 at 0x%llx\n",
+			__func__,
+			(unsigned long long)pci_resource_start(pci_dev, 1));
+		release_mem_region(pci_resource_start(pci_dev, 0),
+			pci_resource_len(pci_dev, 0));
+		return -EBUSY;
+	}
+	return 0;
+}
+
+static int netup_unidvb_request_modules(struct device *dev)
+{
+	static const char * const modules[] = {
+		"lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL
+	};
+	const char * const *curr_mod = modules;
+	int err;
+
+	while (*curr_mod != NULL) {
+		err = request_module(*curr_mod);
+		if (err) {
+			dev_warn(dev, "request_module(%s) failed: %d\n",
+				*curr_mod, err);
+		}
+		++curr_mod;
+	}
+	return 0;
+}
+
+static int netup_unidvb_initdev(struct pci_dev *pci_dev,
+				const struct pci_device_id *pci_id)
+{
+	u8 board_revision;
+	u16 board_vendor;
+	struct netup_unidvb_dev *ndev;
+	int old_firmware = 0;
+
+	netup_unidvb_request_modules(&pci_dev->dev);
+
+	/* Check card revision */
+	if (pci_dev->revision != NETUP_PCI_DEV_REVISION) {
+		dev_err(&pci_dev->dev,
+			"netup_unidvb: expected card revision %d, got %d\n",
+			NETUP_PCI_DEV_REVISION, pci_dev->revision);
+		dev_err(&pci_dev->dev,
+			"Please upgrade firmware!\n");
+		dev_err(&pci_dev->dev,
+			"Instructions on http://www.netup.tv\n");
+		old_firmware = 1;
+		spi_enable = 1;
+	}
+
+	/* allocate device context */
+	ndev = kzalloc(sizeof(*ndev), GFP_KERNEL);
+
+	if (!ndev)
+		goto dev_alloc_err;
+	memset(ndev, 0, sizeof(*ndev));
+	ndev->old_fw = old_firmware;
+	ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME);
+	if (!ndev->wq) {
+		dev_err(&pci_dev->dev,
+			"%s(): unable to create workqueue\n", __func__);
+		goto wq_create_err;
+	}
+	ndev->pci_dev = pci_dev;
+	ndev->pci_bus = pci_dev->bus->number;
+	ndev->pci_slot = PCI_SLOT(pci_dev->devfn);
+	ndev->pci_func = PCI_FUNC(pci_dev->devfn);
+	ndev->board_num = ndev->pci_bus*10 + ndev->pci_slot;
+	pci_set_drvdata(pci_dev, ndev);
+	/* PCI init */
+	dev_info(&pci_dev->dev, "%s(): PCI device (%d). Bus:0x%x Slot:0x%x\n",
+		__func__, ndev->board_num, ndev->pci_bus, ndev->pci_slot);
+
+	if (pci_enable_device(pci_dev)) {
+		dev_err(&pci_dev->dev, "%s(): pci_enable_device failed\n",
+			__func__);
+		goto pci_enable_err;
+	}
+	/* read PCI info */
+	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &board_revision);
+	pci_read_config_word(pci_dev, PCI_VENDOR_ID, &board_vendor);
+	if (board_vendor != NETUP_VENDOR_ID) {
+		dev_err(&pci_dev->dev, "%s(): unknown board vendor 0x%x",
+			__func__, board_vendor);
+		goto pci_detect_err;
+	}
+	dev_info(&pci_dev->dev,
+		"%s(): board vendor 0x%x, revision 0x%x\n",
+		__func__, board_vendor, board_revision);
+	pci_set_master(pci_dev);
+	if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+		dev_err(&pci_dev->dev,
+			"%s(): 32bit PCI DMA is not supported\n", __func__);
+		goto pci_detect_err;
+	}
+	dev_info(&pci_dev->dev, "%s(): using 32bit PCI DMA\n", __func__);
+	/* Clear "no snoop" and "relaxed ordering" bits, use default MRRS. */
+	pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL,
+		PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN |
+		PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
+	/* Adjust PCIe completion timeout. */
+	pcie_capability_clear_and_set_word(pci_dev,
+		PCI_EXP_DEVCTL2, 0xf, 0x2);
+
+	if (netup_unidvb_request_mmio(pci_dev)) {
+		dev_err(&pci_dev->dev,
+			"%s(): unable to request MMIO regions\n", __func__);
+		goto pci_detect_err;
+	}
+	ndev->lmmio0 = ioremap(pci_resource_start(pci_dev, 0),
+		pci_resource_len(pci_dev, 0));
+	if (!ndev->lmmio0) {
+		dev_err(&pci_dev->dev,
+			"%s(): unable to remap MMIO bar 0\n", __func__);
+		goto pci_bar0_error;
+	}
+	ndev->lmmio1 = ioremap(pci_resource_start(pci_dev, 1),
+		pci_resource_len(pci_dev, 1));
+	if (!ndev->lmmio1) {
+		dev_err(&pci_dev->dev,
+			"%s(): unable to remap MMIO bar 1\n", __func__);
+		goto pci_bar1_error;
+	}
+	ndev->bmmio0 = (u8 __iomem *)ndev->lmmio0;
+	ndev->bmmio1 = (u8 __iomem *)ndev->lmmio1;
+	dev_info(&pci_dev->dev,
+		"%s(): PCI MMIO at 0x%p (%d); 0x%p (%d); IRQ %d",
+		__func__,
+		ndev->lmmio0, (u32)pci_resource_len(pci_dev, 0),
+		ndev->lmmio1, (u32)pci_resource_len(pci_dev, 1),
+		pci_dev->irq);
+	if (request_irq(pci_dev->irq, netup_unidvb_isr,
+			IRQF_SHARED | IRQF_DISABLED,
+			"netup_unidvb", pci_dev) < 0) {
+		dev_err(&pci_dev->dev,
+			"%s(): can't get IRQ %d\n", __func__, pci_dev->irq);
+		goto irq_request_err;
+	}
+	ndev->dma_size = 2 * 188 *
+		NETUP_DMA_BLOCKS_COUNT * NETUP_DMA_PACKETS_COUNT;
+	ndev->dma_virt = dma_alloc_coherent(&pci_dev->dev,
+		ndev->dma_size, &ndev->dma_phys, GFP_KERNEL);
+	if (!ndev->dma_virt) {
+		dev_err(&pci_dev->dev, "%s(): unable to allocate DMA buffer\n",
+			__func__);
+		goto dma_alloc_err;
+	}
+	netup_unidvb_dev_enable(ndev);
+	if (spi_enable && netup_spi_init(ndev)) {
+		dev_warn(&pci_dev->dev,
+			"netup_unidvb: SPI flash setup failed\n");
+		goto spi_setup_err;
+	}
+	if (old_firmware) {
+		dev_err(&pci_dev->dev,
+			"netup_unidvb: card initialization was incomplete\n");
+		return 0;
+	}
+	if (netup_i2c_register(ndev)) {
+		dev_err(&pci_dev->dev, "netup_unidvb: I2C setup failed\n");
+		goto i2c_setup_err;
+	}
+	/* enable I2C IRQs */
+	writew(NETUP_UNIDVB_IRQ_I2C0 | NETUP_UNIDVB_IRQ_I2C1,
+		ndev->bmmio0 + REG_IMASK_SET);
+	usleep_range(5000, 10000);
+	if (netup_unidvb_dvb_setup(ndev)) {
+		dev_err(&pci_dev->dev, "netup_unidvb: DVB setup failed\n");
+		goto dvb_setup_err;
+	}
+	if (netup_unidvb_ci_setup(ndev, pci_dev)) {
+		dev_err(&pci_dev->dev, "netup_unidvb: CI setup failed\n");
+		goto ci_setup_err;
+	}
+	if (netup_unidvb_dma_setup(ndev)) {
+		dev_err(&pci_dev->dev, "netup_unidvb: DMA setup failed\n");
+		goto dma_setup_err;
+	}
+	dev_info(&pci_dev->dev,
+		"netup_unidvb: device has been initialized\n");
+	return 0;
+dma_setup_err:
+	netup_unidvb_ci_unregister(ndev, 0);
+	netup_unidvb_ci_unregister(ndev, 1);
+ci_setup_err:
+	netup_unidvb_dvb_fini(ndev, 0);
+	netup_unidvb_dvb_fini(ndev, 1);
+dvb_setup_err:
+	netup_i2c_unregister(ndev);
+i2c_setup_err:
+	if (ndev->spi)
+		netup_spi_release(ndev);
+spi_setup_err:
+	dma_free_coherent(&pci_dev->dev, ndev->dma_size,
+			ndev->dma_virt, ndev->dma_phys);
+dma_alloc_err:
+	free_irq(pci_dev->irq, pci_dev);
+irq_request_err:
+	iounmap(ndev->lmmio1);
+pci_bar1_error:
+	iounmap(ndev->lmmio0);
+pci_bar0_error:
+	release_mem_region(pci_resource_start(pci_dev, 0),
+		pci_resource_len(pci_dev, 0));
+	release_mem_region(pci_resource_start(pci_dev, 1),
+		pci_resource_len(pci_dev, 1));
+pci_detect_err:
+	pci_disable_device(pci_dev);
+pci_enable_err:
+	pci_set_drvdata(pci_dev, NULL);
+	destroy_workqueue(ndev->wq);
+wq_create_err:
+	kfree(ndev);
+dev_alloc_err:
+	dev_err(&pci_dev->dev,
+		"%s(): failed to initizalize device\n", __func__);
+	return -EIO;
+}
+
+static void netup_unidvb_finidev(struct pci_dev *pci_dev)
+{
+	struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev);
+
+	dev_info(&pci_dev->dev, "%s(): trying to stop device\n", __func__);
+	if (!ndev->old_fw) {
+		netup_unidvb_dma_fini(ndev, 0);
+		netup_unidvb_dma_fini(ndev, 1);
+		netup_unidvb_ci_unregister(ndev, 0);
+		netup_unidvb_ci_unregister(ndev, 1);
+		netup_unidvb_dvb_fini(ndev, 0);
+		netup_unidvb_dvb_fini(ndev, 1);
+		netup_i2c_unregister(ndev);
+	}
+	if (ndev->spi)
+		netup_spi_release(ndev);
+	writew(0xffff, ndev->bmmio0 + REG_IMASK_CLEAR);
+	dma_free_coherent(&ndev->pci_dev->dev, ndev->dma_size,
+			ndev->dma_virt, ndev->dma_phys);
+	free_irq(pci_dev->irq, pci_dev);
+	iounmap(ndev->lmmio0);
+	iounmap(ndev->lmmio1);
+	release_mem_region(pci_resource_start(pci_dev, 0),
+		pci_resource_len(pci_dev, 0));
+	release_mem_region(pci_resource_start(pci_dev, 1),
+		pci_resource_len(pci_dev, 1));
+	pci_disable_device(pci_dev);
+	pci_set_drvdata(pci_dev, NULL);
+	destroy_workqueue(ndev->wq);
+	kfree(ndev);
+	dev_info(&pci_dev->dev,
+		"%s(): device has been successfully stopped\n", __func__);
+}
+
+
+static struct pci_device_id netup_unidvb_pci_tbl[] = {
+	{ PCI_DEVICE(0x1b55, 0x18f6) },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl);
+
+static struct pci_driver netup_unidvb_pci_driver = {
+	.name     = "netup_unidvb",
+	.id_table = netup_unidvb_pci_tbl,
+	.probe    = netup_unidvb_initdev,
+	.remove   = netup_unidvb_finidev,
+	.suspend  = NULL,
+	.resume   = NULL,
+};
+
+static int __init netup_unidvb_init(void)
+{
+	return pci_register_driver(&netup_unidvb_pci_driver);
+}
+
+static void __exit netup_unidvb_fini(void)
+{
+	pci_unregister_driver(&netup_unidvb_pci_driver);
+}
+
+module_init(netup_unidvb_init);
+module_exit(netup_unidvb_fini);
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
new file mode 100644
index 0000000..eaaa2d0
--- /dev/null
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
@@ -0,0 +1,381 @@
+/*
+ * netup_unidvb_i2c.c
+ *
+ * Internal I2C bus driver for NetUP Universal Dual DVB-CI
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include "netup_unidvb.h"
+
+#define NETUP_I2C_BUS0_ADDR		0x4800
+#define NETUP_I2C_BUS1_ADDR		0x4840
+#define NETUP_I2C_TIMEOUT		1000
+
+/* twi_ctrl0_stat reg bits */
+#define TWI_IRQEN_COMPL	0x1
+#define TWI_IRQEN_ANACK 0x2
+#define TWI_IRQEN_DNACK 0x4
+#define TWI_IRQ_COMPL	(TWI_IRQEN_COMPL << 8)
+#define TWI_IRQ_ANACK	(TWI_IRQEN_ANACK << 8)
+#define TWI_IRQ_DNACK	(TWI_IRQEN_DNACK << 8)
+#define TWI_IRQ_TX	0x800
+#define TWI_IRQ_RX	0x1000
+#define TWI_IRQEN	(TWI_IRQEN_COMPL | TWI_IRQEN_ANACK | TWI_IRQEN_DNACK)
+/* twi_addr_ctrl1 reg bits*/
+#define TWI_TRANSFER	0x100
+#define TWI_NOSTOP	0x200
+#define TWI_SOFT_RESET	0x2000
+/* twi_clkdiv reg value */
+#define TWI_CLKDIV	156
+/* fifo_stat_ctrl reg bits */
+#define FIFO_IRQEN	0x8000
+#define FIFO_RESET	0x4000
+/* FIFO size */
+#define FIFO_SIZE	16
+
+struct netup_i2c_fifo_regs {
+	union {
+		__u8	data8;
+		__le16	data16;
+		__le32	data32;
+	};
+	__u8		padding[4];
+	__le16		stat_ctrl;
+} __packed __aligned(1);
+
+struct netup_i2c_regs {
+	__le16				clkdiv;
+	__le16				twi_ctrl0_stat;
+	__le16				twi_addr_ctrl1;
+	__le16				length;
+	__u8				padding1[8];
+	struct netup_i2c_fifo_regs	tx_fifo;
+	__u8				padding2[6];
+	struct netup_i2c_fifo_regs	rx_fifo;
+} __packed __aligned(1);
+
+irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c)
+{
+	u16 reg, tmp;
+	unsigned long flags;
+	irqreturn_t iret = IRQ_HANDLED;
+
+	spin_lock_irqsave(&i2c->lock, flags);
+	reg = readw(&i2c->regs->twi_ctrl0_stat);
+	writew(reg & ~TWI_IRQEN, &i2c->regs->twi_ctrl0_stat);
+	dev_dbg(i2c->adap.dev.parent,
+		"%s(): twi_ctrl0_state 0x%x\n", __func__, reg);
+	if ((reg & TWI_IRQEN_COMPL) != 0 && (reg & TWI_IRQ_COMPL)) {
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): TWI_IRQEN_COMPL\n", __func__);
+		i2c->state = STATE_DONE;
+		goto irq_ok;
+	}
+	if ((reg & TWI_IRQEN_ANACK) != 0 && (reg & TWI_IRQ_ANACK)) {
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): TWI_IRQEN_ANACK\n", __func__);
+		i2c->state = STATE_ERROR;
+		goto irq_ok;
+	}
+	if ((reg & TWI_IRQEN_DNACK) != 0 && (reg & TWI_IRQ_DNACK)) {
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): TWI_IRQEN_DNACK\n", __func__);
+		i2c->state = STATE_ERROR;
+		goto irq_ok;
+	}
+	if ((reg & TWI_IRQ_RX) != 0) {
+		tmp = readw(&i2c->regs->rx_fifo.stat_ctrl);
+		writew(tmp & ~FIFO_IRQEN, &i2c->regs->rx_fifo.stat_ctrl);
+		i2c->state = STATE_WANT_READ;
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): want read\n", __func__);
+		goto irq_ok;
+	}
+	if ((reg & TWI_IRQ_TX) != 0) {
+		tmp = readw(&i2c->regs->tx_fifo.stat_ctrl);
+		writew(tmp & ~FIFO_IRQEN, &i2c->regs->tx_fifo.stat_ctrl);
+		i2c->state = STATE_WANT_WRITE;
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): want write\n", __func__);
+		goto irq_ok;
+	}
+	dev_warn(&i2c->adap.dev, "%s(): not mine interrupt\n", __func__);
+	iret = IRQ_NONE;
+irq_ok:
+	spin_unlock_irqrestore(&i2c->lock, flags);
+	if (iret == IRQ_HANDLED)
+		wake_up(&i2c->wq);
+	return iret;
+}
+
+static void netup_i2c_reset(struct netup_i2c *i2c)
+{
+	dev_dbg(i2c->adap.dev.parent, "%s()\n", __func__);
+	i2c->state = STATE_DONE;
+	writew(TWI_SOFT_RESET, &i2c->regs->twi_addr_ctrl1);
+	writew(TWI_CLKDIV, &i2c->regs->clkdiv);
+	writew(FIFO_RESET, &i2c->regs->tx_fifo.stat_ctrl);
+	writew(FIFO_RESET, &i2c->regs->rx_fifo.stat_ctrl);
+	writew(0x800, &i2c->regs->tx_fifo.stat_ctrl);
+	writew(0x800, &i2c->regs->rx_fifo.stat_ctrl);
+}
+
+static void netup_i2c_fifo_tx(struct netup_i2c *i2c)
+{
+	u8 data;
+	u32 fifo_space = FIFO_SIZE -
+		(readw(&i2c->regs->tx_fifo.stat_ctrl) & 0x3f);
+	u32 msg_length = i2c->msg->len - i2c->xmit_size;
+
+	msg_length = (msg_length < fifo_space ? msg_length : fifo_space);
+	while (msg_length--) {
+		data = i2c->msg->buf[i2c->xmit_size++];
+		writeb(data, &i2c->regs->tx_fifo.data8);
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): write 0x%02x\n", __func__, data);
+	}
+	if (i2c->xmit_size < i2c->msg->len) {
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): TX IRQ enabled\n", __func__);
+		writew(readw(&i2c->regs->tx_fifo.stat_ctrl) | FIFO_IRQEN,
+			&i2c->regs->tx_fifo.stat_ctrl);
+	}
+}
+
+static void netup_i2c_fifo_rx(struct netup_i2c *i2c)
+{
+	u8 data;
+	u32 fifo_size = readw(&i2c->regs->rx_fifo.stat_ctrl) & 0x3f;
+
+	dev_dbg(i2c->adap.dev.parent,
+		"%s(): RX fifo size %d\n", __func__, fifo_size);
+	while (fifo_size--) {
+		data = readb(&i2c->regs->rx_fifo.data8);
+		if ((i2c->msg->flags & I2C_M_RD) != 0 &&
+					i2c->xmit_size < i2c->msg->len) {
+			i2c->msg->buf[i2c->xmit_size++] = data;
+			dev_dbg(i2c->adap.dev.parent,
+				"%s(): read 0x%02x\n", __func__, data);
+		}
+	}
+	if (i2c->xmit_size < i2c->msg->len) {
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): RX IRQ enabled\n", __func__);
+		writew(readw(&i2c->regs->rx_fifo.stat_ctrl) | FIFO_IRQEN,
+			&i2c->regs->rx_fifo.stat_ctrl);
+	}
+}
+
+static void netup_i2c_start_xfer(struct netup_i2c *i2c)
+{
+	u16 rdflag = ((i2c->msg->flags & I2C_M_RD) ? 1 : 0);
+	u16 reg = readw(&i2c->regs->twi_ctrl0_stat);
+
+	writew(TWI_IRQEN | reg, &i2c->regs->twi_ctrl0_stat);
+	writew(i2c->msg->len, &i2c->regs->length);
+	writew(TWI_TRANSFER | (i2c->msg->addr << 1) | rdflag,
+		&i2c->regs->twi_addr_ctrl1);
+	dev_dbg(i2c->adap.dev.parent,
+		"%s(): length %d twi_addr_ctrl1 0x%x twi_ctrl0_stat 0x%x\n",
+		__func__, readw(&i2c->regs->length),
+		readw(&i2c->regs->twi_addr_ctrl1),
+		readw(&i2c->regs->twi_ctrl0_stat));
+	i2c->state = STATE_WAIT;
+	i2c->xmit_size = 0;
+	if (!rdflag)
+		netup_i2c_fifo_tx(i2c);
+	else
+		writew(FIFO_IRQEN | readw(&i2c->regs->rx_fifo.stat_ctrl),
+			&i2c->regs->rx_fifo.stat_ctrl);
+}
+
+static int netup_i2c_xfer(struct i2c_adapter *adap,
+			  struct i2c_msg *msgs, int num)
+{
+	unsigned long flags;
+	int i, trans_done, res = num;
+	struct netup_i2c *i2c = i2c_get_adapdata(adap);
+	u16 reg;
+
+	if (num <= 0) {
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): num == %d\n", __func__, num);
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&i2c->lock, flags);
+	if (i2c->state != STATE_DONE) {
+		dev_dbg(i2c->adap.dev.parent,
+			"%s(): i2c->state == %d, resetting I2C\n",
+			__func__, i2c->state);
+		netup_i2c_reset(i2c);
+	}
+	dev_dbg(i2c->adap.dev.parent, "%s() num %d\n", __func__, num);
+	for (i = 0; i < num; i++) {
+		i2c->msg = &msgs[i];
+		netup_i2c_start_xfer(i2c);
+		trans_done = 0;
+		while (!trans_done) {
+			spin_unlock_irqrestore(&i2c->lock, flags);
+			if (wait_event_timeout(i2c->wq,
+					i2c->state != STATE_WAIT,
+					msecs_to_jiffies(NETUP_I2C_TIMEOUT))) {
+				spin_lock_irqsave(&i2c->lock, flags);
+				switch (i2c->state) {
+				case STATE_WANT_READ:
+					netup_i2c_fifo_rx(i2c);
+					break;
+				case STATE_WANT_WRITE:
+					netup_i2c_fifo_tx(i2c);
+					break;
+				case STATE_DONE:
+					if ((i2c->msg->flags & I2C_M_RD) != 0 &&
+						i2c->xmit_size != i2c->msg->len)
+						netup_i2c_fifo_rx(i2c);
+					dev_dbg(i2c->adap.dev.parent,
+						"%s(): msg %d OK\n",
+						__func__, i);
+					trans_done = 1;
+					break;
+				case STATE_ERROR:
+					res = -EIO;
+					dev_dbg(i2c->adap.dev.parent,
+						"%s(): error state\n",
+						__func__);
+					goto done;
+				default:
+					dev_dbg(i2c->adap.dev.parent,
+						"%s(): invalid state %d\n",
+						__func__, i2c->state);
+					res = -EINVAL;
+					goto done;
+				}
+				if (!trans_done) {
+					i2c->state = STATE_WAIT;
+					reg = readw(
+						&i2c->regs->twi_ctrl0_stat);
+					writew(TWI_IRQEN | reg,
+						&i2c->regs->twi_ctrl0_stat);
+				}
+				spin_unlock_irqrestore(&i2c->lock, flags);
+			} else {
+				spin_lock_irqsave(&i2c->lock, flags);
+				dev_dbg(i2c->adap.dev.parent,
+					"%s(): wait timeout\n", __func__);
+				res = -ETIMEDOUT;
+				goto done;
+			}
+			spin_lock_irqsave(&i2c->lock, flags);
+		}
+	}
+done:
+	spin_unlock_irqrestore(&i2c->lock, flags);
+	dev_dbg(i2c->adap.dev.parent, "%s(): result %d\n", __func__, res);
+	return res;
+}
+
+static u32 netup_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm netup_i2c_algorithm = {
+	.master_xfer	= netup_i2c_xfer,
+	.functionality	= netup_i2c_func,
+};
+
+static struct i2c_adapter netup_i2c_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= NETUP_UNIDVB_NAME,
+	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
+	.algo		= &netup_i2c_algorithm,
+};
+
+static int netup_i2c_init(struct netup_unidvb_dev *ndev, int bus_num)
+{
+	int ret;
+	struct netup_i2c *i2c;
+
+	if (bus_num < 0 || bus_num > 1) {
+		dev_err(&ndev->pci_dev->dev,
+			"%s(): invalid bus_num %d\n", __func__, bus_num);
+		return -EINVAL;
+	}
+	i2c = &ndev->i2c[bus_num];
+	spin_lock_init(&i2c->lock);
+	init_waitqueue_head(&i2c->wq);
+	i2c->regs = (struct netup_i2c_regs *)(ndev->bmmio0 +
+		(bus_num == 0 ? NETUP_I2C_BUS0_ADDR : NETUP_I2C_BUS1_ADDR));
+	netup_i2c_reset(i2c);
+	i2c->adap = netup_i2c_adapter;
+	i2c->adap.dev.parent = &ndev->pci_dev->dev;
+	i2c_set_adapdata(&i2c->adap, i2c);
+	ret = i2c_add_adapter(&i2c->adap);
+	if (ret) {
+		dev_err(&ndev->pci_dev->dev,
+			"%s(): failed to add I2C adapter\n", __func__);
+		return ret;
+	}
+	dev_info(&ndev->pci_dev->dev,
+		"%s(): registered I2C bus %d at 0x%x\n",
+		__func__,
+		bus_num, (bus_num == 0 ?
+			NETUP_I2C_BUS0_ADDR :
+			NETUP_I2C_BUS1_ADDR));
+	return 0;
+}
+
+static void netup_i2c_remove(struct netup_unidvb_dev *ndev, int bus_num)
+{
+	struct netup_i2c *i2c;
+
+	if (bus_num < 0 || bus_num > 1) {
+		dev_err(&ndev->pci_dev->dev,
+			"%s(): invalid bus number %d\n", __func__, bus_num);
+		return;
+	}
+	i2c = &ndev->i2c[bus_num];
+	netup_i2c_reset(i2c);
+	/* remove adapter */
+	i2c_del_adapter(&i2c->adap);
+	dev_info(&ndev->pci_dev->dev,
+		"netup_i2c_remove: unregistered I2C bus %d\n", bus_num);
+}
+
+int netup_i2c_register(struct netup_unidvb_dev *ndev)
+{
+	int ret;
+
+	ret = netup_i2c_init(ndev, 0);
+	if (ret)
+		return ret;
+	ret = netup_i2c_init(ndev, 1);
+	if (ret) {
+		netup_i2c_remove(ndev, 0);
+		return ret;
+	}
+	return 0;
+}
+
+void netup_i2c_unregister(struct netup_unidvb_dev *ndev)
+{
+	netup_i2c_remove(ndev, 0);
+	netup_i2c_remove(ndev, 1);
+}
+
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c
new file mode 100644
index 0000000..f55b327
--- /dev/null
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c
@@ -0,0 +1,252 @@
+/*
+ * netup_unidvb_spi.c
+ *
+ * Internal SPI driver for NetUP Universal Dual DVB-CI
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 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.
+ */
+
+#include "netup_unidvb.h"
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+#include <linux/mtd/partitions.h>
+#include <mtd/mtd-abi.h>
+
+#define NETUP_SPI_CTRL_IRQ	0x1000
+#define NETUP_SPI_CTRL_IMASK	0x2000
+#define NETUP_SPI_CTRL_START	0x8000
+#define NETUP_SPI_CTRL_LAST_CS	0x4000
+
+#define NETUP_SPI_TIMEOUT	6000
+
+enum netup_spi_state {
+	SPI_STATE_START,
+	SPI_STATE_DONE,
+};
+
+struct netup_spi_regs {
+	__u8	data[1024];
+	__le16	control_stat;
+	__le16	clock_divider;
+} __packed __aligned(1);
+
+struct netup_spi {
+	struct device			*dev;
+	struct spi_master		*master;
+	struct netup_spi_regs		*regs;
+	u8 __iomem			*mmio;
+	spinlock_t			lock;
+	wait_queue_head_t		waitq;
+	enum netup_spi_state		state;
+};
+
+static char netup_spi_name[64] = "fpga";
+
+static struct mtd_partition netup_spi_flash_partitions = {
+	.name = netup_spi_name,
+	.size = 0x1000000, /* 16MB */
+	.offset = 0,
+	.mask_flags = MTD_CAP_ROM
+};
+
+static struct flash_platform_data spi_flash_data = {
+	.name = "netup0_m25p128",
+	.parts = &netup_spi_flash_partitions,
+	.nr_parts = 1,
+};
+
+static struct spi_board_info netup_spi_board = {
+	.modalias = "m25p128",
+	.max_speed_hz = 11000000,
+	.chip_select = 0,
+	.mode = SPI_MODE_0,
+	.platform_data = &spi_flash_data,
+};
+
+irqreturn_t netup_spi_interrupt(struct netup_spi *spi)
+{
+	u16 reg;
+	unsigned long flags;
+
+	if (!spi) {
+		dev_dbg(&spi->master->dev,
+			"%s(): SPI not initialized\n", __func__);
+		return IRQ_NONE;
+	}
+	spin_lock_irqsave(&spi->lock, flags);
+	reg = readw(&spi->regs->control_stat);
+	if (!(reg & NETUP_SPI_CTRL_IRQ)) {
+		spin_unlock_irqrestore(&spi->lock, flags);
+		dev_dbg(&spi->master->dev,
+			"%s(): not mine interrupt\n", __func__);
+		return IRQ_NONE;
+	}
+	writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
+	reg = readw(&spi->regs->control_stat);
+	writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
+	spi->state = SPI_STATE_DONE;
+	wake_up(&spi->waitq);
+	spin_unlock_irqrestore(&spi->lock, flags);
+	dev_dbg(&spi->master->dev,
+		"%s(): SPI interrupt handled\n", __func__);
+	return IRQ_HANDLED;
+}
+
+static int netup_spi_transfer(struct spi_master *master,
+			      struct spi_message *msg)
+{
+	struct netup_spi *spi = spi_master_get_devdata(master);
+	struct spi_transfer *t;
+	int result = 0;
+	u32 tr_size;
+
+	/* reset CS */
+	writew(NETUP_SPI_CTRL_LAST_CS, &spi->regs->control_stat);
+	writew(0, &spi->regs->control_stat);
+	list_for_each_entry(t, &msg->transfers, transfer_list) {
+		tr_size = t->len;
+		while (tr_size) {
+			u32 frag_offset = t->len - tr_size;
+			u32 frag_size = (tr_size > sizeof(spi->regs->data)) ?
+					sizeof(spi->regs->data) : tr_size;
+			int frag_last = 0;
+
+			if (list_is_last(&t->transfer_list,
+					&msg->transfers) &&
+					frag_offset + frag_size == t->len) {
+				frag_last = 1;
+			}
+			if (t->tx_buf) {
+				memcpy_toio(spi->regs->data,
+					t->tx_buf + frag_offset,
+					frag_size);
+			} else {
+				memset_io(spi->regs->data,
+					0, frag_size);
+			}
+			spi->state = SPI_STATE_START;
+			writew((frag_size & 0x3ff) |
+				NETUP_SPI_CTRL_IMASK |
+				NETUP_SPI_CTRL_START |
+				(frag_last ? NETUP_SPI_CTRL_LAST_CS : 0),
+				&spi->regs->control_stat);
+			dev_dbg(&spi->master->dev,
+				"%s(): control_stat 0x%04x\n",
+				__func__, readw(&spi->regs->control_stat));
+			wait_event_timeout(spi->waitq,
+				spi->state != SPI_STATE_START,
+				msecs_to_jiffies(NETUP_SPI_TIMEOUT));
+			if (spi->state == SPI_STATE_DONE) {
+				if (t->rx_buf) {
+					memcpy_fromio(t->rx_buf + frag_offset,
+						spi->regs->data, frag_size);
+				}
+			} else {
+				if (spi->state == SPI_STATE_START) {
+					dev_dbg(&spi->master->dev,
+						"%s(): transfer timeout\n",
+						__func__);
+				} else {
+					dev_dbg(&spi->master->dev,
+						"%s(): invalid state %d\n",
+						__func__, spi->state);
+				}
+				result = -EIO;
+				goto done;
+			}
+			tr_size -= frag_size;
+			msg->actual_length += frag_size;
+		}
+	}
+done:
+	msg->status = result;
+	spi_finalize_current_message(master);
+	return result;
+}
+
+static int netup_spi_setup(struct spi_device *spi)
+{
+	return 0;
+}
+
+int netup_spi_init(struct netup_unidvb_dev *ndev)
+{
+	struct spi_master *master;
+	struct netup_spi *nspi;
+
+	master = spi_alloc_master(&ndev->pci_dev->dev,
+		sizeof(struct netup_spi));
+	if (!master) {
+		dev_err(&ndev->pci_dev->dev,
+			"%s(): unable to alloc SPI master\n", __func__);
+		return -EINVAL;
+	}
+	nspi = spi_master_get_devdata(master);
+	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
+	master->bus_num = -1;
+	master->num_chipselect = 1;
+	master->transfer_one_message = netup_spi_transfer;
+	master->setup = netup_spi_setup;
+	spin_lock_init(&nspi->lock);
+	init_waitqueue_head(&nspi->waitq);
+	nspi->master = master;
+	nspi->regs = (struct netup_spi_regs *)(ndev->bmmio0 + 0x4000);
+	writew(2, &nspi->regs->clock_divider);
+	writew(NETUP_UNIDVB_IRQ_SPI, ndev->bmmio0 + REG_IMASK_SET);
+	ndev->spi = nspi;
+	if (spi_register_master(master)) {
+		ndev->spi = NULL;
+		dev_err(&ndev->pci_dev->dev,
+			"%s(): unable to register SPI bus\n", __func__);
+		return -EINVAL;
+	}
+	snprintf(netup_spi_name,
+		sizeof(netup_spi_name),
+		"fpga_%02x:%02x.%01x",
+		ndev->pci_bus,
+		ndev->pci_slot,
+		ndev->pci_func);
+	if (!spi_new_device(master, &netup_spi_board)) {
+		ndev->spi = NULL;
+		dev_err(&ndev->pci_dev->dev,
+			"%s(): unable to create SPI device\n", __func__);
+		return -EINVAL;
+	}
+	dev_dbg(&ndev->pci_dev->dev, "%s(): SPI init OK\n", __func__);
+	return 0;
+}
+
+void netup_spi_release(struct netup_unidvb_dev *ndev)
+{
+	u16 reg;
+	unsigned long flags;
+	struct netup_spi *spi = ndev->spi;
+
+	if (!spi) {
+		dev_dbg(&spi->master->dev,
+			"%s(): SPI not initialized\n", __func__);
+		return;
+	}
+	spin_lock_irqsave(&spi->lock, flags);
+	reg = readw(&spi->regs->control_stat);
+	writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
+	reg = readw(&spi->regs->control_stat);
+	writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
+	spin_unlock_irqrestore(&spi->lock, flags);
+	spi_unregister_master(spi->master);
+	ndev->spi = NULL;
+}
+
+
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH V2 1/5] [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver
  2015-04-15 10:07 ` [PATCH V2 1/5] [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver serjk
@ 2015-05-14 13:58   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2015-05-14 13:58 UTC (permalink / raw)
  To: serjk; +Cc: linux-media, aospan1

Em Wed, 15 Apr 2015 13:07:46 +0300
serjk@netup.ru escreveu:

> From: Kozlov Sergey <serjk@netup.ru>
> 
> Add DVB-S/S2 frontend driver for Sony Horus3A (CXD2832AER) chip
> 
> Changes in version 2:
>     - rename MAINTAINERS entry
>     - fix coding style
>     - use dynamic debug instead of module-specifig debug parameter
>     - fix I2C bus error handling
> 
> Signed-off-by: Kozlov Sergey <serjk@netup.ru>
> ---
>  MAINTAINERS                           |    9 +
>  drivers/media/dvb-frontends/Kconfig   |    7 +
>  drivers/media/dvb-frontends/Makefile  |    1 +
>  drivers/media/dvb-frontends/horus3a.c |  413 +++++++++++++++++++++++++++++++++
>  drivers/media/dvb-frontends/horus3a.h |   58 +++++
>  5 files changed, 488 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/horus3a.c
>  create mode 100644 drivers/media/dvb-frontends/horus3a.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index eaf9996..7ba61b2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6269,6 +6269,15 @@ W:	http://linuxtv.org
>  S:	Maintained
>  F:	drivers/media/radio/radio-maxiradio*
>  
> +MEDIA DRIVERS FOR HORUS3A
> +M:	Sergey Kozlov <serjk@netup.ru>
> +L:	linux-media@vger.kernel.org
> +W:	http://linuxtv.org/
> +W:	http://netup.tv/
> +T:	git git://linuxtv.org/media_tree.git
> +S:	Supported
> +F:	drivers/media/dvb-frontends/horus3a*
> +
>  MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
>  M:	Mauro Carvalho Chehab <mchehab@osg.samsung.com>
>  P:	LinuxTV.org Project
> diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
> index bb76727..bd5ab69 100644
> --- a/drivers/media/dvb-frontends/Kconfig
> +++ b/drivers/media/dvb-frontends/Kconfig
> @@ -798,6 +798,13 @@ config DVB_AF9033
>  	depends on DVB_CORE && I2C
>  	default m if !MEDIA_SUBDRV_AUTOSELECT
>  
> +config DVB_HORUS3A
> +	tristate "Sony Horus3A tuner"
> +	depends on DVB_CORE && I2C
> +	default m if !MEDIA_SUBDRV_AUTOSELECT
> +	help
> +	  Say Y when you want to support this frontend.
> +
>  comment "Tools to develop new frontends"
>  
>  config DVB_DUMMY_FE
> diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
> index ba59df6..3aa05f3 100644
> --- a/drivers/media/dvb-frontends/Makefile
> +++ b/drivers/media/dvb-frontends/Makefile
> @@ -116,3 +116,4 @@ obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
>  obj-$(CONFIG_DVB_AF9033) += af9033.o
>  obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
>  obj-$(CONFIG_DVB_TC90522) += tc90522.o
> +obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
> diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c
> new file mode 100644
> index 0000000..54cf112
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/horus3a.c
> @@ -0,0 +1,413 @@
> +/*
> + * horus3a.h
> + *
> + * Sony Horus3A DVB-S/S2 tuner driver
> + *
> + * Copyright 2012 Sony Corporation
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/dvb/frontend.h>
> +#include <linux/types.h>
> +#include "horus3a.h"
> +#include "dvb_frontend.h"
> +
> +enum horus3a_state {
> +	STATE_UNKNOWN,
> +	STATE_SLEEP,
> +	STATE_ACTIVE
> +};
> +
> +struct horus3a_priv {
> +	u32			frequency;
> +	u8			i2c_address;
> +	struct i2c_adapter	*i2c;
> +	enum horus3a_state	state;
> +	void			*set_tuner_data;
> +	int			(*set_tuner)(void *, int);
> +};
> +
> +static void horus3a_i2c_debug(struct horus3a_priv *priv,
> +			      u8 reg, u8 write, const u8 *data, u32 len)
> +{
> +	dev_dbg(&priv->i2c->dev, "horus3a: I2C %s reg 0x%02x size %d\n",
> +		(write == 0 ? "read" : "write"), reg, len);
> +#if defined(CONFIG_DYNAMIC_DEBUG)
> +	dynamic_hex_dump("horus3a: I2C data: ",
> +		DUMP_PREFIX_OFFSET, 16, 1, data, len, false);
> +#endif
> +}
> +
> +static int horus3a_write_regs(struct horus3a_priv *priv,
> +			      u8 reg, const u8 *data, u32 len)
> +{
> +	int ret;
> +	u8 buf[len+1];
> +	struct i2c_msg msg[1] = {
> +		{
> +			.addr = priv->i2c_address,
> +			.flags = 0,
> +			.len = sizeof(buf),
> +			.buf = buf,
> +		}
> +	};
> +
> +	horus3a_i2c_debug(priv, reg, 1, data, len);
> +
> +	buf[0] = reg;
> +	memcpy(&buf[1], data, len);
> +
> +	ret = i2c_transfer(priv->i2c, msg, 1);
> +	if (ret >= 0 && ret != 1)
> +		ret = -EREMOTEIO;
> +	if (ret < 0) {
> +		dev_warn(&priv->i2c->dev,
> +			"%s: i2c wr failed=%d reg=%02x len=%d\n",
> +			KBUILD_MODNAME, ret, reg, len);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int horus3a_write_reg(struct horus3a_priv *priv, u8 reg, u8 val)
> +{
> +	return horus3a_write_regs(priv, reg, &val, 1);
> +}
> +
> +static int horus3a_enter_power_save(struct horus3a_priv *priv)
> +{
> +	u8 data[2];
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state == STATE_SLEEP)
> +		return 0;
> +	/* IQ Generator disable */
> +	horus3a_write_reg(priv, 0x2a, 0x79);
> +	/* MDIV_EN = 0 */
> +	horus3a_write_reg(priv, 0x29, 0x70);
> +	/* VCO disable preparation */
> +	horus3a_write_reg(priv, 0x28, 0x3e);
> +	/* VCO buffer disable */
> +	horus3a_write_reg(priv, 0x2a, 0x19);
> +	/* VCO calibration disable */
> +	horus3a_write_reg(priv, 0x1c, 0x00);
> +	/* Power save setting (xtal is not stopped) */
> +	data[0] = 0xC0;
> +	/* LNA is Disabled */
> +	data[1] = 0xA7;
> +	/* 0x11 - 0x12 */
> +	horus3a_write_regs(priv, 0x11, data, sizeof(data));
> +	priv->state = STATE_SLEEP;
> +	return 0;
> +}
> +
> +static int horus3a_leave_power_save(struct horus3a_priv *priv)
> +{
> +	u8 data[2];
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state == STATE_ACTIVE)
> +		return 0;
> +	/* Leave power save */
> +	data[0] = 0x00;
> +	/* LNA is Disabled */
> +	data[1] = 0xa7;
> +	/* 0x11 - 0x12 */
> +	horus3a_write_regs(priv, 0x11, data, sizeof(data));
> +	/* VCO buffer enable */
> +	horus3a_write_reg(priv, 0x2a, 0x79);
> +	/* VCO calibration enable */
> +	horus3a_write_reg(priv, 0x1c, 0xc0);
> +	/* MDIV_EN = 1 */
> +	horus3a_write_reg(priv, 0x29, 0x71);
> +	usleep_range(5000, 7000);
> +	priv->state = STATE_ACTIVE;
> +	return 0;
> +}
> +
> +static int horus3a_init(struct dvb_frontend *fe)
> +{
> +	struct horus3a_priv *priv = fe->tuner_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	return 0;
> +}
> +
> +static int horus3a_release(struct dvb_frontend *fe)
> +{
> +	struct horus3a_priv *priv = fe->tuner_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	kfree(fe->tuner_priv);
> +	fe->tuner_priv = NULL;
> +	return 0;
> +}
> +
> +static int horus3a_sleep(struct dvb_frontend *fe)
> +{
> +	struct horus3a_priv *priv = fe->tuner_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	horus3a_enter_power_save(priv);
> +	return 0;
> +}
> +
> +static int horus3a_set_params(struct dvb_frontend *fe)
> +{
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +	struct horus3a_priv *priv = fe->tuner_priv;
> +	u32 frequency = p->frequency;
> +	u32 symbol_rate = p->symbol_rate/1000;
> +	u8 mixdiv = 0;
> +	u8 mdiv = 0;
> +	u32 ms = 0;
> +	u8 f_ctl = 0;
> +	u8 g_ctl = 0;
> +	u8 fc_lpf = 0;
> +	u8 data[5];
> +
> +	dev_dbg(&priv->i2c->dev, "%s(): frequency %dkHz symbol_rate %dksps\n",
> +		__func__, frequency, symbol_rate);
> +	if (priv->set_tuner)
> +		priv->set_tuner(priv->set_tuner_data, 0);
> +	if (priv->state == STATE_SLEEP)
> +		horus3a_leave_power_save(priv);
> +
> +	/* frequency should be X MHz (X : integer) */
> +	frequency = DIV_ROUND_CLOSEST(frequency, 1000) * 1000;
> +	if (frequency <= 1155000) {
> +		mixdiv = 4;
> +		mdiv = 1;
> +	} else {
> +		mixdiv = 2;
> +		mdiv = 0;
> +	}
> +	/* Assumed that fREF == 1MHz (1000kHz) */
> +	ms = DIV_ROUND_CLOSEST((frequency * mixdiv) / 2, 1000);
> +	if (ms > 0x7FFF) { /* 15 bit */
> +		dev_err(&priv->i2c->dev, "horus3a: invalid frequency %d\n",
> +			frequency);
> +		return -EINVAL;
> +	}
> +	if (frequency < 975000) {
> +		/* F_CTL=11100 G_CTL=001 */
> +		f_ctl = 0x1C;
> +		g_ctl = 0x01;
> +	} else if (frequency < 1050000) {
> +		/* F_CTL=11000 G_CTL=010 */
> +		f_ctl = 0x18;
> +		g_ctl = 0x02;
> +	} else if (frequency < 1150000) {
> +		/* F_CTL=10100 G_CTL=010 */
> +		f_ctl = 0x14;
> +		g_ctl = 0x02;
> +	} else if (frequency < 1250000) {
> +		/* F_CTL=10000 G_CTL=011 */
> +		f_ctl = 0x10;
> +		g_ctl = 0x03;
> +	} else if (frequency < 1350000) {
> +		/* F_CTL=01100 G_CTL=100 */
> +		f_ctl = 0x0C;
> +		g_ctl = 0x04;
> +	} else if (frequency < 1450000) {
> +		/* F_CTL=01010 G_CTL=100 */
> +		f_ctl = 0x0A;
> +		g_ctl = 0x04;
> +	} else if (frequency < 1600000) {
> +		/* F_CTL=00111 G_CTL=101 */
> +		f_ctl = 0x07;
> +		g_ctl = 0x05;
> +	} else if (frequency < 1800000) {
> +		/* F_CTL=00100 G_CTL=010 */
> +		f_ctl = 0x04;
> +		g_ctl = 0x02;
> +	} else if (frequency < 2000000) {
> +		/* F_CTL=00010 G_CTL=001 */
> +		f_ctl = 0x02;
> +		g_ctl = 0x01;
> +	} else {
> +		/* F_CTL=00000 G_CTL=000 */
> +		f_ctl = 0x00;
> +		g_ctl = 0x00;
> +	}
> +	/* LPF cutoff frequency setting */
> +	switch (p->delivery_system) {
> +	case SYS_DVBS:
> +		/*
> +		 * rolloff = 0.35
> +		 * SR <= 4.3
> +		 * fc_lpf = 5
> +		 * 4.3 < SR <= 10
> +		 * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 =
> +		 *	SR * 1.175 = SR * (47/40)
> +		 * 10 < SR
> +		 * fc_lpf = SR * (1 + rolloff) / 2 + 5 =
> +		 *	SR * 0.675 + 5 = SR * (27/40) + 5
> +		 * NOTE: The result should be round up.
> +		 */
> +		if (symbol_rate <= 4300)
> +			fc_lpf = 5;
> +		else if (symbol_rate <= 10000)
> +			fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 47, 40000);
> +		else
> +			fc_lpf = (u8)(DIV_ROUND_UP(symbol_rate * 27,
> +					40000) + 5);
> +		/* 5 <= fc_lpf <= 36 */
> +		if (fc_lpf > 36)
> +			fc_lpf = 36;
> +		break;
> +	case SYS_DVBS2:
> +		/*
> +		 * rolloff = 0.2
> +		 * SR <= 4.5
> +		 * fc_lpf = 5
> +		 * 4.5 < SR <= 10
> +		 * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 =
> +		 *	SR * 1.1 = SR * (11/10)
> +		 * 10 < SR
> +		 * fc_lpf = SR * (1 + rolloff) / 2 + 5 =
> +		 *	SR * 0.6 + 5 = SR * (3/5) + 5
> +		 * NOTE: The result should be round up.
> +		 */

Hmm... sorry for not noticing this at the first review, but assuming that 
rolloff will always be 0.2 for DVB-S2 is wrong.

The rolloff should actually be passed via DVBv5 API for DVB-S2:
	http://linuxtv.org/downloads/v4l-dvb-apis/FE_GET_SET_PROPERTY.html#frontend-property-satellital-systems
	http://linuxtv.org/downloads/v4l-dvb-apis/FE_GET_SET_PROPERTY.html#DTV-ROLLOFF 

So, the code should actually be doing something like:

	int rolloff;
	switch (p->rolloff) {
	case ROLLOFF_35:
		rolloff = 35;
		break;
	case ROLLOFF_20:
		rolloff = 20;
		break;
	case ROLLOFF_25:
		rolloff = 25;
		break;
	case ROLLOFF_AUTO:
	default:	/* Auto is not supported */
		return -EINVAL;
	}

and use the rolloff/100 on the calculus for fc_lpf, as specified on your
comments.

> +		if (symbol_rate <= 4500)
> +			fc_lpf = 5;
> +		else if (symbol_rate <= 10000)
> +			fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 11, 10000);
> +		else
> +			fc_lpf = (u8)(DIV_ROUND_UP(symbol_rate * 3,
> +					5000) + 5);
> +		/* 5 <= fc_lpf <= 36 is valid */
> +		if (fc_lpf > 36)
> +			fc_lpf = 36;
> +		break;
> +	default:
> +		dev_err(&priv->i2c->dev,
> +			"horus3a: invalid delivery system %d\n",
> +			p->delivery_system);
> +		return -EINVAL;
> +	}
> +	/* 0x00 - 0x04 */
> +	data[0] = (u8)((ms >> 7) & 0xFF);
> +	data[1] = (u8)((ms << 1) & 0xFF);
> +	data[2] = 0x00;
> +	data[3] = 0x00;
> +	data[4] = (u8)(mdiv << 7);
> +	horus3a_write_regs(priv, 0x00, data, sizeof(data));
> +	/* Write G_CTL, F_CTL */
> +	horus3a_write_reg(priv, 0x09, (u8)((g_ctl << 5) | f_ctl));
> +	/* Write LPF cutoff frequency */
> +	horus3a_write_reg(priv, 0x37, (u8)(0x80 | (fc_lpf << 1)));
> +	/* Start Calibration */
> +	horus3a_write_reg(priv, 0x05, 0x80);
> +	/* IQ Generator enable */
> +	horus3a_write_reg(priv, 0x2a, 0x7b);
> +	/* tuner stabilization time */
> +	msleep(60);
> +	/* Store tuned frequency to the struct */
> +	priv->frequency = ms * 2 * 1000 / mixdiv;
> +	return 0;
> +}
> +

> +static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency)
> +{
> +	struct horus3a_priv *priv = fe->tuner_priv;
> +	*frequency = priv->frequency;

This is a minor issue, but please check your patches with checkpach.pl.
You should add a blank line between variable declarations and the code.
So, the proper CodingStyle would be:

static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
	struct horus3a_priv *priv = fe->tuner_priv;

	*frequency = priv->frequency;

> +	return 0;
> +}
> +
> +static struct dvb_tuner_ops horus3a_tuner_ops = {
> +	.info = {
> +		.name = "Sony Horus3a",
> +		.frequency_min = 950000,
> +		.frequency_max = 2150000,
> +		.frequency_step = 1000,
> +	},
> +	.init = horus3a_init,
> +	.release = horus3a_release,
> +	.sleep = horus3a_sleep,
> +	.set_params = horus3a_set_params,
> +	.get_frequency = horus3a_get_frequency,
> +};
> +
> +struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
> +				    const struct horus3a_config *config,
> +				    struct i2c_adapter *i2c)
> +{
> +	u8 buf[3], val;
> +	struct horus3a_priv *priv = NULL;
> +
> +	priv = kzalloc(sizeof(struct horus3a_priv), GFP_KERNEL);
> +	if (priv == NULL)
> +		return NULL;
> +	priv->i2c_address = (config->i2c_address >> 1);
> +	priv->i2c = i2c;
> +	priv->set_tuner_data = config->set_tuner_priv;
> +	priv->set_tuner = config->set_tuner_callback;
> +
> +	if (fe->ops.i2c_gate_ctrl)
> +		fe->ops.i2c_gate_ctrl(fe, 1);
> +
> +	/* wait 4ms after power on */
> +	usleep_range(4000, 6000);
> +	/* IQ Generator disable */
> +	horus3a_write_reg(priv, 0x2a, 0x79);
> +	/* REF_R = Xtal Frequency */
> +	buf[0] = config->xtal_freq_mhz;
> +	buf[1] = config->xtal_freq_mhz;
> +	buf[2] = 0;
> +	/* 0x6 - 0x8 */
> +	horus3a_write_regs(priv, 0x6, buf, 3);
> +	/* IQ Out = Single Ended */
> +	horus3a_write_reg(priv, 0x0a, 0x40);
> +	switch (config->xtal_freq_mhz) {
> +	case 27:
> +		val = 0x1f;
> +		break;
> +	case 24:
> +		val = 0x10;
> +		break;
> +	case 16:
> +		val = 0xc;
> +		break;
> +	default:
> +		val = 0;
> +		dev_warn(&priv->i2c->dev,
> +			"horus3a: invalid xtal frequency %dMHz\n",
> +			config->xtal_freq_mhz);
> +		break;
> +	}
> +	val <<= 2;
> +	horus3a_write_reg(priv, 0x0e, val);
> +	horus3a_enter_power_save(priv);
> +	usleep_range(3000, 5000);
> +
> +	if (fe->ops.i2c_gate_ctrl)
> +		fe->ops.i2c_gate_ctrl(fe, 0);
> +
> +	memcpy(&fe->ops.tuner_ops, &horus3a_tuner_ops,
> +				sizeof(struct dvb_tuner_ops));
> +	fe->tuner_priv = priv;
> +	dev_info(&priv->i2c->dev,
> +		"Sony HORUS3A attached on addr=%x at I2C adapter %p\n",
> +		priv->i2c_address, priv->i2c);
> +	return fe;
> +}
> +EXPORT_SYMBOL(horus3a_attach);
> +
> +MODULE_DESCRIPTION("Sony HORUS3A sattelite tuner driver");
> +MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/dvb-frontends/horus3a.h b/drivers/media/dvb-frontends/horus3a.h
> new file mode 100644
> index 0000000..c38f946
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/horus3a.h
> @@ -0,0 +1,58 @@
> +/*
> + * horus3a.h
> + *
> + * Sony Horus3A DVB-S/S2 tuner driver
> + *
> + * Copyright 2012 Sony Corporation
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> +  */
> +
> +#ifndef __DVB_HORUS3A_H__
> +#define __DVB_HORUS3A_H__
> +
> +#include <linux/kconfig.h>
> +#include <linux/dvb/frontend.h>
> +#include <linux/i2c.h>
> +
> +/**
> + * struct horus3a_config - the configuration of Horus3A tuner driver
> + * @i2c_address:    I2C address of the tuner
> + * @xtal_freq_mhz:  Oscillator frequency, MHz
> + * @set_tuner_priv: Callback function private context
> + * @set_tuner_callback: Callback function that notifies the parent driver
> + *          which tuner is active now
> + */
> +struct horus3a_config {
> +	u8	i2c_address;
> +	u8	xtal_freq_mhz;
> +	void	*set_tuner_priv;
> +	int	(*set_tuner_callback)(void *, int);
> +};
> +
> +#if IS_ENABLED(CONFIG_DVB_HORUS3A)

We're actually using IS_REACHABLE(), as IS_ENABLED doesn't work fine
on some situations.

> +extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
> +					const struct horus3a_config *config,
> +					struct i2c_adapter *i2c);
> +#else
> +static inline struct dvb_frontend *horus3a_attach(
> +					const struct cxd2820r_config *config,
> +					struct i2c_adapter *i2c)
> +{
> +	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +#endif
> +
> +#endif

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V2 3/5] [media] lnbh25: LNBH25 SEC controller driver
  2015-04-15 10:07 ` [PATCH V2 3/5] [media] lnbh25: LNBH25 SEC controller driver serjk
@ 2015-05-14 14:05   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2015-05-14 14:05 UTC (permalink / raw)
  To: serjk; +Cc: linux-media, aospan1

Em Wed, 15 Apr 2015 13:07:48 +0300
serjk@netup.ru escreveu:

> From: Kozlov Sergey <serjk@netup.ru>
> 
> Add DVB SEC frontend driver for STM LNBH25PQR chip.
> 
> Changes in version 2:
>     - rename MAINTAINERS entry
>     - fix coding style
>     - use dynamic debug instead of module-specifig debug parameter
>     - fix I2C bus error handling
> 
> Signed-off-by: Kozlov Sergey <serjk@netup.ru>
> ---
>  MAINTAINERS                          |    9 ++
>  drivers/media/dvb-frontends/Kconfig  |    8 ++
>  drivers/media/dvb-frontends/Makefile |    1 +
>  drivers/media/dvb-frontends/lnbh25.c |  192 ++++++++++++++++++++++++++++++++++
>  drivers/media/dvb-frontends/lnbh25.h |   56 ++++++++++
>  5 files changed, 266 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/lnbh25.c
>  create mode 100644 drivers/media/dvb-frontends/lnbh25.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9950fbe..4695cdc 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6287,6 +6287,15 @@ T:	git git://linuxtv.org/media_tree.git
>  S:	Supported
>  F:	drivers/media/dvb-frontends/horus3a*
>  
> +MEDIA DRIVERS FOR LNBH25
> +M:	Sergey Kozlov <serjk@netup.ru>
> +L:	linux-media@vger.kernel.org
> +W:	http://linuxtv.org/
> +W:	http://netup.tv/
> +T:	git git://linuxtv.org/media_tree.git
> +S:	Supported
> +F:	drivers/media/dvb-frontends/lnbh25*
> +
>  MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
>  M:	Mauro Carvalho Chehab <mchehab@osg.samsung.com>
>  P:	LinuxTV.org Project
> diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
> index d178aca..eec0405 100644
> --- a/drivers/media/dvb-frontends/Kconfig
> +++ b/drivers/media/dvb-frontends/Kconfig
> @@ -695,6 +695,14 @@ comment "SEC control devices for DVB-S"
>  
>  source "drivers/media/dvb-frontends/drx39xyj/Kconfig"
>  
> +config DVB_LNBH25
> +	tristate "LNBH25 SEC controller"
> +	depends on DVB_CORE && I2C
> +	default m if !MEDIA_SUBDRV_AUTOSELECT
> +	help
> +	  An SEC control chip.
> +	  Say Y when you want to support this chip.
> +
>  config DVB_LNBP21
>  	tristate "LNBP21/LNBH24 SEC controllers"
>  	depends on DVB_CORE && I2C
> diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
> index 0b19c10..06a0d21 100644
> --- a/drivers/media/dvb-frontends/Makefile
> +++ b/drivers/media/dvb-frontends/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
>  obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
>  obj-$(CONFIG_DVB_LG2160) += lg2160.o
>  obj-$(CONFIG_DVB_CX24123) += cx24123.o
> +obj-$(CONFIG_DVB_LNBH25) += lnbh25.o
>  obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
>  obj-$(CONFIG_DVB_LNBP22) += lnbp22.o
>  obj-$(CONFIG_DVB_ISL6405) += isl6405.o
> diff --git a/drivers/media/dvb-frontends/lnbh25.c b/drivers/media/dvb-frontends/lnbh25.c
> new file mode 100644
> index 0000000..e44a088
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/lnbh25.c
> @@ -0,0 +1,192 @@
> +/*
> + * lnbh25.c
> + *
> + * Driver for LNB supply and control IC LNBH25
> + *
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +
> +#include "dvb_frontend.h"
> +#include "lnbh25.h"
> +
> +/**
> + * struct lnbh25_priv - LNBH25 driver private data
> + * @i2c:		pointer to the I2C adapter structure
> + * @i2c_address:	I2C address of LNBH25 SEC chip
> + * @config:		Registers configuration:
> + *			offset 0: 1st register address, always 0x02 (DATA1)
> + *			offset 1: DATA1 register value
> + *			offset 2: DATA2 register value
> + */
> +struct lnbh25_priv {
> +	struct i2c_adapter	*i2c;
> +	u8			i2c_address;
> +	u8			config[3];
> +};
> +
> +#define LNBH25_STATUS_OFL	0x1
> +#define LNBH25_STATUS_VMON	0x4
> +#define LNBH25_VSEL_13		0x03
> +#define LNBH25_VSEL_18		0x0a
> +
> +static int lnbh25_read_vmon(struct lnbh25_priv *priv)
> +{
> +	int i, ret;
> +	u8 addr = 0x00;
> +	u8 status[6];
> +	struct i2c_msg msg[2] = {
> +		{
> +			.addr = priv->i2c_address,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = &addr
> +		}, {
> +			.addr = priv->i2c_address,
> +			.flags = I2C_M_RD,
> +			.len = sizeof(status),
> +			.buf = status
> +		}
> +	};
> +
> +	for (i = 0; i < 2; i++) {
> +		ret = i2c_transfer(priv->i2c, &msg[i], 1);
> +		if (ret >= 0 && ret != 1)
> +			ret = -EIO;
> +		if (ret < 0) {
> +			dev_dbg(&priv->i2c->dev,
> +				"%s(): I2C transfer %d failed (%d)\n",
> +				__func__, i, ret);
> +			return ret;
> +		}
> +	}
> +#if defined(CONFIG_DYNAMIC_DEBUG)
> +	dynamic_hex_dump("lnbh25_read_vmon: ", DUMP_PREFIX_OFFSET,
> +		16, 1, status, sizeof(status), false);
> +#endif

This is OK as is, as this is just for debugging purposes, but why don't
you use, instead, print_hex_dump()? (same applies to the other patches
on this series)

That would avoid the #if, and would work with both dynamic and
non-dynamic cases, as it is declared on include/linux/printk.h as:

#if defined(CONFIG_DYNAMIC_DEBUG)
#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len)	\
	dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true)
#else
extern void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
				 const void *buf, size_t len);
#endif /* defined(CONFIG_DYNAMIC_DEBUG) */

> +
> +	if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) {
> +		dev_err(&priv->i2c->dev,
> +			"%s(): voltage in failure state, status reg 0x%x\n",
> +			__func__, status[0]);
> +		return -EIO;
> +	}
> +	return 0;
> +}
> +
> +static int lnbh25_set_voltage(struct dvb_frontend *fe,
> +			      fe_sec_voltage_t voltage)
> +{
> +	int ret;
> +	u8 data1_reg;
> +	const char *vsel;
> +	struct lnbh25_priv *priv = fe->sec_priv;
> +	struct i2c_msg msg = {
> +		.addr = priv->i2c_address,
> +		.flags = 0,
> +		.len = sizeof(priv->config),
> +		.buf = priv->config
> +	};
> +
> +	switch (voltage) {
> +	case SEC_VOLTAGE_OFF:
> +		data1_reg = 0x00;
> +		vsel = "Off";
> +		break;
> +	case SEC_VOLTAGE_13:
> +		data1_reg = LNBH25_VSEL_13;
> +		vsel = "13V";
> +		break;
> +	case SEC_VOLTAGE_18:
> +		data1_reg = LNBH25_VSEL_18;
> +		vsel = "18V";
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	priv->config[1] = data1_reg;
> +	dev_dbg(&priv->i2c->dev,
> +		"%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n",
> +		__func__, vsel, priv->i2c_address,
> +		priv->config[0], priv->config[1], priv->config[2]);
> +	ret = i2c_transfer(priv->i2c, &msg, 1);
> +	if (ret >= 0 && ret != 1)
> +		ret = -EIO;
> +	if (ret < 0) {
> +		dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +	if (voltage != SEC_VOLTAGE_OFF) {
> +		msleep(120);
> +		ret = lnbh25_read_vmon(priv);
> +	} else {
> +		msleep(20);
> +		ret = 0;
> +	}
> +	return ret;
> +}
> +
> +static void lnbh25_release(struct dvb_frontend *fe)
> +{
> +	struct lnbh25_priv *priv = fe->sec_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF);
> +	kfree(fe->sec_priv);
> +	fe->sec_priv = NULL;
> +}
> +
> +struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe,
> +				   struct lnbh25_config *cfg,
> +				   struct i2c_adapter *i2c)
> +{
> +	struct lnbh25_priv *priv;
> +
> +	dev_dbg(&i2c->dev, "%s()\n", __func__);
> +	priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL);
> +	if (!priv)
> +		return NULL;
> +	priv->i2c_address = (cfg->i2c_address >> 1);
> +	priv->i2c = i2c;
> +	priv->config[0] = 0x02;
> +	priv->config[1] = 0x00;
> +	priv->config[2] = cfg->data2_config;
> +	fe->sec_priv = priv;
> +	if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) {
> +		dev_err(&i2c->dev,
> +			"%s(): no LNBH25 found at I2C addr 0x%02x\n",
> +			__func__, priv->i2c_address);
> +		kfree(priv);
> +		fe->sec_priv = NULL;
> +		return NULL;
> +	}
> +
> +	fe->ops.release_sec = lnbh25_release;
> +	fe->ops.set_voltage = lnbh25_set_voltage;
> +
> +	dev_err(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n",
> +		__func__, priv->i2c_address);
> +	return fe;
> +}
> +EXPORT_SYMBOL(lnbh25_attach);
> +
> +MODULE_DESCRIPTION("ST LNBH25 driver");
> +MODULE_AUTHOR("info@netup.ru");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/dvb-frontends/lnbh25.h b/drivers/media/dvb-frontends/lnbh25.h
> new file mode 100644
> index 0000000..7fc5123
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/lnbh25.h
> @@ -0,0 +1,56 @@
> +/*
> + * lnbh25.c
> + *
> + * Driver for LNB supply and control IC LNBH25
> + *
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#ifndef LNBH25_H
> +#define LNBH25_H
> +
> +#include <linux/i2c.h>
> +#include <linux/kconfig.h>
> +#include <linux/dvb/frontend.h>
> +
> +/* 22 kHz tone enabled. Tone output controlled by DSQIN pin */
> +#define	LNBH25_TEN	0x01
> +/* Low power mode activated (used only with 22 kHz tone output disabled) */
> +#define LNBH25_LPM	0x02
> +/* DSQIN input pin is set to receive external 22 kHz TTL signal source */
> +#define LNBH25_EXTM	0x04
> +
> +struct lnbh25_config {
> +	u8	i2c_address;
> +	u8	data2_config;
> +};
> +
> +#if IS_ENABLED(CONFIG_DVB_LNBH25)

IS_REACHABLE()

> +struct dvb_frontend *lnbh25_attach(
> +	struct dvb_frontend *fe,
> +	struct lnbh25_config *cfg,
> +	struct i2c_adapter *i2c);
> +#else
> +static inline dvb_frontend *lnbh25_attach(
> +	struct dvb_frontend *fe,
> +	struct lnbh25_config *cfg,
> +	struct i2c_adapter *i2c)
> +{
> +	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +#endif
> +
> +#endif

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V2 2/5] [media] ascot2e: Sony Ascot2e DVB-C/T/T2 tuner driver
  2015-04-15 10:07 ` [PATCH V2 2/5] [media] ascot2e: Sony Ascot2e DVB-C/T/T2 " serjk
@ 2015-05-14 14:07   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2015-05-14 14:07 UTC (permalink / raw)
  To: serjk; +Cc: linux-media, aospan1

Em Wed, 15 Apr 2015 13:07:47 +0300
serjk@netup.ru escreveu:

> From: Kozlov Sergey <serjk@netup.ru>
> 
> Add DVB-T/T2/C frontend driver for Sony Ascot2e (CXD2861ER) chip.
> 
> Changes in version 2:
>     - rename MAINTAINERS entry
>     - fix coding style
>     - use dynamic debug instead of module-specifig debug parameter
>     - fix I2C bus error handling
> 
> Signed-off-by: Kozlov Sergey <serjk@netup.ru>
> ---
>  MAINTAINERS                           |    9 +
>  drivers/media/dvb-frontends/Kconfig   |    7 +
>  drivers/media/dvb-frontends/Makefile  |    1 +
>  drivers/media/dvb-frontends/ascot2e.c |  543 +++++++++++++++++++++++++++++++++
>  drivers/media/dvb-frontends/ascot2e.h |   58 ++++
>  5 files changed, 618 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/ascot2e.c
>  create mode 100644 drivers/media/dvb-frontends/ascot2e.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7ba61b2..9950fbe 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6269,6 +6269,15 @@ W:	http://linuxtv.org
>  S:	Maintained
>  F:	drivers/media/radio/radio-maxiradio*
>  
> +MEDIA DRIVERS FOR ASCOT2E
> +M:	Sergey Kozlov <serjk@netup.ru>
> +L:	linux-media@vger.kernel.org
> +W:	http://linuxtv.org
> +W:	http://netup.tv/
> +T:	git git://linuxtv.org/media_tree.git
> +S:	Supported
> +F:	drivers/media/dvb-frontends/ascot2e*
> +
>  MEDIA DRIVERS FOR HORUS3A
>  M:	Sergey Kozlov <serjk@netup.ru>
>  L:	linux-media@vger.kernel.org
> diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
> index bd5ab69..d178aca 100644
> --- a/drivers/media/dvb-frontends/Kconfig
> +++ b/drivers/media/dvb-frontends/Kconfig
> @@ -805,6 +805,13 @@ config DVB_HORUS3A
>  	help
>  	  Say Y when you want to support this frontend.
>  
> +config DVB_ASCOT2E
> +	tristate "Sony Ascot2E tuner"
> +	depends on DVB_CORE && I2C
> +	default m if !MEDIA_SUBDRV_AUTOSELECT
> +	help
> +	  Say Y when you want to support this frontend.
> +
>  comment "Tools to develop new frontends"
>  
>  config DVB_DUMMY_FE
> diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
> index 3aa05f3..0b19c10 100644
> --- a/drivers/media/dvb-frontends/Makefile
> +++ b/drivers/media/dvb-frontends/Makefile
> @@ -117,3 +117,4 @@ obj-$(CONFIG_DVB_AF9033) += af9033.o
>  obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
>  obj-$(CONFIG_DVB_TC90522) += tc90522.o
>  obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
> +obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
> diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c
> new file mode 100644
> index 0000000..a87cfee
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/ascot2e.c
> @@ -0,0 +1,543 @@
> +/*
> + * ascot2e.c
> + *
> + * Sony Ascot3E DVB-T/T2/C/C2 tuner driver
> + *
> + * Copyright 2012 Sony Corporation
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> +  */
> +
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/dvb/frontend.h>
> +#include <linux/types.h>
> +#include "ascot2e.h"
> +#include "dvb_frontend.h"
> +
> +enum ascot2e_state {
> +	STATE_UNKNOWN,
> +	STATE_SLEEP,
> +	STATE_ACTIVE
> +};
> +
> +struct ascot2e_priv {
> +	u32			frequency;
> +	u8			i2c_address;
> +	struct i2c_adapter	*i2c;
> +	enum ascot2e_state	state;
> +	void			*set_tuner_data;
> +	int			(*set_tuner)(void *, int);
> +};
> +
> +enum ascot2e_tv_system_t {
> +	ASCOT2E_DTV_DVBT_5,
> +	ASCOT2E_DTV_DVBT_6,
> +	ASCOT2E_DTV_DVBT_7,
> +	ASCOT2E_DTV_DVBT_8,
> +	ASCOT2E_DTV_DVBT2_1_7,
> +	ASCOT2E_DTV_DVBT2_5,
> +	ASCOT2E_DTV_DVBT2_6,
> +	ASCOT2E_DTV_DVBT2_7,
> +	ASCOT2E_DTV_DVBT2_8,
> +	ASCOT2E_DTV_DVBC_6,
> +	ASCOT2E_DTV_DVBC_8,
> +	ASCOT2E_DTV_DVBC2_6,
> +	ASCOT2E_DTV_DVBC2_8,
> +	ASCOT2E_DTV_UNKNOWN
> +};
> +
> +struct ascot2e_band_sett {
> +	u8	if_out_sel;
> +	u8	agc_sel;
> +	u8	mix_oll;
> +	u8	rf_gain;
> +	u8	if_bpf_gc;
> +	u8	fif_offset;
> +	u8	bw_offset;
> +	u8	bw;
> +	u8	rf_oldet;
> +	u8	if_bpf_f0;
> +};
> +
> +#define ASCOT2E_AUTO		0xff
> +#define ASCOT2E_OFFSET(ofs)	((u8)(ofs) & 0x1F)
> +#define ASCOT2E_BW_6		0x00
> +#define ASCOT2E_BW_7		0x01
> +#define ASCOT2E_BW_8		0x02
> +#define ASCOT2E_BW_1_7		0x03
> +
> +static struct ascot2e_band_sett ascot2e_sett[] = {
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	  ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6,  0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	  ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6,  0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	  ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7,  0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	  ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8,  0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	ASCOT2E_OFFSET(-10), ASCOT2E_OFFSET(-16), ASCOT2E_BW_1_7, 0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	  ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6,  0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	  ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6,  0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	  ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7,  0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
> +	  ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8,  0x0B, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03,
> +	  ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-8), ASCOT2E_BW_6,  0x09, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03,
> +	  ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(-1), ASCOT2E_BW_8,  0x09, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01,
> +	  ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_6,  0x09, 0x00 },
> +	{ ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01,
> +	  ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(2),  ASCOT2E_BW_8,  0x09, 0x00 }
> +};
> +
> +static void ascot2e_i2c_debug(struct ascot2e_priv *priv,
> +			      u8 reg, u8 write, const u8 *data, u32 len)
> +{
> +	dev_dbg(&priv->i2c->dev, "ascot2e: I2C %s reg 0x%02x size %d\n",
> +		(write == 0 ? "read" : "write"), reg, len);
> +#if defined(CONFIG_DYNAMIC_DEBUG)
> +	dynamic_hex_dump("ascot2e: I2C data: ",
> +		DUMP_PREFIX_OFFSET, 16, 1, data, len, false);
> +#endif
> +}
> +
> +static int ascot2e_write_regs(struct ascot2e_priv *priv,
> +			      u8 reg, const u8 *data, u32 len)
> +{
> +	int ret;
> +	u8 buf[len+1];
> +	struct i2c_msg msg[1] = {
> +		{
> +			.addr = priv->i2c_address,
> +			.flags = 0,
> +			.len = sizeof(buf),
> +			.buf = buf,
> +		}
> +	};
> +
> +	ascot2e_i2c_debug(priv, reg, 1, data, len);
> +	buf[0] = reg;
> +	memcpy(&buf[1], data, len);
> +
> +	ret = i2c_transfer(priv->i2c, msg, 1);
> +	if (ret >= 0 && ret != 1)
> +		ret = -EREMOTEIO;
> +	if (ret < 0) {
> +		dev_warn(&priv->i2c->dev,
> +			"%s: i2c wr failed=%d reg=%02x len=%d\n",
> +			KBUILD_MODNAME, ret, reg, len);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int ascot2e_write_reg(struct ascot2e_priv *priv, u8 reg, u8 val)
> +{
> +	return ascot2e_write_regs(priv, reg, &val, 1);
> +}
> +
> +static int ascot2e_read_regs(struct ascot2e_priv *priv,
> +			     u8 reg, u8 *val, u32 len)
> +{
> +	int ret;
> +	struct i2c_msg msg[2] = {
> +		{
> +			.addr = priv->i2c_address,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = &reg,
> +		}, {
> +			.addr = priv->i2c_address,
> +			.flags = I2C_M_RD,
> +			.len = len,
> +			.buf = val,
> +		}
> +	};
> +
> +	ret = i2c_transfer(priv->i2c, &msg[0], 1);
> +	if (ret >= 0 && ret != 1)
> +		ret = -EREMOTEIO;
> +	if (ret < 0) {
> +		dev_warn(&priv->i2c->dev,
> +			"%s: I2C rw failed=%d addr=%02x reg=%02x\n",
> +			KBUILD_MODNAME, ret, priv->i2c_address, reg);
> +		return ret;
> +	}
> +
> +	ret = i2c_transfer(priv->i2c, &msg[1], 1);
> +	if (ret >= 0 && ret != 1)
> +		ret = -EREMOTEIO;
> +	if (ret < 0) {
> +		dev_warn(&priv->i2c->dev,
> +			"%s: i2c rd failed=%d addr=%02x reg=%02x\n",
> +			KBUILD_MODNAME, ret, priv->i2c_address, reg);
> +		return ret;
> +	}
> +	ascot2e_i2c_debug(priv, reg, 0, val, len);
> +	return 0;
> +}
> +
> +static int ascot2e_read_reg(struct ascot2e_priv *priv, u8 reg, u8 *val)
> +{
> +	return ascot2e_read_regs(priv, reg, val, 1);
> +}
> +
> +static int ascot2e_set_reg_bits(struct ascot2e_priv *priv,
> +				u8 reg, u8 data, u8 mask)
> +{
> +	int res;
> +	u8 rdata;
> +
> +	if (mask != 0xff) {
> +		res = ascot2e_read_reg(priv, reg, &rdata);
> +		if (res != 0)
> +			return res;
> +		data = ((data & mask) | (rdata & (mask ^ 0xFF)));
> +	}
> +	return ascot2e_write_reg(priv, reg, data);
> +}
> +
> +static int ascot2e_enter_power_save(struct ascot2e_priv *priv)
> +{
> +	u8 data[2];
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state == STATE_SLEEP)
> +		return 0;
> +	data[0] = 0x00;
> +	data[1] = 0x04;
> +	ascot2e_write_regs(priv, 0x14, data, 2);
> +	ascot2e_write_reg(priv, 0x50, 0x01);
> +	priv->state = STATE_SLEEP;
> +	return 0;
> +}
> +
> +static int ascot2e_leave_power_save(struct ascot2e_priv *priv)
> +{
> +	u8 data[2] = { 0xFB, 0x0F };
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state == STATE_ACTIVE)
> +		return 0;
> +	ascot2e_write_regs(priv, 0x14, data, 2);
> +	ascot2e_write_reg(priv, 0x50, 0x00);
> +	priv->state = STATE_ACTIVE;
> +	return 0;
> +}
> +
> +static int ascot2e_init(struct dvb_frontend *fe)
> +{
> +	struct ascot2e_priv *priv = fe->tuner_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	return ascot2e_leave_power_save(priv);
> +}
> +
> +static int ascot2e_release(struct dvb_frontend *fe)
> +{
> +	struct ascot2e_priv *priv = fe->tuner_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	kfree(fe->tuner_priv);
> +	fe->tuner_priv = NULL;
> +	return 0;
> +}
> +
> +static int ascot2e_sleep(struct dvb_frontend *fe)
> +{
> +	struct ascot2e_priv *priv = fe->tuner_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	ascot2e_enter_power_save(priv);
> +	return 0;
> +}
> +
> +static enum ascot2e_tv_system_t ascot2e_get_tv_system(struct dvb_frontend *fe)
> +{
> +	enum ascot2e_tv_system_t system = ASCOT2E_DTV_UNKNOWN;
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +	struct ascot2e_priv *priv = fe->tuner_priv;
> +
> +	if (p->delivery_system == SYS_DVBT) {
> +		if (p->bandwidth_hz <= 5000000)
> +			system = ASCOT2E_DTV_DVBT_5;
> +		else if (p->bandwidth_hz <= 6000000)
> +			system = ASCOT2E_DTV_DVBT_6;
> +		else if (p->bandwidth_hz <= 7000000)
> +			system = ASCOT2E_DTV_DVBT_7;
> +		else if (p->bandwidth_hz <= 8000000)
> +			system = ASCOT2E_DTV_DVBT_8;
> +		else {
> +			system = ASCOT2E_DTV_DVBT_8;
> +			p->bandwidth_hz = 8000000;
> +		}
> +	} else if (p->delivery_system == SYS_DVBT2) {
> +		if (p->bandwidth_hz <= 5000000)
> +			system = ASCOT2E_DTV_DVBT2_5;
> +		else if (p->bandwidth_hz <= 6000000)
> +			system = ASCOT2E_DTV_DVBT2_6;
> +		else if (p->bandwidth_hz <= 7000000)
> +			system = ASCOT2E_DTV_DVBT2_7;
> +		else if (p->bandwidth_hz <= 8000000)
> +			system = ASCOT2E_DTV_DVBT2_8;
> +		else {
> +			system = ASCOT2E_DTV_DVBT2_8;
> +			p->bandwidth_hz = 8000000;
> +		}
> +	} else if (p->delivery_system == SYS_DVBC_ANNEX_A) {
> +		/* only 8MHz bandwidth supported now */
> +		system = ASCOT2E_DTV_DVBC_8;
> +		p->bandwidth_hz = 8000000;

Hmm... Why? It seems on my eyes that the rest of the code would
accept ASCOT2E_DTV_DVBC_6. Wasn't it tested, or is it broken?

> +	}
> +	dev_dbg(&priv->i2c->dev,
> +		"%s(): ASCOT2E DTV system %d (delsys %d, bandwidth %d)\n",
> +		__func__, (int)system, p->delivery_system, p->bandwidth_hz);
> +	return system;
> +}
> +
> +static int ascot2e_set_params(struct dvb_frontend *fe)
> +{
> +	u8 data[10];
> +	u32 frequency;
> +	enum ascot2e_tv_system_t tv_system;
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +	struct ascot2e_priv *priv = fe->tuner_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n",
> +		__func__, p->frequency / 1000);
> +	tv_system = ascot2e_get_tv_system(fe);
> +
> +	if (tv_system == ASCOT2E_DTV_UNKNOWN) {
> +		dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +	if (priv->set_tuner)
> +		priv->set_tuner(priv->set_tuner_data, 1);
> +	frequency = roundup(p->frequency / 1000, 25);
> +	if (priv->state == STATE_SLEEP)
> +		ascot2e_leave_power_save(priv);
> +
> +	/* IF_OUT_SEL / AGC_SEL setting */
> +	data[0] = 0x00;
> +	if (ascot2e_sett[tv_system].agc_sel != ASCOT2E_AUTO) {
> +		/* AGC pin setting from parameter table */
> +		data[0] |= (u8)(
> +			(ascot2e_sett[tv_system].agc_sel & 0x03) << 3);
> +	}
> +	if (ascot2e_sett[tv_system].if_out_sel != ASCOT2E_AUTO) {
> +		/* IFOUT pin setting from parameter table */
> +		data[0] |= (u8)(
> +			(ascot2e_sett[tv_system].if_out_sel & 0x01) << 2);
> +	}
> +	/* Set bit[4:2] only */
> +	ascot2e_set_reg_bits(priv, 0x05, data[0], 0x1c);
> +	/* 0x06 - 0x0F */
> +	/* REF_R setting (0x06) */
> +	if (tv_system == ASCOT2E_DTV_DVBC_6 ||
> +			tv_system == ASCOT2E_DTV_DVBC_8) {
> +		/* xtal, xtal*2 */
> +		data[0] = (frequency > 500000) ? 16 : 32;
> +	} else {
> +		/* xtal/8, xtal/4 */
> +		data[0] = (frequency > 500000) ? 2 : 4;
> +	}
> +	/* XOSC_SEL=100uA */
> +	data[1] = 0x04;
> +	/* KBW setting (0x08), KC0 setting (0x09), KC1 setting (0x0A) */
> +	if (tv_system == ASCOT2E_DTV_DVBC_6 ||
> +			tv_system == ASCOT2E_DTV_DVBC_8) {
> +		data[2] = 18;
> +		data[3] = 120;
> +		data[4] = 20;
> +	} else {
> +		data[2] = 48;
> +		data[3] = 10;
> +		data[4] = 30;
> +	}
> +	/* ORDER/R2_RANGE/R2_BANK/C2_BANK setting (0x0B) */
> +	if (tv_system == ASCOT2E_DTV_DVBC_6 ||
> +			tv_system == ASCOT2E_DTV_DVBC_8)
> +		data[5] = (frequency > 500000) ? 0x08 : 0x0c;
> +	else
> +		data[5] = (frequency > 500000) ? 0x30 : 0x38;
> +	/* Set MIX_OLL (0x0C) value from parameter table */
> +	data[6] = ascot2e_sett[tv_system].mix_oll;
> +	/* Set RF_GAIN (0x0D) setting from parameter table */
> +	if (ascot2e_sett[tv_system].rf_gain == ASCOT2E_AUTO) {
> +		/* RF_GAIN auto control enable */
> +		ascot2e_write_reg(priv, 0x4E, 0x01);
> +		/* RF_GAIN Default value */
> +		data[7] = 0x00;
> +	} else {
> +		/* RF_GAIN auto control disable */
> +		ascot2e_write_reg(priv, 0x4E, 0x00);
> +		data[7] = ascot2e_sett[tv_system].rf_gain;
> +	}
> +	/* Set IF_BPF_GC/FIF_OFFSET (0x0E) value from parameter table */
> +	data[8] = (u8)((ascot2e_sett[tv_system].fif_offset << 3) |
> +		(ascot2e_sett[tv_system].if_bpf_gc & 0x07));
> +	/* Set BW_OFFSET (0x0F) value from parameter table */
> +	data[9] = ascot2e_sett[tv_system].bw_offset;
> +	ascot2e_write_regs(priv, 0x06, data, 10);
> +	/*
> +	 * 0x45 - 0x47
> +	 * LNA optimization setting
> +	 * RF_LNA_DIST1-5, RF_LNA_CM
> +	 */
> +	if (tv_system == ASCOT2E_DTV_DVBC_6 ||
> +			tv_system == ASCOT2E_DTV_DVBC_8) {
> +		data[0] = 0x0F;
> +		data[1] = 0x00;
> +		data[2] = 0x01;
> +	} else {
> +		data[0] = 0x0F;
> +		data[1] = 0x00;
> +		data[2] = 0x03;
> +	}
> +	ascot2e_write_regs(priv, 0x45, data, 3);
> +	/* 0x49 - 0x4A
> +	 Set RF_OLDET_ENX/RF_OLDET_OLL value from parameter table */
> +	data[0] = ascot2e_sett[tv_system].rf_oldet;
> +	/* Set IF_BPF_F0 value from parameter table */
> +	data[1] = ascot2e_sett[tv_system].if_bpf_f0;
> +	ascot2e_write_regs(priv, 0x49, data, 2);
> +	/*
> +	 * Tune now
> +	 * RFAGC fast mode / RFAGC auto control enable
> +	 * (set bit[7], bit[5:4] only)
> +	 * vco_cal = 1, set MIX_OL_CPU_EN
> +	 */
> +	ascot2e_set_reg_bits(priv, 0x0c, 0x90, 0xb0);
> +	/* Logic wake up, CPU wake up */
> +	data[0] = 0xc4;
> +	data[1] = 0x40;
> +	ascot2e_write_regs(priv, 0x03, data, 2);
> +	/* 0x10 - 0x14 */
> +	data[0] = (u8)(frequency & 0xFF);         /* 0x10: FRF_L */
> +	data[1] = (u8)((frequency >> 8) & 0xFF);  /* 0x11: FRF_M */
> +	data[2] = (u8)((frequency >> 16) & 0x0F); /* 0x12: FRF_H (bit[3:0]) */
> +	/* 0x12: BW (bit[5:4]) */
> +	data[2] |= (u8)(ascot2e_sett[tv_system].bw << 4);
> +	data[3] = 0xFF; /* 0x13: VCO calibration enable */
> +	data[4] = 0xFF; /* 0x14: Analog block enable */
> +	/* Tune (Burst write) */
> +	ascot2e_write_regs(priv, 0x10, data, 5);
> +	msleep(50);
> +	/* CPU deep sleep */
> +	ascot2e_write_reg(priv, 0x04, 0x00);
> +	/* Logic sleep */
> +	ascot2e_write_reg(priv, 0x03, 0xC0);
> +	/* RFAGC normal mode (set bit[5:4] only) */
> +	ascot2e_set_reg_bits(priv, 0x0C, 0x00, 0x30);
> +	priv->frequency = frequency;
> +	return 0;
> +}
> +
> +static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency)
> +{
> +	struct ascot2e_priv *priv = fe->tuner_priv;
> +
> +	*frequency = priv->frequency * 1000;
> +	return 0;
> +}
> +
> +static struct dvb_tuner_ops ascot2e_tuner_ops = {
> +	.info = {
> +		.name = "Sony ASCOT2E",
> +		.frequency_min = 1000000,
> +		.frequency_max = 1200000000,
> +		.frequency_step = 25000,
> +	},
> +	.init = ascot2e_init,
> +	.release = ascot2e_release,
> +	.sleep = ascot2e_sleep,
> +	.set_params = ascot2e_set_params,
> +	.get_frequency = ascot2e_get_frequency,
> +};
> +
> +struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
> +				    const struct ascot2e_config *config,
> +				    struct i2c_adapter *i2c)
> +{
> +	u8 data[4];
> +	struct ascot2e_priv *priv = NULL;
> +
> +	priv = kzalloc(sizeof(struct ascot2e_priv), GFP_KERNEL);
> +	if (priv == NULL)
> +		return NULL;
> +	priv->i2c_address = (config->i2c_address >> 1);
> +	priv->i2c = i2c;
> +	priv->set_tuner_data = config->set_tuner_priv;
> +	priv->set_tuner = config->set_tuner_callback;
> +
> +	if (fe->ops.i2c_gate_ctrl)
> +		fe->ops.i2c_gate_ctrl(fe, 1);
> +
> +	/* 16 MHz xTal frequency */
> +	data[0] = 16;
> +	/* VCO current setting */
> +	data[1] = 0x06;
> +	/* Logic wake up, CPU boot */
> +	data[2] = 0xC4;
> +	data[3] = 0x40;
> +	ascot2e_write_regs(priv, 0x01, data, 4);
> +	/* RFVGA optimization setting (RF_DIST0 - RF_DIST2) */
> +	data[0] = 0x10;
> +	data[1] = 0x3F;
> +	data[2] = 0x25;
> +	ascot2e_write_regs(priv, 0x22, data, 3);
> +	/* PLL mode setting */
> +	ascot2e_write_reg(priv, 0x28, 0x1e);
> +	/* RSSI setting */
> +	ascot2e_write_reg(priv, 0x59, 0x04);
> +	/* TODO check CPU HW error state here */
> +	msleep(80);
> +	/* Xtal oscillator current control setting */
> +	ascot2e_write_reg(priv, 0x4c, 0x01);
> +	/* XOSC_SEL=100uA */
> +	ascot2e_write_reg(priv, 0x07, 0x04);
> +	/* CPU deep sleep */
> +	ascot2e_write_reg(priv, 0x04, 0x00);
> +	/* Logic sleep */
> +	ascot2e_write_reg(priv, 0x03, 0xc0);
> +	/* Power save setting */
> +	data[0] = 0x00;
> +	data[1] = 0x04;
> +	ascot2e_write_regs(priv, 0x14, data, 2);
> +	ascot2e_write_reg(priv, 0x50, 0x01);
> +	priv->state = STATE_SLEEP;
> +
> +	if (fe->ops.i2c_gate_ctrl)
> +		fe->ops.i2c_gate_ctrl(fe, 0);
> +
> +	memcpy(&fe->ops.tuner_ops, &ascot2e_tuner_ops,
> +				sizeof(struct dvb_tuner_ops));
> +	fe->tuner_priv = priv;
> +	dev_info(&priv->i2c->dev,
> +		"Sony ASCOT2E attached on addr=%x at I2C adapter %p\n",
> +		priv->i2c_address, priv->i2c);
> +	return fe;
> +}
> +EXPORT_SYMBOL(ascot2e_attach);
> +
> +MODULE_DESCRIPTION("Sony ASCOT2E terr/cab tuner driver");
> +MODULE_AUTHOR("info@netup.ru");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/dvb-frontends/ascot2e.h b/drivers/media/dvb-frontends/ascot2e.h
> new file mode 100644
> index 0000000..b9e12a3
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/ascot2e.h
> @@ -0,0 +1,58 @@
> +/*
> + * ascot2e.h
> + *
> + * Sony Ascot3E DVB-T/T2/C/C2 tuner driver
> + *
> + * Copyright 2012 Sony Corporation
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> +  */
> +
> +#ifndef __DVB_ASCOT2E_H__
> +#define __DVB_ASCOT2E_H__
> +
> +#include <linux/kconfig.h>
> +#include <linux/dvb/frontend.h>
> +#include <linux/i2c.h>
> +
> +/**
> + * struct ascot2e_config - the configuration of Ascot2E tuner driver
> + * @i2c_address:	I2C address of the tuner
> + * @xtal_freq_mhz:	Oscillator frequency, MHz
> + * @set_tuner_priv:	Callback function private context
> + * @set_tuner_callback:	Callback function that notifies the parent driver
> + *			which tuner is active now
> + */
> +struct ascot2e_config {
> +	u8	i2c_address;
> +	u8	xtal_freq_mhz;
> +	void	*set_tuner_priv;
> +	int	(*set_tuner_callback)(void *, int);
> +};
> +
> +#if IS_ENABLED(CONFIG_DVB_ASCOT2E)

IS_REACHABLE()

> +extern struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
> +					const struct ascot2e_config *config,
> +					struct i2c_adapter *i2c);
> +#else
> +static inline struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
> +					const struct ascot2e_config *config,
> +					struct i2c_adapter *i2c)
> +{
> +	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +#endif
> +
> +#endif

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V2 4/5] [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver
  2015-04-15 10:07 ` [PATCH V2 4/5] [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver serjk
@ 2015-05-14 14:15   ` Mauro Carvalho Chehab
  2015-06-30 10:28     ` Kozlov Sergey
  0 siblings, 1 reply; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2015-05-14 14:15 UTC (permalink / raw)
  To: serjk; +Cc: linux-media, aospan1

Em Wed, 15 Apr 2015 13:07:49 +0300
serjk@netup.ru escreveu:

> From: Kozlov Sergey <serjk@netup.ru>
> 
> Add DVB-C/T/T2/S/S2 demodulator frontend driver Sony CXD2841ER chip.
> 
> Changes in version 2:
>     - rename MAINTAINERS entry
>     - fix coding style
>     - use dynamic debug instead of module-specifig debug parameter
>     - fix I2C bus error handling
>     - provide signal statistics via DVBv5 API instead of v3
> 
> Signed-off-by: Kozlov Sergey <serjk@netup.ru>
> ---
>  MAINTAINERS                                  |    9 +
>  drivers/media/dvb-frontends/Kconfig          |    7 +
>  drivers/media/dvb-frontends/Makefile         |    1 +
>  drivers/media/dvb-frontends/cxd2841er.c      | 2721 ++++++++++++++++++++++++++
>  drivers/media/dvb-frontends/cxd2841er.h      |   65 +
>  drivers/media/dvb-frontends/cxd2841er_priv.h |   43 +
>  6 files changed, 2846 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2841er.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2841er.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2841er_priv.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 4695cdc..968a044 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6278,6 +6278,15 @@ T:	git git://linuxtv.org/media_tree.git
>  S:	Supported
>  F:	drivers/media/dvb-frontends/ascot2e*
>  
> +MEDIA DRIVERS FOR CXD2841ER
> +M:	Sergey Kozlov <serjk@netup.ru>
> +L:	linux-media@vger.kernel.org
> +W:	http://linuxtv.org/
> +W:	http://netup.tv/
> +T:	git git://linuxtv.org/media_tree.git
> +S:	Supported
> +F:	drivers/media/dvb-frontends/cxd2841er*
> +
>  MEDIA DRIVERS FOR HORUS3A
>  M:	Sergey Kozlov <serjk@netup.ru>
>  L:	linux-media@vger.kernel.org
> diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
> index eec0405..1428fdd 100644
> --- a/drivers/media/dvb-frontends/Kconfig
> +++ b/drivers/media/dvb-frontends/Kconfig
> @@ -441,6 +441,13 @@ config DVB_CXD2820R
>  	help
>  	  Say Y when you want to support this frontend.
>  
> +config DVB_CXD2841ER
> +	tristate "Sony CXD2841ER"
> +	depends on DVB_CORE && I2C
> +	default m if !MEDIA_SUBDRV_AUTOSELECT
> +	help
> +	  Say Y when you want to support this frontend.
> +
>  config DVB_RTL2830
>  	tristate "Realtek RTL2830 DVB-T"
>  	depends on DVB_CORE && I2C && I2C_MUX
> diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
> index 06a0d21..f67cb8c 100644
> --- a/drivers/media/dvb-frontends/Makefile
> +++ b/drivers/media/dvb-frontends/Makefile
> @@ -104,6 +104,7 @@ obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o
>  obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
>  obj-$(CONFIG_DVB_STV0367) += stv0367.o
>  obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
> +obj-$(CONFIG_DVB_CXD2841ER) += cxd2841er.o
>  obj-$(CONFIG_DVB_DRXK) += drxk.o
>  obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
>  obj-$(CONFIG_DVB_SI2165) += si2165.o
> diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c
> new file mode 100644
> index 0000000..dc9498b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2841er.c
> @@ -0,0 +1,2721 @@
> +/*
> + * cxd2841er.c
> + *
> + * Sony CXD2441ER digital demodulator driver
> + *
> + * Copyright 2012 Sony Corporation
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> +  */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include <linux/bitops.h>
> +#include <linux/math64.h>
> +#include <linux/log2.h>
> +#include <linux/dynamic_debug.h>
> +
> +#include "dvb_math.h"
> +#include "dvb_frontend.h"
> +#include "cxd2841er.h"
> +#include "cxd2841er_priv.h"
> +
> +enum cxd2841er_state {
> +	STATE_SHUTDOWN = 0,
> +	STATE_SLEEP_S,
> +	STATE_ACTIVE_S,
> +	STATE_SLEEP_TC,
> +	STATE_ACTIVE_TC
> +};
> +
> +struct cxd2841er_priv {
> +	struct dvb_frontend		frontend;
> +	struct i2c_adapter		*i2c;
> +	u8				i2c_addr_slvx;
> +	u8				i2c_addr_slvt;
> +	const struct cxd2841er_config	*config;
> +	enum cxd2841er_state		state;
> +	u8				system;
> +};
> +
> +static const struct cxd2841er_cnr_data s_cn_data[] = {
> +	{ 0x033e, 0 }, { 0x0339, 100 }, { 0x0333, 200 },
> +	{ 0x032e, 300 }, { 0x0329, 400 }, { 0x0324, 500 },
> +	{ 0x031e, 600 }, { 0x0319, 700 }, { 0x0314, 800 },
> +	{ 0x030f, 900 }, { 0x030a, 1000 }, { 0x02ff, 1100 },
> +	{ 0x02f4, 1200 }, { 0x02e9, 1300 }, { 0x02de, 1400 },
> +	{ 0x02d4, 1500 }, { 0x02c9, 1600 }, { 0x02bf, 1700 },
> +	{ 0x02b5, 1800 }, { 0x02ab, 1900 }, { 0x02a1, 2000 },
> +	{ 0x029b, 2100 }, { 0x0295, 2200 }, { 0x0290, 2300 },
> +	{ 0x028a, 2400 }, { 0x0284, 2500 }, { 0x027f, 2600 },
> +	{ 0x0279, 2700 }, { 0x0274, 2800 }, { 0x026e, 2900 },
> +	{ 0x0269, 3000 }, { 0x0262, 3100 }, { 0x025c, 3200 },
> +	{ 0x0255, 3300 }, { 0x024f, 3400 }, { 0x0249, 3500 },
> +	{ 0x0242, 3600 }, { 0x023c, 3700 }, { 0x0236, 3800 },
> +	{ 0x0230, 3900 }, { 0x022a, 4000 }, { 0x0223, 4100 },
> +	{ 0x021c, 4200 }, { 0x0215, 4300 }, { 0x020e, 4400 },
> +	{ 0x0207, 4500 }, { 0x0201, 4600 }, { 0x01fa, 4700 },
> +	{ 0x01f4, 4800 }, { 0x01ed, 4900 }, { 0x01e7, 5000 },
> +	{ 0x01e0, 5100 }, { 0x01d9, 5200 }, { 0x01d2, 5300 },
> +	{ 0x01cb, 5400 }, { 0x01c4, 5500 }, { 0x01be, 5600 },
> +	{ 0x01b7, 5700 }, { 0x01b1, 5800 }, { 0x01aa, 5900 },
> +	{ 0x01a4, 6000 }, { 0x019d, 6100 }, { 0x0196, 6200 },
> +	{ 0x018f, 6300 }, { 0x0189, 6400 }, { 0x0182, 6500 },
> +	{ 0x017c, 6600 }, { 0x0175, 6700 }, { 0x016f, 6800 },
> +	{ 0x0169, 6900 }, { 0x0163, 7000 }, { 0x015c, 7100 },
> +	{ 0x0156, 7200 }, { 0x0150, 7300 }, { 0x014a, 7400 },
> +	{ 0x0144, 7500 }, { 0x013e, 7600 }, { 0x0138, 7700 },
> +	{ 0x0132, 7800 }, { 0x012d, 7900 }, { 0x0127, 8000 },
> +	{ 0x0121, 8100 }, { 0x011c, 8200 }, { 0x0116, 8300 },
> +	{ 0x0111, 8400 }, { 0x010b, 8500 }, { 0x0106, 8600 },
> +	{ 0x0101, 8700 }, { 0x00fc, 8800 }, { 0x00f7, 8900 },
> +	{ 0x00f2, 9000 }, { 0x00ee, 9100 }, { 0x00ea, 9200 },
> +	{ 0x00e6, 9300 }, { 0x00e2, 9400 }, { 0x00de, 9500 },
> +	{ 0x00da, 9600 }, { 0x00d7, 9700 }, { 0x00d3, 9800 },
> +	{ 0x00d0, 9900 }, { 0x00cc, 10000 }, { 0x00c7, 10100 },
> +	{ 0x00c3, 10200 }, { 0x00bf, 10300 }, { 0x00ba, 10400 },
> +	{ 0x00b6, 10500 }, { 0x00b2, 10600 }, { 0x00ae, 10700 },
> +	{ 0x00aa, 10800 }, { 0x00a7, 10900 }, { 0x00a3, 11000 },
> +	{ 0x009f, 11100 }, { 0x009c, 11200 }, { 0x0098, 11300 },
> +	{ 0x0094, 11400 }, { 0x0091, 11500 }, { 0x008e, 11600 },
> +	{ 0x008a, 11700 }, { 0x0087, 11800 }, { 0x0084, 11900 },
> +	{ 0x0081, 12000 }, { 0x007e, 12100 }, { 0x007b, 12200 },
> +	{ 0x0079, 12300 }, { 0x0076, 12400 }, { 0x0073, 12500 },
> +	{ 0x0071, 12600 }, { 0x006e, 12700 }, { 0x006c, 12800 },
> +	{ 0x0069, 12900 }, { 0x0067, 13000 }, { 0x0065, 13100 },
> +	{ 0x0062, 13200 }, { 0x0060, 13300 }, { 0x005e, 13400 },
> +	{ 0x005c, 13500 }, { 0x005a, 13600 }, { 0x0058, 13700 },
> +	{ 0x0056, 13800 }, { 0x0054, 13900 }, { 0x0052, 14000 },
> +	{ 0x0050, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 },
> +	{ 0x004b, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 },
> +	{ 0x0046, 14700 }, { 0x0044, 14800 }, { 0x0043, 14900 },
> +	{ 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 },
> +	{ 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 },
> +	{ 0x0037, 15700 }, { 0x0036, 15800 }, { 0x0034, 15900 },
> +	{ 0x0033, 16000 }, { 0x0032, 16100 }, { 0x0031, 16200 },
> +	{ 0x0030, 16300 }, { 0x002f, 16400 }, { 0x002e, 16500 },
> +	{ 0x002d, 16600 }, { 0x002c, 16700 }, { 0x002b, 16800 },
> +	{ 0x002a, 16900 }, { 0x0029, 17000 }, { 0x0028, 17100 },
> +	{ 0x0027, 17200 }, { 0x0026, 17300 }, { 0x0025, 17400 },
> +	{ 0x0024, 17500 }, { 0x0023, 17600 }, { 0x0022, 17800 },
> +	{ 0x0021, 17900 }, { 0x0020, 18000 }, { 0x001f, 18200 },
> +	{ 0x001e, 18300 }, { 0x001d, 18500 }, { 0x001c, 18700 },
> +	{ 0x001b, 18900 }, { 0x001a, 19000 }, { 0x0019, 19200 },
> +	{ 0x0018, 19300 }, { 0x0017, 19500 }, { 0x0016, 19700 },
> +	{ 0x0015, 19900 }, { 0x0014, 20000 },
> +};
> +
> +static const struct cxd2841er_cnr_data s2_cn_data[] = {
> +	{ 0x05af, 0 }, { 0x0597, 100 }, { 0x057e, 200 },
> +	{ 0x0567, 300 }, { 0x0550, 400 }, { 0x0539, 500 },
> +	{ 0x0522, 600 }, { 0x050c, 700 }, { 0x04f6, 800 },
> +	{ 0x04e1, 900 }, { 0x04cc, 1000 }, { 0x04b6, 1100 },
> +	{ 0x04a1, 1200 }, { 0x048c, 1300 }, { 0x0477, 1400 },
> +	{ 0x0463, 1500 }, { 0x044f, 1600 }, { 0x043c, 1700 },
> +	{ 0x0428, 1800 }, { 0x0416, 1900 }, { 0x0403, 2000 },
> +	{ 0x03ef, 2100 }, { 0x03dc, 2200 }, { 0x03c9, 2300 },
> +	{ 0x03b6, 2400 }, { 0x03a4, 2500 }, { 0x0392, 2600 },
> +	{ 0x0381, 2700 }, { 0x036f, 2800 }, { 0x035f, 2900 },
> +	{ 0x034e, 3000 }, { 0x033d, 3100 }, { 0x032d, 3200 },
> +	{ 0x031d, 3300 }, { 0x030d, 3400 }, { 0x02fd, 3500 },
> +	{ 0x02ee, 3600 }, { 0x02df, 3700 }, { 0x02d0, 3800 },
> +	{ 0x02c2, 3900 }, { 0x02b4, 4000 }, { 0x02a6, 4100 },
> +	{ 0x0299, 4200 }, { 0x028c, 4300 }, { 0x027f, 4400 },
> +	{ 0x0272, 4500 }, { 0x0265, 4600 }, { 0x0259, 4700 },
> +	{ 0x024d, 4800 }, { 0x0241, 4900 }, { 0x0236, 5000 },
> +	{ 0x022b, 5100 }, { 0x0220, 5200 }, { 0x0215, 5300 },
> +	{ 0x020a, 5400 }, { 0x0200, 5500 }, { 0x01f6, 5600 },
> +	{ 0x01ec, 5700 }, { 0x01e2, 5800 }, { 0x01d8, 5900 },
> +	{ 0x01cf, 6000 }, { 0x01c6, 6100 }, { 0x01bc, 6200 },
> +	{ 0x01b3, 6300 }, { 0x01aa, 6400 }, { 0x01a2, 6500 },
> +	{ 0x0199, 6600 }, { 0x0191, 6700 }, { 0x0189, 6800 },
> +	{ 0x0181, 6900 }, { 0x0179, 7000 }, { 0x0171, 7100 },
> +	{ 0x0169, 7200 }, { 0x0161, 7300 }, { 0x015a, 7400 },
> +	{ 0x0153, 7500 }, { 0x014b, 7600 }, { 0x0144, 7700 },
> +	{ 0x013d, 7800 }, { 0x0137, 7900 }, { 0x0130, 8000 },
> +	{ 0x012a, 8100 }, { 0x0124, 8200 }, { 0x011e, 8300 },
> +	{ 0x0118, 8400 }, { 0x0112, 8500 }, { 0x010c, 8600 },
> +	{ 0x0107, 8700 }, { 0x0101, 8800 }, { 0x00fc, 8900 },
> +	{ 0x00f7, 9000 }, { 0x00f2, 9100 }, { 0x00ec, 9200 },
> +	{ 0x00e7, 9300 }, { 0x00e2, 9400 }, { 0x00dd, 9500 },
> +	{ 0x00d8, 9600 }, { 0x00d4, 9700 }, { 0x00cf, 9800 },
> +	{ 0x00ca, 9900 }, { 0x00c6, 10000 }, { 0x00c2, 10100 },
> +	{ 0x00be, 10200 }, { 0x00b9, 10300 }, { 0x00b5, 10400 },
> +	{ 0x00b1, 10500 }, { 0x00ae, 10600 }, { 0x00aa, 10700 },
> +	{ 0x00a6, 10800 }, { 0x00a3, 10900 }, { 0x009f, 11000 },
> +	{ 0x009b, 11100 }, { 0x0098, 11200 }, { 0x0095, 11300 },
> +	{ 0x0091, 11400 }, { 0x008e, 11500 }, { 0x008b, 11600 },
> +	{ 0x0088, 11700 }, { 0x0085, 11800 }, { 0x0082, 11900 },
> +	{ 0x007f, 12000 }, { 0x007c, 12100 }, { 0x007a, 12200 },
> +	{ 0x0077, 12300 }, { 0x0074, 12400 }, { 0x0072, 12500 },
> +	{ 0x006f, 12600 }, { 0x006d, 12700 }, { 0x006b, 12800 },
> +	{ 0x0068, 12900 }, { 0x0066, 13000 }, { 0x0064, 13100 },
> +	{ 0x0061, 13200 }, { 0x005f, 13300 }, { 0x005d, 13400 },
> +	{ 0x005b, 13500 }, { 0x0059, 13600 }, { 0x0057, 13700 },
> +	{ 0x0055, 13800 }, { 0x0053, 13900 }, { 0x0051, 14000 },
> +	{ 0x004f, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 },
> +	{ 0x004a, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 },
> +	{ 0x0045, 14700 }, { 0x0044, 14800 }, { 0x0042, 14900 },
> +	{ 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 },
> +	{ 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 },
> +	{ 0x0038, 15600 }, { 0x0037, 15700 }, { 0x0036, 15800 },
> +	{ 0x0034, 15900 }, { 0x0033, 16000 }, { 0x0032, 16100 },
> +	{ 0x0031, 16200 }, { 0x0030, 16300 }, { 0x002f, 16400 },
> +	{ 0x002e, 16500 }, { 0x002d, 16600 }, { 0x002c, 16700 },
> +	{ 0x002b, 16800 }, { 0x002a, 16900 }, { 0x0029, 17000 },
> +	{ 0x0028, 17100 }, { 0x0027, 17200 }, { 0x0026, 17300 },
> +	{ 0x0025, 17400 }, { 0x0024, 17500 }, { 0x0023, 17600 },
> +	{ 0x0022, 17800 }, { 0x0021, 17900 }, { 0x0020, 18000 },
> +	{ 0x001f, 18200 }, { 0x001e, 18300 }, { 0x001d, 18500 },
> +	{ 0x001c, 18700 }, { 0x001b, 18900 }, { 0x001a, 19000 },
> +	{ 0x0019, 19200 }, { 0x0018, 19300 }, { 0x0017, 19500 },
> +	{ 0x0016, 19700 }, { 0x0015, 19900 }, { 0x0014, 20000 },
> +};
> +
> +#define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5))
> +
> +static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv,
> +				u8 addr, u8 reg, u8 write,
> +				const u8 *data, u32 len)
> +{
> +	dev_dbg(&priv->i2c->dev,
> +		"cxd2841er: I2C %s addr %02x reg 0x%02x size %d\n",
> +		(write == 0 ? "read" : "write"), addr, reg, len);
> +#if defined(CONFIG_DYNAMIC_DEBUG)
> +	dynamic_hex_dump("cxd2841er: I2C data: ",
> +		DUMP_PREFIX_OFFSET, 16, 1, data, len, false);
> +#endif
> +}
> +
> +static int cxd2841er_write_regs(struct cxd2841er_priv *priv,
> +				u8 addr, u8 reg, const u8 *data, u32 len)
> +{
> +	int ret;
> +	u8 buf[len+1];
> +	u8 i2c_addr = (addr == I2C_SLVX ?
> +		priv->i2c_addr_slvx : priv->i2c_addr_slvt);
> +	struct i2c_msg msg[1] = {
> +		{
> +			.addr = i2c_addr,
> +			.flags = 0,
> +			.len = sizeof(buf),
> +			.buf = buf,
> +		}
> +	};
> +
> +	cxd2841er_i2c_debug(priv, i2c_addr, reg, 1, data, len);
> +	buf[0] = reg;
> +	memcpy(&buf[1], data, len);
> +
> +	ret = i2c_transfer(priv->i2c, msg, 1);
> +	if (ret >= 0 && ret != 1)
> +		ret = -EIO;
> +	if (ret < 0) {
> +		dev_warn(&priv->i2c->dev,
> +			"%s: i2c wr failed=%d addr=%02x reg=%02x len=%d\n",
> +			KBUILD_MODNAME, ret, i2c_addr, reg, len);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int cxd2841er_write_reg(struct cxd2841er_priv *priv,
> +			       u8 addr, u8 reg, u8 val)
> +{
> +	return cxd2841er_write_regs(priv, addr, reg, &val, 1);
> +}
> +
> +static int cxd2841er_read_regs(struct cxd2841er_priv *priv,
> +			       u8 addr, u8 reg, u8 *val, u32 len)
> +{
> +	int ret;
> +	u8 i2c_addr = (addr == I2C_SLVX ?
> +		priv->i2c_addr_slvx : priv->i2c_addr_slvt);
> +	struct i2c_msg msg[2] = {
> +		{
> +			.addr = i2c_addr,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = &reg,
> +		}, {
> +			.addr = i2c_addr,
> +			.flags = I2C_M_RD,
> +			.len = len,
> +			.buf = val,
> +		}
> +	};
> +
> +	ret = i2c_transfer(priv->i2c, &msg[0], 1);
> +	if (ret >= 0 && ret != 1)
> +		ret = -EIO;
> +	if (ret < 0) {
> +		dev_warn(&priv->i2c->dev,
> +			"%s: i2c rw failed=%d addr=%02x reg=%02x\n",
> +			KBUILD_MODNAME, ret, i2c_addr, reg);
> +		return ret;
> +	}
> +	ret = i2c_transfer(priv->i2c, &msg[1], 1);
> +	if (ret >= 0 && ret != 1)
> +		ret = -EIO;
> +	if (ret < 0) {
> +		dev_warn(&priv->i2c->dev,
> +			"%s: i2c rd failed=%d addr=%02x reg=%02x\n",
> +			KBUILD_MODNAME, ret, i2c_addr, reg);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int cxd2841er_read_reg(struct cxd2841er_priv *priv,
> +			      u8 addr, u8 reg, u8 *val)
> +{
> +	return cxd2841er_read_regs(priv, addr, reg, val, 1);
> +}
> +
> +static int cxd2841er_set_reg_bits(struct cxd2841er_priv *priv,
> +				  u8 addr, u8 reg, u8 data, u8 mask)
> +{
> +	int res;
> +	u8 rdata;
> +
> +	if (mask != 0xff) {
> +		res = cxd2841er_read_reg(priv, addr, reg, &rdata);
> +		if (res)
> +			return res;
> +		data = ((data & mask) | (rdata & (mask ^ 0xFF)));
> +	}
> +	return cxd2841er_write_reg(priv, addr, reg, data);
> +}
> +
> +static int cxd2841er_dvbs2_set_symbol_rate(struct cxd2841er_priv *priv,
> +					   u32 symbol_rate)
> +{
> +	u32 reg_value = 0;
> +	u8 data[3] = {0, 0, 0};
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	/*
> +	 * regValue = (symbolRateKSps * 2^14 / 1000) + 0.5
> +	 *          = ((symbolRateKSps * 2^14) + 500) / 1000
> +	 *          = ((symbolRateKSps * 16384) + 500) / 1000
> +	 */
> +	reg_value = DIV_ROUND_CLOSEST(symbol_rate * 16384, 1000);
> +	if ((reg_value == 0) || (reg_value > 0xFFFFF)) {
> +		dev_err(&priv->i2c->dev,
> +			"%s(): reg_value is out of range\n", __func__);
> +		return -EINVAL;
> +	}
> +	data[0] = (u8)((reg_value >> 16) & 0x0F);
> +	data[1] = (u8)((reg_value >>  8) & 0xFF);
> +	data[2] = (u8)(reg_value & 0xFF);
> +	/* Set SLV-T Bank : 0xAE */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0x20, data, 3);
> +	return 0;
> +}
> +
> +static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
> +					u8 system);
> +
> +static int cxd2841er_sleep_s_to_active_s(struct cxd2841er_priv *priv,
> +					 u8 system, u32 symbol_rate)
> +{
> +	int ret;
> +	u8 data[4] = { 0, 0, 0, 0 };
> +
> +	if (priv->state != STATE_SLEEP_S) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, (int)priv->state);
> +		return -EINVAL;
> +	}
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_set_ts_clock_mode(priv, SYS_DVBS);
> +	/* Set demod mode */
> +	if (system == SYS_DVBS) {
> +		data[0] = 0x0A;
> +	} else if (system == SYS_DVBS2) {
> +		data[0] = 0x0B;
> +	} else {
> +		dev_err(&priv->i2c->dev, "%s(): invalid delsys %d\n",
> +			__func__, system);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, data[0]);
> +	/* DVB-S/S2 */
> +	data[0] = 0x00;
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Enable S/S2 auto detection 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, data[0]);
> +	/* Set SLV-T Bank : 0xAE */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
> +	/* Enable S/S2 auto detection 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, data[0]);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Enable demod clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
> +	/* Enable ADC clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x01);
> +	/* Enable ADC 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
> +	/* Enable ADC 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x3f);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Enable ADC 3 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
> +	/* Set SLV-T Bank : 0xA3 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa3);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xac, 0x00);
> +	data[0] = 0x07;
> +	data[1] = 0x3B;
> +	data[2] = 0x08;
> +	data[3] = 0xC5;
> +	/* Set SLV-T Bank : 0xAB */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xab);
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0x98, data, 4);
> +	data[0] = 0x05;
> +	data[1] = 0x80;
> +	data[2] = 0x0A;
> +	data[3] = 0x80;
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0xa8, data, 4);
> +	data[0] = 0x0C;
> +	data[1] = 0xCC;
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0xc3, data, 2);
> +	/* Set demod parameter */
> +	ret = cxd2841er_dvbs2_set_symbol_rate(priv, symbol_rate);
> +	if (ret != 0)
> +		return ret;
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* disable Hi-Z setting 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x10);
> +	/* disable Hi-Z setting 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
> +	priv->state = STATE_ACTIVE_S;
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc_to_active_t_band(struct cxd2841er_priv *priv,
> +					       u32 bandwidth);
> +
> +static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
> +						u32 bandwidth);
> +
> +static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
> +					       u32 bandwidth);
> +
> +static int cxd2841er_retune_active(struct cxd2841er_priv *priv,
> +				   struct dtv_frontend_properties *p)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_S &&
> +			priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* disable TS output */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
> +	if (priv->state == STATE_ACTIVE_S)
> +		return cxd2841er_dvbs2_set_symbol_rate(
> +				priv, p->symbol_rate / 1000);
> +	else if (priv->state == STATE_ACTIVE_TC) {
> +		switch (priv->system) {
> +		case SYS_DVBT:
> +			return cxd2841er_sleep_tc_to_active_t_band(
> +					priv, p->bandwidth_hz);
> +		case SYS_DVBT2:
> +			return cxd2841er_sleep_tc_to_active_t2_band(
> +					priv, p->bandwidth_hz);
> +		case SYS_DVBC_ANNEX_A:
> +			return cxd2841er_sleep_tc_to_active_c_band(
> +					priv, 8000000);
> +		}
> +	}
> +	dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
> +		__func__, priv->system);
> +	return -EINVAL;
> +}
> +
> +static int cxd2841er_active_s_to_sleep_s(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_S) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* disable TS output */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
> +	/* enable Hi-Z setting 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1f);
> +	/* enable Hi-Z setting 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* disable ADC 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* disable ADC clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x00);
> +	/* disable ADC 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
> +	/* disable ADC 3 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
> +	/* SADC Bias ON */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
> +	/* disable demod clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
> +	/* Set SLV-T Bank : 0xAE */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
> +	/* disable S/S2 auto detection1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* disable S/S2 auto detection2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, 0x00);
> +	priv->state = STATE_SLEEP_S;
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_s_to_shutdown(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_SLEEP_S) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Disable DSQOUT */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
> +	/* Disable DSQIN */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x9c, 0x00);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Disable oscillator */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01);
> +	/* Set demod mode */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
> +	priv->state = STATE_SHUTDOWN;
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_SLEEP_TC) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Disable oscillator */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01);
> +	/* Set demod mode */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
> +	priv->state = STATE_SHUTDOWN;
> +	return 0;
> +}
> +
> +static int cxd2841er_active_t_to_sleep_tc(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* disable TS output */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
> +	/* enable Hi-Z setting 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
> +	/* enable Hi-Z setting 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* disable ADC 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Disable ADC 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
> +	/* Disable ADC 3 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
> +	/* Disable ADC clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
> +	/* Disable RF level monitor */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
> +	/* Disable demod clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
> +	priv->state = STATE_SLEEP_TC;
> +	return 0;
> +}
> +
> +static int cxd2841er_active_t2_to_sleep_tc(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* disable TS output */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
> +	/* enable Hi-Z setting 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
> +	/* enable Hi-Z setting 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
> +	/* Cancel DVB-T2 setting */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x40);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x21);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xfb);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x00, 0x0f);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x00, 0x3f);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* disable ADC 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Disable ADC 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
> +	/* Disable ADC 3 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
> +	/* Disable ADC clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
> +	/* Disable RF level monitor */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
> +	/* Disable demod clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
> +	priv->state = STATE_SLEEP_TC;
> +	return 0;
> +}
> +
> +static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* disable TS output */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
> +	/* enable Hi-Z setting 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
> +	/* enable Hi-Z setting 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
> +	/* Cancel DVB-C setting */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa3, 0x00, 0x1f);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* disable ADC 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Disable ADC 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
> +	/* Disable ADC 3 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
> +	/* Disable ADC clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
> +	/* Disable RF level monitor */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
> +	/* Disable demod clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
> +	priv->state = STATE_SLEEP_TC;
> +	return 0;
> +}
> +
> +static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_SHUTDOWN) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Clear all demodulator registers */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00);
> +	usleep_range(3000, 5000);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Set demod SW reset */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01);
> +	/* Set X'tal clock to 20.5Mhz */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00);
> +	/* Set demod mode */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a);
> +	/* Clear demod SW reset */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00);
> +	usleep_range(1000, 2000);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* enable DSQOUT */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1F);
> +	/* enable DSQIN */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x9C, 0x40);
> +	/* TADC Bias On */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
> +	/* SADC Bias On */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
> +	priv->state = STATE_SLEEP_S;
> +	return 0;
> +}
> +
> +static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_SHUTDOWN) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Clear all demodulator registers */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00);
> +	usleep_range(3000, 5000);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Set demod SW reset */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01);
> +	/* Set X'tal clock to 20.5Mhz */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00);
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00);
> +	/* Clear demod SW reset */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00);
> +	usleep_range(1000, 2000);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* TADC Bias On */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
> +	/* SADC Bias On */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
> +	priv->state = STATE_SLEEP_TC;
> +	return 0;
> +}
> +
> +static int cxd2841er_tune_done(struct cxd2841er_priv *priv)
> +{
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0, 0);
> +	/* SW Reset */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xfe, 0x01);
> +	/* Enable TS output */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x00);
> +	return 0;
> +}
> +
> +/* Set TS parallel mode */
> +static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
> +					u8 system)
> +{
> +	u8 serial_ts, ts_rate_ctrl_off, ts_in_off;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0xc4, &serial_ts);
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0xd3, &ts_rate_ctrl_off);
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0xde, &ts_in_off);
> +	dev_dbg(&priv->i2c->dev, "%s(): ser_ts=0x%02x rate_ctrl_off=0x%02x in_off=0x%02x\n",
> +		__func__, serial_ts, ts_rate_ctrl_off, ts_in_off);
> +
> +	/*
> +	 * slave    Bank    Addr    Bit    default    Name
> +	 * <SLV-T>  00h     D9h     [7:0]  8'h08      OTSCKPERIOD
> +	 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xd9, 0x08);
> +	/*
> +	 * Disable TS IF Clock
> +	 * slave    Bank    Addr    Bit    default    Name
> +	 * <SLV-T>  00h     32h     [0]    1'b1       OREG_CK_TSIF_EN
> +	 */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x00, 0x01);
> +	/*
> +	 * slave    Bank    Addr    Bit    default    Name
> +	 * <SLV-T>  00h     33h     [1:0]  2'b01      OREG_CKSEL_TSIF
> +	 */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x33, 0x00, 0x03);
> +	/*
> +	 * Enable TS IF Clock
> +	 * slave    Bank    Addr    Bit    default    Name
> +	 * <SLV-T>  00h     32h     [0]    1'b1       OREG_CK_TSIF_EN
> +	 */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x01, 0x01);
> +
> +	if (system == SYS_DVBT) {
> +		/* Enable parity period for DVB-T */
> +		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +		cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01);
> +	} else if (system == SYS_DVBC_ANNEX_A) {
> +		/* Enable parity period for DVB-C */
> +		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
> +		cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01);
> +	}
> +}
> +
> +static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv)
> +{
> +	u8 chip_id;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0, 0);
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id);
> +	return chip_id;
> +}
> +
> +static int cxd2841er_read_status_s(struct dvb_frontend *fe,
> +				   fe_status_t *status)
> +{
> +	u8 reg = 0;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	*status = 0;
> +	if (priv->state != STATE_ACTIVE_S) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0xA0 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
> +	/*
> +	 *  slave     Bank      Addr      Bit      Signal name
> +	 * <SLV-T>    A0h       11h       [2]      ITSLOCK
> +	 */
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0x11, &reg);
> +	if (reg & 0x04) {
> +		*status = FE_HAS_SIGNAL
> +			| FE_HAS_CARRIER
> +			| FE_HAS_VITERBI
> +			| FE_HAS_SYNC
> +			| FE_HAS_LOCK;
> +	}
> +	dev_dbg(&priv->i2c->dev, "%s(): result 0x%x\n", __func__, *status);
> +	return 0;
> +}
> +
> +static int cxd2841er_read_status_t_t2(struct cxd2841er_priv *priv,
> +				      u8 *sync, u8 *tslock, u8 *unlock)
> +{
> +	u8 data = 0;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_TC)
> +		return -EINVAL;
> +	if (priv->system == SYS_DVBT) {
> +		/* Set SLV-T Bank : 0x10 */
> +		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	} else {
> +		/* Set SLV-T Bank : 0x20 */
> +		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
> +	}
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data);
> +	if ((data & 0x07) == 0x07) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): invalid hardware state detected\n", __func__);
> +		*sync = 0;
> +		*tslock = 0;
> +		*unlock = 0;
> +	} else {
> +		*sync = ((data & 0x07) == 0x6 ? 1 : 0);
> +		*tslock = ((data & 0x20) ? 1 : 0);
> +		*unlock = ((data & 0x10) ? 1 : 0);
> +	}
> +	return 0;
> +}
> +
> +static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock)
> +{
> +	u8 data;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_TC)
> +		return -EINVAL;
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0x88, &data);
> +	if ((data & 0x01) == 0) {
> +		*tslock = 0;
> +	} else {
> +		cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data);
> +		*tslock = ((data & 0x20) ? 1 : 0);
> +	}
> +	return 0;
> +}
> +
> +static int cxd2841er_read_status_tc(struct dvb_frontend *fe,
> +				    fe_status_t *status)
> +{
> +	int ret = 0;
> +	u8 sync = 0;
> +	u8 tslock = 0;
> +	u8 unlock = 0;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	*status = 0;
> +	if (priv->state == STATE_ACTIVE_TC) {
> +		if (priv->system == SYS_DVBT || priv->system == SYS_DVBT2) {
> +			ret = cxd2841er_read_status_t_t2(
> +				priv, &sync, &tslock, &unlock);
> +			if (ret)
> +				goto done;
> +			if (unlock)
> +				goto done;
> +			if (sync)
> +				*status = FE_HAS_SIGNAL |
> +					FE_HAS_CARRIER |
> +					FE_HAS_VITERBI |
> +					FE_HAS_SYNC;
> +			if (tslock)
> +				*status |= FE_HAS_LOCK;
> +		} else if (priv->system == SYS_DVBC_ANNEX_A) {
> +			ret = cxd2841er_read_status_c(priv, &tslock);
> +			if (ret)
> +				goto done;
> +			if (tslock)
> +				*status = FE_HAS_SIGNAL |
> +					FE_HAS_CARRIER |
> +					FE_HAS_VITERBI |
> +					FE_HAS_SYNC |
> +					FE_HAS_LOCK;
> +		}
> +	}
> +done:
> +	dev_dbg(&priv->i2c->dev, "%s(): status 0x%x\n", __func__, *status);
> +	return ret;
> +}
> +
> +static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv,
> +					     int *offset)
> +{
> +	u8 data[3];
> +	u8 is_hs_mode;
> +	s32 cfrl_ctrlval;
> +	s32 temp_div, temp_q, temp_r;
> +
> +	if (priv->state != STATE_ACTIVE_S) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	/*
> +	 * Get High Sampling Rate mode
> +	 *  slave     Bank      Addr      Bit      Signal name
> +	 * <SLV-T>    A0h       10h       [0]      ITRL_LOCK
> +	 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data[0]);
> +	if (data[0] & 0x01) {
> +		/*
> +		 *  slave     Bank      Addr      Bit      Signal name
> +		 * <SLV-T>    A0h       50h       [4]      IHSMODE
> +		 */
> +		cxd2841er_read_reg(priv, I2C_SLVT, 0x50, &data[0]);
> +		is_hs_mode = (data[0] & 0x10 ? 1 : 0);
> +	} else {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): unable to detect sampling rate mode\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +	/*
> +	 *  slave     Bank      Addr      Bit      Signal name
> +	 * <SLV-T>    A0h       45h       [4:0]    ICFRL_CTRLVAL[20:16]
> +	 * <SLV-T>    A0h       46h       [7:0]    ICFRL_CTRLVAL[15:8]
> +	 * <SLV-T>    A0h       47h       [7:0]    ICFRL_CTRLVAL[7:0]
> +	 */
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x45, data, 3);
> +	cfrl_ctrlval = sign_extend32((((u32)data[0] & 0x1F) << 16) |
> +				(((u32)data[1] & 0xFF) <<  8) |
> +				((u32)data[2] & 0xFF), 20);
> +	temp_div = (is_hs_mode ? 1048576 : 1572864);
> +	if (cfrl_ctrlval > 0) {
> +		temp_q = div_s64_rem(97375LL * cfrl_ctrlval,
> +			temp_div, &temp_r);
> +	} else {
> +		temp_q = div_s64_rem(-97375LL * cfrl_ctrlval,
> +			temp_div, &temp_r);
> +	}
> +	if (temp_r >= temp_div / 2)
> +		temp_q++;
> +	if (cfrl_ctrlval > 0)
> +		temp_q *= -1;
> +	*offset = temp_q;
> +	return 0;
> +}
> +
> +int cxd2841er_get_carrier_offset_t2(
> +	struct cxd2841er_priv *priv, u32 bandwidth, int *offset)
> +{
> +	u8 data[4];
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	if (priv->system != SYS_DVBT2) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
> +			__func__, priv->system);
> +		return -EINVAL;
> +	}
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data));
> +	*offset = -1 * sign_extend32(
> +		((u32)(data[0] & 0x0F) << 24) | ((u32)data[1] << 16) |
> +		((u32)data[2] << 8) | (u32)data[3], 27);
> +	switch (bandwidth) {
> +	case 1712000:
> +		*offset /= 582;
> +		break;
> +	case 5000000:
> +	case 6000000:
> +	case 7000000:
> +	case 8000000:
> +		*offset *= (bandwidth / 1000000);
> +		*offset /= 940;
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
> +			__func__, bandwidth);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +int cxd2841er_get_carrier_offset_c(
> +	struct cxd2841er_priv *priv, int *offset)
> +{
> +	u8 data[2];
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	if (priv->system != SYS_DVBC_ANNEX_A) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
> +			__func__, priv->system);
> +		return -EINVAL;
> +	}
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x15, data, sizeof(data));
> +	*offset = div_s64(41000LL * sign_extend32((((u32)data[0] & 0x3f) << 8)
> +						| (u32)data[1], 13), 16384);
> +	return 0;
> +}
> +
> +static int cxd2841er_read_packet_errors_t(
> +		struct cxd2841er_priv *priv, u32 *penum)
> +{
> +	u8 data[3];
> +
> +	*penum = 0;
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data));
> +	if (data[2] & 0x01)
> +		*penum = ((u32)data[0] << 8) | (u32)data[1];
> +	return 0;
> +}
> +
> +static int cxd2841er_read_packet_errors_t2(
> +		struct cxd2841er_priv *priv, u32 *penum)
> +{
> +	u8 data[3];
> +
> +	*penum = 0;
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24);
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0xfd, data, sizeof(data));
> +	if (data[0] & 0x01)
> +		*penum = ((u32)data[1] << 8) | (u32)data[2];
> +	return 0;
> +}
> +
> +static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv)
> +{
> +	u8 data[11];
> +	u32 bit_error, bit_count;
> +	u32 temp_q, temp_r;
> +
> +	/* Set SLV-T Bank : 0xA0 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
> +	/*
> +	 *  slave     Bank      Addr      Bit      Signal name
> +	 * <SLV-T>    A0h       35h       [0]      IFVBER_VALID
> +	 * <SLV-T>    A0h       36h       [5:0]    IFVBER_BITERR[21:16]
> +	 * <SLV-T>    A0h       37h       [7:0]    IFVBER_BITERR[15:8]
> +	 * <SLV-T>    A0h       38h       [7:0]    IFVBER_BITERR[7:0]
> +	 * <SLV-T>    A0h       3Dh       [5:0]    IFVBER_BITNUM[21:16]
> +	 * <SLV-T>    A0h       3Eh       [7:0]    IFVBER_BITNUM[15:8]
> +	 * <SLV-T>    A0h       3Fh       [7:0]    IFVBER_BITNUM[7:0]
> +	 */
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11);
> +	if (data[0] & 0x01) {
> +		bit_error = ((u32)(data[1]  & 0x3F) << 16) |
> +			((u32)(data[2]  & 0xFF) <<  8) |
> +			(u32)(data[3]  & 0xFF);
> +		bit_count = ((u32)(data[8]  & 0x3F) << 16) |
> +			((u32)(data[9]  & 0xFF) <<  8) |
> +			(u32)(data[10] & 0xFF);
> +		/*
> +		 *	BER = bitError / bitCount
> +		 *	= (bitError * 10^7) / bitCount
> +		 *	= ((bitError * 625 * 125 * 128) / bitCount
> +		 */
> +		if ((bit_count == 0) || (bit_error > bit_count)) {
> +			dev_dbg(&priv->i2c->dev,
> +				"%s(): invalid bit_error %d, bit_count %d\n",
> +				__func__, bit_error, bit_count);
> +			return 0;
> +		}
> +		temp_q = div_u64_rem(10000000ULL * bit_error,
> +						bit_count, &temp_r);
> +		if (bit_count != 1 && temp_r >= bit_count / 2)
> +			temp_q++;
> +		return temp_q;
> +	}
> +	dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__);
> +	return 0;
> +}
> +
> +
> +static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv)
> +{
> +	u8 data[5];
> +	u32 bit_error, period;
> +	u32 temp_q, temp_r;
> +	u32 result = 0;
> +
> +	/* Set SLV-T Bank : 0xB2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2);
> +	/*
> +	 *  slave     Bank      Addr      Bit      Signal name
> +	 * <SLV-T>    B2h       30h       [0]      IFLBER_VALID
> +	 * <SLV-T>    B2h       31h       [3:0]    IFLBER_BITERR[27:24]
> +	 * <SLV-T>    B2h       32h       [7:0]    IFLBER_BITERR[23:16]
> +	 * <SLV-T>    B2h       33h       [7:0]    IFLBER_BITERR[15:8]
> +	 * <SLV-T>    B2h       34h       [7:0]    IFLBER_BITERR[7:0]
> +	 */
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5);
> +	if (data[0] & 0x01) {
> +		/* Bit error count */
> +		bit_error = ((u32)(data[1] & 0x0F) << 24) |
> +			((u32)(data[2] & 0xFF) << 16) |
> +			((u32)(data[3] & 0xFF) <<  8) |
> +			(u32)(data[4] & 0xFF);
> +
> +		/* Set SLV-T Bank : 0xA0 */
> +		cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
> +		cxd2841er_read_reg(priv, I2C_SLVT, 0x7a, data);
> +		/* Measurement period */
> +		period = (u32)(1 << (data[0] & 0x0F));
> +		if (period == 0) {
> +			dev_dbg(&priv->i2c->dev,
> +				"%s(): period is 0\n", __func__);
> +			return 0;
> +		}
> +		if (bit_error > (period * 64800)) {
> +			dev_dbg(&priv->i2c->dev,
> +				"%s(): invalid bit_err 0x%x period 0x%x\n",
> +				__func__, bit_error, period);
> +			return 0;
> +		}
> +		/*
> +		 * BER = bitError / (period * 64800)
> +		 *	= (bitError * 10^7) / (period * 64800)
> +		 *	= (bitError * 10^5) / (period * 648)
> +		 *	= (bitError * 12500) / (period * 81)
> +		 *	= (bitError * 10) * 1250 / (period * 81)
> +		 */
> +		temp_q = div_u64_rem(12500ULL * bit_error,
> +					period * 81, &temp_r);
> +		if (temp_r >= period * 40)
> +			temp_q++;
> +		result = temp_q;
> +	} else {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): no data available\n", __func__);
> +	}
> +	return result;
> +}
> +
> +static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber)
> +{
> +	u8 data[4];
> +	u32 div, q, r;
> +	u32 bit_err, period_exp, n_ldpc;
> +
> +	*ber = 0;
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): invalid state %d\n", __func__, priv->state);
> +		return -EINVAL;
> +	}
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x39, data, sizeof(data));
> +	if (!(data[0] & 0x10)) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): no valid BER data\n", __func__);
> +		return 0;
> +	}
> +	bit_err = ((u32)(data[0] & 0x0f) << 24) |
> +		((u32)data[1] << 16) |
> +		((u32)data[2] << 8) |
> +		(u32)data[3];
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
> +	period_exp = data[0] & 0x0f;
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22);
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data);
> +	n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800);
> +	if (bit_err > ((1U << period_exp) * n_ldpc)) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): invalid BER value\n", __func__);
> +		return -EINVAL;
> +	}
> +	if (period_exp >= 4) {
> +		div = (1U << (period_exp - 4)) * (n_ldpc / 200);
> +		q = div_u64_rem(3125ULL * bit_err, div, &r);
> +	} else {
> +		div = (1U << period_exp) * (n_ldpc / 200);
> +		q = div_u64_rem(50000ULL * bit_err, div, &r);
> +	}
> +	*ber = (r >= div / 2) ? q + 1 : q;
> +	return 0;
> +}
> +
> +static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber)
> +{
> +	u8 data[2];
> +	u32 div, q, r;
> +	u32 bit_err, period;
> +
> +	*ber = 0;
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): invalid state %d\n", __func__, priv->state);
> +		return -EINVAL;
> +	}
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0x39, data);
> +	if (!(data[0] & 0x01)) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): no valid BER data\n", __func__);
> +		return 0;
> +	}
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data));
> +	bit_err = ((u32)data[0] << 8) | (u32)data[1];
> +	cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
> +	period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07));
> +	div = period / 128;
> +	q = div_u64_rem(78125ULL * bit_err, div, &r);
> +	*ber = (r >= div / 2) ? q + 1 : q;
> +	return 0;
> +}
> +
> +static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys)
> +{
> +	u8 data[3];
> +	u32 res = 0, value;
> +	int min_index, max_index, index;
> +	static const struct cxd2841er_cnr_data *cn_data;
> +
> +	/* Set SLV-T Bank : 0xA1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa1);
> +	/*
> +	 *  slave     Bank      Addr      Bit     Signal name
> +	 * <SLV-T>    A1h       10h       [0]     ICPM_QUICKRDY
> +	 * <SLV-T>    A1h       11h       [4:0]   ICPM_QUICKCNDT[12:8]
> +	 * <SLV-T>    A1h       12h       [7:0]   ICPM_QUICKCNDT[7:0]
> +	 */
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x10, data, 3);
> +	if (data[0] & 0x01) {
> +		value = ((u32)(data[1] & 0x1F) << 8) | (u32)(data[2] & 0xFF);
> +		min_index = 0;
> +		if (delsys == SYS_DVBS) {
> +			cn_data = s_cn_data;
> +			max_index = sizeof(s_cn_data) /
> +				sizeof(s_cn_data[0]) - 1;
> +		} else {
> +			cn_data = s2_cn_data;
> +			max_index = sizeof(s2_cn_data) /
> +				sizeof(s2_cn_data[0]) - 1;
> +		}
> +		if (value >= cn_data[min_index].value) {
> +			res = cn_data[min_index].cnr_x1000;
> +			goto done;
> +		}
> +		if (value <= cn_data[max_index].value) {
> +			res = cn_data[max_index].cnr_x1000;
> +			goto done;
> +		}
> +		while ((max_index - min_index) > 1) {
> +			index = (max_index + min_index) / 2;
> +			if (value == cn_data[index].value) {
> +				res = cn_data[index].cnr_x1000;
> +				goto done;
> +			} else if (value > cn_data[index].value)
> +				max_index = index;
> +			else
> +				min_index = index;
> +			if ((max_index - min_index) <= 1) {
> +				if (value == cn_data[max_index].value) {
> +					res = cn_data[max_index].cnr_x1000;
> +					goto done;
> +				} else {
> +					res = cn_data[min_index].cnr_x1000;
> +					goto done;
> +				}
> +			}
> +		}
> +	} else {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): no data available\n", __func__);
> +	}
> +done:
> +	return res;
> +}
> +
> +static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr)
> +{
> +	u32 reg;
> +	u8 data[2];
> +
> +	*snr = 0;
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): invalid state %d\n", __func__, priv->state);
> +		return -EINVAL;
> +	}
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
> +	reg = ((u32)data[0] << 8) | (u32)data[1];
> +	if (reg == 0) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): reg value out of range\n", __func__);
> +		return 0;
> +	}
> +	if (reg > 4996)
> +		reg = 4996;
> +	*snr = 10000 * ((intlog10(reg) - intlog10(5350 - reg)) >> 24) + 28500;
> +	return 0;
> +}
> +
> +int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr)
> +{
> +	u32 reg;
> +	u8 data[2];
> +
> +	*snr = 0;
> +	if (priv->state != STATE_ACTIVE_TC) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): invalid state %d\n", __func__, priv->state);
> +		return -EINVAL;
> +	}
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
> +	reg = ((u32)data[0] << 8) | (u32)data[1];
> +	if (reg == 0) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): reg value out of range\n", __func__);
> +		return 0;
> +	}
> +	if (reg > 10876)
> +		reg = 10876;
> +	*snr = 10000 * ((intlog10(reg) -
> +		intlog10(12600 - reg)) >> 24) + 32000;
> +	return 0;
> +}
> +
> +static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv,
> +					u8 delsys)
> +{
> +	u8 data[2];
> +
> +	cxd2841er_write_reg(
> +		priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20));
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2);
> +	return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4;
> +}
> +
> +static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv)
> +{
> +	u8 data[2];
> +
> +	/* Set SLV-T Bank : 0xA0 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
> +	/*
> +	 *  slave     Bank      Addr      Bit       Signal name
> +	 * <SLV-T>    A0h       1Fh       [4:0]     IRFAGC_GAIN[12:8]
> +	 * <SLV-T>    A0h       20h       [7:0]     IRFAGC_GAIN[7:0]
> +	 */
> +	cxd2841er_read_regs(priv, I2C_SLVT, 0x1f, data, 2);
> +	return ((((u16)data[0] & 0x1F) << 8) | (u16)(data[1] & 0xFF)) << 3;
> +}
> +
> +static int cxd2841er_read_ber(struct dvb_frontend *fe, u32 *ber)
> +{
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	*ber = 0;
> +	switch (p->delivery_system) {
> +	case SYS_DVBS:
> +		*ber = cxd2841er_mon_read_ber_s(priv);
> +		break;
> +	case SYS_DVBS2:
> +		*ber = cxd2841er_mon_read_ber_s2(priv);
> +		break;
> +	case SYS_DVBT:
> +		return cxd2841er_read_ber_t(priv, ber);
> +	case SYS_DVBT2:
> +		return cxd2841er_read_ber_t2(priv, ber);
> +	default:
> +		*ber = 0;
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static int cxd2841er_read_signal_strength(struct dvb_frontend *fe,
> +					  u16 *strength)
> +{
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	switch (p->delivery_system) {
> +	case SYS_DVBT:
> +	case SYS_DVBT2:
> +		*strength = 65535 - cxd2841er_read_agc_gain_t_t2(
> +			priv, p->delivery_system);
> +		break;
> +	case SYS_DVBS:
> +	case SYS_DVBS2:
> +		*strength = 65535 - cxd2841er_read_agc_gain_s(priv);
> +		break;
> +	default:
> +		*strength = 0;
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static int cxd2841er_read_snr(struct dvb_frontend *fe, u16 *snr)
> +{
> +	u32 tmp = 0;
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	switch (p->delivery_system) {
> +	case SYS_DVBT:
> +		cxd2841er_read_snr_t(priv, &tmp);
> +		break;
> +	case SYS_DVBT2:
> +		cxd2841er_read_snr_t2(priv, &tmp);
> +		break;
> +	case SYS_DVBS:
> +	case SYS_DVBS2:
> +		tmp = cxd2841er_dvbs_read_snr(priv, p->delivery_system);
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s(): unknown delivery system %d\n",
> +			__func__, p->delivery_system);
> +		break;
> +	}
> +	*snr = tmp & 0xffff;
> +	return 0;
> +}
> +
> +static int cxd2841er_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
> +{
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	switch (p->delivery_system) {
> +	case SYS_DVBT:
> +		cxd2841er_read_packet_errors_t(priv, ucblocks);
> +		break;
> +	case SYS_DVBT2:
> +		cxd2841er_read_packet_errors_t2(priv, ucblocks);
> +		break;
> +	default:
> +		*ucblocks = 0;
> +		break;
> +	}
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	return 0;
> +}
> +
> +static int cxd2841er_dvbt2_set_profile(
> +	struct cxd2841er_priv *priv, enum cxd2841er_dvbt2_profile_t profile)
> +{
> +	u8 tune_mode;
> +	u8 seq_not2d_time;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	switch (profile) {
> +	case DVBT2_PROFILE_BASE:
> +		tune_mode = 0x01;
> +		seq_not2d_time = 12;
> +		break;
> +	case DVBT2_PROFILE_LITE:
> +		tune_mode = 0x05;
> +		seq_not2d_time = 40;
> +		break;
> +	case DVBT2_PROFILE_ANY:
> +		tune_mode = 0x00;
> +		seq_not2d_time = 40;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0x2E */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2e);
> +	/* Set profile and tune mode */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x10, tune_mode, 0x07);
> +	/* Set SLV-T Bank : 0x2B */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
> +	/* Set early unlock detection time */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x9d, seq_not2d_time);
> +	return 0;
> +}
> +
> +static int cxd2841er_dvbt2_set_plp_config(struct cxd2841er_priv *priv,
> +					  u8 is_auto, u8 plp_id)
> +{
> +	if (is_auto) {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s() using auto PLP selection\n", __func__);
> +	} else {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s() using manual PLP selection, ID %d\n",
> +			__func__, plp_id);
> +	}
> +	/* Set SLV-T Bank : 0x23 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23);
> +	if (!is_auto) {
> +		/* Manual PLP selection mode. Set the data PLP Id. */
> +		cxd2841er_write_reg(priv, I2C_SLVT, 0xaf, plp_id);
> +	}
> +	/* Auto PLP select (Scanning mode = 0x00). Data PLP select = 0x01. */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xad, (is_auto ? 0x00 : 0x01));
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
> +						u32 bandwidth)
> +{
> +	u32 iffreq;
> +	u8 b20_9f[5];
> +	u8 b10_a6[14];
> +	u8 b10_b6[3];
> +	u8 b10_d7;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	switch (bandwidth) {
> +	case 8000000:
> +		/* bank 0x20, reg 0x9f */
> +		b20_9f[0] = 0x11;
> +		b20_9f[1] = 0xf0;
> +		b20_9f[2] = 0x00;
> +		b20_9f[3] = 0x00;
> +		b20_9f[4] = 0x00;
> +		/* bank 0x10, reg 0xa6 */
> +		b10_a6[0] = 0x26;
> +		b10_a6[1] = 0xaf;
> +		b10_a6[2] = 0x06;
> +		b10_a6[3] = 0xcd;
> +		b10_a6[4] = 0x13;
> +		b10_a6[5] = 0xbb;
> +		b10_a6[6] = 0x28;
> +		b10_a6[7] = 0xba;
> +		b10_a6[8] = 0x23;
> +		b10_a6[9] = 0xa9;
> +		b10_a6[10] = 0x1f;
> +		b10_a6[11] = 0xa8;
> +		b10_a6[12] = 0x2c;
> +		b10_a6[13] = 0xc8;
> +		iffreq = MAKE_IFFREQ_CONFIG(4.80);
> +		b10_d7 = 0x00;
> +		break;
> +	case 7000000:
> +		/* bank 0x20, reg 0x9f */
> +		b20_9f[0] = 0x14;
> +		b20_9f[1] = 0x80;
> +		b20_9f[2] = 0x00;
> +		b20_9f[3] = 0x00;
> +		b20_9f[4] = 0x00;
> +		/* bank 0x10, reg 0xa6 */
> +		b10_a6[0] = 0x2C;
> +		b10_a6[1] = 0xBD;
> +		b10_a6[2] = 0x02;
> +		b10_a6[3] = 0xCF;
> +		b10_a6[4] = 0x04;
> +		b10_a6[5] = 0xF8;
> +		b10_a6[6] = 0x23;
> +		b10_a6[7] = 0xA6;
> +		b10_a6[8] = 0x29;
> +		b10_a6[9] = 0xB0;
> +		b10_a6[10] = 0x26;
> +		b10_a6[11] = 0xA9;
> +		b10_a6[12] = 0x21;
> +		b10_a6[13] = 0xA5;
> +		iffreq = MAKE_IFFREQ_CONFIG(4.2);
> +		b10_d7 = 0x02;
> +		break;
> +	case 6000000:
> +		/* bank 0x20, reg 0x9f */
> +		b20_9f[0] = 0x17;
> +		b20_9f[1] = 0xEA;
> +		b20_9f[2] = 0xAA;
> +		b20_9f[3] = 0xAA;
> +		b20_9f[4] = 0xAA;
> +		/* bank 0x10, reg 0xa6 */
> +		b10_a6[0] = 0x27;
> +		b10_a6[1] = 0xA7;
> +		b10_a6[2] = 0x28;
> +		b10_a6[3] = 0xB3;
> +		b10_a6[4] = 0x02;
> +		b10_a6[5] = 0xF0;
> +		b10_a6[6] = 0x01;
> +		b10_a6[7] = 0xE8;
> +		b10_a6[8] = 0x00;
> +		b10_a6[9] = 0xCF;
> +		b10_a6[10] = 0x00;
> +		b10_a6[11] = 0xE6;
> +		b10_a6[12] = 0x23;
> +		b10_a6[13] = 0xA4;
> +		iffreq = MAKE_IFFREQ_CONFIG(3.6);
> +		b10_d7 = 0x04;
> +		break;
> +	case 5000000:
> +		/* bank 0x20, reg 0x9f */
> +		b20_9f[0] = 0x1C;
> +		b20_9f[1] = 0xB3;
> +		b20_9f[2] = 0x33;
> +		b20_9f[3] = 0x33;
> +		b20_9f[4] = 0x33;
> +		/* bank 0x10, reg 0xa6 */
> +		b10_a6[0] = 0x27;
> +		b10_a6[1] = 0xA7;
> +		b10_a6[2] = 0x28;
> +		b10_a6[3] = 0xB3;
> +		b10_a6[4] = 0x02;
> +		b10_a6[5] = 0xF0;
> +		b10_a6[6] = 0x01;
> +		b10_a6[7] = 0xE8;
> +		b10_a6[8] = 0x00;
> +		b10_a6[9] = 0xCF;
> +		b10_a6[10] = 0x00;
> +		b10_a6[11] = 0xE6;
> +		b10_a6[12] = 0x23;
> +		b10_a6[13] = 0xA4;
> +		iffreq = MAKE_IFFREQ_CONFIG(3.6);
> +		b10_d7 = 0x06;
> +		break;
> +	case 1712000:
> +		/* bank 0x20, reg 0x9f */
> +		b20_9f[0] = 0x58;
> +		b20_9f[1] = 0xE2;
> +		b20_9f[2] = 0xAF;
> +		b20_9f[3] = 0xE0;
> +		b20_9f[4] = 0xBC;
> +		/* bank 0x10, reg 0xa6 */
> +		b10_a6[0] = 0x25;
> +		b10_a6[1] = 0xA0;
> +		b10_a6[2] = 0x36;
> +		b10_a6[3] = 0x8D;
> +		b10_a6[4] = 0x2E;
> +		b10_a6[5] = 0x94;
> +		b10_a6[6] = 0x28;
> +		b10_a6[7] = 0x9B;
> +		b10_a6[8] = 0x32;
> +		b10_a6[9] = 0x90;
> +		b10_a6[10] = 0x2C;
> +		b10_a6[11] = 0x9D;
> +		b10_a6[12] = 0x29;
> +		b10_a6[13] = 0x99;
> +		iffreq = MAKE_IFFREQ_CONFIG(3.5);
> +		b10_d7 = 0x03;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	/* Set SLV-T Bank : 0x20 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x20);
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0x9f, b20_9f, sizeof(b20_9f));
> +	/* Set SLV-T Bank : 0x27 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
> +	cxd2841er_set_reg_bits(
> +		priv, I2C_SLVT, 0x7a,
> +		(bandwidth == 1712000 ? 0x03 : 0x00), 0x0f);
> +	/* Set SLV-T Bank : 0x10 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	/* Group delay equaliser sett. for ASCOT2E */
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0xa6, b10_a6, sizeof(b10_a6));
> +	/* <IF freq setting> */
> +	b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
> +	b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
> +	b10_b6[2] = (u8)(iffreq & 0xff);
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
> +	/* System bandwidth setting */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, b10_d7, 0x07);
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc_to_active_t_band(
> +		struct cxd2841er_priv *priv, u32 bandwidth)
> +{
> +	u8 b13_9c[2] = { 0x01, 0x14 };
> +	u8 bw8mhz_b10_9f[] = { 0x11, 0xF0, 0x00, 0x00, 0x00 };
> +	u8 bw8mhz_b10_a6[] = { 0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB,
> +			0x28, 0xBA, 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8 };
> +	u8 bw8mhz_b10_d9[] = { 0x01, 0xE0 };
> +	u8 bw8mhz_b17_38[] = { 0x01, 0x02 };
> +	u8 bw7mhz_b10_9f[] = { 0x14, 0x80, 0x00, 0x00, 0x00 };
> +	u8 bw7mhz_b10_a6[] = { 0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8,
> +			0x23, 0xA6, 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5 };
> +	u8 bw7mhz_b10_d9[] = { 0x12, 0xF8 };
> +	u8 bw7mhz_b17_38[] = { 0x00, 0x03 };
> +	u8 bw6mhz_b10_9f[] = { 0x17, 0xEA, 0xAA, 0xAA, 0xAA };
> +	u8 bw6mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0,
> +			0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
> +	u8 bw6mhz_b10_d9[] = { 0x1F, 0xDC };
> +	u8 bw6mhz_b17_38[] = { 0x00, 0x03 };
> +	u8 bw5mhz_b10_9f[] = { 0x1C, 0xB3, 0x33, 0x33, 0x33 };
> +	u8 bw5mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0,
> +			0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
> +	u8 bw5mhz_b10_d9[] = { 0x26, 0x3C };
> +	u8 bw5mhz_b17_38[] = { 0x00, 0x03 };
> +	u8 b10_b6[3];
> +	u8 d7val;
> +	u32 iffreq;
> +	u8 *b10_9f;
> +	u8 *b10_a6;
> +	u8 *b10_d9;
> +	u8 *b17_38;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
> +	/* Echo performance optimization setting */
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0x9c, b13_9c, sizeof(b13_9c));
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +
> +	switch (bandwidth) {
> +	case 8000000:
> +		b10_9f = bw8mhz_b10_9f;
> +		b10_a6 = bw8mhz_b10_a6;
> +		b10_d9 = bw8mhz_b10_d9;
> +		b17_38 = bw8mhz_b17_38;
> +		d7val = 0;
> +		iffreq = MAKE_IFFREQ_CONFIG(4.80);
> +		break;
> +	case 7000000:
> +		b10_9f = bw7mhz_b10_9f;
> +		b10_a6 = bw7mhz_b10_a6;
> +		b10_d9 = bw7mhz_b10_d9;
> +		b17_38 = bw7mhz_b17_38;
> +		d7val = 2;
> +		iffreq = MAKE_IFFREQ_CONFIG(4.20);
> +		break;
> +	case 6000000:
> +		b10_9f = bw6mhz_b10_9f;
> +		b10_a6 = bw6mhz_b10_a6;
> +		b10_d9 = bw6mhz_b10_d9;
> +		b17_38 = bw6mhz_b17_38;
> +		d7val = 4;
> +		iffreq = MAKE_IFFREQ_CONFIG(3.60);
> +		break;
> +	case 5000000:
> +		b10_9f = bw5mhz_b10_9f;
> +		b10_a6 = bw5mhz_b10_a6;
> +		b10_d9 = bw5mhz_b10_d9;
> +		b17_38 = bw5mhz_b17_38;
> +		d7val = 6;
> +		iffreq = MAKE_IFFREQ_CONFIG(3.60);
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
> +			__func__, bandwidth);
> +		return -EINVAL;
> +	}
> +	/* <IF freq setting> */
> +	b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
> +	b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
> +	b10_b6[2] = (u8)(iffreq & 0xff);
> +	cxd2841er_write_regs(
> +		priv, I2C_SLVT, 0x9f, b10_9f, sizeof(bw8mhz_b10_9f));
> +	cxd2841er_write_regs(
> +		priv, I2C_SLVT, 0xa6, b10_a6, sizeof(bw8mhz_b10_a6));
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, d7val, 0x7);
> +	cxd2841er_write_regs(
> +		priv, I2C_SLVT, 0xd9, b10_d9, sizeof(bw8mhz_b10_d9));
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
> +	cxd2841er_write_regs(
> +		priv, I2C_SLVT, 0x38, b17_38, sizeof(bw8mhz_b17_38));
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
> +					       u32 bandwidth)
> +{
> +	u8 bw7_8mhz_b10_a6[] = {
> +		0x2D, 0xC7, 0x04, 0xF4, 0x07, 0xC5, 0x2A, 0xB8,
> +		0x27, 0x9E, 0x27, 0xA4, 0x29, 0xAB };
> +	u8 bw6mhz_b10_a6[] = {
> +		0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
> +		0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
> +	u8 b10_b6[3];
> +	u32 iffreq;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	switch (bandwidth) {
> +	case 8000000:
> +	case 7000000:
> +		cxd2841er_write_regs(
> +			priv, I2C_SLVT, 0xa6,
> +			bw7_8mhz_b10_a6, sizeof(bw7_8mhz_b10_a6));
> +		iffreq = MAKE_IFFREQ_CONFIG(4.9);
> +		break;
> +	case 6000000:
> +		cxd2841er_write_regs(
> +			priv, I2C_SLVT, 0xa6,
> +			bw6mhz_b10_a6, sizeof(bw6mhz_b10_a6));
> +		iffreq = MAKE_IFFREQ_CONFIG(3.7);
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n",
> +			__func__, bandwidth);
> +		return -EINVAL;
> +	}
> +	/* <IF freq setting> */
> +	b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
> +	b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
> +	b10_b6[2] = (u8)(iffreq & 0xff);
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
> +	/* Set SLV-T Bank : 0x11 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
> +	switch (bandwidth) {
> +	case 8000000:
> +	case 7000000:
> +		cxd2841er_set_reg_bits(
> +			priv, I2C_SLVT, 0xa3, 0x00, 0x1f);
> +		break;
> +	case 6000000:
> +		cxd2841er_set_reg_bits(
> +			priv, I2C_SLVT, 0xa3, 0x14, 0x1f);
> +		break;
> +	}
> +	/* Set SLV-T Bank : 0x40 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
> +	switch (bandwidth) {
> +	case 8000000:
> +		cxd2841er_set_reg_bits(
> +			priv, I2C_SLVT, 0x26, 0x0b, 0x0f);
> +		cxd2841er_write_reg(priv, I2C_SLVT,  0x27, 0x3e);
> +		break;
> +	case 7000000:
> +		cxd2841er_set_reg_bits(
> +			priv, I2C_SLVT, 0x26, 0x09, 0x0f);
> +		cxd2841er_write_reg(priv, I2C_SLVT,  0x27, 0xd6);
> +		break;
> +	case 6000000:
> +		cxd2841er_set_reg_bits(
> +			priv, I2C_SLVT, 0x26, 0x08, 0x0f);
> +		cxd2841er_write_reg(priv, I2C_SLVT,  0x27, 0x6e);
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
> +					  u32 bandwidth)
> +{
> +	u8 data[2] = { 0x09, 0x54 };
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_set_ts_clock_mode(priv, SYS_DVBT);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Set demod mode */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Enable demod clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
> +	/* Disable RF level monitor */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
> +	/* Enable ADC clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
> +	/* Enable ADC 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
> +	/* xtal freq 20.5MHz */
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
> +	/* Enable ADC 4 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
> +	/* Set SLV-T Bank : 0x10 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	/* IFAGC gain settings */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f);
> +	/* Set SLV-T Bank : 0x11 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
> +	/* BBAGC TARGET level setting */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50);
> +	/* Set SLV-T Bank : 0x10 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	/* ASCOT setting ON */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
> +	/* Set SLV-T Bank : 0x18 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18);
> +	/* Pre-RS BER moniter setting */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x36, 0x40, 0x07);
> +	/* FEC Auto Recovery setting */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x01, 0x01);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* TSIF setting */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
> +	cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Disable HiZ Setting 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
> +	/* Disable HiZ Setting 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
> +	priv->state = STATE_ACTIVE_TC;
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
> +					   u32 bandwidth)
> +{
> +	u8 data[2] = { 0x09, 0x54 };
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Set demod mode */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x02);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Enable demod clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
> +	/* Disable RF level monitor */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
> +	/* Enable ADC clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
> +	/* Enable ADC 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
> +	/* xtal freq 20.5MHz */
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
> +	/* Enable ADC 4 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
> +	/* Set SLV-T Bank : 0x10 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	/* IFAGC gain settings */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f);
> +	/* Set SLV-T Bank : 0x11 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
> +	/* BBAGC TARGET level setting */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50);
> +	/* Set SLV-T Bank : 0x10 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	/* ASCOT setting ON */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
> +	/* Set SLV-T Bank : 0x20 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
> +	/* Acquisition optimization setting */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x8b, 0x3c);
> +	/* Set SLV-T Bank : 0x2b */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* TSIF setting */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
> +	/* DVB-T2 initial setting */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x10);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x34);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xd8);
> +	/* Set SLV-T Bank : 0x2a */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x04, 0x0f);
> +	/* Set SLV-T Bank : 0x2b */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f);
> +
> +	cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth);
> +
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Disable HiZ Setting 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
> +	/* Disable HiZ Setting 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
> +	priv->state = STATE_ACTIVE_TC;
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv,
> +					  u32 bandwidth)
> +{
> +	u8 data[2] = { 0x09, 0x54 };
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_set_ts_clock_mode(priv, SYS_DVBC_ANNEX_A);
> +	/* Set SLV-X Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
> +	/* Set demod mode */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x04);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Enable demod clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
> +	/* Disable RF level monitor */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
> +	/* Enable ADC clock */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
> +	/* Enable ADC 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
> +	/* xtal freq 20.5MHz */
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
> +	/* Enable ADC 4 */
> +	cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
> +	/* Set SLV-T Bank : 0x10 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	/* IFAGC gain settings */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x09, 0x1f);
> +	/* Set SLV-T Bank : 0x11 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
> +	/* BBAGC TARGET level setting */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x48);
> +	/* Set SLV-T Bank : 0x10 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	/* ASCOT setting ON */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
> +	/* Set SLV-T Bank : 0x40 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
> +	/* Demod setting */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc3, 0x00, 0x04);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* TSIF setting */
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
> +
> +	cxd2841er_sleep_tc_to_active_c_band(priv, 8000000);
> +	/* Set SLV-T Bank : 0x00 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	/* Disable HiZ Setting 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
> +	/* Disable HiZ Setting 2 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
> +	priv->state = STATE_ACTIVE_TC;
> +	return 0;
> +}
> +
> +static int cxd2841er_get_frontend(struct dvb_frontend *fe)
> +{
> +	fe_status_t status = 0;
> +	u16 strength = 0, snr = 0;
> +	u32 errors = 0, ber = 0;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state == STATE_ACTIVE_S)
> +		cxd2841er_read_status_s(fe, &status);
> +	else if (priv->state == STATE_ACTIVE_TC)
> +		cxd2841er_read_status_tc(fe, &status);
> +
> +	if (status & FE_HAS_LOCK) {
> +		cxd2841er_read_signal_strength(fe, &strength);
> +		p->strength.len = 1;
> +		p->strength.stat[0].scale = FE_SCALE_RELATIVE;
> +		p->strength.stat[0].uvalue = strength;
> +		cxd2841er_read_snr(fe, &snr);
> +		p->cnr.len = 1;
> +		p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
> +		p->cnr.stat[0].svalue = snr;
> +		cxd2841er_read_ucblocks(fe, &errors);
> +		p->block_error.len = 1;
> +		p->block_error.stat[0].scale = FE_SCALE_COUNTER;
> +		p->block_error.stat[0].uvalue = errors;
> +		cxd2841er_read_ber(fe, &ber);
> +		p->post_bit_error.len = 1;
> +		p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
> +		p->post_bit_error.stat[0].uvalue = ber;
> +	} else {
> +		p->strength.len = 1;
> +		p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		p->cnr.len = 1;
> +		p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		p->block_error.len = 1;
> +		p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		p->post_bit_error.len = 1;
> +		p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +	}
> +	return 0;
> +}
> +
> +static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
> +{
> +	int ret = 0, i, timeout, carr_offset;
> +	fe_status_t status;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +	u32 symbol_rate = p->symbol_rate/1000;
> +
> +	dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n",
> +		__func__,
> +		(p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"),
> +		 p->frequency, symbol_rate);

I was unable to identify how ROLLOFF is handled for DVB-S2 in this code.

> +	switch (priv->state) {
> +	case STATE_SLEEP_S:
> +		ret = cxd2841er_sleep_s_to_active_s(
> +			priv, p->delivery_system, symbol_rate);
> +		break;
> +	case STATE_ACTIVE_S:
> +		ret = cxd2841er_retune_active(priv, p);
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +	if (ret) {
> +		dev_dbg(&priv->i2c->dev, "%s(): tune failed\n", __func__);
> +		goto done;
> +	}
> +	if (fe->ops.i2c_gate_ctrl)
> +		fe->ops.i2c_gate_ctrl(fe, 1);
> +	if (fe->ops.tuner_ops.set_params)
> +		fe->ops.tuner_ops.set_params(fe);
> +	if (fe->ops.i2c_gate_ctrl)
> +		fe->ops.i2c_gate_ctrl(fe, 0);
> +	cxd2841er_tune_done(priv);
> +	timeout = ((3000000 + (symbol_rate - 1)) / symbol_rate) + 150;
> +	for (i = 0; i < timeout / CXD2841ER_DVBS_POLLING_INVL; i++) {
> +		usleep_range(CXD2841ER_DVBS_POLLING_INVL*1000,
> +			(CXD2841ER_DVBS_POLLING_INVL + 2) * 1000);
> +		cxd2841er_read_status_s(fe, &status);
> +		if (status & FE_HAS_LOCK)
> +			break;
> +	}
> +	if (status & FE_HAS_LOCK) {
> +		if (cxd2841er_get_carrier_offset_s_s2(
> +				priv, &carr_offset)) {
> +			ret = -EINVAL;
> +			goto done;
> +		}
> +		dev_dbg(&priv->i2c->dev, "%s(): carrier_offset=%d\n",
> +			__func__, carr_offset);
> +	}
> +done:
> +	return ret;
> +}
> +
> +static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe)
> +{
> +	int ret = 0, timeout;
> +	fe_status_t status;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (p->delivery_system == SYS_DVBT) {
> +		priv->system = SYS_DVBT;
> +		switch (priv->state) {
> +		case STATE_SLEEP_TC:
> +			ret = cxd2841er_sleep_tc_to_active_t(
> +				priv, p->bandwidth_hz);
> +			break;
> +		case STATE_ACTIVE_TC:
> +			ret = cxd2841er_retune_active(priv, p);
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +				__func__, priv->state);
> +			ret = -EINVAL;
> +		}
> +	} else if (p->delivery_system == SYS_DVBT2) {
> +		priv->system = SYS_DVBT2;
> +		cxd2841er_dvbt2_set_plp_config(priv,
> +			(int)(p->stream_id > 255), p->stream_id);
> +		cxd2841er_dvbt2_set_profile(priv, DVBT2_PROFILE_BASE);
> +		switch (priv->state) {
> +		case STATE_SLEEP_TC:
> +			ret = cxd2841er_sleep_tc_to_active_t2(priv,
> +				p->bandwidth_hz);
> +			break;
> +		case STATE_ACTIVE_TC:
> +			ret = cxd2841er_retune_active(priv, p);
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +				__func__, priv->state);
> +			ret = -EINVAL;
> +		}
> +	} else if (p->delivery_system == SYS_DVBC_ANNEX_A ||
> +			p->delivery_system == SYS_DVBC_ANNEX_C) {
> +		priv->system = SYS_DVBC_ANNEX_A;
> +		switch (priv->state) {
> +		case STATE_SLEEP_TC:
> +			ret = cxd2841er_sleep_tc_to_active_c(
> +				priv, p->bandwidth_hz);
> +			break;
> +		case STATE_ACTIVE_TC:
> +			ret = cxd2841er_retune_active(priv, p);
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
> +				__func__, priv->state);
> +			ret = -EINVAL;
> +		}
> +	} else {
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): invalid delivery system %d\n",
> +			__func__, p->delivery_system);
> +		ret = -EINVAL;
> +	}
> +	if (ret)
> +		goto done;
> +	if (fe->ops.i2c_gate_ctrl)
> +		fe->ops.i2c_gate_ctrl(fe, 1);
> +	if (fe->ops.tuner_ops.set_params)
> +		fe->ops.tuner_ops.set_params(fe);
> +	if (fe->ops.i2c_gate_ctrl)
> +		fe->ops.i2c_gate_ctrl(fe, 0);
> +	cxd2841er_tune_done(priv);
> +	timeout = 2500;
> +	while (timeout > 0) {
> +		ret = cxd2841er_read_status_tc(fe, &status);
> +		if (ret)
> +			goto done;
> +		if (status & FE_HAS_LOCK)
> +			break;
> +		msleep(20);
> +		timeout -= 20;
> +	}
> +	if (timeout < 0)
> +		dev_dbg(&priv->i2c->dev,
> +			"%s(): LOCK wait timeout\n", __func__);
> +done:
> +	return ret;
> +}
> +
> +static int cxd2841er_tune_s(struct dvb_frontend *fe,
> +			    bool re_tune,
> +			    unsigned int mode_flags,
> +			    unsigned int *delay,
> +			    fe_status_t *status)
> +{
> +	int ret, carrier_offset;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +
> +	dev_dbg(&priv->i2c->dev, "%s() re_tune=%d\n", __func__, re_tune);
> +	if (re_tune) {
> +		ret = cxd2841er_set_frontend_s(fe);
> +		if (ret)
> +			return ret;
> +		cxd2841er_read_status_s(fe, status);
> +		if (*status & FE_HAS_LOCK) {
> +			if (cxd2841er_get_carrier_offset_s_s2(
> +					priv, &carrier_offset))
> +				return -EINVAL;
> +			p->frequency += carrier_offset;
> +			ret = cxd2841er_set_frontend_s(fe);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +	*delay = HZ / 5;
> +	return cxd2841er_read_status_s(fe, status);
> +}
> +
> +static int cxd2841er_tune_tc(struct dvb_frontend *fe,
> +			     bool re_tune,
> +			     unsigned int mode_flags,
> +			     unsigned int *delay,
> +			     fe_status_t *status)
> +{
> +	int ret, carrier_offset;
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
> +
> +	dev_dbg(&priv->i2c->dev, "%s(): re_tune %d\n", __func__, re_tune);
> +	if (re_tune) {
> +		ret = cxd2841er_set_frontend_tc(fe);
> +		if (ret)
> +			return ret;
> +		cxd2841er_read_status_tc(fe, status);
> +		if (*status & FE_HAS_LOCK) {
> +			switch (priv->system) {
> +			case SYS_DVBT:
> +			case SYS_DVBT2:
> +				ret = cxd2841er_get_carrier_offset_t2(
> +					priv, p->bandwidth_hz,
> +					&carrier_offset);
> +				break;
> +			case SYS_DVBC_ANNEX_A:
> +				ret = cxd2841er_get_carrier_offset_c(
> +					priv, &carrier_offset);
> +				break;
> +			default:
> +				dev_dbg(&priv->i2c->dev,
> +					"%s(): invalid delivery system %d\n",
> +					__func__, priv->system);
> +				return -EINVAL;
> +			}
> +			if (ret)
> +				return ret;
> +			dev_dbg(&priv->i2c->dev, "%s(): carrier offset %d\n",
> +				__func__, carrier_offset);
> +			p->frequency += carrier_offset;
> +			ret = cxd2841er_set_frontend_tc(fe);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +	*delay = HZ / 5;
> +	return cxd2841er_read_status_tc(fe, status);
> +}
> +
> +static int cxd2841er_sleep_s(struct dvb_frontend *fe)
> +{
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_active_s_to_sleep_s(fe->demodulator_priv);
> +	cxd2841er_sleep_s_to_shutdown(fe->demodulator_priv);
> +	return 0;
> +}
> +
> +static int cxd2841er_sleep_tc(struct dvb_frontend *fe)
> +{
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	if (priv->state == STATE_ACTIVE_TC) {
> +		switch (priv->system) {
> +		case SYS_DVBT:
> +			cxd2841er_active_t_to_sleep_tc(priv);
> +			break;
> +		case SYS_DVBT2:
> +			cxd2841er_active_t2_to_sleep_tc(priv);
> +			break;
> +		case SYS_DVBC_ANNEX_A:
> +			cxd2841er_active_c_to_sleep_tc(priv);
> +			break;
> +		default:
> +			dev_warn(&priv->i2c->dev,
> +				"%s(): unknown delivery system %d\n",
> +				__func__, priv->system);
> +		}
> +	}
> +	if (priv->state != STATE_SLEEP_TC) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	cxd2841er_sleep_tc_to_shutdown(priv);
> +	return 0;
> +}
> +
> +static int cxd2841er_send_burst(struct dvb_frontend *fe,
> +				fe_sec_mini_cmd_t burst)
> +{
> +	u8 data;
> +	struct cxd2841er_priv *priv  = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s(): burst mode %s\n", __func__,
> +		(burst == SEC_MINI_A ? "A" : "B"));
> +	if (priv->state != STATE_SLEEP_S &&
> +			priv->state != STATE_ACTIVE_S) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	data = (burst == SEC_MINI_A ? 0 : 1);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x34, 0x01);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x35, data);
> +	return 0;
> +}
> +
> +static int cxd2841er_set_tone(struct dvb_frontend *fe,
> +			      fe_sec_tone_mode_t tone)
> +{
> +	u8 data;
> +	struct cxd2841er_priv *priv  = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s(): tone %s\n", __func__,
> +		(tone == SEC_TONE_ON ? "On" : "Off"));
> +	if (priv->state != STATE_SLEEP_S &&
> +			priv->state != STATE_ACTIVE_S) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	data = (tone == SEC_TONE_ON ? 1 : 0);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x36, data);
> +	return 0;
> +}
> +
> +static int cxd2841er_send_diseqc_msg(struct dvb_frontend *fe,
> +				     struct dvb_diseqc_master_cmd *cmd)
> +{
> +	int i;
> +	u8 data[12];
> +	struct cxd2841er_priv *priv  = fe->demodulator_priv;
> +
> +	if (priv->state != STATE_SLEEP_S &&
> +			priv->state != STATE_ACTIVE_S) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
> +			__func__, priv->state);
> +		return -EINVAL;
> +	}
> +	dev_dbg(&priv->i2c->dev,
> +		"%s(): cmd->len %d\n", __func__, cmd->msg_len);
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
> +	/* DiDEqC enable */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x33, 0x01);
> +	/* cmd1 length & data */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x3d, cmd->msg_len);
> +	memset(data, 0, sizeof(data));
> +	for (i = 0; i < cmd->msg_len && i < sizeof(data); i++)
> +		data[i] = cmd->msg[i];
> +	cxd2841er_write_regs(priv, I2C_SLVT, 0x3e, data, sizeof(data));
> +	/* repeat count for cmd1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x37, 1);
> +	/* repeat count for cmd2: always 0 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x38, 0);
> +	/* start transmit */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x32, 0x01);
> +	/* wait for 1 sec timeout */
> +	for (i = 0; i < 50; i++) {
> +		cxd2841er_read_reg(priv, I2C_SLVT, 0x10, data);
> +		if (!data[0]) {
> +			dev_dbg(&priv->i2c->dev,
> +				"%s(): DiSEqC cmd has been sent\n", __func__);
> +			return 0;
> +		}
> +		msleep(20);
> +	}
> +	dev_dbg(&priv->i2c->dev,
> +		"%s(): DiSEqC cmd transmit timeout\n", __func__);
> +	return -ETIMEDOUT;
> +}
> +
> +static void cxd2841er_release(struct dvb_frontend *fe)
> +{
> +	struct cxd2841er_priv *priv  = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	kfree(priv);
> +}
> +
> +static int cxd2841er_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
> +{
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s(): enable=%d\n", __func__, enable);
> +	cxd2841er_set_reg_bits(
> +		priv, I2C_SLVX, 0x8, (enable ? 0x01 : 0x00), 0x01);
> +	return 0;
> +}
> +
> +static enum dvbfe_algo cxd2841er_get_algo(struct dvb_frontend *fe)
> +{
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	return DVBFE_ALGO_HW;
> +}
> +
> +static int cxd2841er_init_s(struct dvb_frontend *fe)
> +{
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_shutdown_to_sleep_s(priv);
> +	/* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xb9, 0x01, 0x01);
> +	return 0;
> +}
> +
> +static int cxd2841er_init_tc(struct dvb_frontend *fe)
> +{
> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
> +
> +	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
> +	cxd2841er_shutdown_to_sleep_tc(priv);
> +	/* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcb, 0x40, 0x40);
> +	/* SONY_DEMOD_CONFIG_IFAGC_ADC_FS = 0 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0xcd, 0x50);
> +	/* SONY_DEMOD_CONFIG_PARALLEL_SEL = 1 */
> +	cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
> +	cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x80);
> +	return 0;
> +}
> +
> +static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops;
> +static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops;
> +static struct dvb_frontend_ops cxd2841er_dvbc_ops;
> +
> +static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
> +					     struct i2c_adapter *i2c,
> +					     u8 system)
> +{
> +	u8 chip_id = 0;
> +	const char *type;
> +	struct cxd2841er_priv *priv = NULL;
> +
> +	/* allocate memory for the internal state */
> +	priv = kzalloc(sizeof(struct cxd2841er_priv), GFP_KERNEL);
> +	if (!priv)
> +		return NULL;
> +	priv->i2c = i2c;
> +	priv->config = cfg;
> +	priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1;
> +	priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1;
> +	/* create dvb_frontend */
> +	switch (system) {
> +	case SYS_DVBS:
> +		memcpy(&priv->frontend.ops,
> +			&cxd2841er_dvbs_s2_ops,
> +			sizeof(struct dvb_frontend_ops));
> +		type = "S/S2";
> +		break;
> +	case SYS_DVBT:
> +		memcpy(&priv->frontend.ops,
> +			&cxd2841er_dvbt_t2_ops,
> +			sizeof(struct dvb_frontend_ops));
> +		type = "T/T2";
> +		break;
> +	case SYS_DVBC_ANNEX_A:
> +		memcpy(&priv->frontend.ops,
> +			&cxd2841er_dvbc_ops,
> +			sizeof(struct dvb_frontend_ops));
> +		type = "C/C2";
> +		break;
> +	default:
> +		kfree(priv);
> +		return NULL;
> +	}
> +	priv->frontend.demodulator_priv = priv;
> +	dev_info(&priv->i2c->dev,
> +		"%s(): attaching CXD2841ER DVB-%s frontend\n",
> +		__func__, type);
> +	dev_info(&priv->i2c->dev,
> +		"%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n",
> +		__func__, priv->i2c,
> +		priv->i2c_addr_slvx, priv->i2c_addr_slvt);
> +	chip_id = cxd2841er_chip_id(priv);
> +	if (chip_id != CXD2841ER_CHIP_ID) {
> +		dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n",
> +			__func__, chip_id);
> +		priv->frontend.demodulator_priv = NULL;
> +		kfree(priv);
> +		return NULL;
> +	}
> +	dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n",
> +		__func__, chip_id);
> +	return &priv->frontend;
> +}
> +
> +struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
> +					struct i2c_adapter *i2c)
> +{
> +	return cxd2841er_attach(cfg, i2c, SYS_DVBS);
> +}
> +EXPORT_SYMBOL(cxd2841er_attach_s);
> +
> +struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg,
> +					struct i2c_adapter *i2c)
> +{
> +	return cxd2841er_attach(cfg, i2c, SYS_DVBT);
> +}
> +EXPORT_SYMBOL(cxd2841er_attach_t);
> +
> +struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg,
> +					struct i2c_adapter *i2c)
> +{
> +	return cxd2841er_attach(cfg, i2c, SYS_DVBC_ANNEX_A);
> +}
> +EXPORT_SYMBOL(cxd2841er_attach_c);
> +
> +static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = {
> +	.delsys = { SYS_DVBS, SYS_DVBS2 },
> +	.info = {
> +		.name		= "Sony CXD2841ER DVB-S/S2 demodulator",
> +		.frequency_min	= 500000,
> +		.frequency_max	= 2500000,
> +		.frequency_stepsize	= 0,
> +		.symbol_rate_min = 1000000,
> +		.symbol_rate_max = 45000000,
> +		.symbol_rate_tolerance = 500,
> +		.caps = FE_CAN_INVERSION_AUTO |
> +			FE_CAN_FEC_AUTO |
> +			FE_CAN_QPSK,
> +	},
> +	.init = cxd2841er_init_s,
> +	.sleep = cxd2841er_sleep_s,
> +	.release = cxd2841er_release,
> +	.set_frontend = cxd2841er_set_frontend_s,
> +	.get_frontend = cxd2841er_get_frontend,
> +	.read_status = cxd2841er_read_status_s,
> +	.i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
> +	.get_frontend_algo = cxd2841er_get_algo,
> +	.set_tone = cxd2841er_set_tone,
> +	.diseqc_send_burst = cxd2841er_send_burst,
> +	.diseqc_send_master_cmd = cxd2841er_send_diseqc_msg,
> +	.tune = cxd2841er_tune_s
> +};
> +
> +static struct  dvb_frontend_ops cxd2841er_dvbt_t2_ops = {
> +	.delsys = { SYS_DVBT, SYS_DVBT2 },
> +	.info = {
> +		.name	= "Sony CXD2841ER DVB-T/T2 demodulator",
> +		.caps = FE_CAN_FEC_1_2 |
> +			FE_CAN_FEC_2_3 |
> +			FE_CAN_FEC_3_4 |
> +			FE_CAN_FEC_5_6 |
> +			FE_CAN_FEC_7_8 |
> +			FE_CAN_FEC_AUTO |
> +			FE_CAN_QPSK |
> +			FE_CAN_QAM_16 |
> +			FE_CAN_QAM_32 |
> +			FE_CAN_QAM_64 |
> +			FE_CAN_QAM_128 |
> +			FE_CAN_QAM_256 |
> +			FE_CAN_QAM_AUTO |
> +			FE_CAN_TRANSMISSION_MODE_AUTO |
> +			FE_CAN_GUARD_INTERVAL_AUTO |
> +			FE_CAN_HIERARCHY_AUTO |
> +			FE_CAN_MUTE_TS |
> +			FE_CAN_2G_MODULATION,
> +		.frequency_min = 42000000,
> +		.frequency_max = 1002000000
> +	},
> +	.init = cxd2841er_init_tc,
> +	.sleep = cxd2841er_sleep_tc,
> +	.release = cxd2841er_release,
> +	.set_frontend = cxd2841er_set_frontend_tc,
> +	.get_frontend = cxd2841er_get_frontend,
> +	.read_status = cxd2841er_read_status_tc,
> +	.tune = cxd2841er_tune_tc,
> +	.i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
> +	.get_frontend_algo = cxd2841er_get_algo
> +};
> +
> +static struct  dvb_frontend_ops cxd2841er_dvbc_ops = {
> +	.delsys = { SYS_DVBC_ANNEX_A },
> +	.info = {
> +		.name	= "Sony CXD2841ER DVB-C demodulator",
> +		.caps = FE_CAN_FEC_1_2 |
> +			FE_CAN_FEC_2_3 |
> +			FE_CAN_FEC_3_4 |
> +			FE_CAN_FEC_5_6 |
> +			FE_CAN_FEC_7_8 |
> +			FE_CAN_FEC_AUTO |
> +			FE_CAN_QAM_16 |
> +			FE_CAN_QAM_32 |
> +			FE_CAN_QAM_64 |
> +			FE_CAN_QAM_128 |
> +			FE_CAN_QAM_256 |
> +			FE_CAN_QAM_AUTO |
> +			FE_CAN_INVERSION_AUTO,
> +		.frequency_min = 42000000,
> +		.frequency_max = 1002000000
> +	},
> +	.init = cxd2841er_init_tc,
> +	.sleep = cxd2841er_sleep_tc,
> +	.release = cxd2841er_release,
> +	.set_frontend = cxd2841er_set_frontend_tc,
> +	.get_frontend = cxd2841er_get_frontend,
> +	.read_status = cxd2841er_read_status_tc,
> +	.tune = cxd2841er_tune_tc,
> +	.i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
> +	.get_frontend_algo = cxd2841er_get_algo,
> +};
> +
> +MODULE_DESCRIPTION("Sony CXD2841ER DVB-C/C2/T/T2/S/S2 demodulator driver");
> +MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h
> new file mode 100644
> index 0000000..5322d37
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2841er.h
> @@ -0,0 +1,65 @@
> +/*
> + * cxd2841er.h
> + *
> + * Sony CXD2441ER digital demodulator driver public definitions
> + *
> + * Copyright 2012 Sony Corporation
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> +  */
> +
> +#ifndef CXD2841ER_H
> +#define CXD2841ER_H
> +
> +#include <linux/kconfig.h>
> +#include <linux/dvb/frontend.h>
> +
> +struct cxd2841er_config {
> +	u8	i2c_addr;
> +};
> +
> +#if IS_ENABLED(CONFIG_DVB_CXD2841ER)

IS_REACHABLE()

> +extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
> +					       struct i2c_adapter *i2c);
> +
> +extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg,
> +					       struct i2c_adapter *i2c);
> +
> +extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg,
> +					       struct i2c_adapter *i2c);
> +#else
> +static inline struct dvb_frontend *cxd2841er_attach_s(
> +					struct cxd2841er_config *cfg,
> +					struct i2c_adapter *i2c)
> +{
> +	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +
> +static inline struct dvb_frontend *cxd2841er_attach_t(
> +		struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
> +{
> +	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +
> +static inline struct dvb_frontend *cxd2841er_attach_c(
> +		struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
> +{
> +	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +#endif
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h
> new file mode 100644
> index 0000000..33e2f49
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2841er_priv.h
> @@ -0,0 +1,43 @@
> +/*
> + * cxd2841er_priv.h
> + *
> + * Sony CXD2441ER digital demodulator driver internal definitions
> + *
> + * Copyright 2012 Sony Corporation
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#ifndef CXD2841ER_PRIV_H
> +#define CXD2841ER_PRIV_H
> +
> +#define I2C_SLVX			0
> +#define I2C_SLVT			1
> +
> +#define CXD2841ER_CHIP_ID		0xa7
> +
> +#define CXD2841ER_DVBS_POLLING_INVL	10
> +
> +struct cxd2841er_cnr_data {
> +	u32 value;
> +	int cnr_x1000;
> +};
> +
> +enum cxd2841er_dvbt2_profile_t {
> +	DVBT2_PROFILE_ANY = 0,
> +	DVBT2_PROFILE_BASE = 1,
> +	DVBT2_PROFILE_LITE = 2
> +};
> +
> +#endif

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V2 5/5] [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver
  2015-04-15 10:07 ` [PATCH V2 5/5] [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver serjk
@ 2015-05-14 14:20   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2015-05-14 14:20 UTC (permalink / raw)
  To: serjk; +Cc: linux-media, aospan1

Em Wed, 15 Apr 2015 13:07:50 +0300
serjk@netup.ru escreveu:

> From: Kozlov Sergey <serjk@netup.ru>
> 
> Add NetUP Dual Universal CI PCIe board driver.
> The board has
>     - two CI slots
>     - two I2C adapters
>     - SPI master bus for accessing flash memory containing
>       FPGA firmware
> 
> Changes in version 2:
>     - rename driver directory from netup to netup_unidvb
>     - rename MAINTAINERS entry
>     - fix coding style
>     - use dynamic debug instead of module-specifig debug parameters
>     - remove unneccecary msleep in the I2C driver code
>     - use videobuf2 API instead of videobuf
> 
> Signed-off-by: Kozlov Sergey <serjk@netup.ru>

This one looked ok.

I'll mark the hole series as "changes requested" at patchwork, as
it doesn't make sense to apply just this patch without the previous
ones. So, resubmit this one at the version 3.

Thanks!
Mauro

> ---
>  MAINTAINERS                                        |    9 +
>  drivers/media/pci/Kconfig                          |    1 +
>  drivers/media/pci/Makefile                         |    3 +-
>  drivers/media/pci/netup_unidvb/Kconfig             |   12 +
>  drivers/media/pci/netup_unidvb/Makefile            |    9 +
>  drivers/media/pci/netup_unidvb/netup_unidvb.h      |  133 +++
>  drivers/media/pci/netup_unidvb/netup_unidvb_ci.c   |  248 +++++
>  drivers/media/pci/netup_unidvb/netup_unidvb_core.c | 1002 ++++++++++++++++++++
>  drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c  |  381 ++++++++
>  drivers/media/pci/netup_unidvb/netup_unidvb_spi.c  |  252 +++++
>  10 files changed, 2049 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/pci/netup_unidvb/Kconfig
>  create mode 100644 drivers/media/pci/netup_unidvb/Makefile
>  create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb.h
>  create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
>  create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_core.c
>  create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
>  create mode 100644 drivers/media/pci/netup_unidvb/netup_unidvb_spi.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 968a044..bf6f3cb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6305,6 +6305,15 @@ T:	git git://linuxtv.org/media_tree.git
>  S:	Supported
>  F:	drivers/media/dvb-frontends/lnbh25*
>  
> +MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices
> +M:	Sergey Kozlov <serjk@netup.ru>
> +L:	linux-media@vger.kernel.org
> +W:	http://linuxtv.org/
> +W:	http://netup.tv/
> +T:	git git://linuxtv.org/media_tree.git
> +S:	Supported
> +F:	drivers/media/pci/netup_unidvb/*
> +
>  MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
>  M:	Mauro Carvalho Chehab <mchehab@osg.samsung.com>
>  P:	LinuxTV.org Project
> diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
> index 218144a..4bc9188 100644
> --- a/drivers/media/pci/Kconfig
> +++ b/drivers/media/pci/Kconfig
> @@ -47,6 +47,7 @@ source "drivers/media/pci/mantis/Kconfig"
>  source "drivers/media/pci/ngene/Kconfig"
>  source "drivers/media/pci/ddbridge/Kconfig"
>  source "drivers/media/pci/smipcie/Kconfig"
> +source "drivers/media/pci/netup_unidvb/Kconfig"
>  endif
>  
>  endif #MEDIA_PCI_SUPPORT
> diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
> index 0baf0d2..515cf0f 100644
> --- a/drivers/media/pci/Makefile
> +++ b/drivers/media/pci/Makefile
> @@ -12,7 +12,8 @@ obj-y        +=	ttpci/		\
>  		ngene/		\
>  		ddbridge/	\
>  		saa7146/	\
> -		smipcie/
> +		smipcie/	\
> +		netup_unidvb/
>  
>  obj-$(CONFIG_VIDEO_IVTV) += ivtv/
>  obj-$(CONFIG_VIDEO_ZORAN) += zoran/
> diff --git a/drivers/media/pci/netup_unidvb/Kconfig b/drivers/media/pci/netup_unidvb/Kconfig
> new file mode 100644
> index 0000000..f277b0b
> --- /dev/null
> +++ b/drivers/media/pci/netup_unidvb/Kconfig
> @@ -0,0 +1,12 @@
> +config DVB_NETUP_UNIDVB
> +	tristate "NetUP Universal DVB card support"
> +	depends on DVB_CORE && VIDEO_DEV && PCI && I2C && SPI_MASTER
> +    select VIDEOBUF2_DVB
> +    select VIDEOBUF2_VMALLOC
> +	select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT
> +	select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT
> +	select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT
> +	select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT
> +	---help---
> +	  Support for NetUP PCI express Universal DVB card.
> +
> diff --git a/drivers/media/pci/netup_unidvb/Makefile b/drivers/media/pci/netup_unidvb/Makefile
> new file mode 100644
> index 0000000..ee6ae05
> --- /dev/null
> +++ b/drivers/media/pci/netup_unidvb/Makefile
> @@ -0,0 +1,9 @@
> +netup-unidvb-objs += netup_unidvb_core.o
> +netup-unidvb-objs += netup_unidvb_i2c.o
> +netup-unidvb-objs += netup_unidvb_ci.o
> +netup-unidvb-objs += netup_unidvb_spi.o
> +
> +obj-$(CONFIG_DVB_NETUP_UNIDVB) += netup-unidvb.o
> +
> +ccflags-y += -Idrivers/media/dvb-core
> +ccflags-y += -Idrivers/media/dvb-frontends
> diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb.h b/drivers/media/pci/netup_unidvb/netup_unidvb.h
> new file mode 100644
> index 0000000..fa95110
> --- /dev/null
> +++ b/drivers/media/pci/netup_unidvb/netup_unidvb.h
> @@ -0,0 +1,133 @@
> +/*
> + * netup_unidvb.h
> + *
> + * Data type definitions for NetUP Universal Dual DVB-CI
> + *
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/i2c.h>
> +#include <linux/workqueue.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-device.h>
> +#include <media/videobuf2-dvb.h>
> +#include <dvb_ca_en50221.h>
> +
> +#define NETUP_UNIDVB_NAME	"netup_unidvb"
> +#define NETUP_UNIDVB_VERSION	"0.0.1"
> +#define NETUP_VENDOR_ID		0x1b55
> +#define NETUP_PCI_DEV_REVISION  0x2
> +
> +/* IRQ-related regisers */
> +#define REG_ISR			0x4890
> +#define REG_ISR_MASKED		0x4892
> +#define REG_IMASK_SET		0x4894
> +#define REG_IMASK_CLEAR		0x4896
> +/* REG_ISR register bits */
> +#define NETUP_UNIDVB_IRQ_SPI	(1 << 0)
> +#define NETUP_UNIDVB_IRQ_I2C0	(1 << 1)
> +#define NETUP_UNIDVB_IRQ_I2C1	(1 << 2)
> +#define NETUP_UNIDVB_IRQ_FRA0	(1 << 4)
> +#define NETUP_UNIDVB_IRQ_FRA1	(1 << 5)
> +#define NETUP_UNIDVB_IRQ_FRB0	(1 << 6)
> +#define NETUP_UNIDVB_IRQ_FRB1	(1 << 7)
> +#define NETUP_UNIDVB_IRQ_DMA1	(1 << 8)
> +#define NETUP_UNIDVB_IRQ_DMA2	(1 << 9)
> +#define NETUP_UNIDVB_IRQ_CI	(1 << 10)
> +#define NETUP_UNIDVB_IRQ_CAM0	(1 << 11)
> +#define NETUP_UNIDVB_IRQ_CAM1	(1 << 12)
> +
> +struct netup_dma {
> +	u8			num;
> +	spinlock_t		lock;
> +	struct netup_unidvb_dev	*ndev;
> +	struct netup_dma_regs	*regs;
> +	u32			ring_buffer_size;
> +	u8			*addr_virt;
> +	dma_addr_t		addr_phys;
> +	u64			addr_last;
> +	u32			high_addr;
> +	u32			data_offset;
> +	u32			data_size;
> +	struct list_head	free_buffers;
> +	struct work_struct	work;
> +	struct timer_list	timeout;
> +};
> +
> +enum netup_i2c_state {
> +	STATE_DONE,
> +	STATE_WAIT,
> +	STATE_WANT_READ,
> +	STATE_WANT_WRITE,
> +	STATE_ERROR
> +};
> +
> +struct netup_i2c_regs;
> +
> +struct netup_i2c {
> +	spinlock_t			lock;
> +	wait_queue_head_t		wq;
> +	struct i2c_adapter		adap;
> +	struct netup_unidvb_dev		*dev;
> +	struct netup_i2c_regs		*regs;
> +	struct i2c_msg			*msg;
> +	enum netup_i2c_state		state;
> +	u32				xmit_size;
> +};
> +
> +struct netup_ci_state {
> +	struct dvb_ca_en50221		ca;
> +	u8 __iomem			*membase8_config;
> +	u8 __iomem			*membase8_io;
> +	struct netup_unidvb_dev		*dev;
> +	int status;
> +	int nr;
> +};
> +
> +struct netup_spi;
> +
> +struct netup_unidvb_dev {
> +	struct pci_dev			*pci_dev;
> +	int				pci_bus;
> +	int				pci_slot;
> +	int				pci_func;
> +	int				board_num;
> +	int				old_fw;
> +	u32 __iomem			*lmmio0;
> +	u8 __iomem			*bmmio0;
> +	u32 __iomem			*lmmio1;
> +	u8 __iomem			*bmmio1;
> +	u8				*dma_virt;
> +	dma_addr_t			dma_phys;
> +	u32				dma_size;
> +	struct vb2_dvb_frontends	frontends[2];
> +	struct netup_i2c		i2c[2];
> +	struct workqueue_struct		*wq;
> +	struct netup_dma		dma[2];
> +	struct netup_ci_state		ci[2];
> +	struct netup_spi		*spi;
> +};
> +
> +int netup_i2c_register(struct netup_unidvb_dev *ndev);
> +void netup_i2c_unregister(struct netup_unidvb_dev *ndev);
> +irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev);
> +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c);
> +irqreturn_t netup_spi_interrupt(struct netup_spi *spi);
> +int netup_unidvb_ci_register(struct netup_unidvb_dev *dev,
> +			     int num, struct pci_dev *pci_dev);
> +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num);
> +int netup_spi_init(struct netup_unidvb_dev *ndev);
> +void netup_spi_release(struct netup_unidvb_dev *ndev);
> diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
> new file mode 100644
> index 0000000..751b51b
> --- /dev/null
> +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
> @@ -0,0 +1,248 @@
> +/*
> + * netup_unidvb_ci.c
> + *
> + * DVB CAM support for NetUP Universal Dual DVB-CI
> + *
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/kmod.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include "netup_unidvb.h"
> +
> +/* CI slot 0 base address */
> +#define CAM0_CONFIG		0x0
> +#define CAM0_IO			0x8000
> +#define CAM0_MEM		0x10000
> +#define CAM0_SZ			32
> +/* CI slot 1 base address */
> +#define CAM1_CONFIG		0x20000
> +#define CAM1_IO			0x28000
> +#define CAM1_MEM		0x30000
> +#define CAM1_SZ			32
> +/* ctrlstat registers */
> +#define CAM_CTRLSTAT_READ_SET	0x4980
> +#define CAM_CTRLSTAT_CLR	0x4982
> +/* register bits */
> +#define BIT_CAM_STCHG		(1<<0)
> +#define BIT_CAM_PRESENT		(1<<1)
> +#define BIT_CAM_RESET		(1<<2)
> +#define BIT_CAM_BYPASS		(1<<3)
> +#define BIT_CAM_READY		(1<<4)
> +#define BIT_CAM_ERROR		(1<<5)
> +#define BIT_CAM_OVERCURR	(1<<6)
> +/* BIT_CAM_BYPASS bit shift for SLOT 1 */
> +#define CAM1_SHIFT 8
> +
> +irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev)
> +{
> +	writew(0x101, ndev->bmmio0 + CAM_CTRLSTAT_CLR);
> +	return IRQ_HANDLED;
> +}
> +
> +static int netup_unidvb_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221,
> +				       int slot)
> +{
> +	struct netup_ci_state *state = en50221->data;
> +	struct netup_unidvb_dev *dev = state->dev;
> +	u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
> +
> +	dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x\n",
> +		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
> +	if (slot != 0)
> +		return -EINVAL;
> +	/* pass data to CAM module */
> +	writew(BIT_CAM_BYPASS << shift, dev->bmmio0 + CAM_CTRLSTAT_CLR);
> +	dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x done\n",
> +		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
> +	return 0;
> +}
> +
> +static int netup_unidvb_ci_slot_shutdown(struct dvb_ca_en50221 *en50221,
> +					 int slot)
> +{
> +	struct netup_ci_state *state = en50221->data;
> +	struct netup_unidvb_dev *dev = state->dev;
> +
> +	dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__);
> +	return 0;
> +}
> +
> +static int netup_unidvb_ci_slot_reset(struct dvb_ca_en50221 *en50221,
> +				      int slot)
> +{
> +	struct netup_ci_state *state = en50221->data;
> +	struct netup_unidvb_dev *dev = state->dev;
> +	unsigned long timeout = 0;
> +	u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
> +	u16 ci_stat = 0;
> +	int reset_counter = 3;
> +
> +	dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n",
> +		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
> +reset:
> +	timeout = jiffies + msecs_to_jiffies(5000);
> +	/* start reset */
> +	writew(BIT_CAM_RESET << shift, dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
> +	dev_dbg(&dev->pci_dev->dev, "%s(): waiting for reset\n", __func__);
> +	/* wait until reset done */
> +	while (time_before(jiffies, timeout)) {
> +		ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
> +		if (ci_stat & (BIT_CAM_READY << shift))
> +			break;
> +		udelay(1000);
> +	}
> +	if (!(ci_stat & (BIT_CAM_READY << shift)) && reset_counter > 0) {
> +		dev_dbg(&dev->pci_dev->dev,
> +			"%s(): CAMP reset timeout! Will try again..\n",
> +			 __func__);
> +		reset_counter--;
> +		goto reset;
> +	}
> +	return 0;
> +}
> +
> +static int netup_unidvb_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
> +					    int slot, int open)
> +{
> +	struct netup_ci_state *state = en50221->data;
> +	struct netup_unidvb_dev *dev = state->dev;
> +	u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
> +	u16 ci_stat = 0;
> +
> +	dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n",
> +		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
> +	ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
> +	if (ci_stat & (BIT_CAM_READY << shift)) {
> +		state->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
> +			DVB_CA_EN50221_POLL_CAM_READY;
> +	} else if (ci_stat & (BIT_CAM_PRESENT << shift)) {
> +		state->status = DVB_CA_EN50221_POLL_CAM_PRESENT;
> +	} else {
> +		state->status = 0;
> +	}
> +	return state->status;
> +}
> +
> +static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
> +					      int slot, int addr)
> +{
> +	struct netup_ci_state *state = en50221->data;
> +	struct netup_unidvb_dev *dev = state->dev;
> +	u8 val = state->membase8_config[addr];
> +
> +	dev_dbg(&dev->pci_dev->dev,
> +		"%s(): addr=0x%x val=0x%x\n", __func__, addr, val);
> +	return val;
> +}
> +
> +static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
> +					       int slot, int addr, u8 data)
> +{
> +	struct netup_ci_state *state = en50221->data;
> +	struct netup_unidvb_dev *dev = state->dev;
> +
> +	dev_dbg(&dev->pci_dev->dev,
> +		"%s(): addr=0x%x data=0x%x\n", __func__, addr, data);
> +	state->membase8_config[addr] = data;
> +	return 0;
> +}
> +
> +static int netup_unidvb_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
> +					int slot, u8 addr)
> +{
> +	struct netup_ci_state *state = en50221->data;
> +	struct netup_unidvb_dev *dev = state->dev;
> +	u8 val = state->membase8_io[addr];
> +
> +	dev_dbg(&dev->pci_dev->dev,
> +		"%s(): addr=0x%x val=0x%x\n", __func__, addr, val);
> +	return val;
> +}
> +
> +static int netup_unidvb_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221,
> +					 int slot, u8 addr, u8 data)
> +{
> +	struct netup_ci_state *state = en50221->data;
> +	struct netup_unidvb_dev *dev = state->dev;
> +
> +	dev_dbg(&dev->pci_dev->dev,
> +		"%s(): addr=0x%x data=0x%x\n", __func__, addr, data);
> +	state->membase8_io[addr] = data;
> +	return 0;
> +}
> +
> +int netup_unidvb_ci_register(struct netup_unidvb_dev *dev,
> +			     int num, struct pci_dev *pci_dev)
> +{
> +	int result;
> +	struct netup_ci_state *state;
> +
> +	if (num < 0 || num > 1) {
> +		dev_err(&pci_dev->dev, "%s(): invalid CI adapter %d\n",
> +			__func__, num);
> +		return -EINVAL;
> +	}
> +	state = &dev->ci[num];
> +	state->nr = num;
> +	state->membase8_config = dev->bmmio1 +
> +		((num == 0) ? CAM0_CONFIG : CAM1_CONFIG);
> +	state->membase8_io = dev->bmmio1 +
> +		((num == 0) ? CAM0_IO : CAM1_IO);
> +	state->dev = dev;
> +	state->ca.owner = THIS_MODULE;
> +	state->ca.read_attribute_mem = netup_unidvb_ci_read_attribute_mem;
> +	state->ca.write_attribute_mem = netup_unidvb_ci_write_attribute_mem;
> +	state->ca.read_cam_control = netup_unidvb_ci_read_cam_ctl;
> +	state->ca.write_cam_control = netup_unidvb_ci_write_cam_ctl;
> +	state->ca.slot_reset = netup_unidvb_ci_slot_reset;
> +	state->ca.slot_shutdown = netup_unidvb_ci_slot_shutdown;
> +	state->ca.slot_ts_enable = netup_unidvb_ci_slot_ts_ctl;
> +	state->ca.poll_slot_status = netup_unidvb_poll_ci_slot_status;
> +	state->ca.data = state;
> +	result = dvb_ca_en50221_init(&dev->frontends[num].adapter,
> +		&state->ca, 0, 1);
> +	if (result < 0) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): dvb_ca_en50221_init result %d\n",
> +			__func__, result);
> +		return result;
> +	}
> +	writew(NETUP_UNIDVB_IRQ_CI, (u16 *)(dev->bmmio0 + REG_IMASK_SET));
> +	dev_info(&pci_dev->dev,
> +		"%s(): CI adapter %d init done\n", __func__, num);
> +	return 0;
> +}
> +
> +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num)
> +{
> +	struct netup_ci_state *state;
> +
> +	dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__);
> +	if (num < 0 || num > 1) {
> +		dev_err(&dev->pci_dev->dev, "%s(): invalid CI adapter %d\n",
> +				__func__, num);
> +		return;
> +	}
> +	state = &dev->ci[num];
> +	dvb_ca_en50221_release(&state->ca);
> +}
> +
> diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
> new file mode 100644
> index 0000000..181dd6a
> --- /dev/null
> +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
> @@ -0,0 +1,1002 @@
> +/*
> + * netup_unidvb_core.c
> + *
> + * Main module for NetUP Universal Dual DVB-CI
> + *
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/kmod.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/list.h>
> +#include <media/videobuf2-vmalloc.h>
> +
> +#include "netup_unidvb.h"
> +#include "cxd2841er.h"
> +#include "horus3a.h"
> +#include "ascot2e.h"
> +#include "lnbh25.h"
> +
> +static int spi_enable;
> +module_param(spi_enable, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
> +
> +MODULE_DESCRIPTION("Driver for NetUP Dual Universal DVB CI PCIe card");
> +MODULE_AUTHOR("info@netup.ru");
> +MODULE_VERSION(NETUP_UNIDVB_VERSION);
> +MODULE_LICENSE("GPL");
> +
> +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
> +
> +/* Avalon-MM PCI-E registers */
> +#define	AVL_PCIE_IENR		0x50
> +#define AVL_PCIE_ISR		0x40
> +#define AVL_IRQ_ENABLE		0x80
> +#define AVL_IRQ_ASSERTED	0x80
> +/* GPIO registers */
> +#define GPIO_REG_IO		0x4880
> +#define GPIO_REG_IO_TOGGLE	0x4882
> +#define GPIO_REG_IO_SET		0x4884
> +#define GPIO_REG_IO_CLEAR	0x4886
> +/* GPIO bits */
> +#define GPIO_FEA_RESET		(1 << 0)
> +#define GPIO_FEB_RESET		(1 << 1)
> +#define GPIO_RFA_CTL		(1 << 2)
> +#define GPIO_RFB_CTL		(1 << 3)
> +#define GPIO_FEA_TU_RESET	(1 << 4)
> +#define GPIO_FEB_TU_RESET	(1 << 5)
> +/* DMA base address */
> +#define NETUP_DMA0_ADDR		0x4900
> +#define NETUP_DMA1_ADDR		0x4940
> +/* 8 DMA blocks * 128 packets * 188 bytes*/
> +#define NETUP_DMA_BLOCKS_COUNT	8
> +#define NETUP_DMA_PACKETS_COUNT	128
> +/* DMA status bits */
> +#define BIT_DMA_RUN		1
> +#define BIT_DMA_ERROR		2
> +#define BIT_DMA_IRQ		0x200
> +
> +/**
> + * struct netup_dma_regs - the map of DMA module registers
> + * @ctrlstat_set:	Control register, write to set control bits
> + * @ctrlstat_clear:	Control register, write to clear control bits
> + * @start_addr_lo:	DMA ring buffer start address, lower part
> + * @start_addr_hi:	DMA ring buffer start address, higher part
> + * @size:		DMA ring buffer size register
> +			Bits [0-7]:	DMA packet size, 188 bytes
> +			Bits [16-23]:	packets count in block, 128 packets
> +			Bits [24-31]:	blocks count, 8 blocks
> + * @timeout:		DMA timeout in units of 8ns
> +			For example, value of 375000000 equals to 3 sec
> + * @curr_addr_lo:	Current ring buffer head address, lower part
> + * @curr_addr_hi:	Current ring buffer head address, higher part
> + * @stat_pkt_received:	Statistic register, not tested
> + * @stat_pkt_accepted:	Statistic register, not tested
> + * @stat_pkt_overruns:	Statistic register, not tested
> + * @stat_pkt_underruns:	Statistic register, not tested
> + * @stat_fifo_overruns:	Statistic register, not tested
> + */
> +struct netup_dma_regs {
> +	__le32	ctrlstat_set;
> +	__le32	ctrlstat_clear;
> +	__le32	start_addr_lo;
> +	__le32	start_addr_hi;
> +	__le32	size;
> +	__le32	timeout;
> +	__le32	curr_addr_lo;
> +	__le32	curr_addr_hi;
> +	__le32	stat_pkt_received;
> +	__le32	stat_pkt_accepted;
> +	__le32	stat_pkt_overruns;
> +	__le32	stat_pkt_underruns;
> +	__le32	stat_fifo_overruns;
> +} __packed __aligned(1);
> +
> +struct netup_unidvb_buffer {
> +	struct vb2_buffer	vb;
> +	struct list_head	list;
> +	u32			size;
> +};
> +
> +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc);
> +static void netup_unidvb_queue_cleanup(struct netup_dma *dma);
> +
> +static struct cxd2841er_config demod_config = {
> +	.i2c_addr = 0xc8
> +};
> +
> +static struct horus3a_config horus3a_conf = {
> +	.i2c_address = 0xc0,
> +	.xtal_freq_mhz = 16,
> +	.set_tuner_callback = netup_unidvb_tuner_ctrl
> +};
> +
> +static struct ascot2e_config ascot2e_conf = {
> +	.i2c_address = 0xc2,
> +	.set_tuner_callback = netup_unidvb_tuner_ctrl
> +};
> +
> +static struct lnbh25_config lnbh25_conf = {
> +	.i2c_address = 0x10,
> +	.data2_config = LNBH25_TEN | LNBH25_EXTM
> +};
> +
> +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc)
> +{
> +	u8 reg, mask;
> +	struct netup_dma *dma = priv;
> +	struct netup_unidvb_dev *ndev;
> +
> +	if (!priv)
> +		return -EINVAL;
> +	ndev = dma->ndev;
> +	dev_dbg(&ndev->pci_dev->dev, "%s(): num %d is_dvb_tc %d\n",
> +		__func__, dma->num, is_dvb_tc);
> +	reg = readb(ndev->bmmio0 + GPIO_REG_IO);
> +	mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL;
> +	if (!is_dvb_tc)
> +		reg |= mask;
> +	else
> +		reg &= ~mask;
> +	writeb(reg, ndev->bmmio0 + GPIO_REG_IO);
> +	return 0;
> +}
> +
> +static void netup_unidvb_dev_enable(struct netup_unidvb_dev *ndev)
> +{
> +	u16 gpio_reg;
> +
> +	/* enable PCI-E interrupts */
> +	writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR);
> +	/* unreset frontends bits[0:1] */
> +	writeb(0x00, ndev->bmmio0 + GPIO_REG_IO);
> +	msleep(100);
> +	gpio_reg =
> +		GPIO_FEA_RESET | GPIO_FEB_RESET |
> +		GPIO_FEA_TU_RESET | GPIO_FEB_TU_RESET |
> +		GPIO_RFA_CTL | GPIO_RFB_CTL;
> +	writeb(gpio_reg, ndev->bmmio0 + GPIO_REG_IO);
> +	dev_dbg(&ndev->pci_dev->dev,
> +		"%s(): AVL_PCIE_IENR 0x%x GPIO_REG_IO 0x%x\n",
> +		__func__, readl(ndev->bmmio0 + AVL_PCIE_IENR),
> +		(int)readb(ndev->bmmio0 + GPIO_REG_IO));
> +
> +}
> +
> +static void netup_unidvb_dma_enable(struct netup_dma *dma, int enable)
> +{
> +	u32 irq_mask = (dma->num == 0 ?
> +		NETUP_UNIDVB_IRQ_DMA1 : NETUP_UNIDVB_IRQ_DMA2);
> +
> +	dev_dbg(&dma->ndev->pci_dev->dev,
> +		"%s(): DMA%d enable %d\n", __func__, dma->num, enable);
> +	if (enable) {
> +		writel(BIT_DMA_RUN, &dma->regs->ctrlstat_set);
> +		writew(irq_mask,
> +			(u16 *)(dma->ndev->bmmio0 + REG_IMASK_SET));
> +	} else {
> +		writel(BIT_DMA_RUN, &dma->regs->ctrlstat_clear);
> +		writew(irq_mask,
> +			(u16 *)(dma->ndev->bmmio0 + REG_IMASK_CLEAR));
> +	}
> +}
> +
> +static irqreturn_t netup_dma_interrupt(struct netup_dma *dma)
> +{
> +	u64 addr_curr;
> +	u32 size;
> +	unsigned long flags;
> +	struct device *dev = &dma->ndev->pci_dev->dev;
> +
> +	spin_lock_irqsave(&dma->lock, flags);
> +	addr_curr = ((u64)readl(&dma->regs->curr_addr_hi) << 32) |
> +		(u64)readl(&dma->regs->curr_addr_lo) | dma->high_addr;
> +	/* clear IRQ */
> +	writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear);
> +	/* sanity check */
> +	if (addr_curr < dma->addr_phys ||
> +			addr_curr > dma->addr_phys +  dma->ring_buffer_size) {
> +		if (addr_curr != 0) {
> +			dev_err(dev,
> +				"%s(): addr 0x%llx not from 0x%llx:0x%llx\n",
> +				__func__, addr_curr, (u64)dma->addr_phys,
> +				(u64)(dma->addr_phys + dma->ring_buffer_size));
> +		}
> +		goto irq_handled;
> +	}
> +	size = (addr_curr >= dma->addr_last) ?
> +		(u32)(addr_curr - dma->addr_last) :
> +		(u32)(dma->ring_buffer_size - (dma->addr_last - addr_curr));
> +	if (dma->data_size != 0) {
> +		printk_ratelimited("%s(): lost interrupt, data size %d\n",
> +			__func__, dma->data_size);
> +		dma->data_size += size;
> +	}
> +	if (dma->data_size == 0 || dma->data_size > dma->ring_buffer_size) {
> +		dma->data_size = size;
> +		dma->data_offset = (u32)(dma->addr_last - dma->addr_phys);
> +	}
> +	dma->addr_last = addr_curr;
> +	queue_work(dma->ndev->wq, &dma->work);
> +irq_handled:
> +	spin_unlock_irqrestore(&dma->lock, flags);
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t netup_unidvb_isr(int irq, void *dev_id)
> +{
> +	struct pci_dev *pci_dev = (struct pci_dev *)dev_id;
> +	struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev);
> +	u32 reg40, reg_isr;
> +	irqreturn_t iret = IRQ_NONE;
> +
> +	/* disable interrupts */
> +	writel(0, ndev->bmmio0 + AVL_PCIE_IENR);
> +	/* check IRQ source */
> +	reg40 = readl(ndev->bmmio0 + AVL_PCIE_ISR);
> +	if ((reg40 & AVL_IRQ_ASSERTED) != 0) {
> +		/* IRQ is being signaled */
> +		reg_isr = readw(ndev->bmmio0 + REG_ISR);
> +		if (reg_isr & NETUP_UNIDVB_IRQ_I2C0) {
> +			iret = netup_i2c_interrupt(&ndev->i2c[0]);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_I2C1) {
> +			iret = netup_i2c_interrupt(&ndev->i2c[1]);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_SPI) {
> +			iret = netup_spi_interrupt(ndev->spi);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_DMA1) {
> +			iret = netup_dma_interrupt(&ndev->dma[0]);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_DMA2) {
> +			iret = netup_dma_interrupt(&ndev->dma[1]);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_CI) {
> +			iret = netup_ci_interrupt(ndev);
> +		} else {
> +			dev_err(&pci_dev->dev,
> +				"%s(): unknown interrupt 0x%x\n",
> +				__func__, reg_isr);
> +		}
> +	}
> +	/* re-enable interrupts */
> +	writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR);
> +	return iret;
> +}
> +
> +static int netup_unidvb_queue_setup(struct vb2_queue *vq,
> +				    const struct v4l2_format *fmt,
> +				    unsigned int *nbuffers,
> +				    unsigned int *nplanes,
> +				    unsigned int sizes[],
> +				    void *alloc_ctxs[])
> +{
> +	struct netup_dma *dma = vb2_get_drv_priv(vq);
> +
> +	dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__);
> +
> +	*nplanes = 1;
> +	if (vq->num_buffers + *nbuffers < VIDEO_MAX_FRAME)
> +		*nbuffers = VIDEO_MAX_FRAME - vq->num_buffers;
> +	sizes[0] = PAGE_ALIGN(NETUP_DMA_PACKETS_COUNT * 188);
> +	dev_dbg(&dma->ndev->pci_dev->dev, "%s() nbuffers=%d sizes[0]=%d\n",
> +		__func__, *nbuffers, sizes[0]);
> +	return 0;
> +}
> +
> +static int netup_unidvb_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct netup_dma *dma = vb2_get_drv_priv(vb->vb2_queue);
> +	struct netup_unidvb_buffer *buf = container_of(vb,
> +				struct netup_unidvb_buffer, vb);
> +
> +	dev_dbg(&dma->ndev->pci_dev->dev, "%s(): buf 0x%p\n", __func__, buf);
> +	buf->size = 0;
> +	return 0;
> +}
> +
> +static void netup_unidvb_buf_queue(struct vb2_buffer *vb)
> +{
> +	unsigned long flags;
> +	struct netup_dma *dma = vb2_get_drv_priv(vb->vb2_queue);
> +	struct netup_unidvb_buffer *buf = container_of(vb,
> +				struct netup_unidvb_buffer, vb);
> +
> +	dev_dbg(&dma->ndev->pci_dev->dev, "%s(): %p\n", __func__, buf);
> +	spin_lock_irqsave(&dma->lock, flags);
> +	list_add_tail(&buf->list, &dma->free_buffers);
> +	spin_unlock_irqrestore(&dma->lock, flags);
> +	mod_timer(&dma->timeout, jiffies + msecs_to_jiffies(1000));
> +}
> +
> +static int netup_unidvb_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> +	struct netup_dma *dma = vb2_get_drv_priv(q);
> +
> +	dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__);
> +	netup_unidvb_dma_enable(dma, 1);
> +	return 0;
> +}
> +
> +static void netup_unidvb_stop_streaming(struct vb2_queue *q)
> +{
> +	struct netup_dma *dma = vb2_get_drv_priv(q);
> +
> +	dev_dbg(&dma->ndev->pci_dev->dev, "%s()\n", __func__);
> +	netup_unidvb_dma_enable(dma, 0);
> +	netup_unidvb_queue_cleanup(dma);
> +}
> +
> +static struct vb2_ops dvb_qops = {
> +	.queue_setup		= netup_unidvb_queue_setup,
> +	.buf_prepare		= netup_unidvb_buf_prepare,
> +	.buf_queue		= netup_unidvb_buf_queue,
> +	.start_streaming	= netup_unidvb_start_streaming,
> +	.stop_streaming		= netup_unidvb_stop_streaming,
> +};
> +
> +static int netup_unidvb_queue_init(struct netup_dma *dma,
> +				   struct vb2_queue *vb_queue)
> +{
> +	int res;
> +
> +	/* Init videobuf2 queue structure */
> +	vb_queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	vb_queue->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
> +	vb_queue->drv_priv = dma;
> +	vb_queue->buf_struct_size = sizeof(struct netup_unidvb_buffer);
> +	vb_queue->ops = &dvb_qops;
> +	vb_queue->mem_ops = &vb2_vmalloc_memops;
> +	vb_queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	res = vb2_queue_init(vb_queue);
> +	if (res != 0) {
> +		dev_err(&dma->ndev->pci_dev->dev,
> +			"%s(): vb2_queue_init failed (%d)\n", __func__, res);
> +	}
> +	return res;
> +}
> +
> +static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev,
> +				 int num)
> +{
> +	struct vb2_dvb_frontend *fe0, *fe1, *fe2;
> +
> +	if (num < 0 || num > 1) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to init DVB bus %d\n", __func__, num);
> +		return -ENODEV;
> +	}
> +	mutex_init(&ndev->frontends[num].lock);
> +	INIT_LIST_HEAD(&ndev->frontends[num].felist);
> +	if (vb2_dvb_alloc_frontend(&ndev->frontends[num], 1) == NULL ||
> +		vb2_dvb_alloc_frontend(
> +			&ndev->frontends[num], 2) == NULL ||
> +		vb2_dvb_alloc_frontend(
> +			&ndev->frontends[num], 3) == NULL) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to to alllocate vb2_dvb_frontend\n",
> +			__func__);
> +		return -ENOMEM;
> +	}
> +	fe0 = vb2_dvb_get_frontend(&ndev->frontends[num], 1);
> +	fe1 = vb2_dvb_get_frontend(&ndev->frontends[num], 2);
> +	fe2 = vb2_dvb_get_frontend(&ndev->frontends[num], 3);
> +	if (fe0 == NULL || fe1 == NULL || fe2 == NULL) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): frontends has not been allocated\n", __func__);
> +		return -EINVAL;
> +	}
> +	netup_unidvb_queue_init(&ndev->dma[num], &fe0->dvb.dvbq);
> +	netup_unidvb_queue_init(&ndev->dma[num], &fe1->dvb.dvbq);
> +	netup_unidvb_queue_init(&ndev->dma[num], &fe2->dvb.dvbq);
> +	fe0->dvb.name = "netup_fe0";
> +	fe1->dvb.name = "netup_fe1";
> +	fe2->dvb.name = "netup_fe2";
> +	fe0->dvb.frontend = dvb_attach(cxd2841er_attach_s,
> +		&demod_config, &ndev->i2c[num].adap);
> +	if (fe0->dvb.frontend == NULL) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to attach DVB-S/S2 frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	horus3a_conf.set_tuner_priv = &ndev->dma[num];
> +	if (!dvb_attach(horus3a_attach, fe0->dvb.frontend,
> +			&horus3a_conf, &ndev->i2c[num].adap)) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to attach DVB-S/S2 tuner frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend,
> +			&lnbh25_conf, &ndev->i2c[num].adap)) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to attach SEC frontend\n", __func__);
> +		goto frontend_detach;
> +	}
> +	/* DVB-T/T2 frontend */
> +	fe1->dvb.frontend = dvb_attach(cxd2841er_attach_t,
> +		&demod_config, &ndev->i2c[num].adap);
> +	if (fe1->dvb.frontend == NULL) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to attach DVB-T frontend\n", __func__);
> +		goto frontend_detach;
> +	}
> +	fe1->dvb.frontend->id = 1;
> +	ascot2e_conf.set_tuner_priv = &ndev->dma[num];
> +	if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend,
> +			&ascot2e_conf, &ndev->i2c[num].adap)) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to attach DVB-T tuner frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	/* DVB-C/C2 frontend */
> +	fe2->dvb.frontend = dvb_attach(cxd2841er_attach_c,
> +				&demod_config, &ndev->i2c[num].adap);
> +	if (fe2->dvb.frontend == NULL) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to attach DVB-C frontend\n", __func__);
> +		goto frontend_detach;
> +	}
> +	fe2->dvb.frontend->id = 2;
> +	if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend,
> +			&ascot2e_conf, &ndev->i2c[num].adap)) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to attach DVB-T/C tuner frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +
> +	if (vb2_dvb_register_bus(&ndev->frontends[num],
> +			THIS_MODULE, NULL,
> +			&ndev->pci_dev->dev, adapter_nr, 1)) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): unable to register DVB bus %d\n",
> +			__func__, num);
> +		goto frontend_detach;
> +	}
> +	dev_info(&ndev->pci_dev->dev, "DVB init done, num=%d\n", num);
> +	return 0;
> +frontend_detach:
> +	vb2_dvb_dealloc_frontends(&ndev->frontends[num]);
> +	return -EINVAL;
> +}
> +
> +static void netup_unidvb_dvb_fini(struct netup_unidvb_dev *ndev, int num)
> +{
> +	if (num < 0 || num > 1) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): unable to unregister DVB bus %d\n",
> +			__func__, num);
> +		return;
> +	}
> +	vb2_dvb_unregister_bus(&ndev->frontends[num]);
> +	dev_info(&ndev->pci_dev->dev,
> +		"%s(): DVB bus %d unregistered\n", __func__, num);
> +}
> +
> +static int netup_unidvb_dvb_setup(struct netup_unidvb_dev *ndev)
> +{
> +	int res;
> +
> +	res = netup_unidvb_dvb_init(ndev, 0);
> +	if (res)
> +		return res;
> +	res = netup_unidvb_dvb_init(ndev, 1);
> +	if (res) {
> +		netup_unidvb_dvb_fini(ndev, 0);
> +		return res;
> +	}
> +	return 0;
> +}
> +
> +static int netup_unidvb_ring_copy(struct netup_dma *dma,
> +				  struct netup_unidvb_buffer *buf)
> +{
> +	u32 copy_bytes, ring_bytes;
> +	u32 buff_bytes = NETUP_DMA_PACKETS_COUNT * 188 - buf->size;
> +	u8 *p = vb2_plane_vaddr(&buf->vb, 0);
> +	struct netup_unidvb_dev *ndev = dma->ndev;
> +
> +	if (p == NULL) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): buffer is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +	p += buf->size;
> +	if (dma->data_offset + dma->data_size > dma->ring_buffer_size) {
> +		ring_bytes = dma->ring_buffer_size - dma->data_offset;
> +		copy_bytes = (ring_bytes > buff_bytes) ?
> +			buff_bytes : ring_bytes;
> +		memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes);
> +		p += copy_bytes;
> +		buf->size += copy_bytes;
> +		buff_bytes -= copy_bytes;
> +		dma->data_size -= copy_bytes;
> +		dma->data_offset += copy_bytes;
> +		if (dma->data_offset == dma->ring_buffer_size)
> +			dma->data_offset = 0;
> +	}
> +	if (buff_bytes > 0) {
> +		ring_bytes = dma->data_size;
> +		copy_bytes = (ring_bytes > buff_bytes) ?
> +				buff_bytes : ring_bytes;
> +		memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes);
> +		buf->size += copy_bytes;
> +		dma->data_size -= copy_bytes;
> +		dma->data_offset += copy_bytes;
> +		if (dma->data_offset == dma->ring_buffer_size)
> +			dma->data_offset = 0;
> +	}
> +	return 0;
> +}
> +
> +static void netup_unidvb_dma_worker(struct work_struct *work)
> +{
> +	struct netup_dma *dma = container_of(work, struct netup_dma, work);
> +	struct netup_unidvb_dev *ndev = dma->ndev;
> +	struct netup_unidvb_buffer *buf;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dma->lock, flags);
> +	if (dma->data_size == 0) {
> +		dev_dbg(&ndev->pci_dev->dev,
> +			"%s(): data_size == 0\n", __func__);
> +		goto work_done;
> +	}
> +	while (dma->data_size > 0) {
> +		if (list_empty(&dma->free_buffers)) {
> +			dev_dbg(&ndev->pci_dev->dev,
> +				"%s(): no free buffers\n", __func__);
> +			goto work_done;
> +		}
> +		buf = list_first_entry(&dma->free_buffers,
> +			struct netup_unidvb_buffer, list);
> +		if (buf->size >= NETUP_DMA_PACKETS_COUNT * 188) {
> +			dev_dbg(&ndev->pci_dev->dev,
> +				"%s(): buffer overflow, size %d\n",
> +				__func__, buf->size);
> +			goto work_done;
> +		}
> +		if (netup_unidvb_ring_copy(dma, buf))
> +			goto work_done;
> +		if (buf->size == NETUP_DMA_PACKETS_COUNT * 188) {
> +			list_del(&buf->list);
> +			dev_dbg(&ndev->pci_dev->dev,
> +				"%s(): buffer %p done, size %d\n",
> +				__func__, buf, buf->size);
> +			v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
> +			vb2_set_plane_payload(&buf->vb, 0, buf->size);
> +			vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
> +		}
> +	}
> +work_done:
> +	dma->data_size = 0;
> +	spin_unlock_irqrestore(&dma->lock, flags);
> +}
> +
> +static void netup_unidvb_queue_cleanup(struct netup_dma *dma)
> +{
> +	struct netup_unidvb_buffer *buf;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dma->lock, flags);
> +	while (!list_empty(&dma->free_buffers)) {
> +		buf = list_first_entry(&dma->free_buffers,
> +			struct netup_unidvb_buffer, list);
> +		list_del(&buf->list);
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +	}
> +	spin_unlock_irqrestore(&dma->lock, flags);
> +}
> +
> +static void netup_unidvb_dma_timeout(unsigned long data)
> +{
> +	struct netup_dma *dma = (struct netup_dma *)data;
> +	struct netup_unidvb_dev *ndev = dma->ndev;
> +
> +	dev_dbg(&ndev->pci_dev->dev, "%s()\n", __func__);
> +	netup_unidvb_queue_cleanup(dma);
> +}
> +
> +static int netup_unidvb_dma_init(struct netup_unidvb_dev *ndev, int num)
> +{
> +	struct netup_dma *dma;
> +	struct device *dev = &ndev->pci_dev->dev;
> +
> +	if (num < 0 || num > 1) {
> +		dev_err(dev, "%s(): unable to register DMA%d\n",
> +			__func__, num);
> +		return -ENODEV;
> +	}
> +	dma = &ndev->dma[num];
> +	dev_info(dev, "%s(): starting DMA%d\n", __func__, num);
> +	dma->num = num;
> +	dma->ndev = ndev;
> +	spin_lock_init(&dma->lock);
> +	INIT_WORK(&dma->work, netup_unidvb_dma_worker);
> +	INIT_LIST_HEAD(&dma->free_buffers);
> +	dma->timeout.function = netup_unidvb_dma_timeout;
> +	dma->timeout.data = (unsigned long)dma;
> +	init_timer(&dma->timeout);
> +	dma->ring_buffer_size = ndev->dma_size / 2;
> +	dma->addr_virt = ndev->dma_virt + dma->ring_buffer_size * num;
> +	dma->addr_phys = (dma_addr_t)((u64)ndev->dma_phys +
> +		dma->ring_buffer_size * num);
> +	dev_info(dev, "%s(): DMA%d buffer virt/phys 0x%p/0x%llx size %d\n",
> +		__func__, num, dma->addr_virt,
> +		(unsigned long long)dma->addr_phys,
> +		dma->ring_buffer_size);
> +	memset_io(dma->addr_virt, 0, dma->ring_buffer_size);
> +	dma->addr_last = dma->addr_phys;
> +	dma->high_addr = (u32)(dma->addr_phys & 0xC0000000);
> +	dma->regs = (struct netup_dma_regs *)(num == 0 ?
> +		ndev->bmmio0 + NETUP_DMA0_ADDR :
> +		ndev->bmmio0 + NETUP_DMA1_ADDR);
> +	writel((NETUP_DMA_BLOCKS_COUNT << 24) |
> +		(NETUP_DMA_PACKETS_COUNT << 8) | 188, &dma->regs->size);
> +	writel((u32)(dma->addr_phys & 0x3FFFFFFF), &dma->regs->start_addr_lo);
> +	writel(0, &dma->regs->start_addr_hi);
> +	writel(dma->high_addr, ndev->bmmio0 + 0x1000);
> +	writel(375000000, &dma->regs->timeout);
> +	msleep(1000);
> +	writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear);
> +	return 0;
> +}
> +
> +static void netup_unidvb_dma_fini(struct netup_unidvb_dev *ndev, int num)
> +{
> +	struct netup_dma *dma;
> +
> +	if (num < 0 || num > 1)
> +		return;
> +	dev_dbg(&ndev->pci_dev->dev, "%s(): num %d\n", __func__, num);
> +	dma = &ndev->dma[num];
> +	netup_unidvb_dma_enable(dma, 0);
> +	msleep(50);
> +	cancel_work_sync(&dma->work);
> +	del_timer(&dma->timeout);
> +}
> +
> +static int netup_unidvb_dma_setup(struct netup_unidvb_dev *ndev)
> +{
> +	int res;
> +
> +	res = netup_unidvb_dma_init(ndev, 0);
> +	if (res)
> +		return res;
> +	res = netup_unidvb_dma_init(ndev, 1);
> +	if (res) {
> +		netup_unidvb_dma_fini(ndev, 0);
> +		return res;
> +	}
> +	netup_unidvb_dma_enable(&ndev->dma[0], 0);
> +	netup_unidvb_dma_enable(&ndev->dma[1], 0);
> +	return 0;
> +}
> +
> +static int netup_unidvb_ci_setup(struct netup_unidvb_dev *ndev,
> +				 struct pci_dev *pci_dev)
> +{
> +	int res;
> +
> +	writew(NETUP_UNIDVB_IRQ_CI, ndev->bmmio0 + REG_IMASK_SET);
> +	res = netup_unidvb_ci_register(ndev, 0, pci_dev);
> +	if (res)
> +		return res;
> +	res = netup_unidvb_ci_register(ndev, 1, pci_dev);
> +	if (res)
> +		netup_unidvb_ci_unregister(ndev, 0);
> +	return res;
> +}
> +
> +static int netup_unidvb_request_mmio(struct pci_dev *pci_dev)
> +{
> +	if (!request_mem_region(pci_resource_start(pci_dev, 0),
> +			pci_resource_len(pci_dev, 0), NETUP_UNIDVB_NAME)) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to request MMIO bar 0 at 0x%llx\n",
> +			__func__,
> +			(unsigned long long)pci_resource_start(pci_dev, 0));
> +		return -EBUSY;
> +	}
> +	if (!request_mem_region(pci_resource_start(pci_dev, 1),
> +			pci_resource_len(pci_dev, 1), NETUP_UNIDVB_NAME)) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to request MMIO bar 1 at 0x%llx\n",
> +			__func__,
> +			(unsigned long long)pci_resource_start(pci_dev, 1));
> +		release_mem_region(pci_resource_start(pci_dev, 0),
> +			pci_resource_len(pci_dev, 0));
> +		return -EBUSY;
> +	}
> +	return 0;
> +}
> +
> +static int netup_unidvb_request_modules(struct device *dev)
> +{
> +	static const char * const modules[] = {
> +		"lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL
> +	};
> +	const char * const *curr_mod = modules;
> +	int err;
> +
> +	while (*curr_mod != NULL) {
> +		err = request_module(*curr_mod);
> +		if (err) {
> +			dev_warn(dev, "request_module(%s) failed: %d\n",
> +				*curr_mod, err);
> +		}
> +		++curr_mod;
> +	}
> +	return 0;
> +}
> +
> +static int netup_unidvb_initdev(struct pci_dev *pci_dev,
> +				const struct pci_device_id *pci_id)
> +{
> +	u8 board_revision;
> +	u16 board_vendor;
> +	struct netup_unidvb_dev *ndev;
> +	int old_firmware = 0;
> +
> +	netup_unidvb_request_modules(&pci_dev->dev);
> +
> +	/* Check card revision */
> +	if (pci_dev->revision != NETUP_PCI_DEV_REVISION) {
> +		dev_err(&pci_dev->dev,
> +			"netup_unidvb: expected card revision %d, got %d\n",
> +			NETUP_PCI_DEV_REVISION, pci_dev->revision);
> +		dev_err(&pci_dev->dev,
> +			"Please upgrade firmware!\n");
> +		dev_err(&pci_dev->dev,
> +			"Instructions on http://www.netup.tv\n");
> +		old_firmware = 1;
> +		spi_enable = 1;
> +	}
> +
> +	/* allocate device context */
> +	ndev = kzalloc(sizeof(*ndev), GFP_KERNEL);
> +
> +	if (!ndev)
> +		goto dev_alloc_err;
> +	memset(ndev, 0, sizeof(*ndev));
> +	ndev->old_fw = old_firmware;
> +	ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME);
> +	if (!ndev->wq) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to create workqueue\n", __func__);
> +		goto wq_create_err;
> +	}
> +	ndev->pci_dev = pci_dev;
> +	ndev->pci_bus = pci_dev->bus->number;
> +	ndev->pci_slot = PCI_SLOT(pci_dev->devfn);
> +	ndev->pci_func = PCI_FUNC(pci_dev->devfn);
> +	ndev->board_num = ndev->pci_bus*10 + ndev->pci_slot;
> +	pci_set_drvdata(pci_dev, ndev);
> +	/* PCI init */
> +	dev_info(&pci_dev->dev, "%s(): PCI device (%d). Bus:0x%x Slot:0x%x\n",
> +		__func__, ndev->board_num, ndev->pci_bus, ndev->pci_slot);
> +
> +	if (pci_enable_device(pci_dev)) {
> +		dev_err(&pci_dev->dev, "%s(): pci_enable_device failed\n",
> +			__func__);
> +		goto pci_enable_err;
> +	}
> +	/* read PCI info */
> +	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &board_revision);
> +	pci_read_config_word(pci_dev, PCI_VENDOR_ID, &board_vendor);
> +	if (board_vendor != NETUP_VENDOR_ID) {
> +		dev_err(&pci_dev->dev, "%s(): unknown board vendor 0x%x",
> +			__func__, board_vendor);
> +		goto pci_detect_err;
> +	}
> +	dev_info(&pci_dev->dev,
> +		"%s(): board vendor 0x%x, revision 0x%x\n",
> +		__func__, board_vendor, board_revision);
> +	pci_set_master(pci_dev);
> +	if (!pci_dma_supported(pci_dev, 0xffffffff)) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): 32bit PCI DMA is not supported\n", __func__);
> +		goto pci_detect_err;
> +	}
> +	dev_info(&pci_dev->dev, "%s(): using 32bit PCI DMA\n", __func__);
> +	/* Clear "no snoop" and "relaxed ordering" bits, use default MRRS. */
> +	pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL,
> +		PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN |
> +		PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
> +	/* Adjust PCIe completion timeout. */
> +	pcie_capability_clear_and_set_word(pci_dev,
> +		PCI_EXP_DEVCTL2, 0xf, 0x2);
> +
> +	if (netup_unidvb_request_mmio(pci_dev)) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to request MMIO regions\n", __func__);
> +		goto pci_detect_err;
> +	}
> +	ndev->lmmio0 = ioremap(pci_resource_start(pci_dev, 0),
> +		pci_resource_len(pci_dev, 0));
> +	if (!ndev->lmmio0) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to remap MMIO bar 0\n", __func__);
> +		goto pci_bar0_error;
> +	}
> +	ndev->lmmio1 = ioremap(pci_resource_start(pci_dev, 1),
> +		pci_resource_len(pci_dev, 1));
> +	if (!ndev->lmmio1) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to remap MMIO bar 1\n", __func__);
> +		goto pci_bar1_error;
> +	}
> +	ndev->bmmio0 = (u8 __iomem *)ndev->lmmio0;
> +	ndev->bmmio1 = (u8 __iomem *)ndev->lmmio1;
> +	dev_info(&pci_dev->dev,
> +		"%s(): PCI MMIO at 0x%p (%d); 0x%p (%d); IRQ %d",
> +		__func__,
> +		ndev->lmmio0, (u32)pci_resource_len(pci_dev, 0),
> +		ndev->lmmio1, (u32)pci_resource_len(pci_dev, 1),
> +		pci_dev->irq);
> +	if (request_irq(pci_dev->irq, netup_unidvb_isr,
> +			IRQF_SHARED | IRQF_DISABLED,
> +			"netup_unidvb", pci_dev) < 0) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): can't get IRQ %d\n", __func__, pci_dev->irq);
> +		goto irq_request_err;
> +	}
> +	ndev->dma_size = 2 * 188 *
> +		NETUP_DMA_BLOCKS_COUNT * NETUP_DMA_PACKETS_COUNT;
> +	ndev->dma_virt = dma_alloc_coherent(&pci_dev->dev,
> +		ndev->dma_size, &ndev->dma_phys, GFP_KERNEL);
> +	if (!ndev->dma_virt) {
> +		dev_err(&pci_dev->dev, "%s(): unable to allocate DMA buffer\n",
> +			__func__);
> +		goto dma_alloc_err;
> +	}
> +	netup_unidvb_dev_enable(ndev);
> +	if (spi_enable && netup_spi_init(ndev)) {
> +		dev_warn(&pci_dev->dev,
> +			"netup_unidvb: SPI flash setup failed\n");
> +		goto spi_setup_err;
> +	}
> +	if (old_firmware) {
> +		dev_err(&pci_dev->dev,
> +			"netup_unidvb: card initialization was incomplete\n");
> +		return 0;
> +	}
> +	if (netup_i2c_register(ndev)) {
> +		dev_err(&pci_dev->dev, "netup_unidvb: I2C setup failed\n");
> +		goto i2c_setup_err;
> +	}
> +	/* enable I2C IRQs */
> +	writew(NETUP_UNIDVB_IRQ_I2C0 | NETUP_UNIDVB_IRQ_I2C1,
> +		ndev->bmmio0 + REG_IMASK_SET);
> +	usleep_range(5000, 10000);
> +	if (netup_unidvb_dvb_setup(ndev)) {
> +		dev_err(&pci_dev->dev, "netup_unidvb: DVB setup failed\n");
> +		goto dvb_setup_err;
> +	}
> +	if (netup_unidvb_ci_setup(ndev, pci_dev)) {
> +		dev_err(&pci_dev->dev, "netup_unidvb: CI setup failed\n");
> +		goto ci_setup_err;
> +	}
> +	if (netup_unidvb_dma_setup(ndev)) {
> +		dev_err(&pci_dev->dev, "netup_unidvb: DMA setup failed\n");
> +		goto dma_setup_err;
> +	}
> +	dev_info(&pci_dev->dev,
> +		"netup_unidvb: device has been initialized\n");
> +	return 0;
> +dma_setup_err:
> +	netup_unidvb_ci_unregister(ndev, 0);
> +	netup_unidvb_ci_unregister(ndev, 1);
> +ci_setup_err:
> +	netup_unidvb_dvb_fini(ndev, 0);
> +	netup_unidvb_dvb_fini(ndev, 1);
> +dvb_setup_err:
> +	netup_i2c_unregister(ndev);
> +i2c_setup_err:
> +	if (ndev->spi)
> +		netup_spi_release(ndev);
> +spi_setup_err:
> +	dma_free_coherent(&pci_dev->dev, ndev->dma_size,
> +			ndev->dma_virt, ndev->dma_phys);
> +dma_alloc_err:
> +	free_irq(pci_dev->irq, pci_dev);
> +irq_request_err:
> +	iounmap(ndev->lmmio1);
> +pci_bar1_error:
> +	iounmap(ndev->lmmio0);
> +pci_bar0_error:
> +	release_mem_region(pci_resource_start(pci_dev, 0),
> +		pci_resource_len(pci_dev, 0));
> +	release_mem_region(pci_resource_start(pci_dev, 1),
> +		pci_resource_len(pci_dev, 1));
> +pci_detect_err:
> +	pci_disable_device(pci_dev);
> +pci_enable_err:
> +	pci_set_drvdata(pci_dev, NULL);
> +	destroy_workqueue(ndev->wq);
> +wq_create_err:
> +	kfree(ndev);
> +dev_alloc_err:
> +	dev_err(&pci_dev->dev,
> +		"%s(): failed to initizalize device\n", __func__);
> +	return -EIO;
> +}
> +
> +static void netup_unidvb_finidev(struct pci_dev *pci_dev)
> +{
> +	struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev);
> +
> +	dev_info(&pci_dev->dev, "%s(): trying to stop device\n", __func__);
> +	if (!ndev->old_fw) {
> +		netup_unidvb_dma_fini(ndev, 0);
> +		netup_unidvb_dma_fini(ndev, 1);
> +		netup_unidvb_ci_unregister(ndev, 0);
> +		netup_unidvb_ci_unregister(ndev, 1);
> +		netup_unidvb_dvb_fini(ndev, 0);
> +		netup_unidvb_dvb_fini(ndev, 1);
> +		netup_i2c_unregister(ndev);
> +	}
> +	if (ndev->spi)
> +		netup_spi_release(ndev);
> +	writew(0xffff, ndev->bmmio0 + REG_IMASK_CLEAR);
> +	dma_free_coherent(&ndev->pci_dev->dev, ndev->dma_size,
> +			ndev->dma_virt, ndev->dma_phys);
> +	free_irq(pci_dev->irq, pci_dev);
> +	iounmap(ndev->lmmio0);
> +	iounmap(ndev->lmmio1);
> +	release_mem_region(pci_resource_start(pci_dev, 0),
> +		pci_resource_len(pci_dev, 0));
> +	release_mem_region(pci_resource_start(pci_dev, 1),
> +		pci_resource_len(pci_dev, 1));
> +	pci_disable_device(pci_dev);
> +	pci_set_drvdata(pci_dev, NULL);
> +	destroy_workqueue(ndev->wq);
> +	kfree(ndev);
> +	dev_info(&pci_dev->dev,
> +		"%s(): device has been successfully stopped\n", __func__);
> +}
> +
> +
> +static struct pci_device_id netup_unidvb_pci_tbl[] = {
> +	{ PCI_DEVICE(0x1b55, 0x18f6) },
> +	{ 0, }
> +};
> +MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl);
> +
> +static struct pci_driver netup_unidvb_pci_driver = {
> +	.name     = "netup_unidvb",
> +	.id_table = netup_unidvb_pci_tbl,
> +	.probe    = netup_unidvb_initdev,
> +	.remove   = netup_unidvb_finidev,
> +	.suspend  = NULL,
> +	.resume   = NULL,
> +};
> +
> +static int __init netup_unidvb_init(void)
> +{
> +	return pci_register_driver(&netup_unidvb_pci_driver);
> +}
> +
> +static void __exit netup_unidvb_fini(void)
> +{
> +	pci_unregister_driver(&netup_unidvb_pci_driver);
> +}
> +
> +module_init(netup_unidvb_init);
> +module_exit(netup_unidvb_fini);
> diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
> new file mode 100644
> index 0000000..eaaa2d0
> --- /dev/null
> +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c
> @@ -0,0 +1,381 @@
> +/*
> + * netup_unidvb_i2c.c
> + *
> + * Internal I2C bus driver for NetUP Universal Dual DVB-CI
> + *
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/init.h>
> +#include <linux/delay.h>
> +#include "netup_unidvb.h"
> +
> +#define NETUP_I2C_BUS0_ADDR		0x4800
> +#define NETUP_I2C_BUS1_ADDR		0x4840
> +#define NETUP_I2C_TIMEOUT		1000
> +
> +/* twi_ctrl0_stat reg bits */
> +#define TWI_IRQEN_COMPL	0x1
> +#define TWI_IRQEN_ANACK 0x2
> +#define TWI_IRQEN_DNACK 0x4
> +#define TWI_IRQ_COMPL	(TWI_IRQEN_COMPL << 8)
> +#define TWI_IRQ_ANACK	(TWI_IRQEN_ANACK << 8)
> +#define TWI_IRQ_DNACK	(TWI_IRQEN_DNACK << 8)
> +#define TWI_IRQ_TX	0x800
> +#define TWI_IRQ_RX	0x1000
> +#define TWI_IRQEN	(TWI_IRQEN_COMPL | TWI_IRQEN_ANACK | TWI_IRQEN_DNACK)
> +/* twi_addr_ctrl1 reg bits*/
> +#define TWI_TRANSFER	0x100
> +#define TWI_NOSTOP	0x200
> +#define TWI_SOFT_RESET	0x2000
> +/* twi_clkdiv reg value */
> +#define TWI_CLKDIV	156
> +/* fifo_stat_ctrl reg bits */
> +#define FIFO_IRQEN	0x8000
> +#define FIFO_RESET	0x4000
> +/* FIFO size */
> +#define FIFO_SIZE	16
> +
> +struct netup_i2c_fifo_regs {
> +	union {
> +		__u8	data8;
> +		__le16	data16;
> +		__le32	data32;
> +	};
> +	__u8		padding[4];
> +	__le16		stat_ctrl;
> +} __packed __aligned(1);
> +
> +struct netup_i2c_regs {
> +	__le16				clkdiv;
> +	__le16				twi_ctrl0_stat;
> +	__le16				twi_addr_ctrl1;
> +	__le16				length;
> +	__u8				padding1[8];
> +	struct netup_i2c_fifo_regs	tx_fifo;
> +	__u8				padding2[6];
> +	struct netup_i2c_fifo_regs	rx_fifo;
> +} __packed __aligned(1);
> +
> +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c)
> +{
> +	u16 reg, tmp;
> +	unsigned long flags;
> +	irqreturn_t iret = IRQ_HANDLED;
> +
> +	spin_lock_irqsave(&i2c->lock, flags);
> +	reg = readw(&i2c->regs->twi_ctrl0_stat);
> +	writew(reg & ~TWI_IRQEN, &i2c->regs->twi_ctrl0_stat);
> +	dev_dbg(i2c->adap.dev.parent,
> +		"%s(): twi_ctrl0_state 0x%x\n", __func__, reg);
> +	if ((reg & TWI_IRQEN_COMPL) != 0 && (reg & TWI_IRQ_COMPL)) {
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): TWI_IRQEN_COMPL\n", __func__);
> +		i2c->state = STATE_DONE;
> +		goto irq_ok;
> +	}
> +	if ((reg & TWI_IRQEN_ANACK) != 0 && (reg & TWI_IRQ_ANACK)) {
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): TWI_IRQEN_ANACK\n", __func__);
> +		i2c->state = STATE_ERROR;
> +		goto irq_ok;
> +	}
> +	if ((reg & TWI_IRQEN_DNACK) != 0 && (reg & TWI_IRQ_DNACK)) {
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): TWI_IRQEN_DNACK\n", __func__);
> +		i2c->state = STATE_ERROR;
> +		goto irq_ok;
> +	}
> +	if ((reg & TWI_IRQ_RX) != 0) {
> +		tmp = readw(&i2c->regs->rx_fifo.stat_ctrl);
> +		writew(tmp & ~FIFO_IRQEN, &i2c->regs->rx_fifo.stat_ctrl);
> +		i2c->state = STATE_WANT_READ;
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): want read\n", __func__);
> +		goto irq_ok;
> +	}
> +	if ((reg & TWI_IRQ_TX) != 0) {
> +		tmp = readw(&i2c->regs->tx_fifo.stat_ctrl);
> +		writew(tmp & ~FIFO_IRQEN, &i2c->regs->tx_fifo.stat_ctrl);
> +		i2c->state = STATE_WANT_WRITE;
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): want write\n", __func__);
> +		goto irq_ok;
> +	}
> +	dev_warn(&i2c->adap.dev, "%s(): not mine interrupt\n", __func__);
> +	iret = IRQ_NONE;
> +irq_ok:
> +	spin_unlock_irqrestore(&i2c->lock, flags);
> +	if (iret == IRQ_HANDLED)
> +		wake_up(&i2c->wq);
> +	return iret;
> +}
> +
> +static void netup_i2c_reset(struct netup_i2c *i2c)
> +{
> +	dev_dbg(i2c->adap.dev.parent, "%s()\n", __func__);
> +	i2c->state = STATE_DONE;
> +	writew(TWI_SOFT_RESET, &i2c->regs->twi_addr_ctrl1);
> +	writew(TWI_CLKDIV, &i2c->regs->clkdiv);
> +	writew(FIFO_RESET, &i2c->regs->tx_fifo.stat_ctrl);
> +	writew(FIFO_RESET, &i2c->regs->rx_fifo.stat_ctrl);
> +	writew(0x800, &i2c->regs->tx_fifo.stat_ctrl);
> +	writew(0x800, &i2c->regs->rx_fifo.stat_ctrl);
> +}
> +
> +static void netup_i2c_fifo_tx(struct netup_i2c *i2c)
> +{
> +	u8 data;
> +	u32 fifo_space = FIFO_SIZE -
> +		(readw(&i2c->regs->tx_fifo.stat_ctrl) & 0x3f);
> +	u32 msg_length = i2c->msg->len - i2c->xmit_size;
> +
> +	msg_length = (msg_length < fifo_space ? msg_length : fifo_space);
> +	while (msg_length--) {
> +		data = i2c->msg->buf[i2c->xmit_size++];
> +		writeb(data, &i2c->regs->tx_fifo.data8);
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): write 0x%02x\n", __func__, data);
> +	}
> +	if (i2c->xmit_size < i2c->msg->len) {
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): TX IRQ enabled\n", __func__);
> +		writew(readw(&i2c->regs->tx_fifo.stat_ctrl) | FIFO_IRQEN,
> +			&i2c->regs->tx_fifo.stat_ctrl);
> +	}
> +}
> +
> +static void netup_i2c_fifo_rx(struct netup_i2c *i2c)
> +{
> +	u8 data;
> +	u32 fifo_size = readw(&i2c->regs->rx_fifo.stat_ctrl) & 0x3f;
> +
> +	dev_dbg(i2c->adap.dev.parent,
> +		"%s(): RX fifo size %d\n", __func__, fifo_size);
> +	while (fifo_size--) {
> +		data = readb(&i2c->regs->rx_fifo.data8);
> +		if ((i2c->msg->flags & I2C_M_RD) != 0 &&
> +					i2c->xmit_size < i2c->msg->len) {
> +			i2c->msg->buf[i2c->xmit_size++] = data;
> +			dev_dbg(i2c->adap.dev.parent,
> +				"%s(): read 0x%02x\n", __func__, data);
> +		}
> +	}
> +	if (i2c->xmit_size < i2c->msg->len) {
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): RX IRQ enabled\n", __func__);
> +		writew(readw(&i2c->regs->rx_fifo.stat_ctrl) | FIFO_IRQEN,
> +			&i2c->regs->rx_fifo.stat_ctrl);
> +	}
> +}
> +
> +static void netup_i2c_start_xfer(struct netup_i2c *i2c)
> +{
> +	u16 rdflag = ((i2c->msg->flags & I2C_M_RD) ? 1 : 0);
> +	u16 reg = readw(&i2c->regs->twi_ctrl0_stat);
> +
> +	writew(TWI_IRQEN | reg, &i2c->regs->twi_ctrl0_stat);
> +	writew(i2c->msg->len, &i2c->regs->length);
> +	writew(TWI_TRANSFER | (i2c->msg->addr << 1) | rdflag,
> +		&i2c->regs->twi_addr_ctrl1);
> +	dev_dbg(i2c->adap.dev.parent,
> +		"%s(): length %d twi_addr_ctrl1 0x%x twi_ctrl0_stat 0x%x\n",
> +		__func__, readw(&i2c->regs->length),
> +		readw(&i2c->regs->twi_addr_ctrl1),
> +		readw(&i2c->regs->twi_ctrl0_stat));
> +	i2c->state = STATE_WAIT;
> +	i2c->xmit_size = 0;
> +	if (!rdflag)
> +		netup_i2c_fifo_tx(i2c);
> +	else
> +		writew(FIFO_IRQEN | readw(&i2c->regs->rx_fifo.stat_ctrl),
> +			&i2c->regs->rx_fifo.stat_ctrl);
> +}
> +
> +static int netup_i2c_xfer(struct i2c_adapter *adap,
> +			  struct i2c_msg *msgs, int num)
> +{
> +	unsigned long flags;
> +	int i, trans_done, res = num;
> +	struct netup_i2c *i2c = i2c_get_adapdata(adap);
> +	u16 reg;
> +
> +	if (num <= 0) {
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): num == %d\n", __func__, num);
> +		return -EINVAL;
> +	}
> +	spin_lock_irqsave(&i2c->lock, flags);
> +	if (i2c->state != STATE_DONE) {
> +		dev_dbg(i2c->adap.dev.parent,
> +			"%s(): i2c->state == %d, resetting I2C\n",
> +			__func__, i2c->state);
> +		netup_i2c_reset(i2c);
> +	}
> +	dev_dbg(i2c->adap.dev.parent, "%s() num %d\n", __func__, num);
> +	for (i = 0; i < num; i++) {
> +		i2c->msg = &msgs[i];
> +		netup_i2c_start_xfer(i2c);
> +		trans_done = 0;
> +		while (!trans_done) {
> +			spin_unlock_irqrestore(&i2c->lock, flags);
> +			if (wait_event_timeout(i2c->wq,
> +					i2c->state != STATE_WAIT,
> +					msecs_to_jiffies(NETUP_I2C_TIMEOUT))) {
> +				spin_lock_irqsave(&i2c->lock, flags);
> +				switch (i2c->state) {
> +				case STATE_WANT_READ:
> +					netup_i2c_fifo_rx(i2c);
> +					break;
> +				case STATE_WANT_WRITE:
> +					netup_i2c_fifo_tx(i2c);
> +					break;
> +				case STATE_DONE:
> +					if ((i2c->msg->flags & I2C_M_RD) != 0 &&
> +						i2c->xmit_size != i2c->msg->len)
> +						netup_i2c_fifo_rx(i2c);
> +					dev_dbg(i2c->adap.dev.parent,
> +						"%s(): msg %d OK\n",
> +						__func__, i);
> +					trans_done = 1;
> +					break;
> +				case STATE_ERROR:
> +					res = -EIO;
> +					dev_dbg(i2c->adap.dev.parent,
> +						"%s(): error state\n",
> +						__func__);
> +					goto done;
> +				default:
> +					dev_dbg(i2c->adap.dev.parent,
> +						"%s(): invalid state %d\n",
> +						__func__, i2c->state);
> +					res = -EINVAL;
> +					goto done;
> +				}
> +				if (!trans_done) {
> +					i2c->state = STATE_WAIT;
> +					reg = readw(
> +						&i2c->regs->twi_ctrl0_stat);
> +					writew(TWI_IRQEN | reg,
> +						&i2c->regs->twi_ctrl0_stat);
> +				}
> +				spin_unlock_irqrestore(&i2c->lock, flags);
> +			} else {
> +				spin_lock_irqsave(&i2c->lock, flags);
> +				dev_dbg(i2c->adap.dev.parent,
> +					"%s(): wait timeout\n", __func__);
> +				res = -ETIMEDOUT;
> +				goto done;
> +			}
> +			spin_lock_irqsave(&i2c->lock, flags);
> +		}
> +	}
> +done:
> +	spin_unlock_irqrestore(&i2c->lock, flags);
> +	dev_dbg(i2c->adap.dev.parent, "%s(): result %d\n", __func__, res);
> +	return res;
> +}
> +
> +static u32 netup_i2c_func(struct i2c_adapter *adap)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm netup_i2c_algorithm = {
> +	.master_xfer	= netup_i2c_xfer,
> +	.functionality	= netup_i2c_func,
> +};
> +
> +static struct i2c_adapter netup_i2c_adapter = {
> +	.owner		= THIS_MODULE,
> +	.name		= NETUP_UNIDVB_NAME,
> +	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
> +	.algo		= &netup_i2c_algorithm,
> +};
> +
> +static int netup_i2c_init(struct netup_unidvb_dev *ndev, int bus_num)
> +{
> +	int ret;
> +	struct netup_i2c *i2c;
> +
> +	if (bus_num < 0 || bus_num > 1) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): invalid bus_num %d\n", __func__, bus_num);
> +		return -EINVAL;
> +	}
> +	i2c = &ndev->i2c[bus_num];
> +	spin_lock_init(&i2c->lock);
> +	init_waitqueue_head(&i2c->wq);
> +	i2c->regs = (struct netup_i2c_regs *)(ndev->bmmio0 +
> +		(bus_num == 0 ? NETUP_I2C_BUS0_ADDR : NETUP_I2C_BUS1_ADDR));
> +	netup_i2c_reset(i2c);
> +	i2c->adap = netup_i2c_adapter;
> +	i2c->adap.dev.parent = &ndev->pci_dev->dev;
> +	i2c_set_adapdata(&i2c->adap, i2c);
> +	ret = i2c_add_adapter(&i2c->adap);
> +	if (ret) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): failed to add I2C adapter\n", __func__);
> +		return ret;
> +	}
> +	dev_info(&ndev->pci_dev->dev,
> +		"%s(): registered I2C bus %d at 0x%x\n",
> +		__func__,
> +		bus_num, (bus_num == 0 ?
> +			NETUP_I2C_BUS0_ADDR :
> +			NETUP_I2C_BUS1_ADDR));
> +	return 0;
> +}
> +
> +static void netup_i2c_remove(struct netup_unidvb_dev *ndev, int bus_num)
> +{
> +	struct netup_i2c *i2c;
> +
> +	if (bus_num < 0 || bus_num > 1) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): invalid bus number %d\n", __func__, bus_num);
> +		return;
> +	}
> +	i2c = &ndev->i2c[bus_num];
> +	netup_i2c_reset(i2c);
> +	/* remove adapter */
> +	i2c_del_adapter(&i2c->adap);
> +	dev_info(&ndev->pci_dev->dev,
> +		"netup_i2c_remove: unregistered I2C bus %d\n", bus_num);
> +}
> +
> +int netup_i2c_register(struct netup_unidvb_dev *ndev)
> +{
> +	int ret;
> +
> +	ret = netup_i2c_init(ndev, 0);
> +	if (ret)
> +		return ret;
> +	ret = netup_i2c_init(ndev, 1);
> +	if (ret) {
> +		netup_i2c_remove(ndev, 0);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +void netup_i2c_unregister(struct netup_unidvb_dev *ndev)
> +{
> +	netup_i2c_remove(ndev, 0);
> +	netup_i2c_remove(ndev, 1);
> +}
> +
> diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c
> new file mode 100644
> index 0000000..f55b327
> --- /dev/null
> +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c
> @@ -0,0 +1,252 @@
> +/*
> + * netup_unidvb_spi.c
> + *
> + * Internal SPI driver for NetUP Universal Dual DVB-CI
> + *
> + * Copyright (C) 2014 NetUP Inc.
> + * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
> + * Copyright (C) 2014 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.
> + */
> +
> +#include "netup_unidvb.h"
> +#include <linux/spi/spi.h>
> +#include <linux/spi/flash.h>
> +#include <linux/mtd/partitions.h>
> +#include <mtd/mtd-abi.h>
> +
> +#define NETUP_SPI_CTRL_IRQ	0x1000
> +#define NETUP_SPI_CTRL_IMASK	0x2000
> +#define NETUP_SPI_CTRL_START	0x8000
> +#define NETUP_SPI_CTRL_LAST_CS	0x4000
> +
> +#define NETUP_SPI_TIMEOUT	6000
> +
> +enum netup_spi_state {
> +	SPI_STATE_START,
> +	SPI_STATE_DONE,
> +};
> +
> +struct netup_spi_regs {
> +	__u8	data[1024];
> +	__le16	control_stat;
> +	__le16	clock_divider;
> +} __packed __aligned(1);
> +
> +struct netup_spi {
> +	struct device			*dev;
> +	struct spi_master		*master;
> +	struct netup_spi_regs		*regs;
> +	u8 __iomem			*mmio;
> +	spinlock_t			lock;
> +	wait_queue_head_t		waitq;
> +	enum netup_spi_state		state;
> +};
> +
> +static char netup_spi_name[64] = "fpga";
> +
> +static struct mtd_partition netup_spi_flash_partitions = {
> +	.name = netup_spi_name,
> +	.size = 0x1000000, /* 16MB */
> +	.offset = 0,
> +	.mask_flags = MTD_CAP_ROM
> +};
> +
> +static struct flash_platform_data spi_flash_data = {
> +	.name = "netup0_m25p128",
> +	.parts = &netup_spi_flash_partitions,
> +	.nr_parts = 1,
> +};
> +
> +static struct spi_board_info netup_spi_board = {
> +	.modalias = "m25p128",
> +	.max_speed_hz = 11000000,
> +	.chip_select = 0,
> +	.mode = SPI_MODE_0,
> +	.platform_data = &spi_flash_data,
> +};
> +
> +irqreturn_t netup_spi_interrupt(struct netup_spi *spi)
> +{
> +	u16 reg;
> +	unsigned long flags;
> +
> +	if (!spi) {
> +		dev_dbg(&spi->master->dev,
> +			"%s(): SPI not initialized\n", __func__);
> +		return IRQ_NONE;
> +	}
> +	spin_lock_irqsave(&spi->lock, flags);
> +	reg = readw(&spi->regs->control_stat);
> +	if (!(reg & NETUP_SPI_CTRL_IRQ)) {
> +		spin_unlock_irqrestore(&spi->lock, flags);
> +		dev_dbg(&spi->master->dev,
> +			"%s(): not mine interrupt\n", __func__);
> +		return IRQ_NONE;
> +	}
> +	writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
> +	reg = readw(&spi->regs->control_stat);
> +	writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
> +	spi->state = SPI_STATE_DONE;
> +	wake_up(&spi->waitq);
> +	spin_unlock_irqrestore(&spi->lock, flags);
> +	dev_dbg(&spi->master->dev,
> +		"%s(): SPI interrupt handled\n", __func__);
> +	return IRQ_HANDLED;
> +}
> +
> +static int netup_spi_transfer(struct spi_master *master,
> +			      struct spi_message *msg)
> +{
> +	struct netup_spi *spi = spi_master_get_devdata(master);
> +	struct spi_transfer *t;
> +	int result = 0;
> +	u32 tr_size;
> +
> +	/* reset CS */
> +	writew(NETUP_SPI_CTRL_LAST_CS, &spi->regs->control_stat);
> +	writew(0, &spi->regs->control_stat);
> +	list_for_each_entry(t, &msg->transfers, transfer_list) {
> +		tr_size = t->len;
> +		while (tr_size) {
> +			u32 frag_offset = t->len - tr_size;
> +			u32 frag_size = (tr_size > sizeof(spi->regs->data)) ?
> +					sizeof(spi->regs->data) : tr_size;
> +			int frag_last = 0;
> +
> +			if (list_is_last(&t->transfer_list,
> +					&msg->transfers) &&
> +					frag_offset + frag_size == t->len) {
> +				frag_last = 1;
> +			}
> +			if (t->tx_buf) {
> +				memcpy_toio(spi->regs->data,
> +					t->tx_buf + frag_offset,
> +					frag_size);
> +			} else {
> +				memset_io(spi->regs->data,
> +					0, frag_size);
> +			}
> +			spi->state = SPI_STATE_START;
> +			writew((frag_size & 0x3ff) |
> +				NETUP_SPI_CTRL_IMASK |
> +				NETUP_SPI_CTRL_START |
> +				(frag_last ? NETUP_SPI_CTRL_LAST_CS : 0),
> +				&spi->regs->control_stat);
> +			dev_dbg(&spi->master->dev,
> +				"%s(): control_stat 0x%04x\n",
> +				__func__, readw(&spi->regs->control_stat));
> +			wait_event_timeout(spi->waitq,
> +				spi->state != SPI_STATE_START,
> +				msecs_to_jiffies(NETUP_SPI_TIMEOUT));
> +			if (spi->state == SPI_STATE_DONE) {
> +				if (t->rx_buf) {
> +					memcpy_fromio(t->rx_buf + frag_offset,
> +						spi->regs->data, frag_size);
> +				}
> +			} else {
> +				if (spi->state == SPI_STATE_START) {
> +					dev_dbg(&spi->master->dev,
> +						"%s(): transfer timeout\n",
> +						__func__);
> +				} else {
> +					dev_dbg(&spi->master->dev,
> +						"%s(): invalid state %d\n",
> +						__func__, spi->state);
> +				}
> +				result = -EIO;
> +				goto done;
> +			}
> +			tr_size -= frag_size;
> +			msg->actual_length += frag_size;
> +		}
> +	}
> +done:
> +	msg->status = result;
> +	spi_finalize_current_message(master);
> +	return result;
> +}
> +
> +static int netup_spi_setup(struct spi_device *spi)
> +{
> +	return 0;
> +}
> +
> +int netup_spi_init(struct netup_unidvb_dev *ndev)
> +{
> +	struct spi_master *master;
> +	struct netup_spi *nspi;
> +
> +	master = spi_alloc_master(&ndev->pci_dev->dev,
> +		sizeof(struct netup_spi));
> +	if (!master) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): unable to alloc SPI master\n", __func__);
> +		return -EINVAL;
> +	}
> +	nspi = spi_master_get_devdata(master);
> +	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
> +	master->bus_num = -1;
> +	master->num_chipselect = 1;
> +	master->transfer_one_message = netup_spi_transfer;
> +	master->setup = netup_spi_setup;
> +	spin_lock_init(&nspi->lock);
> +	init_waitqueue_head(&nspi->waitq);
> +	nspi->master = master;
> +	nspi->regs = (struct netup_spi_regs *)(ndev->bmmio0 + 0x4000);
> +	writew(2, &nspi->regs->clock_divider);
> +	writew(NETUP_UNIDVB_IRQ_SPI, ndev->bmmio0 + REG_IMASK_SET);
> +	ndev->spi = nspi;
> +	if (spi_register_master(master)) {
> +		ndev->spi = NULL;
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): unable to register SPI bus\n", __func__);
> +		return -EINVAL;
> +	}
> +	snprintf(netup_spi_name,
> +		sizeof(netup_spi_name),
> +		"fpga_%02x:%02x.%01x",
> +		ndev->pci_bus,
> +		ndev->pci_slot,
> +		ndev->pci_func);
> +	if (!spi_new_device(master, &netup_spi_board)) {
> +		ndev->spi = NULL;
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): unable to create SPI device\n", __func__);
> +		return -EINVAL;
> +	}
> +	dev_dbg(&ndev->pci_dev->dev, "%s(): SPI init OK\n", __func__);
> +	return 0;
> +}
> +
> +void netup_spi_release(struct netup_unidvb_dev *ndev)
> +{
> +	u16 reg;
> +	unsigned long flags;
> +	struct netup_spi *spi = ndev->spi;
> +
> +	if (!spi) {
> +		dev_dbg(&spi->master->dev,
> +			"%s(): SPI not initialized\n", __func__);
> +		return;
> +	}
> +	spin_lock_irqsave(&spi->lock, flags);
> +	reg = readw(&spi->regs->control_stat);
> +	writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
> +	reg = readw(&spi->regs->control_stat);
> +	writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
> +	spin_unlock_irqrestore(&spi->lock, flags);
> +	spi_unregister_master(spi->master);
> +	ndev->spi = NULL;
> +}
> +
> +

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V2 4/5] [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver
  2015-05-14 14:15   ` Mauro Carvalho Chehab
@ 2015-06-30 10:28     ` Kozlov Sergey
  0 siblings, 0 replies; 12+ messages in thread
From: Kozlov Sergey @ 2015-06-30 10:28 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, aospan1

Hi Mauro,

Thanks for your review.
I started work on V3 patch series, and found no information about 
roll-off handling in the CDX2841 demodulator chip documentation. It 
seemks that roll-off is handled automatically.

Best regards,
Sergey

14.05.2015 17:15, Mauro Carvalho Chehab пишет:
> Em Wed, 15 Apr 2015 13:07:49 +0300
> serjk@netup.ru escreveu:
>> +
>> +static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
>> +{
>> +	int ret = 0, i, timeout, carr_offset;
>> +	fe_status_t status;
>> +	struct cxd2841er_priv *priv = fe->demodulator_priv;
>> +	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
>> +	u32 symbol_rate = p->symbol_rate/1000;
>> +
>> +	dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n",
>> +		__func__,
>> +		(p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"),
>> +		 p->frequency, symbol_rate);
>
> I was unable to identify how ROLLOFF is handled for DVB-S2 in this code.
>


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2015-06-30 10:37 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-15 10:07 [PATCH V2 0/5] [media] NetUP Universal DVB PCIe card support serjk
2015-04-15 10:07 ` [PATCH V2 1/5] [media] horus3a: Sony Horus3A DVB-S/S2 tuner driver serjk
2015-05-14 13:58   ` Mauro Carvalho Chehab
2015-04-15 10:07 ` [PATCH V2 2/5] [media] ascot2e: Sony Ascot2e DVB-C/T/T2 " serjk
2015-05-14 14:07   ` Mauro Carvalho Chehab
2015-04-15 10:07 ` [PATCH V2 3/5] [media] lnbh25: LNBH25 SEC controller driver serjk
2015-05-14 14:05   ` Mauro Carvalho Chehab
2015-04-15 10:07 ` [PATCH V2 4/5] [media] cxd2841er: Sony CXD2841ER DVB-S/S2/T/T2/C demodulator driver serjk
2015-05-14 14:15   ` Mauro Carvalho Chehab
2015-06-30 10:28     ` Kozlov Sergey
2015-04-15 10:07 ` [PATCH V2 5/5] [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver serjk
2015-05-14 14:20   ` Mauro Carvalho Chehab

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).