* [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).