All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e
@ 2013-12-08 22:31 Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 01/18] em28xx: add support for Empia EM28178 Antti Palosaari
                   ` (17 more replies)
  0 siblings, 18 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

I think these patches are now in a rather good shape and I will make PULL request soon.
I decided to convert M88TS2022 RF tuner to Kernel I2C driver model, which is new thing as there is no any other tuner driver using that model.

Testers are still wanted. Git tree is waiting for you:
http://git.linuxtv.org/anttip/media_tree.git/shortlog/refs/heads/pctv_461e

Antti

Antti Palosaari (18):
  em28xx: add support for Empia EM28178
  a8293: add small sleep in order to settle LNB voltage
  Montage M88DS3103 DVB-S/S2 demodulator driver
  Montage M88TS2022 silicon tuner driver
  em28xx: add support for PCTV DVB-S2 Stick (461e) [2013:0258]
  MAINTAINERS: add M88DS3103
  MAINTAINERS: add M88TS2022
  m88ts2022: do not use dynamic stack allocation
  m88ds3103: do not use dynamic stack allocation
  m88ds3103: use I2C mux for tuner I2C adapter
  m88ds3103: use kernel macro to round division
  m88ds3103: fix TS mode config
  m88ts2022: reimplement synthesizer calculations
  m88ds3103: remove unneeded AGC from inittab
  m88ds3103: add default value for reg 56
  m88ds3103: I/O optimize inittab write
  m88ts2022: convert to Kernel I2C driver model
  m88ds3103: fix possible i2c deadlock

 MAINTAINERS                                  |   20 +
 drivers/media/dvb-frontends/Kconfig          |    7 +
 drivers/media/dvb-frontends/Makefile         |    1 +
 drivers/media/dvb-frontends/a8293.c          |    2 +
 drivers/media/dvb-frontends/m88ds3103.c      | 1314 ++++++++++++++++++++++++++
 drivers/media/dvb-frontends/m88ds3103.h      |  118 +++
 drivers/media/dvb-frontends/m88ds3103_priv.h |  219 +++++
 drivers/media/tuners/Kconfig                 |    7 +
 drivers/media/tuners/Makefile                |    1 +
 drivers/media/tuners/m88ts2022.c             |  678 +++++++++++++
 drivers/media/tuners/m88ts2022.h             |   58 ++
 drivers/media/tuners/m88ts2022_priv.h        |   38 +
 drivers/media/usb/em28xx/Kconfig             |    2 +
 drivers/media/usb/em28xx/em28xx-cards.c      |   40 +
 drivers/media/usb/em28xx/em28xx-core.c       |    9 +-
 drivers/media/usb/em28xx/em28xx-dvb.c        |   54 ++
 drivers/media/usb/em28xx/em28xx-input.c      |    2 +
 drivers/media/usb/em28xx/em28xx-reg.h        |    1 +
 drivers/media/usb/em28xx/em28xx.h            |    1 +
 19 files changed, 2569 insertions(+), 3 deletions(-)
 create mode 100644 drivers/media/dvb-frontends/m88ds3103.c
 create mode 100644 drivers/media/dvb-frontends/m88ds3103.h
 create mode 100644 drivers/media/dvb-frontends/m88ds3103_priv.h
 create mode 100644 drivers/media/tuners/m88ts2022.c
 create mode 100644 drivers/media/tuners/m88ts2022.h
 create mode 100644 drivers/media/tuners/m88ts2022_priv.h

-- 
1.8.4.2


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

* [PATCH REVIEW 01/18] em28xx: add support for Empia EM28178
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 02/18] a8293: add small sleep in order to settle LNB voltage Antti Palosaari
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

New chip version, which is very similar than EM28174.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/usb/em28xx/em28xx-cards.c | 5 +++++
 drivers/media/usb/em28xx/em28xx-core.c  | 9 ++++++---
 drivers/media/usb/em28xx/em28xx-input.c | 2 ++
 drivers/media/usb/em28xx/em28xx-reg.h   | 1 +
 4 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index a519669..62332a6 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -2968,6 +2968,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
 			dev->wait_after_write = 0;
 			dev->eeprom_addrwidth_16bit = 1;
 			break;
+		case CHIP_ID_EM28178:
+			chip_name = "em28178";
+			dev->wait_after_write = 0;
+			dev->eeprom_addrwidth_16bit = 1;
+			break;
 		case CHIP_ID_EM2883:
 			chip_name = "em2882/3";
 			dev->wait_after_write = 0;
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index fc157af..0c5fdba 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -482,8 +482,10 @@ int em28xx_audio_setup(struct em28xx *dev)
 	int vid1, vid2, feat, cfg;
 	u32 vid;
 
-	if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874
-		|| dev->chip_id == CHIP_ID_EM28174) {
+	if (dev->chip_id == CHIP_ID_EM2870 ||
+	    dev->chip_id == CHIP_ID_EM2874 ||
+	    dev->chip_id == CHIP_ID_EM28174 ||
+	    dev->chip_id == CHIP_ID_EM28178) {
 		/* Digital only device - don't load any alsa module */
 		dev->audio_mode.has_audio = false;
 		dev->has_audio_class = false;
@@ -606,7 +608,8 @@ int em28xx_capture_start(struct em28xx *dev, int start)
 
 	if (dev->chip_id == CHIP_ID_EM2874 ||
 	    dev->chip_id == CHIP_ID_EM2884 ||
-	    dev->chip_id == CHIP_ID_EM28174) {
+	    dev->chip_id == CHIP_ID_EM28174 ||
+	    dev->chip_id == CHIP_ID_EM28178) {
 		/* The Transport Stream Enable Register moved in em2874 */
 		if (!start) {
 			rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index ea181e4..d4a0a75 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -442,6 +442,7 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type)
 	case CHIP_ID_EM2884:
 	case CHIP_ID_EM2874:
 	case CHIP_ID_EM28174:
+	case CHIP_ID_EM28178:
 		return em2874_ir_change_protocol(rc_dev, rc_type);
 	default:
 		printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n",
@@ -633,6 +634,7 @@ static int em28xx_ir_init(struct em28xx *dev)
 		case CHIP_ID_EM2884:
 		case CHIP_ID_EM2874:
 		case CHIP_ID_EM28174:
+		case CHIP_ID_EM28178:
 			ir->get_key = em2874_polling_getkey;
 			rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC |
 					     RC_BIT_RC6_0;
diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h
index 0e04778..b769ceb 100644
--- a/drivers/media/usb/em28xx/em28xx-reg.h
+++ b/drivers/media/usb/em28xx/em28xx-reg.h
@@ -245,6 +245,7 @@ enum em28xx_chip_id {
 	CHIP_ID_EM2874 = 65,
 	CHIP_ID_EM2884 = 68,
 	CHIP_ID_EM28174 = 113,
+	CHIP_ID_EM28178 = 114,
 };
 
 /*
-- 
1.8.4.2


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

* [PATCH REVIEW 02/18] a8293: add small sleep in order to settle LNB voltage
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 01/18] em28xx: add support for Empia EM28178 Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver Antti Palosaari
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

PCTV 461e requires that small delay.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/a8293.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c
index 74fbb5d..780da58 100644
--- a/drivers/media/dvb-frontends/a8293.c
+++ b/drivers/media/dvb-frontends/a8293.c
@@ -96,6 +96,8 @@ static int a8293_set_voltage(struct dvb_frontend *fe,
 	if (ret)
 		goto err;
 
+	usleep_range(1500, 50000);
+
 	return ret;
 err:
 	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-- 
1.8.4.2


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

* [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 01/18] em28xx: add support for Empia EM28178 Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 02/18] a8293: add small sleep in order to settle LNB voltage Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-09  5:58   ` Matthias Schwarzott
  2013-12-18 12:35   ` Mauro Carvalho Chehab
  2013-12-08 22:31 ` [PATCH REVIEW 04/18] Montage M88TS2022 silicon tuner driver Antti Palosaari
                   ` (14 subsequent siblings)
  17 siblings, 2 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

DVB-S/S2 satellite television demodulator driver.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/Kconfig          |    7 +
 drivers/media/dvb-frontends/Makefile         |    1 +
 drivers/media/dvb-frontends/m88ds3103.c      | 1293 ++++++++++++++++++++++++++
 drivers/media/dvb-frontends/m88ds3103.h      |  108 +++
 drivers/media/dvb-frontends/m88ds3103_priv.h |  218 +++++
 5 files changed, 1627 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/m88ds3103.c
 create mode 100644 drivers/media/dvb-frontends/m88ds3103.h
 create mode 100644 drivers/media/dvb-frontends/m88ds3103_priv.h

diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index bddbab4..6c46caf 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -35,6 +35,13 @@ config DVB_STV6110x
 	help
 	  A Silicon tuner that supports DVB-S and DVB-S2 modes
 
+config DVB_M88DS3103
+	tristate "Montage M88DS3103"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.
+
 comment "Multistandard (cable + terrestrial) frontends"
 	depends on DVB_CORE
 
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index f9cb43d..0c75a6a 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_DVB_STV6110) += stv6110.o
 obj-$(CONFIG_DVB_STV0900) += stv0900.o
 obj-$(CONFIG_DVB_STV090x) += stv090x.o
 obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
+obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
 obj-$(CONFIG_DVB_ISL6423) += isl6423.o
 obj-$(CONFIG_DVB_EC100) += ec100.o
 obj-$(CONFIG_DVB_HD29L2) += hd29l2.o
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
new file mode 100644
index 0000000..91b3729
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -0,0 +1,1293 @@
+/*
+ * Montage M88DS3103 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "m88ds3103_priv.h"
+
+static struct dvb_frontend_ops m88ds3103_ops;
+
+/* write multiple registers */
+static int m88ds3103_wr_regs(struct m88ds3103_priv *priv,
+		u8 reg, const u8 *val, int len)
+{
+	int ret;
+	u8 buf[1 + len];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = priv->cfg->i2c_addr,
+			.flags = 0,
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
+
+	buf[0] = reg;
+	memcpy(&buf[1], val, len);
+
+	mutex_lock(&priv->i2c_mutex);
+	ret = i2c_transfer(priv->i2c, msg, 1);
+	mutex_unlock(&priv->i2c_mutex);
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		dev_warn(&priv->i2c->dev,
+				"%s: i2c wr failed=%d reg=%02x len=%d\n",
+				KBUILD_MODNAME, ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+
+	return ret;
+}
+
+/* read multiple registers */
+static int m88ds3103_rd_regs(struct m88ds3103_priv *priv,
+		u8 reg, u8 *val, int len)
+{
+	int ret;
+	u8 buf[len];
+	struct i2c_msg msg[2] = {
+		{
+			.addr = priv->cfg->i2c_addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		}, {
+			.addr = priv->cfg->i2c_addr,
+			.flags = I2C_M_RD,
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
+
+	mutex_lock(&priv->i2c_mutex);
+	ret = i2c_transfer(priv->i2c, msg, 2);
+	mutex_unlock(&priv->i2c_mutex);
+	if (ret == 2) {
+		memcpy(val, buf, len);
+		ret = 0;
+	} else {
+		dev_warn(&priv->i2c->dev,
+				"%s: i2c rd failed=%d reg=%02x len=%d\n",
+				KBUILD_MODNAME, ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+
+	return ret;
+}
+
+/* write single register */
+static int m88ds3103_wr_reg(struct m88ds3103_priv *priv, u8 reg, u8 val)
+{
+	return m88ds3103_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int m88ds3103_rd_reg(struct m88ds3103_priv *priv, u8 reg, u8 *val)
+{
+	return m88ds3103_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+static int m88ds3103_wr_reg_mask(struct m88ds3103_priv *priv,
+		u8 reg, u8 val, u8 mask)
+{
+	int ret;
+	u8 u8tmp;
+
+	/* no need for read if whole reg is written */
+	if (mask != 0xff) {
+		ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1);
+		if (ret)
+			return ret;
+
+		val &= mask;
+		u8tmp &= ~mask;
+		val |= u8tmp;
+	}
+
+	return m88ds3103_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+static int m88ds3103_rd_reg_mask(struct m88ds3103_priv *priv,
+		u8 reg, u8 *val, u8 mask)
+{
+	int ret, i;
+	u8 u8tmp;
+
+	ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1);
+	if (ret)
+		return ret;
+
+	u8tmp &= mask;
+
+	/* find position of the first bit */
+	for (i = 0; i < 8; i++) {
+		if ((mask >> i) & 0x01)
+			break;
+	}
+	*val = u8tmp >> i;
+
+	return 0;
+}
+
+static int m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	u8 u8tmp;
+
+	*status = 0;
+
+	if (!priv->warm) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	switch (c->delivery_system) {
+	case SYS_DVBS:
+		ret = m88ds3103_rd_reg_mask(priv, 0xd1, &u8tmp, 0x07);
+		if (ret)
+			goto err;
+
+		if (u8tmp == 0x07)
+			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+					FE_HAS_VITERBI | FE_HAS_SYNC |
+					FE_HAS_LOCK;
+		break;
+	case SYS_DVBS2:
+		ret = m88ds3103_rd_reg_mask(priv, 0x0d, &u8tmp, 0x8f);
+		if (ret)
+			goto err;
+
+		if (u8tmp == 0x8f)
+			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+					FE_HAS_VITERBI | FE_HAS_SYNC |
+					FE_HAS_LOCK;
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+				__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	priv->fe_status = *status;
+
+	dev_dbg(&priv->i2c->dev, "%s: lock=%02x status=%02x\n",
+			__func__, u8tmp, *status);
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ds3103_set_frontend(struct dvb_frontend *fe)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret, i, len;
+	const struct m88ds3103_reg_val *init;
+	u8 u8tmp, u8tmp1, u8tmp2;
+	u8 buf[2];
+	u16 u16tmp, divide_ratio;
+	u32 tuner_frequency, target_mclk, ts_clk;
+	s32 s32tmp;
+	dev_dbg(&priv->i2c->dev,
+			"%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n",
+			__func__, c->delivery_system,
+			c->modulation, c->frequency, c->symbol_rate,
+			c->inversion, c->pilot, c->rolloff);
+
+	if (!priv->warm) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	/* program tuner */
+	if (fe->ops.tuner_ops.set_params) {
+		ret = fe->ops.tuner_ops.set_params(fe);
+		if (ret)
+			goto err;
+	}
+
+	if (fe->ops.tuner_ops.get_frequency) {
+		ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency);
+		if (ret)
+			goto err;
+	}
+
+	/* reset */
+	ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0x00, 0x01);
+	if (ret)
+		goto err;
+
+	switch (c->delivery_system) {
+	case SYS_DVBS:
+		len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
+		init = m88ds3103_dvbs_init_reg_vals;
+		target_mclk = 96000;
+		break;
+	case SYS_DVBS2:
+		len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
+		init = m88ds3103_dvbs2_init_reg_vals;
+
+		switch (priv->cfg->ts_mode) {
+		case M88DS3103_TS_SERIAL:
+		case M88DS3103_TS_SERIAL_D7:
+			if (c->symbol_rate < 18000000)
+				target_mclk = 96000;
+			else
+				target_mclk = 144000;
+			break;
+		case M88DS3103_TS_PARALLEL:
+		case M88DS3103_TS_PARALLEL_12:
+		case M88DS3103_TS_PARALLEL_16:
+		case M88DS3103_TS_PARALLEL_19_2:
+		case M88DS3103_TS_CI:
+			if (c->symbol_rate < 18000000)
+				target_mclk = 96000;
+			else if (c->symbol_rate < 28000000)
+				target_mclk = 144000;
+			else
+				target_mclk = 192000;
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n",
+					__func__);
+			ret = -EINVAL;
+			goto err;
+		}
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+				__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* program init table */
+	if (c->delivery_system != priv->delivery_system) {
+		dev_dbg(&priv->i2c->dev, "%s: program init\n", __func__);
+		for (i = 0; i < len; i++) {
+			ret = m88ds3103_wr_reg(priv, init[i].reg, init[i].val);
+			if (ret)
+				goto err;
+		}
+	}
+
+	u8tmp1 = 0; /* silence compiler warning */
+	switch (priv->cfg->ts_mode) {
+	case M88DS3103_TS_SERIAL:
+		u8tmp1 = 0x00;
+		ts_clk = 0;
+		u8tmp = 0x04;
+		break;
+	case M88DS3103_TS_SERIAL_D7:
+		u8tmp1 = 0x20;
+		ts_clk = 0;
+		u8tmp = 0x04;
+		break;
+	case M88DS3103_TS_PARALLEL:
+		ts_clk = 24000;
+		u8tmp = 0x00;
+		break;
+	case M88DS3103_TS_PARALLEL_12:
+		ts_clk = 12000;
+		u8tmp = 0x00;
+		break;
+	case M88DS3103_TS_PARALLEL_16:
+		ts_clk = 16000;
+		u8tmp = 0x00;
+		break;
+	case M88DS3103_TS_PARALLEL_19_2:
+		ts_clk = 19200;
+		u8tmp = 0x00;
+		break;
+	case M88DS3103_TS_CI:
+		ts_clk = 6000;
+		u8tmp = 0x01;
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* TS mode */
+	ret = m88ds3103_wr_reg_mask(priv, 0xfd, u8tmp, 0x05);
+	if (ret)
+		goto err;
+
+	switch (priv->cfg->ts_mode) {
+	case M88DS3103_TS_SERIAL:
+	case M88DS3103_TS_SERIAL_D7:
+		ret = m88ds3103_wr_reg_mask(priv, 0x29, u8tmp1, 0x20);
+		if (ret)
+			goto err;
+	}
+
+	if (ts_clk) {
+		divide_ratio = DIV_ROUND_UP(target_mclk, ts_clk);
+		u8tmp1 = divide_ratio / 2;
+		u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
+	} else {
+		divide_ratio = 0;
+		u8tmp1 = 0;
+		u8tmp2 = 0;
+	}
+
+	dev_dbg(&priv->i2c->dev,
+			"%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n",
+			__func__, target_mclk, ts_clk, divide_ratio);
+
+	u8tmp1--;
+	u8tmp2--;
+	/* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */
+	u8tmp1 &= 0x3f;
+	/* u8tmp2[5:0] => ea[5:0] */
+	u8tmp2 &= 0x3f;
+
+	ret = m88ds3103_rd_reg(priv, 0xfe, &u8tmp);
+	if (ret)
+		goto err;
+
+	u8tmp = ((u8tmp  & 0xf0) << 0) | u8tmp1 >> 2;
+	ret = m88ds3103_wr_reg(priv, 0xfe, u8tmp);
+	if (ret)
+		goto err;
+
+	u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
+	ret = m88ds3103_wr_reg(priv, 0xea, u8tmp);
+	if (ret)
+		goto err;
+
+	switch (target_mclk) {
+	case 72000:
+		u8tmp1 = 0x00; /* 0b00 */
+		u8tmp2 = 0x03; /* 0b11 */
+		break;
+	case 96000:
+		u8tmp1 = 0x02; /* 0b10 */
+		u8tmp2 = 0x01; /* 0b01 */
+		break;
+	case 115200:
+		u8tmp1 = 0x01; /* 0b01 */
+		u8tmp2 = 0x01; /* 0b01 */
+		break;
+	case 144000:
+		u8tmp1 = 0x00; /* 0b00 */
+		u8tmp2 = 0x01; /* 0b01 */
+		break;
+	case 192000:
+		u8tmp1 = 0x03; /* 0b11 */
+		u8tmp2 = 0x00; /* 0b00 */
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s: invalid target_mclk\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
+	if (ret)
+		goto err;
+
+	if (c->symbol_rate <= 3000000)
+		u8tmp = 0x20;
+	else if (c->symbol_rate <= 10000000)
+		u8tmp = 0x10;
+	else
+		u8tmp = 0x06;
+
+	ret = m88ds3103_wr_reg(priv, 0xc3, 0x08);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0xc8, u8tmp);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0xc4, 0x08);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0xc7, 0x00);
+	if (ret)
+		goto err;
+
+	u16tmp = (((c->symbol_rate / 1000) << 15) + (M88DS3103_MCLK_KHZ / 4)) /
+			(M88DS3103_MCLK_KHZ / 2);
+	buf[0] = (u16tmp >> 0) & 0xff;
+	buf[1] = (u16tmp >> 8) & 0xff;
+	ret = m88ds3103_wr_regs(priv, 0x61, buf, 2);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x4d, priv->cfg->spec_inv << 1, 0x02);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x30, priv->cfg->agc_inv << 4, 0x10);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0x33, priv->cfg->agc);
+	if (ret)
+		goto err;
+
+	dev_dbg(&priv->i2c->dev, "%s: carrier offset=%d\n", __func__,
+			(tuner_frequency - c->frequency));
+
+	s32tmp = 0x10000 * (tuner_frequency - c->frequency);
+	s32tmp = (2 * s32tmp + M88DS3103_MCLK_KHZ) / (2 * M88DS3103_MCLK_KHZ);
+	if (s32tmp < 0)
+		s32tmp += 0x10000;
+
+	buf[0] = (s32tmp >> 0) & 0xff;
+	buf[1] = (s32tmp >> 8) & 0xff;
+	ret = m88ds3103_wr_regs(priv, 0x5e, buf, 2);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0x00, 0x00);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0xb2, 0x00);
+	if (ret)
+		goto err;
+
+	priv->delivery_system = c->delivery_system;
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ds3103_init(struct dvb_frontend *fe)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	int ret, len, remaining;
+	const struct firmware *fw = NULL;
+	u8 *fw_file = M88DS3103_FIRMWARE;
+	u8 u8tmp;
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+	/* set cold state by default */
+	priv->warm = false;
+
+	/* wake up device from sleep */
+	ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x01, 0x01);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x00, 0x01);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x00, 0x10);
+	if (ret)
+		goto err;
+
+	/* reset */
+	ret = m88ds3103_wr_reg(priv, 0x07, 0x60);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
+	if (ret)
+		goto err;
+
+	/* firmware status */
+	ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
+	if (ret)
+		goto err;
+
+	dev_dbg(&priv->i2c->dev, "%s: firmware=%02x\n", __func__, u8tmp);
+
+	if (u8tmp)
+		goto skip_fw_download;
+
+	/* cold state - try to download firmware */
+	dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n",
+			KBUILD_MODNAME, m88ds3103_ops.info.name);
+
+	/* request the firmware, this will block and timeout */
+	ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
+	if (ret) {
+		dev_err(&priv->i2c->dev, "%s: firmare file '%s' not found\n",
+				KBUILD_MODNAME, fw_file);
+		goto err;
+	}
+
+	dev_info(&priv->i2c->dev, "%s: downloading firmware from file '%s'\n",
+			KBUILD_MODNAME, fw_file);
+
+	ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
+	if (ret)
+		goto err;
+
+	for (remaining = fw->size; remaining > 0;
+			remaining -= (priv->cfg->i2c_wr_max - 1)) {
+		len = remaining;
+		if (len > (priv->cfg->i2c_wr_max - 1))
+			len = (priv->cfg->i2c_wr_max - 1);
+
+		ret = m88ds3103_wr_regs(priv, 0xb0,
+				&fw->data[fw->size - remaining], len);
+		if (ret) {
+			dev_err(&priv->i2c->dev,
+					"%s: firmware download failed=%d\n",
+					KBUILD_MODNAME, ret);
+			goto err;
+		}
+	}
+
+	ret = m88ds3103_wr_reg(priv, 0xb2, 0x00);
+	if (ret)
+		goto err;
+
+	release_firmware(fw);
+	fw = NULL;
+
+	ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
+	if (ret)
+		goto err;
+
+	if (!u8tmp) {
+		dev_info(&priv->i2c->dev, "%s: firmware did not run\n",
+				KBUILD_MODNAME);
+		ret = -EFAULT;
+		goto err;
+	}
+
+	dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n",
+			KBUILD_MODNAME, m88ds3103_ops.info.name);
+	dev_info(&priv->i2c->dev, "%s: firmware version %X.%X\n",
+			KBUILD_MODNAME, (u8tmp >> 4) & 0xf, (u8tmp >> 0 & 0xf));
+
+skip_fw_download:
+	/* warm state */
+	priv->warm = true;
+
+	return 0;
+err:
+	if (fw)
+		release_firmware(fw);
+
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ds3103_sleep(struct dvb_frontend *fe)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	int ret;
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+	priv->delivery_system = SYS_UNDEFINED;
+
+	/* TS Hi-Z */
+	ret = m88ds3103_wr_reg_mask(priv, 0x27, 0x00, 0x01);
+	if (ret)
+		goto err;
+
+	/* sleep */
+	ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ds3103_get_frontend(struct dvb_frontend *fe)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	u8 buf[3];
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+	if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	switch (c->delivery_system) {
+	case SYS_DVBS:
+		ret = m88ds3103_rd_reg(priv, 0xe0, &buf[0]);
+		if (ret)
+			goto err;
+
+		ret = m88ds3103_rd_reg(priv, 0xe6, &buf[1]);
+		if (ret)
+			goto err;
+
+		switch ((buf[0] >> 2) & 0x01) {
+		case 0:
+			c->inversion = INVERSION_OFF;
+			break;
+		case 1:
+			c->inversion = INVERSION_ON;
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
+					__func__);
+		}
+
+		switch ((buf[1] >> 5) & 0x07) {
+		case 0:
+			c->fec_inner = FEC_7_8;
+			break;
+		case 1:
+			c->fec_inner = FEC_5_6;
+			break;
+		case 2:
+			c->fec_inner = FEC_3_4;
+			break;
+		case 3:
+			c->fec_inner = FEC_2_3;
+			break;
+		case 4:
+			c->fec_inner = FEC_1_2;
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n",
+					__func__);
+		}
+
+		c->modulation = QPSK;
+
+		break;
+	case SYS_DVBS2:
+		ret = m88ds3103_rd_reg(priv, 0x7e, &buf[0]);
+		if (ret)
+			goto err;
+
+		ret = m88ds3103_rd_reg(priv, 0x89, &buf[1]);
+		if (ret)
+			goto err;
+
+		ret = m88ds3103_rd_reg(priv, 0xf2, &buf[2]);
+		if (ret)
+			goto err;
+
+		switch ((buf[0] >> 0) & 0x0f) {
+		case 2:
+			c->fec_inner = FEC_2_5;
+			break;
+		case 3:
+			c->fec_inner = FEC_1_2;
+			break;
+		case 4:
+			c->fec_inner = FEC_3_5;
+			break;
+		case 5:
+			c->fec_inner = FEC_2_3;
+			break;
+		case 6:
+			c->fec_inner = FEC_3_4;
+			break;
+		case 7:
+			c->fec_inner = FEC_4_5;
+			break;
+		case 8:
+			c->fec_inner = FEC_5_6;
+			break;
+		case 9:
+			c->fec_inner = FEC_8_9;
+			break;
+		case 10:
+			c->fec_inner = FEC_9_10;
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n",
+					__func__);
+		}
+
+		switch ((buf[0] >> 5) & 0x01) {
+		case 0:
+			c->pilot = PILOT_OFF;
+			break;
+		case 1:
+			c->pilot = PILOT_ON;
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
+					__func__);
+		}
+
+		switch ((buf[0] >> 6) & 0x07) {
+		case 0:
+			c->modulation = QPSK;
+			break;
+		case 1:
+			c->modulation = PSK_8;
+			break;
+		case 2:
+			c->modulation = APSK_16;
+			break;
+		case 3:
+			c->modulation = APSK_32;
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s: invalid modulation\n",
+					__func__);
+		}
+
+		switch ((buf[1] >> 7) & 0x01) {
+		case 0:
+			c->inversion = INVERSION_OFF;
+			break;
+		case 1:
+			c->inversion = INVERSION_ON;
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
+					__func__);
+		}
+
+		switch ((buf[2] >> 0) & 0x03) {
+		case 0:
+			c->rolloff = ROLLOFF_35;
+			break;
+		case 1:
+			c->rolloff = ROLLOFF_25;
+			break;
+		case 2:
+			c->rolloff = ROLLOFF_20;
+			break;
+		default:
+			dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n",
+					__func__);
+		}
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+				__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = m88ds3103_rd_regs(priv, 0x6d, buf, 2);
+	if (ret)
+		goto err;
+
+	c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) *
+			M88DS3103_MCLK_KHZ * 1000 / 0x10000;
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret, i, tmp;
+	u8 buf[3];
+	u16 noise, signal;
+	u32 noise_tot, signal_tot;
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	/* reports SNR in resolution of 0.1 dB */
+
+	/* more iterations for more accurate estimation */
+	#define M88DS3103_SNR_ITERATIONS 3
+
+	switch (c->delivery_system) {
+	case SYS_DVBS:
+		tmp = 0;
+
+		for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
+			ret = m88ds3103_rd_reg(priv, 0xff, &buf[0]);
+			if (ret)
+				goto err;
+
+			tmp += buf[0];
+		}
+
+		/* use of one register limits max value to 15 dB */
+		/* SNR(X) dB = 10 * ln(X) / ln(10) dB */
+		tmp = DIV_ROUND_CLOSEST(tmp, 8 * M88DS3103_SNR_ITERATIONS);
+		if (tmp)
+			*snr = 100ul * intlog2(tmp) / intlog2(10);
+		else
+			*snr = 0;
+		break;
+	case SYS_DVBS2:
+		noise_tot = 0;
+		signal_tot = 0;
+
+		for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
+			ret = m88ds3103_rd_regs(priv, 0x8c, buf, 3);
+			if (ret)
+				goto err;
+
+			noise = buf[1] << 6;    /* [13:6] */
+			noise |= buf[0] & 0x3f; /*  [5:0] */
+			noise >>= 2;
+			signal = buf[2] * buf[2];
+			signal >>= 1;
+
+			noise_tot += noise;
+			signal_tot += signal;
+		}
+
+		noise = noise_tot / M88DS3103_SNR_ITERATIONS;
+		signal = signal_tot / M88DS3103_SNR_ITERATIONS;
+
+		/* SNR(X) dB = 10 * log10(X) dB */
+		if (signal > noise) {
+			tmp = signal / noise;
+			*snr = 100ul * intlog10(tmp) / (1 << 24);
+		} else
+			*snr = 0;
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+				__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+
+static int m88ds3103_set_tone(struct dvb_frontend *fe,
+	fe_sec_tone_mode_t fe_sec_tone_mode)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	int ret;
+	u8 u8tmp, tone, reg_a1_mask;
+	dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__,
+			fe_sec_tone_mode);
+
+	if (!priv->warm) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	switch (fe_sec_tone_mode) {
+	case SEC_TONE_ON:
+		tone = 0;
+		reg_a1_mask = 0x87;
+		break;
+	case SEC_TONE_OFF:
+		tone = 1;
+		reg_a1_mask = 0x00;
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n",
+				__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	u8tmp = tone << 7 | priv->cfg->envelope_mode << 5;
+	ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
+	if (ret)
+		goto err;
+
+	u8tmp = 1 << 2;
+	ret = m88ds3103_wr_reg_mask(priv, 0xa1, u8tmp, reg_a1_mask);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
+		struct dvb_diseqc_master_cmd *diseqc_cmd)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	int ret, i;
+	u8 u8tmp;
+	dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__,
+			diseqc_cmd->msg_len, diseqc_cmd->msg);
+
+	if (!priv->warm) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	u8tmp = priv->cfg->envelope_mode << 5;
+	ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_regs(priv, 0xa3, diseqc_cmd->msg,
+			diseqc_cmd->msg_len);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg(priv, 0xa1,
+			(diseqc_cmd->msg_len - 1) << 3 | 0x07);
+	if (ret)
+		goto err;
+
+	/* DiSEqC message typical period is 54 ms */
+	usleep_range(40000, 60000);
+
+	/* wait DiSEqC TX ready */
+	for (i = 20, u8tmp = 1; i && u8tmp; i--) {
+		usleep_range(5000, 10000);
+
+		ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40);
+		if (ret)
+			goto err;
+	}
+
+	dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+	if (i == 0) {
+		dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__);
+
+		ret = m88ds3103_wr_reg_mask(priv, 0xa1, 0x40, 0xc0);
+		if (ret)
+			goto err;
+	}
+
+	ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0);
+	if (ret)
+		goto err;
+
+	if (i == 0) {
+		ret = -ETIMEDOUT;
+		goto err;
+	}
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
+	fe_sec_mini_cmd_t fe_sec_mini_cmd)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	int ret, i;
+	u8 u8tmp, burst;
+	dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
+			fe_sec_mini_cmd);
+
+	if (!priv->warm) {
+		ret = -EAGAIN;
+		goto err;
+	}
+
+	u8tmp = priv->cfg->envelope_mode << 5;
+	ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
+	if (ret)
+		goto err;
+
+	switch (fe_sec_mini_cmd) {
+	case SEC_MINI_A:
+		burst = 0x02;
+		break;
+	case SEC_MINI_B:
+		burst = 0x01;
+		break;
+	default:
+		dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n",
+				__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = m88ds3103_wr_reg(priv, 0xa1, burst);
+	if (ret)
+		goto err;
+
+	/* DiSEqC ToneBurst period is 12.5 ms */
+	usleep_range(11000, 20000);
+
+	/* wait DiSEqC TX ready */
+	for (i = 5, u8tmp = 1; i && u8tmp; i--) {
+		usleep_range(800, 2000);
+
+		ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40);
+		if (ret)
+			goto err;
+	}
+
+	dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+	ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0);
+	if (ret)
+		goto err;
+
+	if (i == 0) {
+		dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__);
+		ret = -ETIMEDOUT;
+		goto err;
+	}
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ds3103_get_tune_settings(struct dvb_frontend *fe,
+	struct dvb_frontend_tune_settings *s)
+{
+	s->min_delay_ms = 3000;
+
+	return 0;
+}
+
+static u32 m88ds3103_tuner_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
+		struct i2c_msg msg[], int num)
+{
+	struct m88ds3103_priv *priv = i2c_get_adapdata(i2c_adap);
+	int ret;
+	struct i2c_msg gate_open_msg[1] = {
+		{
+			.addr = priv->cfg->i2c_addr,
+			.flags = 0,
+			.len = 2,
+			.buf = "\x03\x11",
+		}
+	};
+	dev_dbg(&priv->i2c->dev, "%s: num=%d\n", __func__, num);
+
+	mutex_lock(&priv->i2c_mutex);
+
+	/* open i2c-gate */
+	ret = i2c_transfer(priv->i2c, gate_open_msg, 1);
+	if (ret != 1) {
+		mutex_unlock(&priv->i2c_mutex);
+		dev_warn(&priv->i2c->dev,
+				"%s: i2c wr failed=%d\n",
+				KBUILD_MODNAME, ret);
+		ret = -EREMOTEIO;
+		goto err;
+	}
+
+	ret = i2c_transfer(priv->i2c, msg, num);
+	mutex_unlock(&priv->i2c_mutex);
+	if (ret < 0)
+		dev_warn(&priv->i2c->dev, "%s: i2c failed=%d\n",
+				KBUILD_MODNAME, ret);
+
+	return ret;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static struct i2c_algorithm m88ds3103_tuner_i2c_algo = {
+	.master_xfer   = m88ds3103_tuner_i2c_xfer,
+	.functionality = m88ds3103_tuner_i2c_func,
+};
+
+static void m88ds3103_release(struct dvb_frontend *fe)
+{
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	i2c_del_adapter(&priv->i2c_adapter);
+	kfree(priv);
+}
+
+struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
+		struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter)
+{
+	int ret;
+	struct m88ds3103_priv *priv;
+	u8 chip_id, u8tmp;
+
+	/* allocate memory for the internal priv */
+	priv = kzalloc(sizeof(struct m88ds3103_priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+		goto err;
+	}
+
+	priv->cfg = cfg;
+	priv->i2c = i2c;
+	mutex_init(&priv->i2c_mutex);
+
+	ret = m88ds3103_rd_reg(priv, 0x01, &chip_id);
+	if (ret)
+		goto err;
+
+	dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+
+	switch (chip_id) {
+	case 0xd0:
+		break;
+	default:
+		goto err;
+	}
+
+	switch (priv->cfg->clock_out) {
+	case M88DS3103_CLOCK_OUT_DISABLED:
+		u8tmp = 0x80;
+		break;
+	case M88DS3103_CLOCK_OUT_ENABLED:
+		u8tmp = 0x00;
+		break;
+	case M88DS3103_CLOCK_OUT_ENABLED_DIV2:
+		u8tmp = 0x10;
+		break;
+	default:
+		goto err;
+	}
+
+	ret = m88ds3103_wr_reg(priv, 0x29, u8tmp);
+	if (ret)
+		goto err;
+
+	/* sleep */
+	ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
+	if (ret)
+		goto err;
+
+	ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
+	if (ret)
+		goto err;
+
+	/* create dvb_frontend */
+	memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
+	priv->fe.demodulator_priv = priv;
+
+	/* create i2c adapter for tuner */
+	strlcpy(priv->i2c_adapter.name, KBUILD_MODNAME,
+			sizeof(priv->i2c_adapter.name));
+	priv->i2c_adapter.algo = &m88ds3103_tuner_i2c_algo;
+	priv->i2c_adapter.algo_data = NULL;
+	i2c_set_adapdata(&priv->i2c_adapter, priv);
+	ret = i2c_add_adapter(&priv->i2c_adapter);
+	if (ret) {
+		dev_err(&i2c->dev, "%s: i2c bus could not be initialized\n",
+				KBUILD_MODNAME);
+		goto err;
+	}
+	*tuner_i2c_adapter = &priv->i2c_adapter;
+
+	return &priv->fe;
+err:
+	dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+	kfree(priv);
+	return NULL;
+}
+EXPORT_SYMBOL(m88ds3103_attach);
+
+static struct dvb_frontend_ops m88ds3103_ops = {
+	.delsys = { SYS_DVBS, SYS_DVBS2 },
+	.info = {
+		.name = "Montage M88DS3103",
+		.frequency_min =  950000,
+		.frequency_max = 2150000,
+		.frequency_tolerance = 5000,
+		.symbol_rate_min =  1000000,
+		.symbol_rate_max = 45000000,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 |
+			FE_CAN_FEC_2_3 |
+			FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_4_5 |
+			FE_CAN_FEC_5_6 |
+			FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 |
+			FE_CAN_FEC_8_9 |
+			FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK |
+			FE_CAN_RECOVER |
+			FE_CAN_2G_MODULATION
+	},
+
+	.release = m88ds3103_release,
+
+	.get_tune_settings = m88ds3103_get_tune_settings,
+
+	.init = m88ds3103_init,
+	.sleep = m88ds3103_sleep,
+
+	.set_frontend = m88ds3103_set_frontend,
+	.get_frontend = m88ds3103_get_frontend,
+
+	.read_status = m88ds3103_read_status,
+	.read_snr = m88ds3103_read_snr,
+
+	.diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd,
+	.diseqc_send_burst = m88ds3103_diseqc_send_burst,
+
+	.set_tone = m88ds3103_set_tone,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(M88DS3103_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h
new file mode 100644
index 0000000..287d62a
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103.h
@@ -0,0 +1,108 @@
+/*
+ * Montage M88DS3103 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef M88DS3103_H
+#define M88DS3103_H
+
+#include <linux/dvb/frontend.h>
+
+struct m88ds3103_config {
+	/*
+	 * I2C address
+	 * 0x68, ...
+	 */
+	u8 i2c_addr;
+
+	/*
+	 * clock
+	 * 27000000
+	 */
+	u32 clock;
+
+	/*
+	 * max bytes I2C provider is asked to write at once
+	 * Note: Buffer is taken from the stack currently!
+	 * Value must be set.
+	 * 33, 65, ...
+	 */
+	u16 i2c_wr_max;
+
+	/*
+	 * TS output mode
+	 */
+#define M88DS3103_TS_SERIAL             0 /* TS output pin D0, normal */
+#define M88DS3103_TS_SERIAL_D7          1 /* TS output pin D7 */
+#define M88DS3103_TS_PARALLEL           2 /* 24 MHz, normal */
+#define M88DS3103_TS_PARALLEL_12        3 /* 12 MHz */
+#define M88DS3103_TS_PARALLEL_16        4 /* 16 MHz */
+#define M88DS3103_TS_PARALLEL_19_2      5 /* 19.2 MHz */
+#define M88DS3103_TS_CI                 6 /* 6 MHz */
+	u8 ts_mode;
+
+	/*
+	 * spectrum inversion
+	 */
+	u8 spec_inv:1;
+
+	/*
+	 * AGC polarity
+	 */
+	u8 agc_inv:1;
+
+	/*
+	 * clock output
+	 */
+#define M88DS3103_CLOCK_OUT_DISABLED        0
+#define M88DS3103_CLOCK_OUT_ENABLED         1
+#define M88DS3103_CLOCK_OUT_ENABLED_DIV2    2
+	u8 clock_out;
+
+	/*
+	 * DiSEqC envelope mode
+	 */
+	u8 envelope_mode:1;
+
+	u8 agc;
+};
+
+/*
+ * Driver implements own I2C-adapter for tuner I2C access. That's since chip
+ * has I2C-gate control which closes gate automatically after I2C transfer.
+ * Using own I2C adapter we can workaround that.
+ */
+
+#if defined(CONFIG_DVB_M88DS3103) || \
+		(defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE))
+extern struct dvb_frontend *m88ds3103_attach(
+		const struct m88ds3103_config *config,
+		struct i2c_adapter *i2c,
+		struct i2c_adapter **tuner_i2c);
+#else
+static inline struct dvb_frontend *m88ds3103_attach(
+		const struct m88ds3103_config *config,
+		struct i2c_adapter *i2c,
+		struct i2c_adapter **tuner_i2c)
+{
+	pr_warn("%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
new file mode 100644
index 0000000..f3d0867
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -0,0 +1,218 @@
+/*
+ * Montage M88DS3103 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef M88DS3103_PRIV_H
+#define M88DS3103_PRIV_H
+
+#include "dvb_frontend.h"
+#include "m88ds3103.h"
+#include "dvb_math.h"
+#include <linux/firmware.h>
+
+#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
+#define M88DS3103_MCLK_KHZ 96000
+
+struct m88ds3103_priv {
+	struct i2c_adapter *i2c;
+	/* mutex needed due to own tuner I2C adapter */
+	struct mutex i2c_mutex;
+	const struct m88ds3103_config *cfg;
+	struct dvb_frontend fe;
+	fe_delivery_system_t delivery_system;
+	fe_status_t fe_status;
+	bool warm; /* FW running */
+	struct i2c_adapter i2c_adapter;
+};
+
+struct m88ds3103_reg_val {
+	u8 reg;
+	u8 val;
+};
+
+static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = {
+	{0x23, 0x07},
+	{0x08, 0x03},
+	{0x0c, 0x02},
+	{0x21, 0x54},
+	{0x25, 0x8a},
+	{0x27, 0x31},
+	{0x30, 0x08},
+	{0x31, 0x40},
+	{0x32, 0x32},
+	{0x33, 0x35},
+	{0x35, 0xff},
+	{0x3a, 0x00},
+	{0x37, 0x10},
+	{0x38, 0x10},
+	{0x39, 0x02},
+	{0x42, 0x60},
+	{0x4a, 0x80},
+	{0x4b, 0x04},
+	{0x4d, 0x91},
+	{0x5d, 0xc8},
+	{0x50, 0x36},
+	{0x51, 0x36},
+	{0x52, 0x36},
+	{0x53, 0x36},
+	{0x63, 0x0f},
+	{0x64, 0x30},
+	{0x65, 0x40},
+	{0x68, 0x26},
+	{0x69, 0x4c},
+	{0x70, 0x20},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0x40},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0x60},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0x80},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0xa0},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0x1f},
+	{0x76, 0x38},
+	{0x77, 0xa6},
+	{0x78, 0x0c},
+	{0x79, 0x80},
+	{0x7f, 0x14},
+	{0x7c, 0x00},
+	{0xae, 0x82},
+	{0x80, 0x64},
+	{0x81, 0x66},
+	{0x82, 0x44},
+	{0x85, 0x04},
+	{0xcd, 0xf4},
+	{0x90, 0x33},
+	{0xa0, 0x44},
+	{0xc0, 0x08},
+	{0xc3, 0x10},
+	{0xc4, 0x08},
+	{0xc5, 0xf0},
+	{0xc6, 0xff},
+	{0xc7, 0x00},
+	{0xc8, 0x1a},
+	{0xc9, 0x80},
+	{0xe0, 0xf8},
+	{0xe6, 0x8b},
+	{0xd0, 0x40},
+	{0xf8, 0x20},
+	{0xfa, 0x0f},
+	{0x00, 0x00},
+	{0xbd, 0x01},
+	{0xb8, 0x00},
+};
+
+static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = {
+	{0x23, 0x07},
+	{0x08, 0x07},
+	{0x0c, 0x02},
+	{0x21, 0x54},
+	{0x25, 0x8a},
+	{0x27, 0x31},
+	{0x30, 0x08},
+	{0x32, 0x32},
+	{0x33, 0x35},
+	{0x35, 0xff},
+	{0x3a, 0x00},
+	{0x37, 0x10},
+	{0x38, 0x10},
+	{0x39, 0x02},
+	{0x42, 0x60},
+	{0x4a, 0x80},
+	{0x4b, 0x04},
+	{0x4d, 0x91},
+	{0x5d, 0xc8},
+	{0x50, 0x36},
+	{0x51, 0x36},
+	{0x52, 0x36},
+	{0x53, 0x36},
+	{0x63, 0x0f},
+	{0x64, 0x10},
+	{0x65, 0x20},
+	{0x68, 0x46},
+	{0x69, 0xcd},
+	{0x70, 0x20},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0x40},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0x60},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0x80},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0xa0},
+	{0x71, 0x70},
+	{0x72, 0x04},
+	{0x73, 0x00},
+	{0x70, 0x1f},
+	{0x76, 0x38},
+	{0x77, 0xa6},
+	{0x78, 0x0c},
+	{0x79, 0x80},
+	{0x7f, 0x14},
+	{0x85, 0x08},
+	{0xcd, 0xf4},
+	{0x90, 0x33},
+	{0x86, 0x00},
+	{0x87, 0x0f},
+	{0x89, 0x00},
+	{0x8b, 0x44},
+	{0x8c, 0x66},
+	{0x9d, 0xc1},
+	{0x8a, 0x10},
+	{0xad, 0x40},
+	{0xa0, 0x44},
+	{0xc0, 0x08},
+	{0xc1, 0x10},
+	{0xc2, 0x08},
+	{0xc3, 0x10},
+	{0xc4, 0x08},
+	{0xc5, 0xf0},
+	{0xc6, 0xff},
+	{0xc7, 0x00},
+	{0xc8, 0x1a},
+	{0xc9, 0x80},
+	{0xca, 0x23},
+	{0xcb, 0x24},
+	{0xcc, 0xf4},
+	{0xce, 0x74},
+	{0x00, 0x00},
+	{0xbd, 0x01},
+	{0xb8, 0x00},
+};
+
+#endif
-- 
1.8.4.2


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

* [PATCH REVIEW 04/18] Montage M88TS2022 silicon tuner driver
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (2 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 05/18] em28xx: add support for PCTV DVB-S2 Stick (461e) [2013:0258] Antti Palosaari
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

M88TS2022 is DVB-S/S2 RF tuner used usually in conjunction with
Montage M88DS3103 DVB-S/S2 demodulator.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/Kconfig          |   7 +
 drivers/media/tuners/Makefile         |   1 +
 drivers/media/tuners/m88ts2022.c      | 664 ++++++++++++++++++++++++++++++++++
 drivers/media/tuners/m88ts2022.h      |  72 ++++
 drivers/media/tuners/m88ts2022_priv.h |  38 ++
 5 files changed, 782 insertions(+)
 create mode 100644 drivers/media/tuners/m88ts2022.c
 create mode 100644 drivers/media/tuners/m88ts2022.h
 create mode 100644 drivers/media/tuners/m88ts2022_priv.h

diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
index 15665de..ba2e365 100644
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -215,6 +215,13 @@ config MEDIA_TUNER_FC2580
 	help
 	  FCI FC2580 silicon tuner driver.
 
+config MEDIA_TUNER_M88TS2022
+	tristate "Montage M88TS2022 silicon tuner"
+	depends on MEDIA_SUPPORT && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Montage M88TS2022 silicon tuner driver.
+
 config MEDIA_TUNER_TUA9001
 	tristate "Infineon TUA 9001 silicon tuner"
 	depends on MEDIA_SUPPORT && I2C
diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile
index 308f108..efe82a9 100644
--- a/drivers/media/tuners/Makefile
+++ b/drivers/media/tuners/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
 obj-$(CONFIG_MEDIA_TUNER_E4000) += e4000.o
 obj-$(CONFIG_MEDIA_TUNER_FC2580) += fc2580.o
 obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o
+obj-$(CONFIG_MEDIA_TUNER_M88TS2022) += m88ts2022.o
 obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
 obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
 obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o
diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c
new file mode 100644
index 0000000..0625e36
--- /dev/null
+++ b/drivers/media/tuners/m88ts2022.c
@@ -0,0 +1,664 @@
+/*
+ * Montage M88TS2022 silicon tuner driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Some calculations are taken from existing TS2020 driver.
+ */
+
+#include "m88ts2022_priv.h"
+
+/* write multiple registers */
+static int m88ts2022_wr_regs(struct m88ts2022_priv *priv,
+		u8 reg, const u8 *val, int len)
+{
+	int ret;
+	u8 buf[1 + len];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = priv->cfg->i2c_addr,
+			.flags = 0,
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
+
+	buf[0] = reg;
+	memcpy(&buf[1], val, len);
+
+	ret = i2c_transfer(priv->i2c, msg, 1);
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		dev_warn(&priv->i2c->dev,
+				"%s: i2c wr failed=%d reg=%02x len=%d\n",
+				KBUILD_MODNAME, ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+
+	return ret;
+}
+
+/* read multiple registers */
+static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg,
+		u8 *val, int len)
+{
+	int ret;
+	u8 buf[len];
+	struct i2c_msg msg[2] = {
+		{
+			.addr = priv->cfg->i2c_addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		}, {
+			.addr = priv->cfg->i2c_addr,
+			.flags = I2C_M_RD,
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
+
+	ret = i2c_transfer(priv->i2c, msg, 2);
+	if (ret == 2) {
+		memcpy(val, buf, len);
+		ret = 0;
+	} else {
+		dev_warn(&priv->i2c->dev,
+				"%s: i2c rd failed=%d reg=%02x len=%d\n",
+				KBUILD_MODNAME, ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+
+	return ret;
+}
+
+/* write single register */
+static int m88ts2022_wr_reg(struct m88ts2022_priv *priv, u8 reg, u8 val)
+{
+	return m88ts2022_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int m88ts2022_rd_reg(struct m88ts2022_priv *priv, u8 reg, u8 *val)
+{
+	return m88ts2022_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+static int m88ts2022_wr_reg_mask(struct m88ts2022_priv *priv,
+		u8 reg, u8 val, u8 mask)
+{
+	int ret;
+	u8 u8tmp;
+
+	/* no need for read if whole reg is written */
+	if (mask != 0xff) {
+		ret = m88ts2022_rd_regs(priv, reg, &u8tmp, 1);
+		if (ret)
+			return ret;
+
+		val &= mask;
+		u8tmp &= ~mask;
+		val |= u8tmp;
+	}
+
+	return m88ts2022_wr_regs(priv, reg, &val, 1);
+}
+
+static int m88ts2022_cmd(struct dvb_frontend *fe,
+		int op, int sleep, u8 reg, u8 mask, u8 val, u8 *reg_val)
+{
+	struct m88ts2022_priv *priv = fe->tuner_priv;
+	int ret, i;
+	u8 u8tmp;
+	struct m88ts2022_reg_val reg_vals[] = {
+		{0x51, 0x1f - op},
+		{0x51, 0x1f},
+		{0x50, 0x00 + op},
+		{0x50, 0x00},
+	};
+
+	for (i = 0; i < 2; i++) {
+		dev_dbg(&priv->i2c->dev,
+				"%s: i=%d op=%02x reg=%02x mask=%02x val=%02x\n",
+				__func__, i, op, reg, mask, val);
+
+		for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
+			ret = m88ts2022_wr_reg(priv, reg_vals[i].reg,
+					reg_vals[i].val);
+			if (ret)
+				goto err;
+		}
+
+		usleep_range(sleep * 1000, sleep * 10000);
+
+		ret = m88ts2022_rd_reg(priv, reg, &u8tmp);
+		if (ret)
+			goto err;
+
+		if ((u8tmp & mask) != val)
+			break;
+	}
+
+	if (reg_val)
+		*reg_val = u8tmp;
+err:
+	return ret;
+}
+
+static int m88ts2022_set_params(struct dvb_frontend *fe)
+{
+	struct m88ts2022_priv *priv = fe->tuner_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret = 0, div;
+	u8 buf[3], u8tmp, cap_code, lpf_mxdiv, div_max, div_min;
+	u16 N_reg, N, K;
+	u32 lpf_gm, lpf_coeff, gdiv28, frequency_khz, frequency_offset;
+	u32 freq_3db;
+	dev_dbg(&priv->i2c->dev,
+			"%s: frequency=%d symbol_rate=%d rolloff=%d\n",
+			__func__, c->frequency, c->symbol_rate, c->rolloff);
+
+	if (c->symbol_rate < 5000000)
+		frequency_offset = 3000000; /* 3 MHz */
+	else
+		frequency_offset = 0;
+
+	frequency_khz = c->frequency + (frequency_offset / 1000);
+
+	if (frequency_khz < 1103000) {
+		div = 2;
+		u8tmp = 0x1b;
+	} else {
+		div = 1;
+		u8tmp = 0x0b;
+	}
+
+	buf[0] = u8tmp;
+	buf[1] = 0x40;
+	ret = m88ts2022_wr_regs(priv, 0x10, buf, 2);
+	if (ret)
+		goto err;
+
+	K = DIV_ROUND_CLOSEST((priv->cfg->clock / 2), 1000000);
+	N = 1ul * frequency_khz * K * div * 2 / (priv->cfg->clock / 1000);
+	N += N % 2;
+
+	if (N < 4095)
+		N_reg = N - 1024;
+	else if (N < 6143)
+		N_reg = N + 1024;
+	else
+		N_reg = N + 3072;
+
+	buf[0] = (N_reg >> 8) & 0x3f;
+	buf[1] = (N_reg >> 0) & 0xff;
+	buf[2] = K - 8;
+	ret = m88ts2022_wr_regs(priv, 0x01, buf, 3);
+	if (ret)
+		goto err;
+
+	priv->frequency_khz = 1ul * N * (priv->cfg->clock / 1000) / K / div / 2;
+
+	dev_dbg(&priv->i2c->dev,
+			"%s: frequency=%d offset=%d K=%d N=%d div=%d\n",
+			__func__, priv->frequency_khz,
+			priv->frequency_khz - c->frequency, K, N, div);
+
+	ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp);
+	if (ret)
+		goto err;
+
+	u8tmp &= 0x7f;
+	if (u8tmp < 64) {
+		ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x80, 0x80);
+		if (ret)
+			goto err;
+
+		ret = m88ts2022_wr_reg(priv, 0x11, 0x6f);
+		if (ret)
+			goto err;
+
+		ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL);
+		if (ret)
+			goto err;
+	}
+
+	ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp);
+	if (ret)
+		goto err;
+
+	u8tmp &= 0x1f;
+	if (u8tmp > 19) {
+		ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x00, 0x02);
+		if (ret)
+			goto err;
+	}
+
+	ret = m88ts2022_cmd(fe, 0x08, 5, 0x3c, 0xff, 0x00, NULL);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_wr_reg(priv, 0x25, 0x00);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_wr_reg(priv, 0x27, 0x70);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_wr_reg(priv, 0x41, 0x09);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_wr_reg(priv, 0x08, 0x0b);
+	if (ret)
+		goto err;
+
+	gdiv28 = DIV_ROUND_CLOSEST(priv->cfg->clock / 1000000 * 1694, 1000);
+
+	ret = m88ts2022_wr_reg(priv, 0x04, gdiv28);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
+	if (ret)
+		goto err;
+
+	cap_code = u8tmp & 0x3f;
+
+	ret = m88ts2022_wr_reg(priv, 0x41, 0x0d);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
+	if (ret)
+		goto err;
+
+	u8tmp &= 0x3f;
+	cap_code = (cap_code + u8tmp) / 2;
+	gdiv28 = gdiv28 * 207 / (cap_code * 2 + 151);
+	div_max = gdiv28 * 135 / 100;
+	div_min = gdiv28 * 78 / 100;
+	if (div_max > 63)
+		div_max = 63;
+
+	freq_3db = 1ul * c->symbol_rate * 135 / 200 + 2000000;
+	freq_3db += frequency_offset;
+	if (freq_3db < 7000000)
+		freq_3db = 7000000;
+	if (freq_3db > 40000000)
+		freq_3db = 40000000;
+
+	lpf_coeff = 3200;
+	lpf_gm = DIV_ROUND_CLOSEST(freq_3db * gdiv28, lpf_coeff *
+			(priv->cfg->clock / 1000));
+	if (lpf_gm > 23)
+		lpf_gm = 23;
+	if (lpf_gm < 1)
+		lpf_gm = 1;
+
+	lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * lpf_coeff *
+			(priv->cfg->clock / 1000), freq_3db);
+
+	if (lpf_mxdiv < div_min) {
+		lpf_gm++;
+		lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * lpf_coeff *
+				(priv->cfg->clock / 1000), freq_3db);
+	}
+
+	if (lpf_mxdiv > div_max)
+		lpf_mxdiv = div_max;
+
+	ret = m88ts2022_wr_reg(priv, 0x04, lpf_mxdiv);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_wr_reg(priv, 0x06, lpf_gm);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
+	if (ret)
+		goto err;
+
+	cap_code = u8tmp & 0x3f;
+
+	ret = m88ts2022_wr_reg(priv, 0x41, 0x09);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp);
+	if (ret)
+		goto err;
+
+	u8tmp &= 0x3f;
+	cap_code = (cap_code + u8tmp) / 2;
+
+	u8tmp = cap_code | 0x80;
+	ret = m88ts2022_wr_reg(priv, 0x25, u8tmp);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_wr_reg(priv, 0x27, 0x30);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_wr_reg(priv, 0x08, 0x09);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_cmd(fe, 0x01, 20, 0x21, 0xff, 0x00, NULL);
+	if (ret)
+		goto err;
+err:
+	if (ret)
+		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int m88ts2022_init(struct dvb_frontend *fe)
+{
+	struct m88ts2022_priv *priv = fe->tuner_priv;
+	int ret, i;
+	u8 u8tmp;
+	static const struct m88ts2022_reg_val reg_vals[] = {
+		{0x7d, 0x9d},
+		{0x7c, 0x9a},
+		{0x7a, 0x76},
+		{0x3b, 0x01},
+		{0x63, 0x88},
+		{0x61, 0x85},
+		{0x22, 0x30},
+		{0x30, 0x40},
+		{0x20, 0x23},
+		{0x24, 0x02},
+		{0x12, 0xa0},
+	};
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+	ret = m88ts2022_wr_reg(priv, 0x00, 0x01);
+	if (ret)
+		goto err;
+
+	ret = m88ts2022_wr_reg(priv, 0x00, 0x03);
+	if (ret)
+		goto err;
+
+	switch (priv->cfg->clock_out) {
+	case M88TS2022_CLOCK_OUT_DISABLED:
+		u8tmp = 0x60;
+		break;
+	case M88TS2022_CLOCK_OUT_ENABLED:
+		u8tmp = 0x70;
+		ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg->clock_out_div);
+		if (ret)
+			goto err;
+		break;
+	case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT:
+		u8tmp = 0x6c;
+		break;
+	default:
+		goto err;
+	}
+
+	ret = m88ts2022_wr_reg(priv, 0x42, u8tmp);
+	if (ret)
+		goto err;
+
+	if (priv->cfg->loop_through)
+		u8tmp = 0xec;
+	else
+		u8tmp = 0x6c;
+
+	ret = m88ts2022_wr_reg(priv, 0x62, u8tmp);
+	if (ret)
+		goto err;
+
+	for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
+		ret = m88ts2022_wr_reg(priv, reg_vals[i].reg, reg_vals[i].val);
+		if (ret)
+			goto err;
+	}
+err:
+	if (ret)
+		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ts2022_sleep(struct dvb_frontend *fe)
+{
+	struct m88ts2022_priv *priv = fe->tuner_priv;
+	int ret;
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+	ret = m88ts2022_wr_reg(priv, 0x00, 0x00);
+	if (ret)
+		goto err;
+err:
+	if (ret)
+		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct m88ts2022_priv *priv = fe->tuner_priv;
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+	*frequency = priv->frequency_khz;
+	return 0;
+}
+
+static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct m88ts2022_priv *priv = fe->tuner_priv;
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+	*frequency = 0; /* Zero-IF */
+	return 0;
+}
+
+static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct m88ts2022_priv *priv = fe->tuner_priv;
+	u8  u8tmp, gain1, gain2, gain3;
+	u16 gain, u16tmp;
+	int ret;
+
+	ret = m88ts2022_rd_reg(priv, 0x3d, &u8tmp);
+	if (ret)
+		goto err;
+
+	gain1 = (u8tmp >> 0) & 0x1f;
+	if (gain1 > 15)
+		gain1 = 15;
+
+	ret = m88ts2022_rd_reg(priv, 0x21, &u8tmp);
+	if (ret)
+		goto err;
+
+	gain2 = (u8tmp >> 0) & 0x1f;
+	if (gain2 < 2)
+		gain2 = 2;
+	if (gain2 > 16)
+		gain2 = 16;
+
+	ret = m88ts2022_rd_reg(priv, 0x66, &u8tmp);
+	if (ret)
+		goto err;
+
+	gain3 = (u8tmp >> 3) & 0x07;
+	if (gain3 > 6)
+		gain3 = 6;
+
+	gain = gain1 * 265 + gain2 * 338 + gain3 * 285;
+
+	/* scale value to 0x0000-0xffff */
+	u16tmp = (0xffff - gain);
+	if (u16tmp < 59000)
+		u16tmp = 59000;
+	else if (u16tmp > 61500)
+		u16tmp = 61500;
+
+	*strength = (u16tmp - 59000) * 0xffff / (61500 - 59000);
+err:
+	if (ret)
+		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int m88ts2022_release(struct dvb_frontend *fe)
+{
+	struct m88ts2022_priv *priv = fe->tuner_priv;
+	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+	kfree(fe->tuner_priv);
+	return 0;
+}
+
+static const struct dvb_tuner_ops m88ts2022_tuner_ops = {
+	.info = {
+		.name          = "Montage M88TS2022",
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+	},
+
+	.release = m88ts2022_release,
+
+	.init = m88ts2022_init,
+	.sleep = m88ts2022_sleep,
+	.set_params = m88ts2022_set_params,
+
+	.get_frequency = m88ts2022_get_frequency,
+	.get_if_frequency = m88ts2022_get_if_frequency,
+	.get_rf_strength = m88ts2022_get_rf_strength,
+};
+
+struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
+		struct i2c_adapter *i2c, const struct m88ts2022_config *cfg)
+{
+	struct m88ts2022_priv *priv;
+	int ret;
+	u8 chip_id, u8tmp;
+
+	priv = kzalloc(sizeof(struct m88ts2022_priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+		goto err;
+	}
+
+	priv->cfg = cfg;
+	priv->i2c = i2c;
+	priv->fe = fe;
+
+	/* check if the tuner is there */
+	ret = m88ts2022_rd_reg(priv, 0x00, &u8tmp);
+	if (ret)
+		goto err;
+
+	if ((u8tmp & 0x03) == 0x00) {
+		ret = m88ts2022_wr_reg(priv, 0x00, 0x01);
+		if (ret < 0)
+			goto err;
+
+		usleep_range(2000, 50000);
+	}
+
+	ret = m88ts2022_wr_reg(priv, 0x00, 0x03);
+	if (ret)
+		goto err;
+
+	usleep_range(2000, 50000);
+
+	ret = m88ts2022_rd_reg(priv, 0x00, &chip_id);
+	if (ret)
+		goto err;
+
+	dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+
+	switch (chip_id) {
+	case 0xc3:
+	case 0x83:
+		break;
+	default:
+		goto err;
+	}
+
+	switch (priv->cfg->clock_out) {
+	case M88TS2022_CLOCK_OUT_DISABLED:
+		u8tmp = 0x60;
+		break;
+	case M88TS2022_CLOCK_OUT_ENABLED:
+		u8tmp = 0x70;
+		ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg->clock_out_div);
+		if (ret)
+			goto err;
+		break;
+	case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT:
+		u8tmp = 0x6c;
+		break;
+	default:
+		goto err;
+	}
+
+	ret = m88ts2022_wr_reg(priv, 0x42, u8tmp);
+	if (ret)
+		goto err;
+
+	if (priv->cfg->loop_through)
+		u8tmp = 0xec;
+	else
+		u8tmp = 0x6c;
+
+	ret = m88ts2022_wr_reg(priv, 0x62, u8tmp);
+	if (ret)
+		goto err;
+
+	/* sleep */
+	ret = m88ts2022_wr_reg(priv, 0x00, 0x00);
+	if (ret)
+		goto err;
+
+	dev_info(&priv->i2c->dev,
+			"%s: Montage M88TS2022 successfully identified\n",
+			KBUILD_MODNAME);
+
+	fe->tuner_priv = priv;
+	memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops,
+			sizeof(struct dvb_tuner_ops));
+err:
+	if (ret) {
+		dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+		kfree(priv);
+		return NULL;
+	}
+
+	return fe;
+}
+EXPORT_SYMBOL(m88ts2022_attach);
+
+MODULE_DESCRIPTION("Montage M88TS2022 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/m88ts2022.h b/drivers/media/tuners/m88ts2022.h
new file mode 100644
index 0000000..fa1112c
--- /dev/null
+++ b/drivers/media/tuners/m88ts2022.h
@@ -0,0 +1,72 @@
+/*
+ * Montage M88TS2022 silicon tuner driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef M88TS2022_H
+#define M88TS2022_H
+
+#include "dvb_frontend.h"
+
+struct m88ts2022_config {
+	/*
+	 * I2C address
+	 * 0x60, ...
+	 */
+	u8 i2c_addr;
+
+	/*
+	 * clock
+	 * 16000000 - 32000000
+	 */
+	u32 clock;
+
+	/*
+	 * RF loop-through
+	 */
+	u8 loop_through:1;
+
+	/*
+	 * clock output
+	 */
+#define M88TS2022_CLOCK_OUT_DISABLED        0
+#define M88TS2022_CLOCK_OUT_ENABLED         1
+#define M88TS2022_CLOCK_OUT_ENABLED_XTALOUT 2
+	u8 clock_out:2;
+
+	/*
+	 * clock output divider
+	 * 1 - 31
+	 */
+	u8 clock_out_div:5;
+};
+
+#if defined(CONFIG_MEDIA_TUNER_M88TS2022) || \
+	(defined(CONFIG_MEDIA_TUNER_M88TS2022_MODULE) && defined(MODULE))
+extern struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, const struct m88ts2022_config *cfg);
+#else
+static inline struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, const struct m88ts2022_config *cfg)
+{
+	pr_warn("%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h
new file mode 100644
index 0000000..190299a
--- /dev/null
+++ b/drivers/media/tuners/m88ts2022_priv.h
@@ -0,0 +1,38 @@
+/*
+ * Montage M88TS2022 silicon tuner driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef M88TS2022_PRIV_H
+#define M88TS2022_PRIV_H
+
+#include "m88ts2022.h"
+
+struct m88ts2022_priv {
+	const struct m88ts2022_config *cfg;
+	struct i2c_adapter *i2c;
+	struct dvb_frontend *fe;
+	u32 frequency_khz;
+};
+
+struct m88ts2022_reg_val {
+	u8 reg;
+	u8 val;
+};
+
+#endif
-- 
1.8.4.2


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

* [PATCH REVIEW 05/18] em28xx: add support for PCTV DVB-S2 Stick (461e) [2013:0258]
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (3 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 04/18] Montage M88TS2022 silicon tuner driver Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 06/18] MAINTAINERS: add M88DS3103 Antti Palosaari
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Device has following chips: Empia EM28178, Montage M88DS3103,
Montage M88TS2022, Allegro A8293.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/usb/em28xx/Kconfig        |  2 ++
 drivers/media/usb/em28xx/em28xx-cards.c | 35 ++++++++++++++++++++++
 drivers/media/usb/em28xx/em28xx-dvb.c   | 53 +++++++++++++++++++++++++++++++++
 drivers/media/usb/em28xx/em28xx.h       |  1 +
 4 files changed, 91 insertions(+)

diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig
index ca5ee6a..d6ba514 100644
--- a/drivers/media/usb/em28xx/Kconfig
+++ b/drivers/media/usb/em28xx/Kconfig
@@ -49,6 +49,8 @@ config VIDEO_EM28XX_DVB
 	select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
 	---help---
 	  This adds support for DVB cards based on the
 	  Empiatech em28xx chips.
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 62332a6..4db5eab 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -356,6 +356,28 @@ static struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
 	{	-1,			-1,	-1,	-1},
 };
 
+/*
+ * 2013:0258 PCTV DVB-S2 Stick (461e)
+ * GPIO 0 = POWER_ON
+ * GPIO 1 = BOOST
+ * GPIO 2 = VUV_LNB (red LED)
+ * GPIO 3 = #EXT_12V
+ * GPIO 4 = INT_DEM
+ * GPIO 5 = INT_LNB
+ * GPIO 6 = #RESET_DEM
+ * GPIO 7 = P07_LED (green LED)
+ */
+static struct em28xx_reg_seq pctv_461e[] = {
+	{EM2874_R80_GPIO_P0_CTRL,      0x7f, 0xff,    0},
+	{0x0d,                 0xff, 0xff,    0},
+	{EM2874_R80_GPIO_P0_CTRL,      0x3f, 0xff,  100}, /* reset demod */
+	{EM2874_R80_GPIO_P0_CTRL,      0x7f, 0xff,  200}, /* reset demod */
+	{0x0d,                 0x42, 0xff,    0},
+	{EM2874_R80_GPIO_P0_CTRL,      0xeb, 0xff,    0},
+	{EM2874_R5F_TS_ENABLE, 0x84, 0x84,    0}, /* parallel? | null discard */
+	{                  -1,   -1,   -1,   -1},
+};
+
 #if 0
 static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
 	{EM2874_R80_GPIO_P0_CTRL,	0x6f,	0xff,	10},
@@ -2043,6 +2065,17 @@ struct em28xx_board em28xx_boards[] = {
 		.tuner_gpio	= default_tuner_gpio,
 		.def_i2c_bus	= 1,
 	},
+	/* 2013:0258 PCTV DVB-S2 Stick (461e)
+	 * Empia EM28178, Montage M88DS3103, Montage M88TS2022, Allegro A8293 */
+	[EM28178_BOARD_PCTV_461E] = {
+		.def_i2c_bus   = 1,
+		.i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+		.name          = "PCTV DVB-S2 Stick (461e)",
+		.tuner_type    = TUNER_ABSENT,
+		.tuner_gpio    = pctv_461e,
+		.has_dvb       = 1,
+		.ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+	},
 };
 const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
 
@@ -2208,6 +2241,8 @@ struct usb_device_id em28xx_id_table[] = {
 			.driver_info = EM2884_BOARD_PCTV_520E },
 	{ USB_DEVICE(0x1b80, 0xe1cc),
 			.driver_info = EM2874_BOARD_DELOCK_61959 },
+	{ USB_DEVICE(0x2013, 0x0258),
+			.driver_info = EM28178_BOARD_PCTV_461E },
 	{ },
 };
 MODULE_DEVICE_TABLE(usb, em28xx_id_table);
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 344042b..4f8f687 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -51,6 +51,8 @@
 #include "a8293.h"
 #include "qt1010.h"
 #include "mb86a20s.h"
+#include "m88ds3103.h"
+#include "m88ts2022.h"
 
 MODULE_DESCRIPTION("driver for em28xx based DVB cards");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -808,6 +810,19 @@ static struct tda18271_config c3tech_duo_tda18271_config = {
 	.small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
 };
 
+static const struct m88ds3103_config pctv_461e_m88ds3103_config = {
+	.i2c_addr = 0x68,
+	.clock = 27000000,
+	.i2c_wr_max = 33,
+	.clock_out = 0,
+	.ts_mode = M88DS3103_TS_PARALLEL_16,
+	.agc = 0x99,
+};
+
+static const struct m88ts2022_config em28xx_m88ts2022_config = {
+	.i2c_addr = 0x60,
+	.clock = 27000000,
+};
 
 /* ------------------------------------------------------------------ */
 
@@ -1330,6 +1345,44 @@ static int em28xx_dvb_init(struct em28xx *dev)
 			goto out_free;
 		}
 		break;
+	case EM28178_BOARD_PCTV_461E:
+		{
+			/* demod I2C adapter */
+			struct i2c_adapter *i2c_adapter;
+
+			/* attach demod */
+			dvb->fe[0] = dvb_attach(m88ds3103_attach,
+					&pctv_461e_m88ds3103_config,
+					&dev->i2c_adap[dev->def_i2c_bus],
+					&i2c_adapter);
+			if (dvb->fe[0] == NULL) {
+				result = -ENODEV;
+				goto out_free;
+			}
+
+			/* attach tuner */
+			if (!dvb_attach(m88ts2022_attach, dvb->fe[0],
+					i2c_adapter,
+					&em28xx_m88ts2022_config)) {
+				dvb_frontend_detach(dvb->fe[0]);
+				result = -ENODEV;
+				goto out_free;
+			}
+
+			/* delegate signal strength measurement to tuner */
+			dvb->fe[0]->ops.read_signal_strength =
+					dvb->fe[0]->ops.tuner_ops.get_rf_strength;
+
+			/* attach SEC */
+			if (!dvb_attach(a8293_attach, dvb->fe[0],
+					&dev->i2c_adap[dev->def_i2c_bus],
+					&em28xx_a8293_config)) {
+				dvb_frontend_detach(dvb->fe[0]);
+				result = -ENODEV;
+				goto out_free;
+			}
+		}
+		break;
 	default:
 		em28xx_errdev("/2: The frontend of your DVB/ATSC card"
 				" isn't supported yet\n");
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index f8726ad..ea058be 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -132,6 +132,7 @@
 #define EM2884_BOARD_C3TECH_DIGITAL_DUO		  88
 #define EM2874_BOARD_DELOCK_61959		  89
 #define EM2874_BOARD_KWORLD_UB435Q_V2		  90
+#define EM28178_BOARD_PCTV_461E                   91
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
-- 
1.8.4.2


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

* [PATCH REVIEW 06/18] MAINTAINERS: add M88DS3103
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (4 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 05/18] em28xx: add support for PCTV DVB-S2 Stick (461e) [2013:0258] Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 07/18] MAINTAINERS: add M88TS2022 Antti Palosaari
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

It is Montage M88DS3103 DVB-S/S2 demodulator driver.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 MAINTAINERS | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8285ed4..0604247 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5347,6 +5347,16 @@ W:	http://www.tazenda.demon.co.uk/phil/linux-hp
 S:	Maintained
 F:	arch/m68k/hp300/
 
+M88DS3103 MEDIA DRIVER
+M:	Antti Palosaari <crope@iki.fi>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+W:	http://palosaari.fi/linux/
+Q:	http://patchwork.linuxtv.org/project/linux-media/list/
+T:	git git://linuxtv.org/anttip/media_tree.git
+S:	Maintained
+F:	drivers/media/dvb-frontends/m88ds3103*
+
 M88RS2000 MEDIA DRIVER
 M:	Malcolm Priestley <tvboxspy@gmail.com>
 L:	linux-media@vger.kernel.org
-- 
1.8.4.2


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

* [PATCH REVIEW 07/18] MAINTAINERS: add M88TS2022
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (5 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 06/18] MAINTAINERS: add M88DS3103 Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 08/18] m88ts2022: do not use dynamic stack allocation Antti Palosaari
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

It is Montage M88TS2022 DVB-S/S2 silicon tuner driver.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 MAINTAINERS | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0604247..16661e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5365,6 +5365,16 @@ Q:	http://patchwork.linuxtv.org/project/linux-media/list/
 S:	Maintained
 F:	drivers/media/dvb-frontends/m88rs2000*
 
+M88TS2022 MEDIA DRIVER
+M:	Antti Palosaari <crope@iki.fi>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+W:	http://palosaari.fi/linux/
+Q:	http://patchwork.linuxtv.org/project/linux-media/list/
+T:	git git://linuxtv.org/anttip/media_tree.git
+S:	Maintained
+F:	drivers/media/tuners/m88ts2022*
+
 MA901 MASTERKIT USB FM RADIO DRIVER
 M:      Alexey Klimov <klimov.linux@gmail.com>
 L:      linux-media@vger.kernel.org
-- 
1.8.4.2


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

* [PATCH REVIEW 08/18] m88ts2022: do not use dynamic stack allocation
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (6 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 07/18] MAINTAINERS: add M88TS2022 Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 09/18] m88ds3103: " Antti Palosaari
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

I2C transfer were using dynamic stack allocation. Get rid of it.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/m88ts2022.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c
index 0625e36..4b3eec2 100644
--- a/drivers/media/tuners/m88ts2022.c
+++ b/drivers/media/tuners/m88ts2022.c
@@ -26,17 +26,22 @@
 static int m88ts2022_wr_regs(struct m88ts2022_priv *priv,
 		u8 reg, const u8 *val, int len)
 {
+#define MAX_WR_LEN 3
+#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1)
 	int ret;
-	u8 buf[1 + len];
+	u8 buf[MAX_WR_XFER_LEN];
 	struct i2c_msg msg[1] = {
 		{
 			.addr = priv->cfg->i2c_addr,
 			.flags = 0,
-			.len = sizeof(buf),
+			.len = 1 + len,
 			.buf = buf,
 		}
 	};
 
+	if (WARN_ON(len > MAX_WR_LEN))
+		return -EINVAL;
+
 	buf[0] = reg;
 	memcpy(&buf[1], val, len);
 
@@ -57,8 +62,10 @@ static int m88ts2022_wr_regs(struct m88ts2022_priv *priv,
 static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg,
 		u8 *val, int len)
 {
+#define MAX_RD_LEN 1
+#define MAX_RD_XFER_LEN (MAX_RD_LEN)
 	int ret;
-	u8 buf[len];
+	u8 buf[MAX_RD_XFER_LEN];
 	struct i2c_msg msg[2] = {
 		{
 			.addr = priv->cfg->i2c_addr,
@@ -68,11 +75,14 @@ static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg,
 		}, {
 			.addr = priv->cfg->i2c_addr,
 			.flags = I2C_M_RD,
-			.len = sizeof(buf),
+			.len = len,
 			.buf = buf,
 		}
 	};
 
+	if (WARN_ON(len > MAX_RD_LEN))
+		return -EINVAL;
+
 	ret = i2c_transfer(priv->i2c, msg, 2);
 	if (ret == 2) {
 		memcpy(val, buf, len);
-- 
1.8.4.2


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

* [PATCH REVIEW 09/18] m88ds3103: do not use dynamic stack allocation
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (7 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 08/18] m88ts2022: do not use dynamic stack allocation Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 10/18] m88ds3103: use I2C mux for tuner I2C adapter Antti Palosaari
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

I2C transfer were using dynamic stack allocation. Get rid of it.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/m88ds3103.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index 91b3729..302c923 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -26,17 +26,22 @@ static struct dvb_frontend_ops m88ds3103_ops;
 static int m88ds3103_wr_regs(struct m88ds3103_priv *priv,
 		u8 reg, const u8 *val, int len)
 {
+#define MAX_WR_LEN 32
+#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1)
 	int ret;
-	u8 buf[1 + len];
+	u8 buf[MAX_WR_XFER_LEN];
 	struct i2c_msg msg[1] = {
 		{
 			.addr = priv->cfg->i2c_addr,
 			.flags = 0,
-			.len = sizeof(buf),
+			.len = 1 + len,
 			.buf = buf,
 		}
 	};
 
+	if (WARN_ON(len > MAX_WR_LEN))
+		return -EINVAL;
+
 	buf[0] = reg;
 	memcpy(&buf[1], val, len);
 
@@ -59,8 +64,10 @@ static int m88ds3103_wr_regs(struct m88ds3103_priv *priv,
 static int m88ds3103_rd_regs(struct m88ds3103_priv *priv,
 		u8 reg, u8 *val, int len)
 {
+#define MAX_RD_LEN 3
+#define MAX_RD_XFER_LEN (MAX_RD_LEN)
 	int ret;
-	u8 buf[len];
+	u8 buf[MAX_RD_XFER_LEN];
 	struct i2c_msg msg[2] = {
 		{
 			.addr = priv->cfg->i2c_addr,
@@ -70,11 +77,14 @@ static int m88ds3103_rd_regs(struct m88ds3103_priv *priv,
 		}, {
 			.addr = priv->cfg->i2c_addr,
 			.flags = I2C_M_RD,
-			.len = sizeof(buf),
+			.len = len,
 			.buf = buf,
 		}
 	};
 
+	if (WARN_ON(len > MAX_RD_LEN))
+		return -EINVAL;
+
 	mutex_lock(&priv->i2c_mutex);
 	ret = i2c_transfer(priv->i2c, msg, 2);
 	mutex_unlock(&priv->i2c_mutex);
-- 
1.8.4.2


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

* [PATCH REVIEW 10/18] m88ds3103: use I2C mux for tuner I2C adapter
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (8 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 09/18] m88ds3103: " Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 11/18] m88ds3103: use kernel macro to round division Antti Palosaari
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari, Jean Delvare

Switch standard I2C adapter to muxed I2C adapter.

David reported that I2C adapter implementation caused deadlock.
I discussed with Jean and he suggested to implement it as a
multiplexed i2c adapter because tuner I2C bus could be seen like
own I2C segment.

Reported-by: David Howells <dhowells@redhat.com>
Cc: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/Kconfig          |  2 +-
 drivers/media/dvb-frontends/m88ds3103.c      | 73 +++++++++++-----------------
 drivers/media/dvb-frontends/m88ds3103_priv.h |  3 +-
 3 files changed, 31 insertions(+), 47 deletions(-)

diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 6c46caf..dd12a1e 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -37,7 +37,7 @@ config DVB_STV6110x
 
 config DVB_M88DS3103
 	tristate "Montage M88DS3103"
-	depends on DVB_CORE && I2C
+	depends on DVB_CORE && I2C && I2C_MUX
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Say Y when you want to support this frontend.
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index 302c923..e07e8d6 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -1108,15 +1108,16 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe,
 	return 0;
 }
 
-static u32 m88ds3103_tuner_i2c_func(struct i2c_adapter *adapter)
+static void m88ds3103_release(struct dvb_frontend *fe)
 {
-	return I2C_FUNC_I2C;
+	struct m88ds3103_priv *priv = fe->demodulator_priv;
+	i2c_del_mux_adapter(priv->i2c_adapter);
+	kfree(priv);
 }
 
-static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
-		struct i2c_msg msg[], int num)
+static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
 {
-	struct m88ds3103_priv *priv = i2c_get_adapdata(i2c_adap);
+	struct m88ds3103_priv *priv = mux_priv;
 	int ret;
 	struct i2c_msg gate_open_msg[1] = {
 		{
@@ -1126,43 +1127,31 @@ static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
 			.buf = "\x03\x11",
 		}
 	};
-	dev_dbg(&priv->i2c->dev, "%s: num=%d\n", __func__, num);
 
 	mutex_lock(&priv->i2c_mutex);
 
-	/* open i2c-gate */
+	/* open tuner I2C repeater for 1 xfer, closes automatically */
 	ret = i2c_transfer(priv->i2c, gate_open_msg, 1);
 	if (ret != 1) {
-		mutex_unlock(&priv->i2c_mutex);
-		dev_warn(&priv->i2c->dev,
-				"%s: i2c wr failed=%d\n",
+		dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d\n",
 				KBUILD_MODNAME, ret);
-		ret = -EREMOTEIO;
-		goto err;
-	}
+		if (ret >= 0)
+			ret = -EREMOTEIO;
 
-	ret = i2c_transfer(priv->i2c, msg, num);
-	mutex_unlock(&priv->i2c_mutex);
-	if (ret < 0)
-		dev_warn(&priv->i2c->dev, "%s: i2c failed=%d\n",
-				KBUILD_MODNAME, ret);
+		return ret;
+	}
 
-	return ret;
-err:
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
-	return ret;
+	return 0;
 }
 
-static struct i2c_algorithm m88ds3103_tuner_i2c_algo = {
-	.master_xfer   = m88ds3103_tuner_i2c_xfer,
-	.functionality = m88ds3103_tuner_i2c_func,
-};
-
-static void m88ds3103_release(struct dvb_frontend *fe)
+static int m88ds3103_deselect(struct i2c_adapter *adap, void *mux_priv,
+		u32 chan)
 {
-	struct m88ds3103_priv *priv = fe->demodulator_priv;
-	i2c_del_adapter(&priv->i2c_adapter);
-	kfree(priv);
+	struct m88ds3103_priv *priv = mux_priv;
+
+	mutex_unlock(&priv->i2c_mutex);
+
+	return 0;
 }
 
 struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
@@ -1228,24 +1217,18 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
 	if (ret)
 		goto err;
 
+	/* create mux i2c adapter for tuner */
+	priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0,
+			m88ds3103_select, m88ds3103_deselect);
+	if (priv->i2c_adapter == NULL)
+		goto err;
+
+	*tuner_i2c_adapter = priv->i2c_adapter;
+
 	/* create dvb_frontend */
 	memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
 	priv->fe.demodulator_priv = priv;
 
-	/* create i2c adapter for tuner */
-	strlcpy(priv->i2c_adapter.name, KBUILD_MODNAME,
-			sizeof(priv->i2c_adapter.name));
-	priv->i2c_adapter.algo = &m88ds3103_tuner_i2c_algo;
-	priv->i2c_adapter.algo_data = NULL;
-	i2c_set_adapdata(&priv->i2c_adapter, priv);
-	ret = i2c_add_adapter(&priv->i2c_adapter);
-	if (ret) {
-		dev_err(&i2c->dev, "%s: i2c bus could not be initialized\n",
-				KBUILD_MODNAME);
-		goto err;
-	}
-	*tuner_i2c_adapter = &priv->i2c_adapter;
-
 	return &priv->fe;
 err:
 	dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
index f3d0867..322db4d 100644
--- a/drivers/media/dvb-frontends/m88ds3103_priv.h
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -25,6 +25,7 @@
 #include "m88ds3103.h"
 #include "dvb_math.h"
 #include <linux/firmware.h>
+#include <linux/i2c-mux.h>
 
 #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
 #define M88DS3103_MCLK_KHZ 96000
@@ -38,7 +39,7 @@ struct m88ds3103_priv {
 	fe_delivery_system_t delivery_system;
 	fe_status_t fe_status;
 	bool warm; /* FW running */
-	struct i2c_adapter i2c_adapter;
+	struct i2c_adapter *i2c_adapter;
 };
 
 struct m88ds3103_reg_val {
-- 
1.8.4.2


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

* [PATCH REVIEW 11/18] m88ds3103: use kernel macro to round division
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (9 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 10/18] m88ds3103: use I2C mux for tuner I2C adapter Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 12/18] m88ds3103: fix TS mode config Antti Palosaari
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

DIV_ROUND_CLOSEST does the job and looks better.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/m88ds3103.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index e07e8d6..bd9effa 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -460,8 +460,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	u16tmp = (((c->symbol_rate / 1000) << 15) + (M88DS3103_MCLK_KHZ / 4)) /
-			(M88DS3103_MCLK_KHZ / 2);
+	u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, M88DS3103_MCLK_KHZ / 2);
 	buf[0] = (u16tmp >> 0) & 0xff;
 	buf[1] = (u16tmp >> 8) & 0xff;
 	ret = m88ds3103_wr_regs(priv, 0x61, buf, 2);
@@ -484,7 +483,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 			(tuner_frequency - c->frequency));
 
 	s32tmp = 0x10000 * (tuner_frequency - c->frequency);
-	s32tmp = (2 * s32tmp + M88DS3103_MCLK_KHZ) / (2 * M88DS3103_MCLK_KHZ);
+	s32tmp = DIV_ROUND_CLOSEST(s32tmp, M88DS3103_MCLK_KHZ);
 	if (s32tmp < 0)
 		s32tmp += 0x10000;
 
-- 
1.8.4.2


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

* [PATCH REVIEW 12/18] m88ds3103: fix TS mode config
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (10 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 11/18] m88ds3103: use kernel macro to round division Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 13/18] m88ts2022: reimplement synthesizer calculations Antti Palosaari
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

TS mode was configured wrongly.

Reported-by: David Howells <dhowells@redhat.com>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/m88ds3103.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index bd9effa..f9d8967 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -321,32 +321,32 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	case M88DS3103_TS_SERIAL:
 		u8tmp1 = 0x00;
 		ts_clk = 0;
-		u8tmp = 0x04;
+		u8tmp = 0x46;
 		break;
 	case M88DS3103_TS_SERIAL_D7:
 		u8tmp1 = 0x20;
 		ts_clk = 0;
-		u8tmp = 0x04;
+		u8tmp = 0x46;
 		break;
 	case M88DS3103_TS_PARALLEL:
 		ts_clk = 24000;
-		u8tmp = 0x00;
+		u8tmp = 0x42;
 		break;
 	case M88DS3103_TS_PARALLEL_12:
 		ts_clk = 12000;
-		u8tmp = 0x00;
+		u8tmp = 0x42;
 		break;
 	case M88DS3103_TS_PARALLEL_16:
 		ts_clk = 16000;
-		u8tmp = 0x00;
+		u8tmp = 0x42;
 		break;
 	case M88DS3103_TS_PARALLEL_19_2:
 		ts_clk = 19200;
-		u8tmp = 0x00;
+		u8tmp = 0x42;
 		break;
 	case M88DS3103_TS_CI:
 		ts_clk = 6000;
-		u8tmp = 0x01;
+		u8tmp = 0x43;
 		break;
 	default:
 		dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__);
@@ -355,7 +355,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 	}
 
 	/* TS mode */
-	ret = m88ds3103_wr_reg_mask(priv, 0xfd, u8tmp, 0x05);
+	ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp);
 	if (ret)
 		goto err;
 
-- 
1.8.4.2


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

* [PATCH REVIEW 13/18] m88ts2022: reimplement synthesizer calculations
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (11 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 12/18] m88ds3103: fix TS mode config Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 14/18] m88ds3103: remove unneeded AGC from inittab Antti Palosaari
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Used synthesizer is very typical integer-N PLL, with configurable
reference frequency divider, output frequency divider and of
course N itself. Most common method to calculate values is first
select output divider, then calculate VCO frequency and finally
calculate PLL N from VCO frequency. Do it that way.

Also make some cleanups for filter logic and signal strength.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/m88ts2022.c | 121 +++++++++++++++++----------------------
 1 file changed, 53 insertions(+), 68 deletions(-)

diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c
index 4b3eec2..b11e740 100644
--- a/drivers/media/tuners/m88ts2022.c
+++ b/drivers/media/tuners/m88ts2022.c
@@ -175,27 +175,33 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
 {
 	struct m88ts2022_priv *priv = fe->tuner_priv;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-	int ret = 0, div;
-	u8 buf[3], u8tmp, cap_code, lpf_mxdiv, div_max, div_min;
-	u16 N_reg, N, K;
-	u32 lpf_gm, lpf_coeff, gdiv28, frequency_khz, frequency_offset;
-	u32 freq_3db;
+	int ret;
+	unsigned int frequency_khz, frequency_offset_khz, f_3db_hz;
+	unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28;
+	u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min;
+	u16 u16tmp;
 	dev_dbg(&priv->i2c->dev,
 			"%s: frequency=%d symbol_rate=%d rolloff=%d\n",
 			__func__, c->frequency, c->symbol_rate, c->rolloff);
+	/*
+	 * Integer-N PLL synthesizer
+	 * kHz is used for all calculations to keep calculations within 32-bit
+	 */
+	f_ref_khz = DIV_ROUND_CLOSEST(priv->cfg->clock, 1000);
+	div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
 
 	if (c->symbol_rate < 5000000)
-		frequency_offset = 3000000; /* 3 MHz */
+		frequency_offset_khz = 3000; /* 3 MHz */
 	else
-		frequency_offset = 0;
+		frequency_offset_khz = 0;
 
-	frequency_khz = c->frequency + (frequency_offset / 1000);
+	frequency_khz = c->frequency + frequency_offset_khz;
 
 	if (frequency_khz < 1103000) {
-		div = 2;
+		div_out = 4;
 		u8tmp = 0x1b;
 	} else {
-		div = 1;
+		div_out = 2;
 		u8tmp = 0x0b;
 	}
 
@@ -205,30 +211,30 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	K = DIV_ROUND_CLOSEST((priv->cfg->clock / 2), 1000000);
-	N = 1ul * frequency_khz * K * div * 2 / (priv->cfg->clock / 1000);
-	N += N % 2;
+	f_vco_khz = frequency_khz * div_out;
+	pll_n = f_vco_khz * div_ref / f_ref_khz;
+	pll_n += pll_n % 2;
+	priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
 
-	if (N < 4095)
-		N_reg = N - 1024;
-	else if (N < 6143)
-		N_reg = N + 1024;
+	if (pll_n < 4095)
+		u16tmp = pll_n - 1024;
+	else if (pll_n < 6143)
+		u16tmp = pll_n + 1024;
 	else
-		N_reg = N + 3072;
+		u16tmp = pll_n + 3072;
 
-	buf[0] = (N_reg >> 8) & 0x3f;
-	buf[1] = (N_reg >> 0) & 0xff;
-	buf[2] = K - 8;
+	buf[0] = (u16tmp >> 8) & 0x3f;
+	buf[1] = (u16tmp >> 0) & 0xff;
+	buf[2] = div_ref - 8;
 	ret = m88ts2022_wr_regs(priv, 0x01, buf, 3);
 	if (ret)
 		goto err;
 
-	priv->frequency_khz = 1ul * N * (priv->cfg->clock / 1000) / K / div / 2;
-
 	dev_dbg(&priv->i2c->dev,
-			"%s: frequency=%d offset=%d K=%d N=%d div=%d\n",
+			"%s: frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
 			__func__, priv->frequency_khz,
-			priv->frequency_khz - c->frequency, K, N, div);
+			priv->frequency_khz - c->frequency, f_vco_khz, pll_n,
+			div_ref, div_out);
 
 	ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL);
 	if (ret)
@@ -284,7 +290,8 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	gdiv28 = DIV_ROUND_CLOSEST(priv->cfg->clock / 1000000 * 1694, 1000);
+	/* filters */
+	gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U);
 
 	ret = m88ts2022_wr_reg(priv, 0x04, gdiv28);
 	if (ret)
@@ -309,35 +316,20 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
 	gdiv28 = gdiv28 * 207 / (cap_code * 2 + 151);
 	div_max = gdiv28 * 135 / 100;
 	div_min = gdiv28 * 78 / 100;
-	if (div_max > 63)
-		div_max = 63;
-
-	freq_3db = 1ul * c->symbol_rate * 135 / 200 + 2000000;
-	freq_3db += frequency_offset;
-	if (freq_3db < 7000000)
-		freq_3db = 7000000;
-	if (freq_3db > 40000000)
-		freq_3db = 40000000;
-
-	lpf_coeff = 3200;
-	lpf_gm = DIV_ROUND_CLOSEST(freq_3db * gdiv28, lpf_coeff *
-			(priv->cfg->clock / 1000));
-	if (lpf_gm > 23)
-		lpf_gm = 23;
-	if (lpf_gm < 1)
-		lpf_gm = 1;
-
-	lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * lpf_coeff *
-			(priv->cfg->clock / 1000), freq_3db);
-
-	if (lpf_mxdiv < div_min) {
-		lpf_gm++;
-		lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * lpf_coeff *
-				(priv->cfg->clock / 1000), freq_3db);
-	}
+	div_max = clamp_val(div_max, 0U, 63U);
+
+	f_3db_hz = c->symbol_rate * 135UL / 200UL;
+	f_3db_hz +=  2000000U + (frequency_offset_khz * 1000U);
+	f_3db_hz = clamp(f_3db_hz, 7000000U, 40000000U);
 
-	if (lpf_mxdiv > div_max)
-		lpf_mxdiv = div_max;
+#define LPF_COEFF 3200U
+	lpf_gm = DIV_ROUND_CLOSEST(f_3db_hz * gdiv28, LPF_COEFF * f_ref_khz);
+	lpf_gm = clamp_val(lpf_gm, 1U, 23U);
+
+	lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz);
+	if (lpf_mxdiv < div_min)
+		lpf_mxdiv = DIV_ROUND_CLOSEST(++lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz);
+	lpf_mxdiv = clamp_val(lpf_mxdiv, 0U, div_max);
 
 	ret = m88ts2022_wr_reg(priv, 0x04, lpf_mxdiv);
 	if (ret)
@@ -492,44 +484,37 @@ static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
 {
 	struct m88ts2022_priv *priv = fe->tuner_priv;
-	u8  u8tmp, gain1, gain2, gain3;
-	u16 gain, u16tmp;
 	int ret;
+	u8 u8tmp;
+	u16 gain, u16tmp;
+	unsigned int gain1, gain2, gain3;
 
 	ret = m88ts2022_rd_reg(priv, 0x3d, &u8tmp);
 	if (ret)
 		goto err;
 
 	gain1 = (u8tmp >> 0) & 0x1f;
-	if (gain1 > 15)
-		gain1 = 15;
+	gain1 = clamp(gain1, 0U, 15U);
 
 	ret = m88ts2022_rd_reg(priv, 0x21, &u8tmp);
 	if (ret)
 		goto err;
 
 	gain2 = (u8tmp >> 0) & 0x1f;
-	if (gain2 < 2)
-		gain2 = 2;
-	if (gain2 > 16)
-		gain2 = 16;
+	gain2 = clamp(gain2, 2U, 16U);
 
 	ret = m88ts2022_rd_reg(priv, 0x66, &u8tmp);
 	if (ret)
 		goto err;
 
 	gain3 = (u8tmp >> 3) & 0x07;
-	if (gain3 > 6)
-		gain3 = 6;
+	gain3 = clamp(gain3, 0U, 6U);
 
 	gain = gain1 * 265 + gain2 * 338 + gain3 * 285;
 
 	/* scale value to 0x0000-0xffff */
 	u16tmp = (0xffff - gain);
-	if (u16tmp < 59000)
-		u16tmp = 59000;
-	else if (u16tmp > 61500)
-		u16tmp = 61500;
+	u16tmp = clamp_val(u16tmp, 59000U, 61500U);
 
 	*strength = (u16tmp - 59000) * 0xffff / (61500 - 59000);
 err:
-- 
1.8.4.2


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

* [PATCH REVIEW 14/18] m88ds3103: remove unneeded AGC from inittab
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (12 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 13/18] m88ts2022: reimplement synthesizer calculations Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 15/18] m88ds3103: add default value for reg 56 Antti Palosaari
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Optimal AGC is highly depended on used RF tuner and due to that
it is already included to chip configuration. However, inittab
has default AGC value, which was later replaced by one from config.

Add also comment to all chip configuration options about default
values and if those are needed to set or not.

Reported-by: David Howells <dhowells@redhat.com>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/m88ds3103.h      | 14 ++++++++++++--
 drivers/media/dvb-frontends/m88ds3103_priv.h |  2 --
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h
index 287d62a..eaa5d10 100644
--- a/drivers/media/dvb-frontends/m88ds3103.h
+++ b/drivers/media/dvb-frontends/m88ds3103.h
@@ -26,26 +26,28 @@
 struct m88ds3103_config {
 	/*
 	 * I2C address
+	 * Default: none, must set
 	 * 0x68, ...
 	 */
 	u8 i2c_addr;
 
 	/*
 	 * clock
+	 * Default: none, must set
 	 * 27000000
 	 */
 	u32 clock;
 
 	/*
 	 * max bytes I2C provider is asked to write at once
-	 * Note: Buffer is taken from the stack currently!
-	 * Value must be set.
+	 * Default: none, must set
 	 * 33, 65, ...
 	 */
 	u16 i2c_wr_max;
 
 	/*
 	 * TS output mode
+	 * Default: M88DS3103_TS_SERIAL
 	 */
 #define M88DS3103_TS_SERIAL             0 /* TS output pin D0, normal */
 #define M88DS3103_TS_SERIAL_D7          1 /* TS output pin D7 */
@@ -58,16 +60,19 @@ struct m88ds3103_config {
 
 	/*
 	 * spectrum inversion
+	 * Default: 0
 	 */
 	u8 spec_inv:1;
 
 	/*
 	 * AGC polarity
+	 * Default: 0
 	 */
 	u8 agc_inv:1;
 
 	/*
 	 * clock output
+	 * Default: M88DS3103_CLOCK_OUT_DISABLED
 	 */
 #define M88DS3103_CLOCK_OUT_DISABLED        0
 #define M88DS3103_CLOCK_OUT_ENABLED         1
@@ -76,9 +81,14 @@ struct m88ds3103_config {
 
 	/*
 	 * DiSEqC envelope mode
+	 * Default: 0
 	 */
 	u8 envelope_mode:1;
 
+	/*
+	 * AGC configuration
+	 * Default: none, must set
+	 */
 	u8 agc;
 };
 
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
index 322db4d..80c5a25 100644
--- a/drivers/media/dvb-frontends/m88ds3103_priv.h
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -57,7 +57,6 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = {
 	{0x30, 0x08},
 	{0x31, 0x40},
 	{0x32, 0x32},
-	{0x33, 0x35},
 	{0x35, 0xff},
 	{0x3a, 0x00},
 	{0x37, 0x10},
@@ -139,7 +138,6 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = {
 	{0x27, 0x31},
 	{0x30, 0x08},
 	{0x32, 0x32},
-	{0x33, 0x35},
 	{0x35, 0xff},
 	{0x3a, 0x00},
 	{0x37, 0x10},
-- 
1.8.4.2


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

* [PATCH REVIEW 15/18] m88ds3103: add default value for reg 56
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (13 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 14/18] m88ds3103: remove unneeded AGC from inittab Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 16/18] m88ds3103: I/O optimize inittab write Antti Palosaari
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Reg 0x56 should be programmed to 0x01. Add default to inittab.

Reported-by: David Howells <dhowells@redhat.com>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/m88ds3103_priv.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
index 80c5a25..9cc29b4 100644
--- a/drivers/media/dvb-frontends/m88ds3103_priv.h
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -71,6 +71,7 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = {
 	{0x51, 0x36},
 	{0x52, 0x36},
 	{0x53, 0x36},
+	{0x56, 0x01},
 	{0x63, 0x0f},
 	{0x64, 0x30},
 	{0x65, 0x40},
@@ -152,6 +153,7 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = {
 	{0x51, 0x36},
 	{0x52, 0x36},
 	{0x53, 0x36},
+	{0x56, 0x01},
 	{0x63, 0x0f},
 	{0x64, 0x10},
 	{0x65, 0x20},
-- 
1.8.4.2


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

* [PATCH REVIEW 16/18] m88ds3103: I/O optimize inittab write
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (14 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 15/18] m88ds3103: add default value for reg 56 Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 17/18] m88ts2022: convert to Kernel I2C driver model Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 18/18] m88ds3103: fix possible i2c deadlock Antti Palosaari
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Write inittab using reg address auto-increment in order to reduce
I/O a little bit.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/m88ds3103.c | 43 +++++++++++++++++++++++++++------
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index f9d8967..76bd85a 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -157,6 +157,38 @@ static int m88ds3103_rd_reg_mask(struct m88ds3103_priv *priv,
 	return 0;
 }
 
+/* write reg val table using reg addr auto increment */
+static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv,
+		const struct m88ds3103_reg_val *tab, int tab_len)
+{
+	int ret, i, j;
+	u8 buf[83];
+	dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
+
+	if (tab_len > 83) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	for (i = 0, j = 0; i < tab_len; i++, j++) {
+		buf[j] = tab[i].val;
+
+		if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1 ||
+				!((j + 1) % (priv->cfg->i2c_wr_max - 1))) {
+			ret = m88ds3103_wr_regs(priv, tab[i].reg - j, buf, j + 1);
+			if (ret)
+				goto err;
+
+			j = -1;
+		}
+	}
+
+	return 0;
+err:
+	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
 static int m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t *status)
 {
 	struct m88ds3103_priv *priv = fe->demodulator_priv;
@@ -214,7 +246,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 {
 	struct m88ds3103_priv *priv = fe->demodulator_priv;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-	int ret, i, len;
+	int ret, len;
 	const struct m88ds3103_reg_val *init;
 	u8 u8tmp, u8tmp1, u8tmp2;
 	u8 buf[2];
@@ -308,12 +340,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
 
 	/* program init table */
 	if (c->delivery_system != priv->delivery_system) {
-		dev_dbg(&priv->i2c->dev, "%s: program init\n", __func__);
-		for (i = 0; i < len; i++) {
-			ret = m88ds3103_wr_reg(priv, init[i].reg, init[i].val);
-			if (ret)
-				goto err;
-		}
+		ret = m88ds3103_wr_reg_val_tab(priv, init, len);
+		if (ret)
+			goto err;
 	}
 
 	u8tmp1 = 0; /* silence compiler warning */
-- 
1.8.4.2


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

* [PATCH REVIEW 17/18] m88ts2022: convert to Kernel I2C driver model
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (15 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 16/18] m88ds3103: I/O optimize inittab write Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  2013-12-08 22:31 ` [PATCH REVIEW 18/18] m88ds3103: fix possible i2c deadlock Antti Palosaari
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Convert driver from proprietary DVB driver model to standard I2C
driver model.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/m88ts2022.c      | 121 ++++++++++++++++++++--------------
 drivers/media/tuners/m88ts2022.h      |  24 ++-----
 drivers/media/tuners/m88ts2022_priv.h |   4 +-
 drivers/media/usb/em28xx/em28xx-dvb.c |  25 +++----
 4 files changed, 90 insertions(+), 84 deletions(-)

diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c
index b11e740..1155603 100644
--- a/drivers/media/tuners/m88ts2022.c
+++ b/drivers/media/tuners/m88ts2022.c
@@ -32,7 +32,7 @@ static int m88ts2022_wr_regs(struct m88ts2022_priv *priv,
 	u8 buf[MAX_WR_XFER_LEN];
 	struct i2c_msg msg[1] = {
 		{
-			.addr = priv->cfg->i2c_addr,
+			.addr = priv->client->addr,
 			.flags = 0,
 			.len = 1 + len,
 			.buf = buf,
@@ -45,11 +45,11 @@ static int m88ts2022_wr_regs(struct m88ts2022_priv *priv,
 	buf[0] = reg;
 	memcpy(&buf[1], val, len);
 
-	ret = i2c_transfer(priv->i2c, msg, 1);
+	ret = i2c_transfer(priv->client->adapter, msg, 1);
 	if (ret == 1) {
 		ret = 0;
 	} else {
-		dev_warn(&priv->i2c->dev,
+		dev_warn(&priv->client->dev,
 				"%s: i2c wr failed=%d reg=%02x len=%d\n",
 				KBUILD_MODNAME, ret, reg, len);
 		ret = -EREMOTEIO;
@@ -68,12 +68,12 @@ static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg,
 	u8 buf[MAX_RD_XFER_LEN];
 	struct i2c_msg msg[2] = {
 		{
-			.addr = priv->cfg->i2c_addr,
+			.addr = priv->client->addr,
 			.flags = 0,
 			.len = 1,
 			.buf = &reg,
 		}, {
-			.addr = priv->cfg->i2c_addr,
+			.addr = priv->client->addr,
 			.flags = I2C_M_RD,
 			.len = len,
 			.buf = buf,
@@ -83,12 +83,12 @@ static int m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg,
 	if (WARN_ON(len > MAX_RD_LEN))
 		return -EINVAL;
 
-	ret = i2c_transfer(priv->i2c, msg, 2);
+	ret = i2c_transfer(priv->client->adapter, msg, 2);
 	if (ret == 2) {
 		memcpy(val, buf, len);
 		ret = 0;
 	} else {
-		dev_warn(&priv->i2c->dev,
+		dev_warn(&priv->client->dev,
 				"%s: i2c rd failed=%d reg=%02x len=%d\n",
 				KBUILD_MODNAME, ret, reg, len);
 		ret = -EREMOTEIO;
@@ -144,7 +144,7 @@ static int m88ts2022_cmd(struct dvb_frontend *fe,
 	};
 
 	for (i = 0; i < 2; i++) {
-		dev_dbg(&priv->i2c->dev,
+		dev_dbg(&priv->client->dev,
 				"%s: i=%d op=%02x reg=%02x mask=%02x val=%02x\n",
 				__func__, i, op, reg, mask, val);
 
@@ -180,14 +180,14 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
 	unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28;
 	u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min;
 	u16 u16tmp;
-	dev_dbg(&priv->i2c->dev,
+	dev_dbg(&priv->client->dev,
 			"%s: frequency=%d symbol_rate=%d rolloff=%d\n",
 			__func__, c->frequency, c->symbol_rate, c->rolloff);
 	/*
 	 * Integer-N PLL synthesizer
 	 * kHz is used for all calculations to keep calculations within 32-bit
 	 */
-	f_ref_khz = DIV_ROUND_CLOSEST(priv->cfg->clock, 1000);
+	f_ref_khz = DIV_ROUND_CLOSEST(priv->cfg.clock, 1000);
 	div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
 
 	if (c->symbol_rate < 5000000)
@@ -230,7 +230,7 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	dev_dbg(&priv->i2c->dev,
+	dev_dbg(&priv->client->dev,
 			"%s: frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
 			__func__, priv->frequency_khz,
 			priv->frequency_khz - c->frequency, f_vco_khz, pll_n,
@@ -374,7 +374,7 @@ static int m88ts2022_set_params(struct dvb_frontend *fe)
 		goto err;
 err:
 	if (ret)
-		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 
 	return ret;
 }
@@ -397,7 +397,7 @@ static int m88ts2022_init(struct dvb_frontend *fe)
 		{0x24, 0x02},
 		{0x12, 0xa0},
 	};
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
 	ret = m88ts2022_wr_reg(priv, 0x00, 0x01);
 	if (ret)
@@ -407,13 +407,13 @@ static int m88ts2022_init(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	switch (priv->cfg->clock_out) {
+	switch (priv->cfg.clock_out) {
 	case M88TS2022_CLOCK_OUT_DISABLED:
 		u8tmp = 0x60;
 		break;
 	case M88TS2022_CLOCK_OUT_ENABLED:
 		u8tmp = 0x70;
-		ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg->clock_out_div);
+		ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div);
 		if (ret)
 			goto err;
 		break;
@@ -428,7 +428,7 @@ static int m88ts2022_init(struct dvb_frontend *fe)
 	if (ret)
 		goto err;
 
-	if (priv->cfg->loop_through)
+	if (priv->cfg.loop_through)
 		u8tmp = 0xec;
 	else
 		u8tmp = 0x6c;
@@ -444,7 +444,7 @@ static int m88ts2022_init(struct dvb_frontend *fe)
 	}
 err:
 	if (ret)
-		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -452,21 +452,21 @@ static int m88ts2022_sleep(struct dvb_frontend *fe)
 {
 	struct m88ts2022_priv *priv = fe->tuner_priv;
 	int ret;
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
 	ret = m88ts2022_wr_reg(priv, 0x00, 0x00);
 	if (ret)
 		goto err;
 err:
 	if (ret)
-		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
 static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
 	struct m88ts2022_priv *priv = fe->tuner_priv;
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
 	*frequency = priv->frequency_khz;
 	return 0;
@@ -475,7 +475,7 @@ static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
 	struct m88ts2022_priv *priv = fe->tuner_priv;
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
 	*frequency = 0; /* Zero-IF */
 	return 0;
@@ -519,19 +519,10 @@ static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
 	*strength = (u16tmp - 59000) * 0xffff / (61500 - 59000);
 err:
 	if (ret)
-		dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
-static int m88ts2022_release(struct dvb_frontend *fe)
-{
-	struct m88ts2022_priv *priv = fe->tuner_priv;
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
-
-	kfree(fe->tuner_priv);
-	return 0;
-}
-
 static const struct dvb_tuner_ops m88ts2022_tuner_ops = {
 	.info = {
 		.name          = "Montage M88TS2022",
@@ -539,8 +530,6 @@ static const struct dvb_tuner_ops m88ts2022_tuner_ops = {
 		.frequency_max = 2150000,
 	},
 
-	.release = m88ts2022_release,
-
 	.init = m88ts2022_init,
 	.sleep = m88ts2022_sleep,
 	.set_params = m88ts2022_set_params,
@@ -550,9 +539,11 @@ static const struct dvb_tuner_ops m88ts2022_tuner_ops = {
 	.get_rf_strength = m88ts2022_get_rf_strength,
 };
 
-struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
-		struct i2c_adapter *i2c, const struct m88ts2022_config *cfg)
+static int m88ts2022_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
 {
+	struct m88ts2022_config *cfg = client->dev.platform_data;
+	struct dvb_frontend *fe = cfg->fe;
 	struct m88ts2022_priv *priv;
 	int ret;
 	u8 chip_id, u8tmp;
@@ -560,13 +551,12 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
 	priv = kzalloc(sizeof(struct m88ts2022_priv), GFP_KERNEL);
 	if (!priv) {
 		ret = -ENOMEM;
-		dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+		dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
 		goto err;
 	}
 
-	priv->cfg = cfg;
-	priv->i2c = i2c;
-	priv->fe = fe;
+	memcpy(&priv->cfg, cfg, sizeof(struct m88ts2022_config));
+	priv->client = client;
 
 	/* check if the tuner is there */
 	ret = m88ts2022_rd_reg(priv, 0x00, &u8tmp);
@@ -591,7 +581,7 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
 	if (ret)
 		goto err;
 
-	dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+	dev_dbg(&priv->client->dev, "%s: chip_id=%02x\n", __func__, chip_id);
 
 	switch (chip_id) {
 	case 0xc3:
@@ -601,13 +591,13 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
 		goto err;
 	}
 
-	switch (priv->cfg->clock_out) {
+	switch (priv->cfg.clock_out) {
 	case M88TS2022_CLOCK_OUT_DISABLED:
 		u8tmp = 0x60;
 		break;
 	case M88TS2022_CLOCK_OUT_ENABLED:
 		u8tmp = 0x70;
-		ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg->clock_out_div);
+		ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div);
 		if (ret)
 			goto err;
 		break;
@@ -622,7 +612,7 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
 	if (ret)
 		goto err;
 
-	if (priv->cfg->loop_through)
+	if (priv->cfg.loop_through)
 		u8tmp = 0xec;
 	else
 		u8tmp = 0x6c;
@@ -636,23 +626,52 @@ struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
 	if (ret)
 		goto err;
 
-	dev_info(&priv->i2c->dev,
+	dev_info(&priv->client->dev,
 			"%s: Montage M88TS2022 successfully identified\n",
 			KBUILD_MODNAME);
 
 	fe->tuner_priv = priv;
 	memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops,
 			sizeof(struct dvb_tuner_ops));
+
+	i2c_set_clientdata(client, priv);
+	return 0;
 err:
-	if (ret) {
-		dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
-		kfree(priv);
-		return NULL;
-	}
+	dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
+	kfree(priv);
+	return ret;
+}
+
+static int m88ts2022_remove(struct i2c_client *client)
+{
+	struct m88ts2022_priv *priv = i2c_get_clientdata(client);
+	struct dvb_frontend *fe = priv->cfg.fe;
+	dev_dbg(&client->dev, "%s:\n", __func__);
 
-	return fe;
+	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+	fe->tuner_priv = NULL;
+	kfree(priv);
+
+	return 0;
 }
-EXPORT_SYMBOL(m88ts2022_attach);
+
+static const struct i2c_device_id m88ts2022_id[] = {
+	{"m88ts2022", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, m88ts2022_id);
+
+static struct i2c_driver m88ts2022_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "m88ts2022",
+	},
+	.probe		= m88ts2022_probe,
+	.remove		= m88ts2022_remove,
+	.id_table	= m88ts2022_id,
+};
+
+module_i2c_driver(m88ts2022_driver);
 
 MODULE_DESCRIPTION("Montage M88TS2022 silicon tuner driver");
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/tuners/m88ts2022.h b/drivers/media/tuners/m88ts2022.h
index fa1112c..1943b83 100644
--- a/drivers/media/tuners/m88ts2022.h
+++ b/drivers/media/tuners/m88ts2022.h
@@ -25,12 +25,6 @@
 
 struct m88ts2022_config {
 	/*
-	 * I2C address
-	 * 0x60, ...
-	 */
-	u8 i2c_addr;
-
-	/*
 	 * clock
 	 * 16000000 - 32000000
 	 */
@@ -54,19 +48,11 @@ struct m88ts2022_config {
 	 * 1 - 31
 	 */
 	u8 clock_out_div:5;
-};
 
-#if defined(CONFIG_MEDIA_TUNER_M88TS2022) || \
-	(defined(CONFIG_MEDIA_TUNER_M88TS2022_MODULE) && defined(MODULE))
-extern struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
-	struct i2c_adapter *i2c, const struct m88ts2022_config *cfg);
-#else
-static inline struct dvb_frontend *m88ts2022_attach(struct dvb_frontend *fe,
-	struct i2c_adapter *i2c, const struct m88ts2022_config *cfg)
-{
-	pr_warn("%s: driver disabled by Kconfig\n", __func__);
-	return NULL;
-}
-#endif
+	/*
+	 * pointer to DVB frontend
+	 */
+	struct dvb_frontend *fe;
+};
 
 #endif
diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h
index 190299a..a1fbb18 100644
--- a/drivers/media/tuners/m88ts2022_priv.h
+++ b/drivers/media/tuners/m88ts2022_priv.h
@@ -24,8 +24,8 @@
 #include "m88ts2022.h"
 
 struct m88ts2022_priv {
-	const struct m88ts2022_config *cfg;
-	struct i2c_adapter *i2c;
+	struct m88ts2022_config cfg;
+	struct i2c_client *client;
 	struct dvb_frontend *fe;
 	u32 frequency_khz;
 };
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 4f8f687..ddc0e60 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -89,6 +89,7 @@ struct em28xx_dvb {
 	struct semaphore      pll_mutex;
 	bool			dont_attach_fe1;
 	int			lna_gpio;
+	struct i2c_client	*i2c_client_tuner;
 };
 
 
@@ -819,11 +820,6 @@ static const struct m88ds3103_config pctv_461e_m88ds3103_config = {
 	.agc = 0x99,
 };
 
-static const struct m88ts2022_config em28xx_m88ts2022_config = {
-	.i2c_addr = 0x60,
-	.clock = 27000000,
-};
-
 /* ------------------------------------------------------------------ */
 
 static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
@@ -1349,6 +1345,11 @@ static int em28xx_dvb_init(struct em28xx *dev)
 		{
 			/* demod I2C adapter */
 			struct i2c_adapter *i2c_adapter;
+			struct i2c_board_info info;
+			struct m88ts2022_config m88ts2022_config = {
+				.clock = 27000000,
+			};
+			memset(&info, 0, sizeof(struct i2c_board_info));
 
 			/* attach demod */
 			dvb->fe[0] = dvb_attach(m88ds3103_attach,
@@ -1361,13 +1362,12 @@ static int em28xx_dvb_init(struct em28xx *dev)
 			}
 
 			/* attach tuner */
-			if (!dvb_attach(m88ts2022_attach, dvb->fe[0],
-					i2c_adapter,
-					&em28xx_m88ts2022_config)) {
-				dvb_frontend_detach(dvb->fe[0]);
-				result = -ENODEV;
-				goto out_free;
-			}
+			m88ts2022_config.fe = dvb->fe[0];
+			strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+			info.addr = 0x60;
+			info.platform_data = &m88ts2022_config;
+			request_module("m88ts2022");
+			dvb->i2c_client_tuner = i2c_new_device(i2c_adapter, &info);
 
 			/* delegate signal strength measurement to tuner */
 			dvb->fe[0]->ops.read_signal_strength =
@@ -1445,6 +1445,7 @@ static int em28xx_dvb_fini(struct em28xx *dev)
 				prevent_sleep(&dvb->fe[1]->ops);
 		}
 
+		i2c_release_client(dvb->i2c_client_tuner);
 		em28xx_unregister_dvb(dvb);
 		kfree(dvb);
 		dev->dvb = NULL;
-- 
1.8.4.2


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

* [PATCH REVIEW 18/18] m88ds3103: fix possible i2c deadlock
  2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
                   ` (16 preceding siblings ...)
  2013-12-08 22:31 ` [PATCH REVIEW 17/18] m88ts2022: convert to Kernel I2C driver model Antti Palosaari
@ 2013-12-08 22:31 ` Antti Palosaari
  17 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-08 22:31 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Adapter is locked by I2C core already. Use unlocked i2c_transfer()
version __i2c_transfer() to avoid deadlock.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/m88ds3103.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index 76bd85a..b5503ed 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -1159,7 +1159,7 @@ static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
 	mutex_lock(&priv->i2c_mutex);
 
 	/* open tuner I2C repeater for 1 xfer, closes automatically */
-	ret = i2c_transfer(priv->i2c, gate_open_msg, 1);
+	ret = __i2c_transfer(priv->i2c, gate_open_msg, 1);
 	if (ret != 1) {
 		dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d\n",
 				KBUILD_MODNAME, ret);
-- 
1.8.4.2


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

* Re: [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver
  2013-12-08 22:31 ` [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver Antti Palosaari
@ 2013-12-09  5:58   ` Matthias Schwarzott
  2013-12-18 12:35   ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 22+ messages in thread
From: Matthias Schwarzott @ 2013-12-09  5:58 UTC (permalink / raw)
  To: Antti Palosaari, linux-media

Hi Antti,
I have a small suggestion, see below.

On 08.12.2013 23:31, Antti Palosaari wrote:
> diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
> new file mode 100644
> index 0000000..91b3729
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/m88ds3103.c
> @@ -0,0 +1,1293 @@
> +/*
> + * Montage M88DS3103 demodulator driver
> + *
> + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
> + *
> + *    This program is free software; you can redistribute it and/or modify
> + *    it under the terms of the GNU General Public License as published by
> + *    the Free Software Foundation; either version 2 of the License, or
> + *    (at your option) any later version.
> + *
> + *    This program is distributed in the hope that it will be useful,
> + *    but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *    GNU General Public License for more details.
> + *
> + *    You should have received a copy of the GNU General Public License along
> + *    with this program; if not, write to the Free Software Foundation, Inc.,
> + *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#include "m88ds3103_priv.h"
> +
> +static struct dvb_frontend_ops m88ds3103_ops;
> +
> +/* write multiple registers */
> +static int m88ds3103_wr_regs(struct m88ds3103_priv *priv,
> +		u8 reg, const u8 *val, int len)
> +{
> +	int ret;
> +	u8 buf[1 + len];

Looking at the recent patches for variable length arrays, I think this
should be converted to fixed size.

> +	struct i2c_msg msg[1] = {
> +		{
> +			.addr = priv->cfg->i2c_addr,
> +			.flags = 0,
> +			.len = sizeof(buf),
> +			.buf = buf,
> +		}
> +	};
> +
> +	buf[0] = reg;
> +	memcpy(&buf[1], val, len);
> +
> +	mutex_lock(&priv->i2c_mutex);
> +	ret = i2c_transfer(priv->i2c, msg, 1);
> +	mutex_unlock(&priv->i2c_mutex);
> +	if (ret == 1) {
> +		ret = 0;
> +	} else {
> +		dev_warn(&priv->i2c->dev,
> +				"%s: i2c wr failed=%d reg=%02x len=%d\n",
> +				KBUILD_MODNAME, ret, reg, len);
> +		ret = -EREMOTEIO;
> +	}
> +
> +	return ret;
> +}
> +
> +/* read multiple registers */
> +static int m88ds3103_rd_regs(struct m88ds3103_priv *priv,
> +		u8 reg, u8 *val, int len)
> +{
> +	int ret;
> +	u8 buf[len];

Same as above.

> +	struct i2c_msg msg[2] = {
> +		{
> +			.addr = priv->cfg->i2c_addr,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = &reg,
> +		}, {
> +			.addr = priv->cfg->i2c_addr,
> +			.flags = I2C_M_RD,
> +			.len = sizeof(buf),
> +			.buf = buf,
> +		}
> +	};
> +
> +	mutex_lock(&priv->i2c_mutex);
> +	ret = i2c_transfer(priv->i2c, msg, 2);
> +	mutex_unlock(&priv->i2c_mutex);
> +	if (ret == 2) {
> +		memcpy(val, buf, len);
> +		ret = 0;
> +	} else {
> +		dev_warn(&priv->i2c->dev,
> +				"%s: i2c rd failed=%d reg=%02x len=%d\n",
> +				KBUILD_MODNAME, ret, reg, len);
> +		ret = -EREMOTEIO;
> +	}
> +
> +	return ret;
> +}
> +

Regards
Matthias



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

* Re: [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver
  2013-12-08 22:31 ` [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver Antti Palosaari
  2013-12-09  5:58   ` Matthias Schwarzott
@ 2013-12-18 12:35   ` Mauro Carvalho Chehab
  2013-12-18 14:40     ` Antti Palosaari
  1 sibling, 1 reply; 22+ messages in thread
From: Mauro Carvalho Chehab @ 2013-12-18 12:35 UTC (permalink / raw)
  To: Antti Palosaari; +Cc: linux-media

Hi Antti,

Em Mon,  9 Dec 2013 00:31:20 +0200
Antti Palosaari <crope@iki.fi> escreveu:

> DVB-S/S2 satellite television demodulator driver.
> 
> Signed-off-by: Antti Palosaari <crope@iki.fi>
> ---
>  drivers/media/dvb-frontends/Kconfig          |    7 +
>  drivers/media/dvb-frontends/Makefile         |    1 +
>  drivers/media/dvb-frontends/m88ds3103.c      | 1293 ++++++++++++++++++++++++++
>  drivers/media/dvb-frontends/m88ds3103.h      |  108 +++
>  drivers/media/dvb-frontends/m88ds3103_priv.h |  218 +++++
>  5 files changed, 1627 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/m88ds3103.c
>  create mode 100644 drivers/media/dvb-frontends/m88ds3103.h
>  create mode 100644 drivers/media/dvb-frontends/m88ds3103_priv.h
> 
> diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
> index bddbab4..6c46caf 100644
> --- a/drivers/media/dvb-frontends/Kconfig
> +++ b/drivers/media/dvb-frontends/Kconfig
> @@ -35,6 +35,13 @@ config DVB_STV6110x
>  	help
>  	  A Silicon tuner that supports DVB-S and DVB-S2 modes
>  
> +config DVB_M88DS3103
> +	tristate "Montage M88DS3103"
> +	depends on DVB_CORE && I2C
> +	default m if !MEDIA_SUBDRV_AUTOSELECT
> +	help
> +	  Say Y when you want to support this frontend.
> +
>  comment "Multistandard (cable + terrestrial) frontends"
>  	depends on DVB_CORE
>  
> diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
> index f9cb43d..0c75a6a 100644
> --- a/drivers/media/dvb-frontends/Makefile
> +++ b/drivers/media/dvb-frontends/Makefile
> @@ -85,6 +85,7 @@ obj-$(CONFIG_DVB_STV6110) += stv6110.o
>  obj-$(CONFIG_DVB_STV0900) += stv0900.o
>  obj-$(CONFIG_DVB_STV090x) += stv090x.o
>  obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
> +obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
>  obj-$(CONFIG_DVB_ISL6423) += isl6423.o
>  obj-$(CONFIG_DVB_EC100) += ec100.o
>  obj-$(CONFIG_DVB_HD29L2) += hd29l2.o
> diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
> new file mode 100644
> index 0000000..91b3729
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/m88ds3103.c
> @@ -0,0 +1,1293 @@
> +/*
> + * Montage M88DS3103 demodulator driver
> + *
> + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
> + *
> + *    This program is free software; you can redistribute it and/or modify
> + *    it under the terms of the GNU General Public License as published by
> + *    the Free Software Foundation; either version 2 of the License, or
> + *    (at your option) any later version.
> + *
> + *    This program is distributed in the hope that it will be useful,
> + *    but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *    GNU General Public License for more details.
> + *
> + *    You should have received a copy of the GNU General Public License along
> + *    with this program; if not, write to the Free Software Foundation, Inc.,
> + *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */

New versions of checkpatch complain about the above:

ERROR: Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.
#73: FILE: drivers/media/dvb-frontends/m88ds3103.c:16:
+ *    You should have received a copy of the GNU General Public License along$

What they're likely want to prevent is to have future big mass patches just
to fix the GNU address.

This is not needed, anyway, as kernel has the /COPYING file with has the
GPLv2 license.

So, could you please remove this and resend the pull request?

> +
> +#include "m88ds3103_priv.h"
> +
> +static struct dvb_frontend_ops m88ds3103_ops;
> +
> +/* write multiple registers */
> +static int m88ds3103_wr_regs(struct m88ds3103_priv *priv,
> +		u8 reg, const u8 *val, int len)
> +{
> +	int ret;
> +	u8 buf[1 + len];

Please, don't use dynamic buffer.

> +	struct i2c_msg msg[1] = {
> +		{
> +			.addr = priv->cfg->i2c_addr,
> +			.flags = 0,
> +			.len = sizeof(buf),
> +			.buf = buf,
> +		}
> +	};
> +
> +	buf[0] = reg;
> +	memcpy(&buf[1], val, len);
> +
> +	mutex_lock(&priv->i2c_mutex);
> +	ret = i2c_transfer(priv->i2c, msg, 1);
> +	mutex_unlock(&priv->i2c_mutex);
> +	if (ret == 1) {
> +		ret = 0;
> +	} else {
> +		dev_warn(&priv->i2c->dev,
> +				"%s: i2c wr failed=%d reg=%02x len=%d\n",
> +				KBUILD_MODNAME, ret, reg, len);
> +		ret = -EREMOTEIO;
> +	}
> +
> +	return ret;
> +}
> +
> +/* read multiple registers */
> +static int m88ds3103_rd_regs(struct m88ds3103_priv *priv,
> +		u8 reg, u8 *val, int len)
> +{
> +	int ret;
> +	u8 buf[len];

Same here.

> +	struct i2c_msg msg[2] = {
> +		{
> +			.addr = priv->cfg->i2c_addr,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = &reg,
> +		}, {
> +			.addr = priv->cfg->i2c_addr,
> +			.flags = I2C_M_RD,
> +			.len = sizeof(buf),
> +			.buf = buf,
> +		}
> +	};
> +
> +	mutex_lock(&priv->i2c_mutex);
> +	ret = i2c_transfer(priv->i2c, msg, 2);
> +	mutex_unlock(&priv->i2c_mutex);
> +	if (ret == 2) {
> +		memcpy(val, buf, len);
> +		ret = 0;
> +	} else {
> +		dev_warn(&priv->i2c->dev,
> +				"%s: i2c rd failed=%d reg=%02x len=%d\n",
> +				KBUILD_MODNAME, ret, reg, len);
> +		ret = -EREMOTEIO;
> +	}
> +
> +	return ret;
> +}
> +
> +/* write single register */
> +static int m88ds3103_wr_reg(struct m88ds3103_priv *priv, u8 reg, u8 val)
> +{
> +	return m88ds3103_wr_regs(priv, reg, &val, 1);
> +}
> +
> +/* read single register */
> +static int m88ds3103_rd_reg(struct m88ds3103_priv *priv, u8 reg, u8 *val)
> +{
> +	return m88ds3103_rd_regs(priv, reg, val, 1);
> +}
> +
> +/* write single register with mask */
> +static int m88ds3103_wr_reg_mask(struct m88ds3103_priv *priv,
> +		u8 reg, u8 val, u8 mask)
> +{
> +	int ret;
> +	u8 u8tmp;
> +
> +	/* no need for read if whole reg is written */
> +	if (mask != 0xff) {
> +		ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1);
> +		if (ret)
> +			return ret;
> +
> +		val &= mask;
> +		u8tmp &= ~mask;
> +		val |= u8tmp;
> +	}
> +
> +	return m88ds3103_wr_regs(priv, reg, &val, 1);
> +}
> +
> +/* read single register with mask */
> +static int m88ds3103_rd_reg_mask(struct m88ds3103_priv *priv,
> +		u8 reg, u8 *val, u8 mask)
> +{
> +	int ret, i;
> +	u8 u8tmp;
> +
> +	ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1);
> +	if (ret)
> +		return ret;
> +
> +	u8tmp &= mask;
> +
> +	/* find position of the first bit */
> +	for (i = 0; i < 8; i++) {
> +		if ((mask >> i) & 0x01)
> +			break;
> +	}
> +	*val = u8tmp >> i;
> +
> +	return 0;
> +}
> +
> +static int m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t *status)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
> +	int ret;
> +	u8 u8tmp;
> +
> +	*status = 0;
> +
> +	if (!priv->warm) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (c->delivery_system) {
> +	case SYS_DVBS:
> +		ret = m88ds3103_rd_reg_mask(priv, 0xd1, &u8tmp, 0x07);
> +		if (ret)
> +			goto err;
> +
> +		if (u8tmp == 0x07)
> +			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
> +					FE_HAS_VITERBI | FE_HAS_SYNC |
> +					FE_HAS_LOCK;
> +		break;
> +	case SYS_DVBS2:
> +		ret = m88ds3103_rd_reg_mask(priv, 0x0d, &u8tmp, 0x8f);
> +		if (ret)
> +			goto err;
> +
> +		if (u8tmp == 0x8f)
> +			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
> +					FE_HAS_VITERBI | FE_HAS_SYNC |
> +					FE_HAS_LOCK;
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
> +				__func__);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	priv->fe_status = *status;
> +
> +	dev_dbg(&priv->i2c->dev, "%s: lock=%02x status=%02x\n",
> +			__func__, u8tmp, *status);
> +
> +	return 0;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int m88ds3103_set_frontend(struct dvb_frontend *fe)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
> +	int ret, i, len;
> +	const struct m88ds3103_reg_val *init;
> +	u8 u8tmp, u8tmp1, u8tmp2;
> +	u8 buf[2];
> +	u16 u16tmp, divide_ratio;
> +	u32 tuner_frequency, target_mclk, ts_clk;
> +	s32 s32tmp;
> +	dev_dbg(&priv->i2c->dev,
> +			"%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n",
> +			__func__, c->delivery_system,
> +			c->modulation, c->frequency, c->symbol_rate,
> +			c->inversion, c->pilot, c->rolloff);
> +
> +	if (!priv->warm) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	/* program tuner */
> +	if (fe->ops.tuner_ops.set_params) {
> +		ret = fe->ops.tuner_ops.set_params(fe);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	if (fe->ops.tuner_ops.get_frequency) {
> +		ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	/* reset */
> +	ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0x00, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	switch (c->delivery_system) {
> +	case SYS_DVBS:
> +		len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
> +		init = m88ds3103_dvbs_init_reg_vals;
> +		target_mclk = 96000;
> +		break;
> +	case SYS_DVBS2:
> +		len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
> +		init = m88ds3103_dvbs2_init_reg_vals;
> +
> +		switch (priv->cfg->ts_mode) {
> +		case M88DS3103_TS_SERIAL:
> +		case M88DS3103_TS_SERIAL_D7:
> +			if (c->symbol_rate < 18000000)
> +				target_mclk = 96000;
> +			else
> +				target_mclk = 144000;
> +			break;
> +		case M88DS3103_TS_PARALLEL:
> +		case M88DS3103_TS_PARALLEL_12:
> +		case M88DS3103_TS_PARALLEL_16:
> +		case M88DS3103_TS_PARALLEL_19_2:
> +		case M88DS3103_TS_CI:
> +			if (c->symbol_rate < 18000000)
> +				target_mclk = 96000;
> +			else if (c->symbol_rate < 28000000)
> +				target_mclk = 144000;
> +			else
> +				target_mclk = 192000;
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n",
> +					__func__);
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
> +				__func__);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	/* program init table */
> +	if (c->delivery_system != priv->delivery_system) {
> +		dev_dbg(&priv->i2c->dev, "%s: program init\n", __func__);
> +		for (i = 0; i < len; i++) {
> +			ret = m88ds3103_wr_reg(priv, init[i].reg, init[i].val);
> +			if (ret)
> +				goto err;
> +		}
> +	}
> +
> +	u8tmp1 = 0; /* silence compiler warning */
> +	switch (priv->cfg->ts_mode) {
> +	case M88DS3103_TS_SERIAL:
> +		u8tmp1 = 0x00;
> +		ts_clk = 0;
> +		u8tmp = 0x04;
> +		break;
> +	case M88DS3103_TS_SERIAL_D7:
> +		u8tmp1 = 0x20;
> +		ts_clk = 0;
> +		u8tmp = 0x04;
> +		break;
> +	case M88DS3103_TS_PARALLEL:
> +		ts_clk = 24000;
> +		u8tmp = 0x00;
> +		break;
> +	case M88DS3103_TS_PARALLEL_12:
> +		ts_clk = 12000;
> +		u8tmp = 0x00;
> +		break;
> +	case M88DS3103_TS_PARALLEL_16:
> +		ts_clk = 16000;
> +		u8tmp = 0x00;
> +		break;
> +	case M88DS3103_TS_PARALLEL_19_2:
> +		ts_clk = 19200;
> +		u8tmp = 0x00;
> +		break;
> +	case M88DS3103_TS_CI:
> +		ts_clk = 6000;
> +		u8tmp = 0x01;
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	/* TS mode */
> +	ret = m88ds3103_wr_reg_mask(priv, 0xfd, u8tmp, 0x05);
> +	if (ret)
> +		goto err;
> +
> +	switch (priv->cfg->ts_mode) {
> +	case M88DS3103_TS_SERIAL:
> +	case M88DS3103_TS_SERIAL_D7:
> +		ret = m88ds3103_wr_reg_mask(priv, 0x29, u8tmp1, 0x20);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	if (ts_clk) {
> +		divide_ratio = DIV_ROUND_UP(target_mclk, ts_clk);
> +		u8tmp1 = divide_ratio / 2;
> +		u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
> +	} else {
> +		divide_ratio = 0;
> +		u8tmp1 = 0;
> +		u8tmp2 = 0;
> +	}
> +
> +	dev_dbg(&priv->i2c->dev,
> +			"%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n",
> +			__func__, target_mclk, ts_clk, divide_ratio);
> +
> +	u8tmp1--;
> +	u8tmp2--;
> +	/* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */
> +	u8tmp1 &= 0x3f;
> +	/* u8tmp2[5:0] => ea[5:0] */
> +	u8tmp2 &= 0x3f;
> +
> +	ret = m88ds3103_rd_reg(priv, 0xfe, &u8tmp);
> +	if (ret)
> +		goto err;
> +
> +	u8tmp = ((u8tmp  & 0xf0) << 0) | u8tmp1 >> 2;
> +	ret = m88ds3103_wr_reg(priv, 0xfe, u8tmp);
> +	if (ret)
> +		goto err;
> +
> +	u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
> +	ret = m88ds3103_wr_reg(priv, 0xea, u8tmp);
> +	if (ret)
> +		goto err;
> +
> +	switch (target_mclk) {
> +	case 72000:
> +		u8tmp1 = 0x00; /* 0b00 */
> +		u8tmp2 = 0x03; /* 0b11 */
> +		break;
> +	case 96000:
> +		u8tmp1 = 0x02; /* 0b10 */
> +		u8tmp2 = 0x01; /* 0b01 */
> +		break;
> +	case 115200:
> +		u8tmp1 = 0x01; /* 0b01 */
> +		u8tmp2 = 0x01; /* 0b01 */
> +		break;
> +	case 144000:
> +		u8tmp1 = 0x00; /* 0b00 */
> +		u8tmp2 = 0x01; /* 0b01 */
> +		break;
> +	case 192000:
> +		u8tmp1 = 0x03; /* 0b11 */
> +		u8tmp2 = 0x00; /* 0b00 */
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s: invalid target_mclk\n", __func__);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
> +	if (ret)
> +		goto err;
> +
> +	if (c->symbol_rate <= 3000000)
> +		u8tmp = 0x20;
> +	else if (c->symbol_rate <= 10000000)
> +		u8tmp = 0x10;
> +	else
> +		u8tmp = 0x06;
> +
> +	ret = m88ds3103_wr_reg(priv, 0xc3, 0x08);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0xc8, u8tmp);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0xc4, 0x08);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0xc7, 0x00);
> +	if (ret)
> +		goto err;
> +
> +	u16tmp = (((c->symbol_rate / 1000) << 15) + (M88DS3103_MCLK_KHZ / 4)) /
> +			(M88DS3103_MCLK_KHZ / 2);
> +	buf[0] = (u16tmp >> 0) & 0xff;
> +	buf[1] = (u16tmp >> 8) & 0xff;
> +	ret = m88ds3103_wr_regs(priv, 0x61, buf, 2);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x4d, priv->cfg->spec_inv << 1, 0x02);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x30, priv->cfg->agc_inv << 4, 0x10);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0x33, priv->cfg->agc);
> +	if (ret)
> +		goto err;
> +
> +	dev_dbg(&priv->i2c->dev, "%s: carrier offset=%d\n", __func__,
> +			(tuner_frequency - c->frequency));
> +
> +	s32tmp = 0x10000 * (tuner_frequency - c->frequency);
> +	s32tmp = (2 * s32tmp + M88DS3103_MCLK_KHZ) / (2 * M88DS3103_MCLK_KHZ);
> +	if (s32tmp < 0)
> +		s32tmp += 0x10000;
> +
> +	buf[0] = (s32tmp >> 0) & 0xff;
> +	buf[1] = (s32tmp >> 8) & 0xff;
> +	ret = m88ds3103_wr_regs(priv, 0x5e, buf, 2);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0x00, 0x00);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0xb2, 0x00);
> +	if (ret)
> +		goto err;
> +
> +	priv->delivery_system = c->delivery_system;
> +
> +	return 0;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int m88ds3103_init(struct dvb_frontend *fe)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	int ret, len, remaining;
> +	const struct firmware *fw = NULL;
> +	u8 *fw_file = M88DS3103_FIRMWARE;
> +	u8 u8tmp;
> +	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
> +
> +	/* set cold state by default */
> +	priv->warm = false;
> +
> +	/* wake up device from sleep */
> +	ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x01, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x00, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x00, 0x10);
> +	if (ret)
> +		goto err;
> +
> +	/* reset */
> +	ret = m88ds3103_wr_reg(priv, 0x07, 0x60);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
> +	if (ret)
> +		goto err;
> +
> +	/* firmware status */
> +	ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
> +	if (ret)
> +		goto err;
> +
> +	dev_dbg(&priv->i2c->dev, "%s: firmware=%02x\n", __func__, u8tmp);
> +
> +	if (u8tmp)
> +		goto skip_fw_download;
> +
> +	/* cold state - try to download firmware */
> +	dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n",
> +			KBUILD_MODNAME, m88ds3103_ops.info.name);
> +
> +	/* request the firmware, this will block and timeout */
> +	ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
> +	if (ret) {
> +		dev_err(&priv->i2c->dev, "%s: firmare file '%s' not found\n",
> +				KBUILD_MODNAME, fw_file);
> +		goto err;
> +	}
> +
> +	dev_info(&priv->i2c->dev, "%s: downloading firmware from file '%s'\n",
> +			KBUILD_MODNAME, fw_file);
> +
> +	ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	for (remaining = fw->size; remaining > 0;
> +			remaining -= (priv->cfg->i2c_wr_max - 1)) {
> +		len = remaining;
> +		if (len > (priv->cfg->i2c_wr_max - 1))
> +			len = (priv->cfg->i2c_wr_max - 1);
> +
> +		ret = m88ds3103_wr_regs(priv, 0xb0,
> +				&fw->data[fw->size - remaining], len);
> +		if (ret) {
> +			dev_err(&priv->i2c->dev,
> +					"%s: firmware download failed=%d\n",
> +					KBUILD_MODNAME, ret);
> +			goto err;
> +		}
> +	}
> +
> +	ret = m88ds3103_wr_reg(priv, 0xb2, 0x00);
> +	if (ret)
> +		goto err;
> +
> +	release_firmware(fw);
> +	fw = NULL;
> +
> +	ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
> +	if (ret)
> +		goto err;
> +
> +	if (!u8tmp) {
> +		dev_info(&priv->i2c->dev, "%s: firmware did not run\n",
> +				KBUILD_MODNAME);
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +
> +	dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n",
> +			KBUILD_MODNAME, m88ds3103_ops.info.name);
> +	dev_info(&priv->i2c->dev, "%s: firmware version %X.%X\n",
> +			KBUILD_MODNAME, (u8tmp >> 4) & 0xf, (u8tmp >> 0 & 0xf));
> +
> +skip_fw_download:
> +	/* warm state */
> +	priv->warm = true;
> +
> +	return 0;
> +err:
> +	if (fw)
> +		release_firmware(fw);
> +
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int m88ds3103_sleep(struct dvb_frontend *fe)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	int ret;
> +	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
> +
> +	priv->delivery_system = SYS_UNDEFINED;
> +
> +	/* TS Hi-Z */
> +	ret = m88ds3103_wr_reg_mask(priv, 0x27, 0x00, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	/* sleep */
> +	ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
> +	if (ret)
> +		goto err;
> +
> +	return 0;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int m88ds3103_get_frontend(struct dvb_frontend *fe)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
> +	int ret;
> +	u8 buf[3];
> +	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
> +
> +	if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (c->delivery_system) {
> +	case SYS_DVBS:
> +		ret = m88ds3103_rd_reg(priv, 0xe0, &buf[0]);
> +		if (ret)
> +			goto err;
> +
> +		ret = m88ds3103_rd_reg(priv, 0xe6, &buf[1]);
> +		if (ret)
> +			goto err;
> +
> +		switch ((buf[0] >> 2) & 0x01) {
> +		case 0:
> +			c->inversion = INVERSION_OFF;
> +			break;
> +		case 1:
> +			c->inversion = INVERSION_ON;
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
> +					__func__);
> +		}
> +
> +		switch ((buf[1] >> 5) & 0x07) {
> +		case 0:
> +			c->fec_inner = FEC_7_8;
> +			break;
> +		case 1:
> +			c->fec_inner = FEC_5_6;
> +			break;
> +		case 2:
> +			c->fec_inner = FEC_3_4;
> +			break;
> +		case 3:
> +			c->fec_inner = FEC_2_3;
> +			break;
> +		case 4:
> +			c->fec_inner = FEC_1_2;
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n",
> +					__func__);
> +		}
> +
> +		c->modulation = QPSK;
> +
> +		break;
> +	case SYS_DVBS2:
> +		ret = m88ds3103_rd_reg(priv, 0x7e, &buf[0]);
> +		if (ret)
> +			goto err;
> +
> +		ret = m88ds3103_rd_reg(priv, 0x89, &buf[1]);
> +		if (ret)
> +			goto err;
> +
> +		ret = m88ds3103_rd_reg(priv, 0xf2, &buf[2]);
> +		if (ret)
> +			goto err;
> +
> +		switch ((buf[0] >> 0) & 0x0f) {
> +		case 2:
> +			c->fec_inner = FEC_2_5;
> +			break;
> +		case 3:
> +			c->fec_inner = FEC_1_2;
> +			break;
> +		case 4:
> +			c->fec_inner = FEC_3_5;
> +			break;
> +		case 5:
> +			c->fec_inner = FEC_2_3;
> +			break;
> +		case 6:
> +			c->fec_inner = FEC_3_4;
> +			break;
> +		case 7:
> +			c->fec_inner = FEC_4_5;
> +			break;
> +		case 8:
> +			c->fec_inner = FEC_5_6;
> +			break;
> +		case 9:
> +			c->fec_inner = FEC_8_9;
> +			break;
> +		case 10:
> +			c->fec_inner = FEC_9_10;
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n",
> +					__func__);
> +		}
> +
> +		switch ((buf[0] >> 5) & 0x01) {
> +		case 0:
> +			c->pilot = PILOT_OFF;
> +			break;
> +		case 1:
> +			c->pilot = PILOT_ON;
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
> +					__func__);
> +		}
> +
> +		switch ((buf[0] >> 6) & 0x07) {
> +		case 0:
> +			c->modulation = QPSK;
> +			break;
> +		case 1:
> +			c->modulation = PSK_8;
> +			break;
> +		case 2:
> +			c->modulation = APSK_16;
> +			break;
> +		case 3:
> +			c->modulation = APSK_32;
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s: invalid modulation\n",
> +					__func__);
> +		}
> +
> +		switch ((buf[1] >> 7) & 0x01) {
> +		case 0:
> +			c->inversion = INVERSION_OFF;
> +			break;
> +		case 1:
> +			c->inversion = INVERSION_ON;
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
> +					__func__);
> +		}
> +
> +		switch ((buf[2] >> 0) & 0x03) {
> +		case 0:
> +			c->rolloff = ROLLOFF_35;
> +			break;
> +		case 1:
> +			c->rolloff = ROLLOFF_25;
> +			break;
> +		case 2:
> +			c->rolloff = ROLLOFF_20;
> +			break;
> +		default:
> +			dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n",
> +					__func__);
> +		}
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
> +				__func__);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	ret = m88ds3103_rd_regs(priv, 0x6d, buf, 2);
> +	if (ret)
> +		goto err;
> +
> +	c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) *
> +			M88DS3103_MCLK_KHZ * 1000 / 0x10000;
> +
> +	return 0;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
> +	int ret, i, tmp;
> +	u8 buf[3];
> +	u16 noise, signal;
> +	u32 noise_tot, signal_tot;
> +	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
> +	/* reports SNR in resolution of 0.1 dB */
> +
> +	/* more iterations for more accurate estimation */
> +	#define M88DS3103_SNR_ITERATIONS 3
> +
> +	switch (c->delivery_system) {
> +	case SYS_DVBS:
> +		tmp = 0;
> +
> +		for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
> +			ret = m88ds3103_rd_reg(priv, 0xff, &buf[0]);
> +			if (ret)
> +				goto err;
> +
> +			tmp += buf[0];
> +		}
> +
> +		/* use of one register limits max value to 15 dB */
> +		/* SNR(X) dB = 10 * ln(X) / ln(10) dB */
> +		tmp = DIV_ROUND_CLOSEST(tmp, 8 * M88DS3103_SNR_ITERATIONS);
> +		if (tmp)
> +			*snr = 100ul * intlog2(tmp) / intlog2(10);
> +		else
> +			*snr = 0;
> +		break;
> +	case SYS_DVBS2:
> +		noise_tot = 0;
> +		signal_tot = 0;
> +
> +		for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
> +			ret = m88ds3103_rd_regs(priv, 0x8c, buf, 3);
> +			if (ret)
> +				goto err;
> +
> +			noise = buf[1] << 6;    /* [13:6] */
> +			noise |= buf[0] & 0x3f; /*  [5:0] */
> +			noise >>= 2;
> +			signal = buf[2] * buf[2];
> +			signal >>= 1;
> +
> +			noise_tot += noise;
> +			signal_tot += signal;
> +		}
> +
> +		noise = noise_tot / M88DS3103_SNR_ITERATIONS;
> +		signal = signal_tot / M88DS3103_SNR_ITERATIONS;
> +
> +		/* SNR(X) dB = 10 * log10(X) dB */
> +		if (signal > noise) {
> +			tmp = signal / noise;
> +			*snr = 100ul * intlog10(tmp) / (1 << 24);
> +		} else
> +			*snr = 0;
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
> +				__func__);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +
> +static int m88ds3103_set_tone(struct dvb_frontend *fe,
> +	fe_sec_tone_mode_t fe_sec_tone_mode)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	int ret;
> +	u8 u8tmp, tone, reg_a1_mask;
> +	dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__,
> +			fe_sec_tone_mode);
> +
> +	if (!priv->warm) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	switch (fe_sec_tone_mode) {
> +	case SEC_TONE_ON:
> +		tone = 0;
> +		reg_a1_mask = 0x87;
> +		break;
> +	case SEC_TONE_OFF:
> +		tone = 1;
> +		reg_a1_mask = 0x00;
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n",
> +				__func__);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	u8tmp = tone << 7 | priv->cfg->envelope_mode << 5;
> +	ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
> +	if (ret)
> +		goto err;
> +
> +	u8tmp = 1 << 2;
> +	ret = m88ds3103_wr_reg_mask(priv, 0xa1, u8tmp, reg_a1_mask);
> +	if (ret)
> +		goto err;
> +
> +	return 0;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
> +		struct dvb_diseqc_master_cmd *diseqc_cmd)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	int ret, i;
> +	u8 u8tmp;
> +	dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__,
> +			diseqc_cmd->msg_len, diseqc_cmd->msg);
> +
> +	if (!priv->warm) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	u8tmp = priv->cfg->envelope_mode << 5;
> +	ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_regs(priv, 0xa3, diseqc_cmd->msg,
> +			diseqc_cmd->msg_len);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg(priv, 0xa1,
> +			(diseqc_cmd->msg_len - 1) << 3 | 0x07);
> +	if (ret)
> +		goto err;
> +
> +	/* DiSEqC message typical period is 54 ms */
> +	usleep_range(40000, 60000);
> +
> +	/* wait DiSEqC TX ready */
> +	for (i = 20, u8tmp = 1; i && u8tmp; i--) {
> +		usleep_range(5000, 10000);
> +
> +		ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
> +
> +	if (i == 0) {
> +		dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__);
> +
> +		ret = m88ds3103_wr_reg_mask(priv, 0xa1, 0x40, 0xc0);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0);
> +	if (ret)
> +		goto err;
> +
> +	if (i == 0) {
> +		ret = -ETIMEDOUT;
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
> +	fe_sec_mini_cmd_t fe_sec_mini_cmd)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	int ret, i;
> +	u8 u8tmp, burst;
> +	dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
> +			fe_sec_mini_cmd);
> +
> +	if (!priv->warm) {
> +		ret = -EAGAIN;
> +		goto err;
> +	}
> +
> +	u8tmp = priv->cfg->envelope_mode << 5;
> +	ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
> +	if (ret)
> +		goto err;
> +
> +	switch (fe_sec_mini_cmd) {
> +	case SEC_MINI_A:
> +		burst = 0x02;
> +		break;
> +	case SEC_MINI_B:
> +		burst = 0x01;
> +		break;
> +	default:
> +		dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n",
> +				__func__);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	ret = m88ds3103_wr_reg(priv, 0xa1, burst);
> +	if (ret)
> +		goto err;
> +
> +	/* DiSEqC ToneBurst period is 12.5 ms */
> +	usleep_range(11000, 20000);
> +
> +	/* wait DiSEqC TX ready */
> +	for (i = 5, u8tmp = 1; i && u8tmp; i--) {
> +		usleep_range(800, 2000);
> +
> +		ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0);
> +	if (ret)
> +		goto err;
> +
> +	if (i == 0) {
> +		dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__);
> +		ret = -ETIMEDOUT;
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int m88ds3103_get_tune_settings(struct dvb_frontend *fe,
> +	struct dvb_frontend_tune_settings *s)
> +{
> +	s->min_delay_ms = 3000;
> +
> +	return 0;
> +}
> +
> +static u32 m88ds3103_tuner_i2c_func(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_I2C;
> +}
> +
> +static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
> +		struct i2c_msg msg[], int num)
> +{
> +	struct m88ds3103_priv *priv = i2c_get_adapdata(i2c_adap);
> +	int ret;
> +	struct i2c_msg gate_open_msg[1] = {
> +		{
> +			.addr = priv->cfg->i2c_addr,
> +			.flags = 0,
> +			.len = 2,
> +			.buf = "\x03\x11",
> +		}
> +	};
> +	dev_dbg(&priv->i2c->dev, "%s: num=%d\n", __func__, num);
> +
> +	mutex_lock(&priv->i2c_mutex);
> +
> +	/* open i2c-gate */
> +	ret = i2c_transfer(priv->i2c, gate_open_msg, 1);
> +	if (ret != 1) {
> +		mutex_unlock(&priv->i2c_mutex);
> +		dev_warn(&priv->i2c->dev,
> +				"%s: i2c wr failed=%d\n",
> +				KBUILD_MODNAME, ret);
> +		ret = -EREMOTEIO;
> +		goto err;
> +	}
> +
> +	ret = i2c_transfer(priv->i2c, msg, num);
> +	mutex_unlock(&priv->i2c_mutex);
> +	if (ret < 0)
> +		dev_warn(&priv->i2c->dev, "%s: i2c failed=%d\n",
> +				KBUILD_MODNAME, ret);
> +
> +	return ret;
> +err:
> +	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static struct i2c_algorithm m88ds3103_tuner_i2c_algo = {
> +	.master_xfer   = m88ds3103_tuner_i2c_xfer,
> +	.functionality = m88ds3103_tuner_i2c_func,
> +};
> +
> +static void m88ds3103_release(struct dvb_frontend *fe)
> +{
> +	struct m88ds3103_priv *priv = fe->demodulator_priv;
> +	i2c_del_adapter(&priv->i2c_adapter);
> +	kfree(priv);
> +}
> +
> +struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
> +		struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter)
> +{
> +	int ret;
> +	struct m88ds3103_priv *priv;
> +	u8 chip_id, u8tmp;
> +
> +	/* allocate memory for the internal priv */
> +	priv = kzalloc(sizeof(struct m88ds3103_priv), GFP_KERNEL);
> +	if (!priv) {
> +		ret = -ENOMEM;
> +		dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
> +		goto err;
> +	}
> +
> +	priv->cfg = cfg;
> +	priv->i2c = i2c;
> +	mutex_init(&priv->i2c_mutex);
> +
> +	ret = m88ds3103_rd_reg(priv, 0x01, &chip_id);
> +	if (ret)
> +		goto err;
> +
> +	dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
> +
> +	switch (chip_id) {
> +	case 0xd0:
> +		break;
> +	default:
> +		goto err;
> +	}
> +
> +	switch (priv->cfg->clock_out) {
> +	case M88DS3103_CLOCK_OUT_DISABLED:
> +		u8tmp = 0x80;
> +		break;
> +	case M88DS3103_CLOCK_OUT_ENABLED:
> +		u8tmp = 0x00;
> +		break;
> +	case M88DS3103_CLOCK_OUT_ENABLED_DIV2:
> +		u8tmp = 0x10;
> +		break;
> +	default:
> +		goto err;
> +	}
> +
> +	ret = m88ds3103_wr_reg(priv, 0x29, u8tmp);
> +	if (ret)
> +		goto err;
> +
> +	/* sleep */
> +	ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
> +	if (ret)
> +		goto err;
> +
> +	ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
> +	if (ret)
> +		goto err;
> +
> +	/* create dvb_frontend */
> +	memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
> +	priv->fe.demodulator_priv = priv;
> +
> +	/* create i2c adapter for tuner */
> +	strlcpy(priv->i2c_adapter.name, KBUILD_MODNAME,
> +			sizeof(priv->i2c_adapter.name));
> +	priv->i2c_adapter.algo = &m88ds3103_tuner_i2c_algo;
> +	priv->i2c_adapter.algo_data = NULL;
> +	i2c_set_adapdata(&priv->i2c_adapter, priv);
> +	ret = i2c_add_adapter(&priv->i2c_adapter);
> +	if (ret) {
> +		dev_err(&i2c->dev, "%s: i2c bus could not be initialized\n",
> +				KBUILD_MODNAME);
> +		goto err;
> +	}
> +	*tuner_i2c_adapter = &priv->i2c_adapter;
> +
> +	return &priv->fe;
> +err:
> +	dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
> +	kfree(priv);
> +	return NULL;
> +}
> +EXPORT_SYMBOL(m88ds3103_attach);
> +
> +static struct dvb_frontend_ops m88ds3103_ops = {
> +	.delsys = { SYS_DVBS, SYS_DVBS2 },
> +	.info = {
> +		.name = "Montage M88DS3103",
> +		.frequency_min =  950000,
> +		.frequency_max = 2150000,
> +		.frequency_tolerance = 5000,
> +		.symbol_rate_min =  1000000,
> +		.symbol_rate_max = 45000000,
> +		.caps = FE_CAN_INVERSION_AUTO |
> +			FE_CAN_FEC_1_2 |
> +			FE_CAN_FEC_2_3 |
> +			FE_CAN_FEC_3_4 |
> +			FE_CAN_FEC_4_5 |
> +			FE_CAN_FEC_5_6 |
> +			FE_CAN_FEC_6_7 |
> +			FE_CAN_FEC_7_8 |
> +			FE_CAN_FEC_8_9 |
> +			FE_CAN_FEC_AUTO |
> +			FE_CAN_QPSK |
> +			FE_CAN_RECOVER |
> +			FE_CAN_2G_MODULATION
> +	},
> +
> +	.release = m88ds3103_release,
> +
> +	.get_tune_settings = m88ds3103_get_tune_settings,
> +
> +	.init = m88ds3103_init,
> +	.sleep = m88ds3103_sleep,
> +
> +	.set_frontend = m88ds3103_set_frontend,
> +	.get_frontend = m88ds3103_get_frontend,
> +
> +	.read_status = m88ds3103_read_status,
> +	.read_snr = m88ds3103_read_snr,
> +
> +	.diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd,
> +	.diseqc_send_burst = m88ds3103_diseqc_send_burst,
> +
> +	.set_tone = m88ds3103_set_tone,
> +};
> +
> +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
> +MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver");
> +MODULE_LICENSE("GPL");
> +MODULE_FIRMWARE(M88DS3103_FIRMWARE);
> diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h
> new file mode 100644
> index 0000000..287d62a
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/m88ds3103.h
> @@ -0,0 +1,108 @@
> +/*
> + * Montage M88DS3103 demodulator driver
> + *
> + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
> + *
> + *    This program is free software; you can redistribute it and/or modify
> + *    it under the terms of the GNU General Public License as published by
> + *    the Free Software Foundation; either version 2 of the License, or
> + *    (at your option) any later version.
> + *
> + *    This program is distributed in the hope that it will be useful,
> + *    but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *    GNU General Public License for more details.
> + *
> + *    You should have received a copy of the GNU General Public License along
> + *    with this program; if not, write to the Free Software Foundation, Inc.,
> + *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#ifndef M88DS3103_H
> +#define M88DS3103_H
> +
> +#include <linux/dvb/frontend.h>
> +
> +struct m88ds3103_config {
> +	/*
> +	 * I2C address
> +	 * 0x68, ...
> +	 */
> +	u8 i2c_addr;
> +
> +	/*
> +	 * clock
> +	 * 27000000
> +	 */
> +	u32 clock;
> +
> +	/*
> +	 * max bytes I2C provider is asked to write at once
> +	 * Note: Buffer is taken from the stack currently!
> +	 * Value must be set.
> +	 * 33, 65, ...
> +	 */
> +	u16 i2c_wr_max;
> +
> +	/*
> +	 * TS output mode
> +	 */
> +#define M88DS3103_TS_SERIAL             0 /* TS output pin D0, normal */
> +#define M88DS3103_TS_SERIAL_D7          1 /* TS output pin D7 */
> +#define M88DS3103_TS_PARALLEL           2 /* 24 MHz, normal */
> +#define M88DS3103_TS_PARALLEL_12        3 /* 12 MHz */
> +#define M88DS3103_TS_PARALLEL_16        4 /* 16 MHz */
> +#define M88DS3103_TS_PARALLEL_19_2      5 /* 19.2 MHz */
> +#define M88DS3103_TS_CI                 6 /* 6 MHz */
> +	u8 ts_mode;
> +
> +	/*
> +	 * spectrum inversion
> +	 */
> +	u8 spec_inv:1;
> +
> +	/*
> +	 * AGC polarity
> +	 */
> +	u8 agc_inv:1;
> +
> +	/*
> +	 * clock output
> +	 */
> +#define M88DS3103_CLOCK_OUT_DISABLED        0
> +#define M88DS3103_CLOCK_OUT_ENABLED         1
> +#define M88DS3103_CLOCK_OUT_ENABLED_DIV2    2
> +	u8 clock_out;
> +
> +	/*
> +	 * DiSEqC envelope mode
> +	 */
> +	u8 envelope_mode:1;
> +
> +	u8 agc;
> +};
> +
> +/*
> + * Driver implements own I2C-adapter for tuner I2C access. That's since chip
> + * has I2C-gate control which closes gate automatically after I2C transfer.
> + * Using own I2C adapter we can workaround that.
> + */
> +
> +#if defined(CONFIG_DVB_M88DS3103) || \
> +		(defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE))
> +extern struct dvb_frontend *m88ds3103_attach(
> +		const struct m88ds3103_config *config,
> +		struct i2c_adapter *i2c,
> +		struct i2c_adapter **tuner_i2c);
> +#else
> +static inline struct dvb_frontend *m88ds3103_attach(
> +		const struct m88ds3103_config *config,
> +		struct i2c_adapter *i2c,
> +		struct i2c_adapter **tuner_i2c)
> +{
> +	pr_warn("%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +#endif
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
> new file mode 100644
> index 0000000..f3d0867
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
> @@ -0,0 +1,218 @@
> +/*
> + * Montage M88DS3103 demodulator driver
> + *
> + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
> + *
> + *    This program is free software; you can redistribute it and/or modify
> + *    it under the terms of the GNU General Public License as published by
> + *    the Free Software Foundation; either version 2 of the License, or
> + *    (at your option) any later version.
> + *
> + *    This program is distributed in the hope that it will be useful,
> + *    but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *    GNU General Public License for more details.
> + *
> + *    You should have received a copy of the GNU General Public License along
> + *    with this program; if not, write to the Free Software Foundation, Inc.,
> + *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#ifndef M88DS3103_PRIV_H
> +#define M88DS3103_PRIV_H
> +
> +#include "dvb_frontend.h"
> +#include "m88ds3103.h"
> +#include "dvb_math.h"
> +#include <linux/firmware.h>
> +
> +#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
> +#define M88DS3103_MCLK_KHZ 96000
> +
> +struct m88ds3103_priv {
> +	struct i2c_adapter *i2c;
> +	/* mutex needed due to own tuner I2C adapter */
> +	struct mutex i2c_mutex;
> +	const struct m88ds3103_config *cfg;
> +	struct dvb_frontend fe;
> +	fe_delivery_system_t delivery_system;
> +	fe_status_t fe_status;
> +	bool warm; /* FW running */
> +	struct i2c_adapter i2c_adapter;
> +};
> +
> +struct m88ds3103_reg_val {
> +	u8 reg;
> +	u8 val;
> +};
> +
> +static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = {

Why to put this inside a header file? Is it shared with some other c file?

> +	{0x23, 0x07},
> +	{0x08, 0x03},
> +	{0x0c, 0x02},
> +	{0x21, 0x54},
> +	{0x25, 0x8a},
> +	{0x27, 0x31},
> +	{0x30, 0x08},
> +	{0x31, 0x40},
> +	{0x32, 0x32},
> +	{0x33, 0x35},
> +	{0x35, 0xff},
> +	{0x3a, 0x00},
> +	{0x37, 0x10},
> +	{0x38, 0x10},
> +	{0x39, 0x02},
> +	{0x42, 0x60},
> +	{0x4a, 0x80},
> +	{0x4b, 0x04},
> +	{0x4d, 0x91},
> +	{0x5d, 0xc8},
> +	{0x50, 0x36},
> +	{0x51, 0x36},
> +	{0x52, 0x36},
> +	{0x53, 0x36},
> +	{0x63, 0x0f},
> +	{0x64, 0x30},
> +	{0x65, 0x40},
> +	{0x68, 0x26},
> +	{0x69, 0x4c},
> +	{0x70, 0x20},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0x40},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0x60},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0x80},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0xa0},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0x1f},
> +	{0x76, 0x38},
> +	{0x77, 0xa6},
> +	{0x78, 0x0c},
> +	{0x79, 0x80},
> +	{0x7f, 0x14},
> +	{0x7c, 0x00},
> +	{0xae, 0x82},
> +	{0x80, 0x64},
> +	{0x81, 0x66},
> +	{0x82, 0x44},
> +	{0x85, 0x04},
> +	{0xcd, 0xf4},
> +	{0x90, 0x33},
> +	{0xa0, 0x44},
> +	{0xc0, 0x08},
> +	{0xc3, 0x10},
> +	{0xc4, 0x08},
> +	{0xc5, 0xf0},
> +	{0xc6, 0xff},
> +	{0xc7, 0x00},
> +	{0xc8, 0x1a},
> +	{0xc9, 0x80},
> +	{0xe0, 0xf8},
> +	{0xe6, 0x8b},
> +	{0xd0, 0x40},
> +	{0xf8, 0x20},
> +	{0xfa, 0x0f},
> +	{0x00, 0x00},
> +	{0xbd, 0x01},
> +	{0xb8, 0x00},
> +};
> +
> +static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = {
> +	{0x23, 0x07},
> +	{0x08, 0x07},
> +	{0x0c, 0x02},
> +	{0x21, 0x54},
> +	{0x25, 0x8a},
> +	{0x27, 0x31},
> +	{0x30, 0x08},
> +	{0x32, 0x32},
> +	{0x33, 0x35},
> +	{0x35, 0xff},
> +	{0x3a, 0x00},
> +	{0x37, 0x10},
> +	{0x38, 0x10},
> +	{0x39, 0x02},
> +	{0x42, 0x60},
> +	{0x4a, 0x80},
> +	{0x4b, 0x04},
> +	{0x4d, 0x91},
> +	{0x5d, 0xc8},
> +	{0x50, 0x36},
> +	{0x51, 0x36},
> +	{0x52, 0x36},
> +	{0x53, 0x36},
> +	{0x63, 0x0f},
> +	{0x64, 0x10},
> +	{0x65, 0x20},
> +	{0x68, 0x46},
> +	{0x69, 0xcd},
> +	{0x70, 0x20},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0x40},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0x60},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0x80},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0xa0},
> +	{0x71, 0x70},
> +	{0x72, 0x04},
> +	{0x73, 0x00},
> +	{0x70, 0x1f},
> +	{0x76, 0x38},
> +	{0x77, 0xa6},
> +	{0x78, 0x0c},
> +	{0x79, 0x80},
> +	{0x7f, 0x14},
> +	{0x85, 0x08},
> +	{0xcd, 0xf4},
> +	{0x90, 0x33},
> +	{0x86, 0x00},
> +	{0x87, 0x0f},
> +	{0x89, 0x00},
> +	{0x8b, 0x44},
> +	{0x8c, 0x66},
> +	{0x9d, 0xc1},
> +	{0x8a, 0x10},
> +	{0xad, 0x40},
> +	{0xa0, 0x44},
> +	{0xc0, 0x08},
> +	{0xc1, 0x10},
> +	{0xc2, 0x08},
> +	{0xc3, 0x10},
> +	{0xc4, 0x08},
> +	{0xc5, 0xf0},
> +	{0xc6, 0xff},
> +	{0xc7, 0x00},
> +	{0xc8, 0x1a},
> +	{0xc9, 0x80},
> +	{0xca, 0x23},
> +	{0xcb, 0x24},
> +	{0xcc, 0xf4},
> +	{0xce, 0x74},
> +	{0x00, 0x00},
> +	{0xbd, 0x01},
> +	{0xb8, 0x00},
> +};
> +
> +#endif


-- 

Cheers,
Mauro

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

* Re: [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver
  2013-12-18 12:35   ` Mauro Carvalho Chehab
@ 2013-12-18 14:40     ` Antti Palosaari
  0 siblings, 0 replies; 22+ messages in thread
From: Antti Palosaari @ 2013-12-18 14:40 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media

On 18.12.2013 14:35, Mauro Carvalho Chehab wrote:
> Hi Antti,
>
> Em Mon,  9 Dec 2013 00:31:20 +0200
> Antti Palosaari <crope@iki.fi> escreveu:
>
>> DVB-S/S2 satellite television demodulator driver.
>> + *    You should have received a copy of the GNU General Public License along
>> + *    with this program; if not, write to the Free Software Foundation, Inc.,
>> + *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
>> + */
>
> New versions of checkpatch complain about the above:
>
> ERROR: Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.
> #73: FILE: drivers/media/dvb-frontends/m88ds3103.c:16:
> + *    You should have received a copy of the GNU General Public License along$
>
> What they're likely want to prevent is to have future big mass patches just
> to fix the GNU address.
>
> This is not needed, anyway, as kernel has the /COPYING file with has the
> GPLv2 license.
>
> So, could you please remove this and resend the pull request?

Hey, that is written almost year ago and those new checkpatch 
requirements are very new, I think from latest release candidate version.

However, I could send new patch top of that set which meets latest 
3.13-rc4 checkpatch requirements.

>> +static int m88ds3103_wr_regs(struct m88ds3103_priv *priv,
>> +	u8 buf[1 + len];
>
> Please, don't use dynamic buffer.

Same here. Fixed already by later patched.

>> +static int m88ds3103_rd_regs(struct m88ds3103_priv *priv,
> Same here.

Same here. Fixed already by later patched.



>> diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h

>> +static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = {
>
> Why to put this inside a header file? Is it shared with some other c file?

It is private header file, not the shared one which is named as 
m88ds3103.h. Having inittabs inside private header file is common 
practice among DVB frontend driver. I think almost all demod driver has 
done it similarly. I suspect the main reason is just to separate static 
stuff out from code to keep driver file itself shorter.

I think it is good practice. It is place for another discussion whether 
it is good practice or not.

regards
Antti

-- 
http://palosaari.fi/

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

end of thread, other threads:[~2013-12-18 14:40 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-08 22:31 [PATCH REVIEW 00/18] M88DS3103 / M88TS2022 / PCTV 461e Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 01/18] em28xx: add support for Empia EM28178 Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 02/18] a8293: add small sleep in order to settle LNB voltage Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 03/18] Montage M88DS3103 DVB-S/S2 demodulator driver Antti Palosaari
2013-12-09  5:58   ` Matthias Schwarzott
2013-12-18 12:35   ` Mauro Carvalho Chehab
2013-12-18 14:40     ` Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 04/18] Montage M88TS2022 silicon tuner driver Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 05/18] em28xx: add support for PCTV DVB-S2 Stick (461e) [2013:0258] Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 06/18] MAINTAINERS: add M88DS3103 Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 07/18] MAINTAINERS: add M88TS2022 Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 08/18] m88ts2022: do not use dynamic stack allocation Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 09/18] m88ds3103: " Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 10/18] m88ds3103: use I2C mux for tuner I2C adapter Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 11/18] m88ds3103: use kernel macro to round division Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 12/18] m88ds3103: fix TS mode config Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 13/18] m88ts2022: reimplement synthesizer calculations Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 14/18] m88ds3103: remove unneeded AGC from inittab Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 15/18] m88ds3103: add default value for reg 56 Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 16/18] m88ds3103: I/O optimize inittab write Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 17/18] m88ts2022: convert to Kernel I2C driver model Antti Palosaari
2013-12-08 22:31 ` [PATCH REVIEW 18/18] m88ds3103: fix possible i2c deadlock Antti Palosaari

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.