linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHv2 1/6] mt2060: add i2c bindings
@ 2016-05-22  2:13 Antti Palosaari
  2016-05-22  2:13 ` [PATCHv2 2/6] mt2060: add param to split long i2c writes Antti Palosaari
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Antti Palosaari @ 2016-05-22  2:13 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Add proper i2c driver model bindings.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/mt2060.c      | 83 ++++++++++++++++++++++++++++++++++++++
 drivers/media/tuners/mt2060.h      | 20 +++++++++
 drivers/media/tuners/mt2060_priv.h |  2 +
 3 files changed, 105 insertions(+)

diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c
index b87b254..aa8280a 100644
--- a/drivers/media/tuners/mt2060.c
+++ b/drivers/media/tuners/mt2060.c
@@ -397,6 +397,89 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
 }
 EXPORT_SYMBOL(mt2060_attach);
 
+static int mt2060_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct mt2060_platform_data *pdata = client->dev.platform_data;
+	struct dvb_frontend *fe;
+	struct mt2060_priv *dev;
+	int ret;
+	u8 chip_id;
+
+	dev_dbg(&client->dev, "\n");
+
+	if (!pdata) {
+		dev_err(&client->dev, "Cannot proceed without platform data\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	fe = pdata->dvb_frontend;
+	dev->config.i2c_address = client->addr;
+	dev->config.clock_out = pdata->clock_out;
+	dev->cfg = &dev->config;
+	dev->i2c = client->adapter;
+	dev->if1_freq = pdata->if1 ? pdata->if1 : 1220;
+	dev->client = client;
+
+	ret = mt2060_readreg(dev, REG_PART_REV, &chip_id);
+	if (ret) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	dev_dbg(&client->dev, "chip id=%02x\n", chip_id);
+
+	if (chip_id != PART_REV) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	dev_info(&client->dev, "Microtune MT2060 successfully identified\n");
+	memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(fe->ops.tuner_ops));
+	fe->ops.tuner_ops.release = NULL;
+	fe->tuner_priv = dev;
+	i2c_set_clientdata(client, dev);
+
+	mt2060_calibrate(dev);
+
+	return 0;
+err:
+	dev_dbg(&client->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int mt2060_remove(struct i2c_client *client)
+{
+	dev_dbg(&client->dev, "\n");
+
+	return 0;
+}
+
+static const struct i2c_device_id mt2060_id_table[] = {
+	{"mt2060", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, mt2060_id_table);
+
+static struct i2c_driver mt2060_driver = {
+	.driver = {
+		.name = "mt2060",
+		.suppress_bind_attrs = true,
+	},
+	.probe		= mt2060_probe,
+	.remove		= mt2060_remove,
+	.id_table	= mt2060_id_table,
+};
+
+module_i2c_driver(mt2060_driver);
+
 MODULE_AUTHOR("Olivier DANET");
 MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/mt2060.h b/drivers/media/tuners/mt2060.h
index 6efed35..05c0d55 100644
--- a/drivers/media/tuners/mt2060.h
+++ b/drivers/media/tuners/mt2060.h
@@ -25,6 +25,26 @@
 struct dvb_frontend;
 struct i2c_adapter;
 
+/*
+ * I2C address
+ * 0x60, ...
+ */
+
+/**
+ * struct mt2060_platform_data - Platform data for the mt2060 driver
+ * @clock_out: Clock output setting. 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1.
+ * @if1: First IF used [MHz]. 0 defaults to 1220.
+ * @dvb_frontend: DVB frontend.
+ */
+
+struct mt2060_platform_data {
+	u8 clock_out;
+	u16 if1;
+	struct dvb_frontend *dvb_frontend;
+};
+
+
+/* configuration struct for mt2060_attach() */
 struct mt2060_config {
 	u8 i2c_address;
 	u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
diff --git a/drivers/media/tuners/mt2060_priv.h b/drivers/media/tuners/mt2060_priv.h
index 2b60de6..dfc4a06 100644
--- a/drivers/media/tuners/mt2060_priv.h
+++ b/drivers/media/tuners/mt2060_priv.h
@@ -95,6 +95,8 @@
 struct mt2060_priv {
 	struct mt2060_config *cfg;
 	struct i2c_adapter   *i2c;
+	struct i2c_client *client;
+	struct mt2060_config config;
 
 	u32 frequency;
 	u16 if1_freq;
-- 
http://palosaari.fi/


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

* [PATCHv2 2/6] mt2060: add param to split long i2c writes
  2016-05-22  2:13 [PATCHv2 1/6] mt2060: add i2c bindings Antti Palosaari
@ 2016-05-22  2:13 ` Antti Palosaari
  2016-05-22  2:13 ` [PATCHv2 3/6] zd1301_demod: ZyDAS ZD1301 DVB-T demodulator driver Antti Palosaari
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Antti Palosaari @ 2016-05-22  2:13 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

Add configuration parameter to split long i2c writes as some I2C
adapters cannot write 10 bytes used as a one go.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/mt2060.c      | 21 +++++++++++++++++----
 drivers/media/tuners/mt2060.h      |  3 +++
 drivers/media/tuners/mt2060_priv.h |  1 +
 3 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c
index aa8280a..6e16a9f 100644
--- a/drivers/media/tuners/mt2060.c
+++ b/drivers/media/tuners/mt2060.c
@@ -71,13 +71,24 @@ static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val)
 // Writes a set of consecutive registers
 static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len)
 {
+	int rem, val_len;
+	u8 xfer_buf[16];
 	struct i2c_msg msg = {
-		.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len
+		.addr = priv->cfg->i2c_address, .flags = 0, .buf = xfer_buf
 	};
-	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
-		printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len);
-		return -EREMOTEIO;
+
+	for (rem = len - 1; rem > 0; rem -= priv->i2c_max_regs) {
+		val_len = min_t(int, rem, priv->i2c_max_regs);
+		msg.len = 1 + val_len;
+		xfer_buf[0] = buf[0] + len - 1 - rem;
+		memcpy(&xfer_buf[1], &buf[1 + len - 1 - rem], val_len);
+
+		if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+			printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n", val_len);
+			return -EREMOTEIO;
+		}
 	}
+
 	return 0;
 }
 
@@ -370,6 +381,7 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
 	priv->cfg      = cfg;
 	priv->i2c      = i2c;
 	priv->if1_freq = if1;
+	priv->i2c_max_regs = ~0;
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
@@ -427,6 +439,7 @@ static int mt2060_probe(struct i2c_client *client,
 	dev->i2c = client->adapter;
 	dev->if1_freq = pdata->if1 ? pdata->if1 : 1220;
 	dev->client = client;
+	dev->i2c_max_regs = pdata->i2c_write_max ? pdata->i2c_write_max - 1 : ~0;
 
 	ret = mt2060_readreg(dev, REG_PART_REV, &chip_id);
 	if (ret) {
diff --git a/drivers/media/tuners/mt2060.h b/drivers/media/tuners/mt2060.h
index 05c0d55..f0572ac 100644
--- a/drivers/media/tuners/mt2060.h
+++ b/drivers/media/tuners/mt2060.h
@@ -34,12 +34,15 @@ struct i2c_adapter;
  * struct mt2060_platform_data - Platform data for the mt2060 driver
  * @clock_out: Clock output setting. 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1.
  * @if1: First IF used [MHz]. 0 defaults to 1220.
+ * @i2c_write_max: Maximum number of bytes I2C adapter can write at once.
+ *  0 defaults to maximum.
  * @dvb_frontend: DVB frontend.
  */
 
 struct mt2060_platform_data {
 	u8 clock_out;
 	u16 if1;
+	unsigned int i2c_write_max:5;
 	struct dvb_frontend *dvb_frontend;
 };
 
diff --git a/drivers/media/tuners/mt2060_priv.h b/drivers/media/tuners/mt2060_priv.h
index dfc4a06..f0fdb83 100644
--- a/drivers/media/tuners/mt2060_priv.h
+++ b/drivers/media/tuners/mt2060_priv.h
@@ -98,6 +98,7 @@ struct mt2060_priv {
 	struct i2c_client *client;
 	struct mt2060_config config;
 
+	u8 i2c_max_regs;
 	u32 frequency;
 	u16 if1_freq;
 	u8  fmfreq;
-- 
http://palosaari.fi/


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

* [PATCHv2 3/6] zd1301_demod: ZyDAS ZD1301 DVB-T demodulator driver
  2016-05-22  2:13 [PATCHv2 1/6] mt2060: add i2c bindings Antti Palosaari
  2016-05-22  2:13 ` [PATCHv2 2/6] mt2060: add param to split long i2c writes Antti Palosaari
@ 2016-05-22  2:13 ` Antti Palosaari
  2016-05-22  2:13 ` [PATCHv2 4/6] MAINTAINERS: add zd1301_demod driver Antti Palosaari
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Antti Palosaari @ 2016-05-22  2:13 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

ZyDAS ZD1301 is chip having USB interface and DVB-T demodulator
integrated. This driver is for demodulator part.
Driver is very reduced, just basic demodulator functionality, no
statistics at all. It registers as a platform driver to driver core.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-frontends/Kconfig        |   7 +
 drivers/media/dvb-frontends/Makefile       |   1 +
 drivers/media/dvb-frontends/zd1301_demod.c | 550 +++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/zd1301_demod.h |  55 +++
 4 files changed, 613 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/zd1301_demod.c
 create mode 100644 drivers/media/dvb-frontends/zd1301_demod.h

diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index a82f77c..539b18a 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -503,6 +503,13 @@ config DVB_AS102_FE
 	depends on DVB_CORE
 	default DVB_AS102
 
+config DVB_ZD1301_DEMOD
+	tristate "ZyDAS ZD1301"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.
+
 comment "DVB-C (cable) frontends"
 	depends on DVB_CORE
 
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index eb7191f..c3779bb 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -123,3 +123,4 @@ obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
 obj-$(CONFIG_DVB_TC90522) += tc90522.o
 obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
 obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
+obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o
diff --git a/drivers/media/dvb-frontends/zd1301_demod.c b/drivers/media/dvb-frontends/zd1301_demod.c
new file mode 100644
index 0000000..1b1f8d5
--- /dev/null
+++ b/drivers/media/dvb-frontends/zd1301_demod.c
@@ -0,0 +1,550 @@
+/*
+ * ZyDAS ZD1301 driver (demodulator)
+ *
+ * Copyright (C) 2015 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.
+ */
+
+#include "zd1301_demod.h"
+
+static u8 zd1301_demod_gain = 0x38;
+module_param_named(gain, zd1301_demod_gain, byte, 0644);
+MODULE_PARM_DESC(gain, "gain (value: 0x00 - 0x70, default: 0x38)");
+
+struct zd1301_demod_dev {
+	struct platform_device *pdev;
+	struct dvb_frontend frontend;
+	struct i2c_adapter adapter;
+	u8 gain;
+};
+
+static int zd1301_demod_wreg(struct zd1301_demod_dev *dev, u16 reg, u8 val)
+{
+	struct platform_device *pdev = dev->pdev;
+	struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data;
+
+	return pdata->reg_write(pdata->reg_priv, reg, val);
+}
+
+static int zd1301_demod_rreg(struct zd1301_demod_dev *dev, u16 reg, u8 *val)
+{
+	struct platform_device *pdev = dev->pdev;
+	struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data;
+
+	return pdata->reg_read(pdata->reg_priv, reg, val);
+}
+
+static int zd1301_demod_set_frontend(struct dvb_frontend *fe)
+{
+	struct zd1301_demod_dev *dev = fe->demodulator_priv;
+	struct platform_device *pdev = dev->pdev;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	u32 if_frequency;
+	u8 r6a50_val;
+
+	dev_dbg(&pdev->dev, "frequency=%u bandwidth_hz=%u\n",
+		c->frequency, c->bandwidth_hz);
+
+	/* 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_if_frequency) {
+		ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+		if (ret)
+			goto err;
+
+		dev_dbg(&pdev->dev, "if_frequency=%u\n", if_frequency);
+
+		if (if_frequency != 36150000)
+		if (ret)
+			goto err;
+	}
+
+	switch (c->bandwidth_hz) {
+	case 6000000:
+		r6a50_val = 0x78;
+		break;
+	case 7000000:
+		r6a50_val = 0x68;
+		break;
+	case 8000000:
+		r6a50_val = 0x58;
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = zd1301_demod_wreg(dev, 0x6a60, 0x11);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a47, 0x46);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a48, 0x46);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a4a, 0x15);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a4b, 0x63);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a5b, 0x99);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a3b, 0x10);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6806, 0x01);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a41, 0x08);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a42, 0x46);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a44, 0x14);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a45, 0x67);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a38, 0x00);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a4c, 0x52);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a49, 0x2a);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6840, 0x2e);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a50, r6a50_val);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a38, 0x07);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&pdev->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int zd1301_demod_sleep(struct dvb_frontend *fe)
+{
+	struct zd1301_demod_dev *dev = fe->demodulator_priv;
+	struct platform_device *pdev = dev->pdev;
+	int ret;
+
+	dev_dbg(&pdev->dev, "\n");
+
+	ret = zd1301_demod_wreg(dev, 0x6a43, 0x70);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x684e, 0x00);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6849, 0x00);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x68e2, 0xd7);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x68e0, 0x39);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6840, 0x21);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&pdev->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int zd1301_demod_init(struct dvb_frontend *fe)
+{
+	struct zd1301_demod_dev *dev = fe->demodulator_priv;
+	struct platform_device *pdev = dev->pdev;
+	int ret;
+
+	dev_dbg(&pdev->dev, "\n");
+
+	ret = zd1301_demod_wreg(dev, 0x6840, 0x26);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x68e0, 0xff);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x68e2, 0xd8);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6849, 0x4e);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x684e, 0x01);
+	if (ret)
+		goto err;
+	ret = zd1301_demod_wreg(dev, 0x6a43, zd1301_demod_gain);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&pdev->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int zd1301_demod_get_tune_settings(struct dvb_frontend *fe,
+					  struct dvb_frontend_tune_settings *settings)
+{
+	struct zd1301_demod_dev *dev = fe->demodulator_priv;
+	struct platform_device *pdev = dev->pdev;
+
+	dev_dbg(&pdev->dev, "\n");
+
+	/* ~180ms seems to be enough */
+	settings->min_delay_ms = 400;
+
+	return 0;
+}
+
+static int zd1301_demod_read_status(struct dvb_frontend *fe,
+				    enum fe_status *status)
+{
+	struct zd1301_demod_dev *dev = fe->demodulator_priv;
+	struct platform_device *pdev = dev->pdev;
+	int ret;
+	u8 u8tmp;
+
+	ret = zd1301_demod_rreg(dev, 0x6a24, &u8tmp);
+	if (ret)
+		goto err;
+	if (u8tmp > 0x00 && u8tmp < 0x20)
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+			  FE_HAS_SYNC | FE_HAS_LOCK;
+	else
+		*status = 0;
+
+	dev_dbg(&pdev->dev, "lock byte=%02x\n", u8tmp);
+
+	/*
+	 * Interesting registers here are:
+	 * 0x6a05: get some gain value
+	 * 0x6a06: get about same gain value than set to 0x6a43
+	 * 0x6a07: get some gain value
+	 * 0x6a43: set gain value by driver
+	 * 0x6a24: get demod lock bits (FSM stage?)
+	 *
+	 * Driver should implement some kind of algorithm to calculate suitable
+	 * value for register 0x6a43, based likely values from register 0x6a05
+	 * and 0x6a07. Looks like gain register 0x6a43 value could be from
+	 * range 0x00 - 0x70.
+	 */
+
+	if (dev->gain != zd1301_demod_gain) {
+		dev->gain = zd1301_demod_gain;
+
+		ret = zd1301_demod_wreg(dev, 0x6a43, dev->gain);
+		if (ret)
+			goto err;
+	}
+
+	return 0;
+err:
+	dev_dbg(&pdev->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static const struct dvb_frontend_ops zd1301_demod_ops = {
+	.delsys = {SYS_DVBT},
+	.info = {
+		.name = "ZyDAS ZD1301",
+		.caps = FE_CAN_FEC_1_2 |
+			FE_CAN_FEC_2_3 |
+			FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 |
+			FE_CAN_FEC_7_8 |
+			FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK |
+			FE_CAN_QAM_16 |
+			FE_CAN_QAM_64 |
+			FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_HIERARCHY_AUTO |
+			FE_CAN_MUTE_TS
+	},
+
+	.sleep = zd1301_demod_sleep,
+	.init = zd1301_demod_init,
+	.set_frontend = zd1301_demod_set_frontend,
+	.get_tune_settings = zd1301_demod_get_tune_settings,
+	.read_status = zd1301_demod_read_status,
+};
+
+struct dvb_frontend *zd1301_demod_get_dvb_frontend(struct platform_device *pdev)
+{
+	struct zd1301_demod_dev *dev = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "\n");
+
+	return &dev->frontend;
+}
+EXPORT_SYMBOL(zd1301_demod_get_dvb_frontend);
+
+static int zd1301_demod_i2c_master_xfer(struct i2c_adapter *adapter,
+					struct i2c_msg msg[], int num)
+{
+	struct zd1301_demod_dev *dev = i2c_get_adapdata(adapter);
+	struct platform_device *pdev = dev->pdev;
+	int ret, i;
+	unsigned long timeout;
+	u8 u8tmp;
+
+	#define I2C_XFER_TIMEOUT 5
+	#define ZD1301_IS_I2C_XFER_WRITE_READ(_msg, _num) \
+		(_num == 2 && !(_msg[0].flags & I2C_M_RD) && (_msg[1].flags & I2C_M_RD))
+	#define ZD1301_IS_I2C_XFER_WRITE(_msg, _num) \
+		(_num == 1 && !(_msg[0].flags & I2C_M_RD))
+	#define ZD1301_IS_I2C_XFER_READ(_msg, _num) \
+		(_num == 1 && (_msg[0].flags & I2C_M_RD))
+	if (ZD1301_IS_I2C_XFER_WRITE_READ(msg, num)) {
+		dev_dbg(&pdev->dev, "write&read msg[0].len=%u msg[1].len=%u\n",
+			msg[0].len, msg[1].len);
+		if (msg[0].len > 1 || msg[1].len > 8) {
+			ret = -EOPNOTSUPP;
+			goto err;
+		}
+
+		ret = zd1301_demod_wreg(dev, 0x6811, 0x80);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6812, 0x05);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6813, msg[1].addr << 1);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6801, msg[0].buf[0]);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6802, 0x00);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6803, 0x06);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6805, 0x00);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6804, msg[1].len);
+		if (ret)
+			goto err;
+
+		/* Poll xfer ready */
+		timeout = jiffies + msecs_to_jiffies(I2C_XFER_TIMEOUT);
+		for (u8tmp = 1; !time_after(jiffies, timeout) && u8tmp;) {
+			usleep_range(500, 800);
+
+			ret = zd1301_demod_rreg(dev, 0x6804, &u8tmp);
+			if (ret)
+				goto err;
+		}
+
+		for (i = 0; i < msg[1].len; i++) {
+			ret = zd1301_demod_rreg(dev, 0x0600 + i, &msg[1].buf[i]);
+			if (ret)
+				goto err;
+		}
+	} else if (ZD1301_IS_I2C_XFER_WRITE(msg, num)) {
+		dev_dbg(&pdev->dev, "write msg[0].len=%u\n", msg[0].len);
+		if (msg[0].len > 1 + 8) {
+			ret = -EOPNOTSUPP;
+			goto err;
+		}
+
+		ret = zd1301_demod_wreg(dev, 0x6811, 0x80);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6812, 0x01);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6813, msg[0].addr << 1);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6800, msg[0].buf[0]);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6802, 0x00);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6803, 0x06);
+		if (ret)
+			goto err;
+
+		for (i = 0; i < msg[0].len - 1; i++) {
+			ret = zd1301_demod_wreg(dev, 0x0600 + i, msg[0].buf[1 + i]);
+			if (ret)
+				goto err;
+		}
+
+		ret = zd1301_demod_wreg(dev, 0x6805, 0x80);
+		if (ret)
+			goto err;
+		ret = zd1301_demod_wreg(dev, 0x6804, msg[0].len - 1);
+		if (ret)
+			goto err;
+
+		/* Poll xfer ready */
+		timeout = jiffies + msecs_to_jiffies(I2C_XFER_TIMEOUT);
+		for (u8tmp = 1; !time_after(jiffies, timeout) && u8tmp;) {
+			usleep_range(500, 800);
+
+			ret = zd1301_demod_rreg(dev, 0x6804, &u8tmp);
+			if (ret)
+				goto err;
+		}
+	} else {
+		dev_dbg(&pdev->dev, "unknown msg[0].len=%u\n", msg[0].len);
+		ret = -EOPNOTSUPP;
+		if (ret)
+			goto err;
+	}
+
+	return num;
+err:
+	dev_dbg(&pdev->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static u32 zd1301_demod_i2c_functionality(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm zd1301_demod_i2c_algorithm = {
+	.master_xfer   = zd1301_demod_i2c_master_xfer,
+	.functionality = zd1301_demod_i2c_functionality,
+};
+
+struct i2c_adapter *zd1301_demod_get_i2c_adapter(struct platform_device *pdev)
+{
+	struct zd1301_demod_dev *dev = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "\n");
+
+	return &dev->adapter;
+}
+EXPORT_SYMBOL(zd1301_demod_get_i2c_adapter);
+
+/* Platform driver interface */
+static int zd1301_demod_probe(struct platform_device *pdev)
+{
+	struct zd1301_demod_dev *dev;
+	struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+
+	dev_dbg(&pdev->dev, "\n");
+
+	if (!pdata) {
+		ret = -EINVAL;
+		dev_err(&pdev->dev, "cannot proceed without platform data\n");
+		goto err;
+	}
+	if (!pdev->dev.parent->driver) {
+		ret = -EINVAL;
+		dev_dbg(&pdev->dev, "no parent device\n");
+		goto err;
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	/* Setup the state */
+	dev->pdev = pdev;
+	dev->gain = zd1301_demod_gain;
+
+	/* Sleep */
+	ret = zd1301_demod_wreg(dev, 0x6840, 0x21);
+	if (ret)
+		goto err_kfree;
+	ret = zd1301_demod_wreg(dev, 0x6a38, 0x07);
+	if (ret)
+		goto err_kfree;
+
+	/* Create I2C adapter */
+	strlcpy(dev->adapter.name, "ZyDAS ZD1301 demod", sizeof(dev->adapter.name));
+	dev->adapter.algo = &zd1301_demod_i2c_algorithm;
+	dev->adapter.algo_data = NULL;
+	dev->adapter.dev.parent = pdev->dev.parent;
+	i2c_set_adapdata(&dev->adapter, dev);
+	ret = i2c_add_adapter(&dev->adapter);
+	if (ret) {
+		dev_err(&pdev->dev, "I2C adapter add failed %d\n", ret);
+		goto err_kfree;
+	}
+
+	/* Create dvb frontend */
+	memcpy(&dev->frontend.ops, &zd1301_demod_ops, sizeof(dev->frontend.ops));
+	dev->frontend.demodulator_priv = dev;
+	platform_set_drvdata(pdev, dev);
+	dev_info(&pdev->dev, "ZyDAS ZD1301 demod attached\n");
+
+	return 0;
+err_kfree:
+	kfree(dev);
+err:
+	dev_dbg(&pdev->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int zd1301_demod_remove(struct platform_device *pdev)
+{
+	struct zd1301_demod_dev *dev = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "\n");
+
+	i2c_del_adapter(&dev->adapter);
+	kfree(dev);
+
+	return 0;
+}
+
+static struct platform_driver zd1301_demod_driver = {
+	.driver = {
+		.name                = "zd1301_demod",
+		.suppress_bind_attrs = true,
+	},
+	.probe          = zd1301_demod_probe,
+	.remove         = zd1301_demod_remove,
+};
+module_platform_driver(zd1301_demod_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("ZyDAS ZD1301 demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/zd1301_demod.h b/drivers/media/dvb-frontends/zd1301_demod.h
new file mode 100644
index 0000000..78a3122
--- /dev/null
+++ b/drivers/media/dvb-frontends/zd1301_demod.h
@@ -0,0 +1,55 @@
+/*
+ * ZyDAS ZD1301 driver (demodulator)
+ *
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ZD1301_DEMOD_H
+#define ZD1301_DEMOD_H
+
+#include <linux/platform_device.h>
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+/**
+ * struct zd1301_demod_platform_data - Platform data for the zd1301_demod driver
+ * @reg_priv: First argument of reg_read and reg_write callbacks.
+ * @reg_read: Register read callback.
+ * @reg_write: Register write callback.
+ */
+
+struct zd1301_demod_platform_data {
+	void *reg_priv;
+	int (*reg_read)(void *, u16, u8 *);
+	int (*reg_write)(void *, u16, u8);
+};
+
+/**
+ * zd1301_demod_get_dvb_frontend() - Get pointer to DVB frontend
+ * @pdev: Pointer to platform device
+ *
+ * Return: Pointer to DVB frontend which given platform device owns.
+ */
+
+struct dvb_frontend *zd1301_demod_get_dvb_frontend(struct platform_device *);
+
+/**
+ * zd1301_demod_get_i2c_adapter() - Get pointer to I2C adapter
+ * @pdev: Pointer to platform device
+ *
+ * Return: Pointer to I2C adapter which given platform device owns.
+ */
+
+struct i2c_adapter *zd1301_demod_get_i2c_adapter(struct platform_device *);
+
+#endif /* ZD1301_DEMOD_H */
-- 
http://palosaari.fi/


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

* [PATCHv2 4/6] MAINTAINERS: add zd1301_demod driver
  2016-05-22  2:13 [PATCHv2 1/6] mt2060: add i2c bindings Antti Palosaari
  2016-05-22  2:13 ` [PATCHv2 2/6] mt2060: add param to split long i2c writes Antti Palosaari
  2016-05-22  2:13 ` [PATCHv2 3/6] zd1301_demod: ZyDAS ZD1301 DVB-T demodulator driver Antti Palosaari
@ 2016-05-22  2:13 ` Antti Palosaari
  2016-05-22  2:13 ` [PATCHv2 5/6] zd1301: ZyDAS ZD1301 DVB USB interface driver Antti Palosaari
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Antti Palosaari @ 2016-05-22  2:13 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

DVB-T demodulator driver for ZyDAS ZD1301 chip.

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

diff --git a/MAINTAINERS b/MAINTAINERS
index 090bec2..a4ae460 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12391,6 +12391,15 @@ L:	zd1211-devs@lists.sourceforge.net (subscribers-only)
 S:	Maintained
 F:	drivers/net/wireless/zydas/zd1211rw/
 
+ZD1301_DEMOD MEDIA DRIVER
+M:	Antti Palosaari <crope@iki.fi>
+L:	linux-media@vger.kernel.org
+W:	https://linuxtv.org/
+W:	http://palosaari.fi/linux/
+Q:	https://patchwork.linuxtv.org/project/linux-media/list/
+S:	Maintained
+F:	drivers/media/dvb-frontends/zd1301_demod*
+
 ZPOOL COMPRESSED PAGE STORAGE API
 M:	Dan Streetman <ddstreet@ieee.org>
 L:	linux-mm@kvack.org
-- 
http://palosaari.fi/


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

* [PATCHv2 5/6] zd1301: ZyDAS ZD1301 DVB USB interface driver
  2016-05-22  2:13 [PATCHv2 1/6] mt2060: add i2c bindings Antti Palosaari
                   ` (2 preceding siblings ...)
  2016-05-22  2:13 ` [PATCHv2 4/6] MAINTAINERS: add zd1301_demod driver Antti Palosaari
@ 2016-05-22  2:13 ` Antti Palosaari
  2016-05-22  2:13 ` [PATCHv2 6/6] MAINTAINERS: add zd1301 " Antti Palosaari
  2016-11-23 21:31 ` [PATCHv2 1/6] mt2060: add i2c bindings Mauro Carvalho Chehab
  5 siblings, 0 replies; 7+ messages in thread
From: Antti Palosaari @ 2016-05-22  2:13 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

ZyDAS ZD1301 is chip having USB interface and DVB-T demodulator
integrated. This driver is for USB interface part.

Device has USB ID 0ace:13a1. Used tuner is MT2060.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/dvb-core/dvb-usb-ids.h  |   1 +
 drivers/media/usb/dvb-usb-v2/Kconfig  |   8 +
 drivers/media/usb/dvb-usb-v2/Makefile |   3 +
 drivers/media/usb/dvb-usb-v2/zd1301.c | 281 ++++++++++++++++++++++++++++++++++
 drivers/media/usb/dvb-usb-v2/zd1301.h |  36 +++++
 5 files changed, 329 insertions(+)
 create mode 100644 drivers/media/usb/dvb-usb-v2/zd1301.c
 create mode 100644 drivers/media/usb/dvb-usb-v2/zd1301.h

diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index a7a4674..7c73086 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -73,6 +73,7 @@
 #define USB_VID_GIGABYTE			0x1044
 #define USB_VID_YUAN				0x1164
 #define USB_VID_XTENSIONS			0x1ae7
+#define USB_VID_ZYDAS				0x0ace
 #define USB_VID_HUMAX_COEX			0x10b9
 #define USB_VID_774				0x7a69
 #define USB_VID_EVOLUTEPC			0x1e59
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 3dc8ef0..c279183 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -151,3 +151,11 @@ config DVB_USB_DVBSKY
 	select DVB_SP2 if MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Say Y here to support the USB receivers from DVBSky.
+
+config DVB_USB_ZD1301
+	tristate "ZyDAS ZD1301"
+	depends on DVB_USB_V2
+	select DVB_ZD1301_DEMOD if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y here to support the ZyDAS ZD1301 DVB USB receiver.
diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile
index f10d4df..969f68e 100644
--- a/drivers/media/usb/dvb-usb-v2/Makefile
+++ b/drivers/media/usb/dvb-usb-v2/Makefile
@@ -40,6 +40,9 @@ obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
 dvb-usb-dvbsky-objs := dvbsky.o
 obj-$(CONFIG_DVB_USB_DVBSKY) += dvb-usb-dvbsky.o
 
+dvb-usb-zd1301-objs := zd1301.o
+obj-$(CONFIG_DVB_USB_ZD1301) += zd1301.o
+
 ccflags-y += -I$(srctree)/drivers/media/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
 ccflags-y += -I$(srctree)/drivers/media/tuners
diff --git a/drivers/media/usb/dvb-usb-v2/zd1301.c b/drivers/media/usb/dvb-usb-v2/zd1301.c
new file mode 100644
index 0000000..677563b
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/zd1301.c
@@ -0,0 +1,281 @@
+/*
+ * ZyDAS ZD1301 driver (USB interface)
+ *
+ * Copyright (C) 2015 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.
+ */
+
+#include "zd1301.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int zd1301_ctrl_msg(struct dvb_usb_device *d, const u8 *wbuf,
+			   unsigned int wlen, u8 *rbuf, unsigned int rlen)
+{
+	struct zd1301_dev *dev = d_to_priv(d);
+	struct usb_interface *intf = d->intf;
+	int ret, actual_length;
+
+	mutex_lock(&d->usb_mutex);
+
+	memcpy(&dev->buf, wbuf, wlen);
+
+	dev_dbg(&intf->dev, ">>> %*ph\n", wlen, dev->buf);
+
+	ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, 0x04), dev->buf,
+			   wlen, &actual_length, 1000);
+	if (ret) {
+		dev_err(&intf->dev, "1st usb_bulk_msg() failed %d\n", ret);
+		goto err_mutex_unlock;
+	}
+
+	if (rlen) {
+		ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, 0x83),
+				   dev->buf, rlen, &actual_length, 1000);
+		if (ret) {
+			dev_err(&intf->dev,
+				"2nd usb_bulk_msg() failed %d\n", ret);
+			goto err_mutex_unlock;
+		}
+
+		dev_dbg(&intf->dev, "<<< %*ph\n", actual_length, dev->buf);
+
+		if (actual_length != rlen) {
+			/*
+			 * Chip replies often with 3 byte len stub. On that case
+			 * we have to query new reply.
+			 */
+			dev_dbg(&intf->dev, "repeating reply message\n");
+
+			ret = usb_bulk_msg(d->udev,
+					   usb_rcvbulkpipe(d->udev, 0x83),
+					   dev->buf, rlen, &actual_length,
+					   1000);
+			if (ret) {
+				dev_err(&intf->dev,
+					"3rd usb_bulk_msg() failed %d\n", ret);
+				goto err_mutex_unlock;
+			}
+
+			dev_dbg(&intf->dev,
+				"<<< %*ph\n", actual_length, dev->buf);
+		}
+
+		memcpy(rbuf, dev->buf, rlen);
+	}
+
+err_mutex_unlock:
+	mutex_unlock(&d->usb_mutex);
+	return ret;
+}
+
+static int zd1301_demod_wreg(void *reg_priv, u16 reg, u8 val)
+{
+	struct dvb_usb_device *d = reg_priv;
+	struct usb_interface *intf = d->intf;
+	int ret;
+	u8 buf[7] = {0x07, 0x00, 0x03, 0x01,
+		     (reg >> 0) & 0xff, (reg >> 8) & 0xff, val};
+
+	ret = zd1301_ctrl_msg(d, buf, 7, NULL, 0);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&intf->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int zd1301_demod_rreg(void *reg_priv, u16 reg, u8 *val)
+{
+	struct dvb_usb_device *d = reg_priv;
+	struct usb_interface *intf = d->intf;
+	int ret;
+	u8 buf[7] = {0x07, 0x00, 0x04, 0x01,
+		     (reg >> 0) & 0xff, (reg >> 8) & 0xff, 0};
+
+	ret = zd1301_ctrl_msg(d, buf, 7, buf, 7);
+	if (ret)
+		goto err;
+
+	*val = buf[6];
+
+	return 0;
+err:
+	dev_dbg(&intf->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int zd1301_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap_to_d(adap);
+	struct zd1301_dev *dev = adap_to_priv(adap);
+	struct usb_interface *intf = d->intf;
+	struct platform_device *pdev;
+	struct i2c_client *client;
+	struct i2c_board_info board_info;
+	struct i2c_adapter *adapter;
+	struct dvb_frontend *frontend;
+	int ret;
+
+	dev_dbg(&intf->dev, "\n");
+
+	/* Add platform demod */
+	dev->demod_pdata.reg_priv = d;
+	dev->demod_pdata.reg_read = zd1301_demod_rreg;
+	dev->demod_pdata.reg_write = zd1301_demod_wreg;
+	request_module("%s", "zd1301_demod");
+	pdev = platform_device_register_data(&intf->dev,
+					     "zd1301_demod",
+					     PLATFORM_DEVID_AUTO,
+					     &dev->demod_pdata,
+					     sizeof(dev->demod_pdata));
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		goto err;
+	}
+	if (!pdev->dev.driver) {
+		ret = -ENODEV;
+		goto err;
+	}
+	if (!try_module_get(pdev->dev.driver->owner)) {
+		ret = -ENODEV;
+		goto err_platform_device_unregister;
+	}
+
+	adapter = zd1301_demod_get_i2c_adapter(pdev);
+	frontend = zd1301_demod_get_dvb_frontend(pdev);
+
+	/* Add I2C tuner */
+	dev->mt2060_pdata.i2c_write_max = 9;
+	dev->mt2060_pdata.dvb_frontend = frontend;
+	memset(&board_info, 0, sizeof(board_info));
+	strlcpy(board_info.type, "mt2060", I2C_NAME_SIZE);
+	board_info.addr = 0x60;
+	board_info.platform_data = &dev->mt2060_pdata;
+	request_module("%s", "mt2060");
+	client = i2c_new_device(adapter, &board_info);
+	if (!client || !client->dev.driver) {
+		ret = -ENODEV;
+		goto err_module_put_demod;
+	}
+	if (!try_module_get(client->dev.driver->owner)) {
+		ret = -ENODEV;
+		goto err_i2c_unregister_device;
+	}
+
+	dev->platform_device_demod = pdev;
+	dev->i2c_client_tuner = client;
+	adap->fe[0] = frontend;
+
+	return 0;
+err_i2c_unregister_device:
+	i2c_unregister_device(client);
+err_module_put_demod:
+	module_put(pdev->dev.driver->owner);
+err_platform_device_unregister:
+	platform_device_unregister(pdev);
+err:
+	dev_dbg(&intf->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static int zd1301_frontend_detach(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap_to_d(adap);
+	struct zd1301_dev *dev = d_to_priv(d);
+	struct usb_interface *intf = d->intf;
+	struct platform_device *pdev;
+	struct i2c_client *client;
+
+	dev_dbg(&intf->dev, "\n");
+
+	client = dev->i2c_client_tuner;
+	pdev = dev->platform_device_demod;
+
+	/* Remove I2C tuner */
+	if (client) {
+		module_put(client->dev.driver->owner);
+		i2c_unregister_device(client);
+	}
+
+	/* Remove platform demod */
+	if (pdev) {
+		module_put(pdev->dev.driver->owner);
+		platform_device_unregister(pdev);
+	}
+
+	return 0;
+}
+
+static int zd1301_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+	struct dvb_usb_device *d = fe_to_d(fe);
+	struct usb_interface *intf = d->intf;
+	int ret;
+	u8 buf[3] = {0x03, 0x00, onoff ? 0x07 : 0x08};
+
+	dev_dbg(&intf->dev, "onoff=%d\n", onoff);
+
+	ret = zd1301_ctrl_msg(d, buf, 3, NULL, 0);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	dev_dbg(&intf->dev, "failed=%d\n", ret);
+	return ret;
+}
+
+static const struct dvb_usb_device_properties zd1301_props = {
+	.driver_name = KBUILD_MODNAME,
+	.owner = THIS_MODULE,
+	.adapter_nr = adapter_nr,
+	.size_of_priv = sizeof(struct zd1301_dev),
+
+	.frontend_attach = zd1301_frontend_attach,
+	.frontend_detach = zd1301_frontend_detach,
+	.streaming_ctrl  = zd1301_streaming_ctrl,
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.stream = DVB_USB_STREAM_BULK(0x81, 6, 21 * 188),
+		},
+	},
+};
+
+static const struct usb_device_id zd1301_id_table[] = {
+	{DVB_USB_DEVICE(USB_VID_ZYDAS, 0x13a1, &zd1301_props,
+			"ZyDAS ZD1301 reference design", NULL)},
+	{}
+};
+MODULE_DEVICE_TABLE(usb, zd1301_id_table);
+
+/* Usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver zd1301_usb_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = zd1301_id_table,
+	.probe = dvb_usbv2_probe,
+	.disconnect = dvb_usbv2_disconnect,
+	.suspend = dvb_usbv2_suspend,
+	.resume = dvb_usbv2_resume,
+	.reset_resume = dvb_usbv2_reset_resume,
+	.no_dynamic_id = 1,
+	.soft_unbind = 1,
+};
+module_usb_driver(zd1301_usb_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("ZyDAS ZD1301 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/usb/dvb-usb-v2/zd1301.h b/drivers/media/usb/dvb-usb-v2/zd1301.h
new file mode 100644
index 0000000..b0c5075
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/zd1301.h
@@ -0,0 +1,36 @@
+/*
+ * ZyDAS ZD1301 driver (USB interface)
+ *
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ZD1301_H
+#define ZD1301_H
+
+#include "dvb_usb.h"
+#include "zd1301_demod.h"
+#include "mt2060.h"
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+struct zd1301_dev {
+	#define BUF_LEN 8
+	u8 buf[BUF_LEN]; /* bulk USB control message */
+	struct dvb_usb_device *d;
+	struct zd1301_demod_platform_data demod_pdata;
+	struct mt2060_platform_data mt2060_pdata;
+	struct platform_device *platform_device_demod;
+	struct i2c_client *i2c_client_tuner;
+};
+
+#endif
-- 
http://palosaari.fi/


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

* [PATCHv2 6/6] MAINTAINERS: add zd1301 DVB USB interface driver
  2016-05-22  2:13 [PATCHv2 1/6] mt2060: add i2c bindings Antti Palosaari
                   ` (3 preceding siblings ...)
  2016-05-22  2:13 ` [PATCHv2 5/6] zd1301: ZyDAS ZD1301 DVB USB interface driver Antti Palosaari
@ 2016-05-22  2:13 ` Antti Palosaari
  2016-11-23 21:31 ` [PATCHv2 1/6] mt2060: add i2c bindings Mauro Carvalho Chehab
  5 siblings, 0 replies; 7+ messages in thread
From: Antti Palosaari @ 2016-05-22  2:13 UTC (permalink / raw)
  To: linux-media; +Cc: Antti Palosaari

DVB USB interface driver for ZyDAS ZD1301 chip.

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

diff --git a/MAINTAINERS b/MAINTAINERS
index a4ae460..d2559e1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12400,6 +12400,15 @@ Q:	https://patchwork.linuxtv.org/project/linux-media/list/
 S:	Maintained
 F:	drivers/media/dvb-frontends/zd1301_demod*
 
+ZD1301 MEDIA DRIVER
+M:	Antti Palosaari <crope@iki.fi>
+L:	linux-media@vger.kernel.org
+W:	https://linuxtv.org/
+W:	http://palosaari.fi/linux/
+Q:	https://patchwork.linuxtv.org/project/linux-media/list/
+S:	Maintained
+F:	drivers/media/usb/dvb-usb-v2/zd1301*
+
 ZPOOL COMPRESSED PAGE STORAGE API
 M:	Dan Streetman <ddstreet@ieee.org>
 L:	linux-mm@kvack.org
-- 
http://palosaari.fi/


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

* Re: [PATCHv2 1/6] mt2060: add i2c bindings
  2016-05-22  2:13 [PATCHv2 1/6] mt2060: add i2c bindings Antti Palosaari
                   ` (4 preceding siblings ...)
  2016-05-22  2:13 ` [PATCHv2 6/6] MAINTAINERS: add zd1301 " Antti Palosaari
@ 2016-11-23 21:31 ` Mauro Carvalho Chehab
  5 siblings, 0 replies; 7+ messages in thread
From: Mauro Carvalho Chehab @ 2016-11-23 21:31 UTC (permalink / raw)
  To: Antti Palosaari; +Cc: linux-media

Hi Antti,

Em Sun, 22 May 2016 05:13:46 +0300
Antti Palosaari <crope@iki.fi> escreveu:

> Add proper i2c driver model bindings.

Just like version 1 of this series, I'm marking it as RFC.

Please add those patches into a git tree and send me a pull request
when you believe that they're ready for merging.

Thanks!
Mauro

> 
> Signed-off-by: Antti Palosaari <crope@iki.fi>
> ---
>  drivers/media/tuners/mt2060.c      | 83 ++++++++++++++++++++++++++++++++++++++
>  drivers/media/tuners/mt2060.h      | 20 +++++++++
>  drivers/media/tuners/mt2060_priv.h |  2 +
>  3 files changed, 105 insertions(+)
> 
> diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c
> index b87b254..aa8280a 100644
> --- a/drivers/media/tuners/mt2060.c
> +++ b/drivers/media/tuners/mt2060.c
> @@ -397,6 +397,89 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
>  }
>  EXPORT_SYMBOL(mt2060_attach);
>  
> +static int mt2060_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct mt2060_platform_data *pdata = client->dev.platform_data;
> +	struct dvb_frontend *fe;
> +	struct mt2060_priv *dev;
> +	int ret;
> +	u8 chip_id;
> +
> +	dev_dbg(&client->dev, "\n");
> +
> +	if (!pdata) {
> +		dev_err(&client->dev, "Cannot proceed without platform data\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
> +	if (!dev) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	fe = pdata->dvb_frontend;
> +	dev->config.i2c_address = client->addr;
> +	dev->config.clock_out = pdata->clock_out;
> +	dev->cfg = &dev->config;
> +	dev->i2c = client->adapter;
> +	dev->if1_freq = pdata->if1 ? pdata->if1 : 1220;
> +	dev->client = client;
> +
> +	ret = mt2060_readreg(dev, REG_PART_REV, &chip_id);
> +	if (ret) {
> +		ret = -ENODEV;
> +		goto err;
> +	}
> +
> +	dev_dbg(&client->dev, "chip id=%02x\n", chip_id);
> +
> +	if (chip_id != PART_REV) {
> +		ret = -ENODEV;
> +		goto err;
> +	}
> +
> +	dev_info(&client->dev, "Microtune MT2060 successfully identified\n");
> +	memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(fe->ops.tuner_ops));
> +	fe->ops.tuner_ops.release = NULL;
> +	fe->tuner_priv = dev;
> +	i2c_set_clientdata(client, dev);
> +
> +	mt2060_calibrate(dev);
> +
> +	return 0;
> +err:
> +	dev_dbg(&client->dev, "failed=%d\n", ret);
> +	return ret;
> +}
> +
> +static int mt2060_remove(struct i2c_client *client)
> +{
> +	dev_dbg(&client->dev, "\n");
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id mt2060_id_table[] = {
> +	{"mt2060", 0},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, mt2060_id_table);
> +
> +static struct i2c_driver mt2060_driver = {
> +	.driver = {
> +		.name = "mt2060",
> +		.suppress_bind_attrs = true,
> +	},
> +	.probe		= mt2060_probe,
> +	.remove		= mt2060_remove,
> +	.id_table	= mt2060_id_table,
> +};
> +
> +module_i2c_driver(mt2060_driver);
> +
>  MODULE_AUTHOR("Olivier DANET");
>  MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver");
>  MODULE_LICENSE("GPL");
> diff --git a/drivers/media/tuners/mt2060.h b/drivers/media/tuners/mt2060.h
> index 6efed35..05c0d55 100644
> --- a/drivers/media/tuners/mt2060.h
> +++ b/drivers/media/tuners/mt2060.h
> @@ -25,6 +25,26 @@
>  struct dvb_frontend;
>  struct i2c_adapter;
>  
> +/*
> + * I2C address
> + * 0x60, ...
> + */
> +
> +/**
> + * struct mt2060_platform_data - Platform data for the mt2060 driver
> + * @clock_out: Clock output setting. 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1.
> + * @if1: First IF used [MHz]. 0 defaults to 1220.
> + * @dvb_frontend: DVB frontend.
> + */
> +
> +struct mt2060_platform_data {
> +	u8 clock_out;
> +	u16 if1;
> +	struct dvb_frontend *dvb_frontend;
> +};
> +
> +
> +/* configuration struct for mt2060_attach() */
>  struct mt2060_config {
>  	u8 i2c_address;
>  	u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
> diff --git a/drivers/media/tuners/mt2060_priv.h b/drivers/media/tuners/mt2060_priv.h
> index 2b60de6..dfc4a06 100644
> --- a/drivers/media/tuners/mt2060_priv.h
> +++ b/drivers/media/tuners/mt2060_priv.h
> @@ -95,6 +95,8 @@
>  struct mt2060_priv {
>  	struct mt2060_config *cfg;
>  	struct i2c_adapter   *i2c;
> +	struct i2c_client *client;
> +	struct mt2060_config config;
>  
>  	u32 frequency;
>  	u16 if1_freq;


Thanks,
Mauro

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

end of thread, other threads:[~2016-11-23 21:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-22  2:13 [PATCHv2 1/6] mt2060: add i2c bindings Antti Palosaari
2016-05-22  2:13 ` [PATCHv2 2/6] mt2060: add param to split long i2c writes Antti Palosaari
2016-05-22  2:13 ` [PATCHv2 3/6] zd1301_demod: ZyDAS ZD1301 DVB-T demodulator driver Antti Palosaari
2016-05-22  2:13 ` [PATCHv2 4/6] MAINTAINERS: add zd1301_demod driver Antti Palosaari
2016-05-22  2:13 ` [PATCHv2 5/6] zd1301: ZyDAS ZD1301 DVB USB interface driver Antti Palosaari
2016-05-22  2:13 ` [PATCHv2 6/6] MAINTAINERS: add zd1301 " Antti Palosaari
2016-11-23 21:31 ` [PATCHv2 1/6] mt2060: add i2c bindings Mauro Carvalho Chehab

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