Skip to contentSkip to conversation

ngene-updates: Hardware support, TS buffer shift fix

PATCH Superseded Daniel Scheller 1 participant 13 messages Feb 24, 2018 Media (V4L/DVB)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover ngene-updates: Hardware support, TS buffer shift fix

From: Daniel Scheller <[email protected]>

Some love for the ngene driver, which runs the older Micronas nGene based cards from Digital Devices like the cineS2 v5.5.

This series changes:

- Two more PCI IDs for more supported PCIe bridge hardware
- Conversion of all printk() to more proper dev_*() based logging (the module_init rather uses pr_*() logging since there's no *dev available yet).
- Support for all available DuoFlex addon modules from that vendor. All addon modules are electrically compatible and use the same interface, and the addons work very well on the aging ngene hardware.
- Check for CXD2099AR addon modules before blindly trying to attach them, removing unnecessary and maybe irritating error logging if such module isn't present.
- Workaround a hardware quirk in conjunction with the CXD2099AR module, CA modules and CAM control communication, causing the TS input buffer to shift albeit different communication paths are ought to be in use.

This series is based on the CXD2099 regmap conversion series, see [1]. Especially [2] is required for the STV0367 enablement patch (and the following ones) since the use of the TDA18212 I2C tuner client relies on the i2c_client variable to be present, which is added in the forementioned CXD2099 series.

Please pick this up and merge.

[1] https://www.spinics.net/lists/linux-media/msg129183.html
[2] https://www.spinics.net/lists/linux-media/msg129187.html

Daniel Scheller (12):
[media] ngene: add two additional PCI IDs
[media] ngene: convert kernellog printing from printk() to dev_*()
macros
[media] ngene: use defines to identify the demod_type [media] ngene: support STV0367 DVB-C/T DuoFlex addons [media] ngene: add XO2 module support
[media] ngene: add support for Sony CXD28xx-based DuoFlex modules [media] ngene: add support for DuoFlex S2 V4 addon modules [media] ngene: deduplicate I2C adapter evaluation [media] ngene: check for CXD2099AR presence before attaching [media] ngene: don't treat non-existing demods as error [media] ngene: move the tsin_exchange() stripcopy block into a
function
[media] ngene: compensate for TS buffer offset shifts

 drivers/media/pci/ngene/Kconfig       |   6 +
 drivers/media/pci/ngene/ngene-cards.c | 590 ++++++++++++++++++++++++++++++----
 drivers/media/pci/ngene/ngene-core.c  | 101 +++---
 drivers/media/pci/ngene/ngene-dvb.c   | 122 +++++--
 drivers/media/pci/ngene/ngene.h       |  23 ++
 5 files changed, 721 insertions(+), 121 deletions(-)
Signature · 1 line
2.16.1
#1 lore raw

PATCH 1/12 ngene: add two additional PCI IDs

From: Daniel Scheller <[email protected]>

Add two more device IDs for cards supported by the ngene driver.

---
 drivers/media/pci/ngene/ngene-cards.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/media/pci/ngene/ngene-cards.c +2 -0
index bb49620540c5..49f78bb31537 100644
@@ -749,6 +749,8 @@ static const struct ngene_info ngene_info_terratec = {
 /****************************************************************************/
 
 static const struct pci_device_id ngene_id_tbl[] = {
+	NGENE_ID(0x18c3, 0xab04, ngene_info_cineS2),
+	NGENE_ID(0x18c3, 0xab05, ngene_info_cineS2v5),
 	NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
 	NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
 	NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
Signature · 1 line
2.16.1
#2 lore raw

PATCH 2/12 ngene: convert kernellog printing from printk() to dev_*() macros

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

Convert all printk() and pr_*() kernel log printing to rather use the dev_*() macros. Not only is it discouraged to use printk() (checkpatch even complains about that), but also this helps identifying the exact PCI device for any printed event, and it makes almost all printing shorter in terms of code style since there's no need to use KERN_* DEVICE_NAME any more (dev_*() will take care of this).

Since the dprintk macro define isn't used anymore, remove it.

---
 drivers/media/pci/ngene/ngene-cards.c | 75 ++++++++++++++++++++---------------
 drivers/media/pci/ngene/ngene-core.c  | 75 +++++++++++++++++------------------
 drivers/media/pci/ngene/ngene-dvb.c   |  4 +-
 3 files changed, 84 insertions(+), 70 deletions(-)
drivers/media/pci/ngene/ngene-cards.c +44 -31
index 49f78bb31537..16666de8cbee 100644
@@ -23,6 +23,8 @@
  * http://www.gnu.org/copyleft/gpl.html
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/pci.h>
@@ -48,6 +50,7 @@
 
 static int tuner_attach_stv6110(struct ngene_channel *chan)
 {
+	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct i2c_adapter *i2c;
 	struct stv090x_config *feconf = (struct stv090x_config *)
 		chan->dev->card_info->fe_config[chan->number];
@@ -63,7 +66,7 @@ static int tuner_attach_stv6110(struct ngene_channel *chan)
 
 	ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
 	if (ctl == NULL) {
-		printk(KERN_ERR	DEVICE_NAME ": No STV6110X found!\n");
+		dev_err(pdev, "No STV6110X found!\n");
 		return -ENODEV;
 	}
 
@@ -100,6 +103,7 @@ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
 
 static int tuner_attach_tda18271(struct ngene_channel *chan)
 {
+	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct i2c_adapter *i2c;
 	struct dvb_frontend *fe;
 
@@ -110,7 +114,7 @@ static int tuner_attach_tda18271(struct ngene_channel *chan)
 	if (chan->fe->ops.i2c_gate_ctrl)
 		chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
 	if (!fe) {
-		printk(KERN_ERR "No TDA18271 found!\n");
+		dev_err(pdev, "No TDA18271 found!\n");
 		return -ENODEV;
 	}
 
@@ -128,6 +132,7 @@ static int tuner_attach_probe(struct ngene_channel *chan)
 
 static int demod_attach_stv0900(struct ngene_channel *chan)
 {
+	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct i2c_adapter *i2c;
 	struct stv090x_config *feconf = (struct stv090x_config *)
 		chan->dev->card_info->fe_config[chan->number];
@@ -144,7 +149,7 @@ static int demod_attach_stv0900(struct ngene_channel *chan)
 			(chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
 						: STV090x_DEMODULATOR_1);
 	if (chan->fe == NULL) {
-		printk(KERN_ERR	DEVICE_NAME ": No STV0900 found!\n");
+		dev_err(pdev, "No STV0900 found!\n");
 		return -ENODEV;
 	}
 
@@ -154,7 +159,7 @@ static int demod_attach_stv0900(struct ngene_channel *chan)
 
 	if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
 			0, chan->dev->card_info->lnb[chan->number])) {
-		printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
+		dev_err(pdev, "No LNBH24 found!\n");
 		dvb_frontend_detach(chan->fe);
 		chan->fe = NULL;
 		return -ENODEV;
@@ -211,6 +216,7 @@ static int port_has_drxk(struct i2c_adapter *i2c, int port)
 static int demod_attach_drxk(struct ngene_channel *chan,
 			     struct i2c_adapter *i2c)
 {
+	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct drxk_config config;
 
 	memset(&config, 0, sizeof(config));
@@ -220,7 +226,7 @@ static int demod_attach_drxk(struct ngene_channel *chan,
 
 	chan->fe = dvb_attach(drxk_attach, &config, i2c);
 	if (!chan->fe) {
-		printk(KERN_ERR "No DRXK found!\n");
+		dev_err(pdev, "No DRXK found!\n");
 		return -ENODEV;
 	}
 	chan->fe->sec_priv = chan;
@@ -231,6 +237,7 @@ static int demod_attach_drxk(struct ngene_channel *chan,
 
 static int cineS2_probe(struct ngene_channel *chan)
 {
+	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct i2c_adapter *i2c;
 	struct stv090x_config *fe_conf;
 	u8 buf[3];
@@ -269,14 +276,14 @@ static int cineS2_probe(struct ngene_channel *chan)
 		}
 		rc = i2c_transfer(i2c, &i2c_msg, 1);
 		if (rc != 1) {
-			printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+			dev_err(pdev, "Could not setup DPNx\n");
 			return -EIO;
 		}
 	} else if (port_has_drxk(i2c, chan->number^2)) {
 		chan->demod_type = 1;
 		demod_attach_drxk(chan, i2c);
 	} else {
-		printk(KERN_ERR "No demod found on chan %d\n", chan->number);
+		dev_err(pdev, "No demod found on chan %d\n", chan->number);
 		return -ENODEV;
 	}
 	return 0;
@@ -299,9 +306,11 @@ static struct mt2131_config m780_tunerconfig = {
  */
 static int demod_attach_lg330x(struct ngene_channel *chan)
 {
+	struct device *pdev = &chan->dev->pci_dev->dev;
+
 	chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter);
 	if (chan->fe == NULL) {
-		printk(KERN_ERR	DEVICE_NAME ": No LGDT330x found!\n");
+		dev_err(pdev, "No LGDT330x found!\n");
 		return -ENODEV;
 	}
 
@@ -313,6 +322,7 @@ static int demod_attach_lg330x(struct ngene_channel *chan)
 
 static int demod_attach_drxd(struct ngene_channel *chan)
 {
+	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct drxd_config *feconf;
 
 	feconf = chan->dev->card_info->fe_config[chan->number];
@@ -320,7 +330,7 @@ static int demod_attach_drxd(struct ngene_channel *chan)
 	chan->fe = dvb_attach(drxd_attach, feconf, chan,
 			&chan->i2c_adapter, &chan->dev->pci_dev->dev);
 	if (!chan->fe) {
-		pr_err("No DRXD found!\n");
+		dev_err(pdev, "No DRXD found!\n");
 		return -ENODEV;
 	}
 	return 0;
@@ -328,6 +338,7 @@ static int demod_attach_drxd(struct ngene_channel *chan)
 
 static int tuner_attach_dtt7520x(struct ngene_channel *chan)
 {
+	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct drxd_config *feconf;
 
 	feconf = chan->dev->card_info->fe_config[chan->number];
@@ -335,7 +346,7 @@ static int tuner_attach_dtt7520x(struct ngene_channel *chan)
 	if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address,
 			&chan->i2c_adapter,
 			feconf->pll_type)) {
-		pr_err("No pll(%d) found!\n", feconf->pll_type);
+		dev_err(pdev, "No pll(%d) found!\n", feconf->pll_type);
 		return -ENODEV;
 	}
 	return 0;
@@ -371,12 +382,13 @@ static int tuner_attach_dtt7520x(struct ngene_channel *chan)
 static int i2c_write_eeprom(struct i2c_adapter *adapter,
 			    u8 adr, u16 reg, u8 data)
 {
+	struct device *pdev = adapter->dev.parent;
 	u8 m[3] = {(reg >> 8), (reg & 0xff), data};
 	struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m,
 			      .len = sizeof(m)};
 
 	if (i2c_transfer(adapter, &msg, 1) != 1) {
-		pr_err(DEVICE_NAME ": Error writing EEPROM!\n");
+		dev_err(pdev, "Error writing EEPROM!\n");
 		return -EIO;
 	}
 	return 0;
@@ -385,6 +397,7 @@ static int i2c_write_eeprom(struct i2c_adapter *adapter,
 static int i2c_read_eeprom(struct i2c_adapter *adapter,
 			   u8 adr, u16 reg, u8 *data, int len)
 {
+	struct device *pdev = adapter->dev.parent;
 	u8 msg[2] = {(reg >> 8), (reg & 0xff)};
 	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
 				   .buf = msg, .len = 2 },
@@ -392,7 +405,7 @@ static int i2c_read_eeprom(struct i2c_adapter *adapter,
 				   .buf = data, .len = len} };
 
 	if (i2c_transfer(adapter, msgs, 2) != 2) {
-		pr_err(DEVICE_NAME ": Error reading EEPROM\n");
+		dev_err(pdev, "Error reading EEPROM\n");
 		return -EIO;
 	}
 	return 0;
@@ -401,6 +414,7 @@ static int i2c_read_eeprom(struct i2c_adapter *adapter,
 static int ReadEEProm(struct i2c_adapter *adapter,
 		      u16 Tag, u32 MaxLen, u8 *data, u32 *pLength)
 {
+	struct device *pdev = adapter->dev.parent;
 	int status = 0;
 	u16 Addr = MICNG_EE_START, Length, tag = 0;
 	u8  EETag[3];
@@ -416,9 +430,8 @@ static int ReadEEProm(struct i2c_adapter *adapter,
 		Addr += sizeof(u16) + 1 + EETag[2];
 	}
 	if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
-		pr_err(DEVICE_NAME
-		       ": Reached EOEE @ Tag = %04x Length = %3d\n",
-		       tag, EETag[2]);
+		dev_err(pdev, "Reached EOEE @ Tag = %04x Length = %3d\n",
+			tag, EETag[2]);
 		return -1;
 	}
 	Length = EETag[2];
@@ -441,6 +454,7 @@ static int ReadEEProm(struct i2c_adapter *adapter,
 static int WriteEEProm(struct i2c_adapter *adapter,
 		       u16 Tag, u32 Length, u8 *data)
 {
+	struct device *pdev = adapter->dev.parent;
 	int status = 0;
 	u16 Addr = MICNG_EE_START;
 	u8 EETag[3];
@@ -458,9 +472,8 @@ static int WriteEEProm(struct i2c_adapter *adapter,
 		Addr += sizeof(u16) + 1 + EETag[2];
 	}
 	if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
-		pr_err(DEVICE_NAME
-		       ": Reached EOEE @ Tag = %04x Length = %3d\n",
-		       tag, EETag[2]);
+		dev_err(pdev, "Reached EOEE @ Tag = %04x Length = %3d\n",
+			tag, EETag[2]);
 		return -1;
 	}
 
@@ -487,13 +500,11 @@ static int WriteEEProm(struct i2c_adapter *adapter,
 			if (status)
 				break;
 			if (Tmp != data[i])
-				pr_err(DEVICE_NAME
-				       "eeprom write error\n");
+				dev_err(pdev, "eeprom write error\n");
 			retry -= 1;
 		}
 		if (status) {
-			pr_err(DEVICE_NAME
-			       ": Timeout polling eeprom\n");
+			dev_err(pdev, "Timeout polling eeprom\n");
 			break;
 		}
 	}
@@ -532,19 +543,20 @@ static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data)
 static s16 osc_deviation(void *priv, s16 deviation, int flag)
 {
 	struct ngene_channel *chan = priv;
+	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct i2c_adapter *adap = &chan->i2c_adapter;
 	u16 data = 0;
 
 	if (flag) {
 		data = (u16) deviation;
-		pr_info(DEVICE_NAME ": write deviation %d\n",
-		       deviation);
+		dev_info(pdev, "write deviation %d\n",
+			 deviation);
 		eeprom_write_ushort(adap, 0x1000 + chan->number, data);
 	} else {
 		if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data))
 			data = 0;
-		pr_info(DEVICE_NAME ": read deviation %d\n",
-		       (s16) data);
+		dev_info(pdev, "read deviation %d\n",
+			 (s16)data);
 	}
 
 	return (s16) data;
@@ -771,7 +783,7 @@ MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
 static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
 					     enum pci_channel_state state)
 {
-	printk(KERN_ERR DEVICE_NAME ": PCI error\n");
+	dev_err(&dev->dev, "PCI error\n");
 	if (state == pci_channel_io_perm_failure)
 		return PCI_ERS_RESULT_DISCONNECT;
 	if (state == pci_channel_io_frozen)
@@ -781,13 +793,13 @@ static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
 
 static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
 {
-	printk(KERN_INFO DEVICE_NAME ": slot reset\n");
+	dev_info(&dev->dev, "slot reset\n");
 	return 0;
 }
 
 static void ngene_resume(struct pci_dev *dev)
 {
-	printk(KERN_INFO DEVICE_NAME ": resume\n");
+	dev_info(&dev->dev, "resume\n");
 }
 
 static const struct pci_error_handlers ngene_errors = {
@@ -807,8 +819,9 @@ static struct pci_driver ngene_pci_driver = {
 
 static __init int module_init_ngene(void)
 {
-	printk(KERN_INFO
-	       "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+	/* pr_*() since we don't have a device to use with dev_*() yet */
+	pr_info("nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+
 	return pci_register_driver(&ngene_pci_driver);
 }
 
drivers/media/pci/ngene/ngene-core.c +37 -38

251 lines not on this page · raw message

drivers/media/pci/ngene/ngene-dvb.c +3 -1
index 03fc218a45e9..f71fd41c762c 100644
@@ -152,7 +152,9 @@ void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
 				stripped++;
 
 			if (ok % 100 == 0 && overflow)
-				printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped);
+				dev_warn(&dev->pci_dev->dev,
+					 "%s: ok %u overflow %u dropped %u\n",
+					 __func__, ok, overflow, stripped);
 #endif
 			buf += 188;
 			len -= 188;
Signature · 1 line
2.16.1
#3 lore raw

PATCH 3/12 ngene: use defines to identify the demod_type

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

Make it more clear which demod_type is used for which hardware by having defines for the possible demod_type values. With that, change the demod_type evaluation in tuner_attach_probe() to a switch-case instead of an if() for each possible value.

---
 drivers/media/pci/ngene/ngene-cards.c | 11 +++++++----
 drivers/media/pci/ngene/ngene.h       |  3 +++
 2 files changed, 10 insertions(+), 4 deletions(-)
drivers/media/pci/ngene/ngene-cards.c +7 -4
index 16666de8cbee..065b83ee569b 100644
@@ -123,10 +123,13 @@ static int tuner_attach_tda18271(struct ngene_channel *chan)
 
 static int tuner_attach_probe(struct ngene_channel *chan)
 {
-	if (chan->demod_type == 0)
+	switch (chan->demod_type) {
+	case DEMOD_TYPE_STV090X:
 		return tuner_attach_stv6110(chan);
-	if (chan->demod_type == 1)
+	case DEMOD_TYPE_DRXK:
 		return tuner_attach_tda18271(chan);
+	}
+
 	return -EINVAL;
 }
 
@@ -251,7 +254,7 @@ static int cineS2_probe(struct ngene_channel *chan)
 		i2c = &chan->dev->channel[1].i2c_adapter;
 
 	if (port_has_stv0900(i2c, chan->number)) {
-		chan->demod_type = 0;
+		chan->demod_type = DEMOD_TYPE_STV090X;
 		fe_conf = chan->dev->card_info->fe_config[chan->number];
 		/* demod found, attach it */
 		rc = demod_attach_stv0900(chan);
@@ -280,7 +283,7 @@ static int cineS2_probe(struct ngene_channel *chan)
 			return -EIO;
 		}
 	} else if (port_has_drxk(i2c, chan->number^2)) {
-		chan->demod_type = 1;
+		chan->demod_type = DEMOD_TYPE_DRXK;
 		demod_attach_drxk(chan, i2c);
 	} else {
 		dev_err(pdev, "No demod found on chan %d\n", chan->number);
drivers/media/pci/ngene/ngene.h +3 -0
index caf8602c7459..9724701a3274 100644
@@ -51,6 +51,9 @@
 #define VIDEO_CAP_MPEG4 512
 #endif
 
+#define DEMOD_TYPE_STV090X	0
+#define DEMOD_TYPE_DRXK		1
+
 enum STREAM {
 	STREAM_VIDEOIN1 = 0,        /* ITU656 or TS Input */
 	STREAM_VIDEOIN2,
Signature · 1 line
2.16.1
#4 lore raw

PATCH 4/12 ngene: support STV0367 DVB-C/T DuoFlex addons

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

Add support for STV0367+TDA18212 based DuoFlex CT addon modules. For this, add a demod probe function and all necessary demod/tuner attach functions which use existing auxiliary drivers (stv0367 and tda18212) to support this hardware. As tda18212 is an I2C client driver, proper cleanup code is added to the deregistration sequence in ngene-core. To not cause use- after-free situations when there's a CXD2099 I2C client connected, which is rather freed in ngene-core.c:cxd_detach(), add i2c_client_fe to struct ngene_channel to keep track if the i2c_client was allocated by a frontend driver, rather than the CI code paths. Also move the I2C access functions to the top of the file and add the required read_regs() function for the tda18212 ping to work.

This adds autoselection (if MEDIA_SUBDRV_AUTOSELECT) of the STV0367 demod driver and TDA18212 tuner driver to Kconfig aswell.

---
This and the following patches require the i2c_client variable that is added by https://www.spinics.net/lists/linux-media/msg129187.html so that makes it a somewhat hard-dependency.

 drivers/media/pci/ngene/Kconfig       |   2 +
 drivers/media/pci/ngene/ngene-cards.c | 194 ++++++++++++++++++++++++++++++----
 drivers/media/pci/ngene/ngene-core.c  |  12 +++
 drivers/media/pci/ngene/ngene.h       |   2 +
 4 files changed, 191 insertions(+), 19 deletions(-)
drivers/media/pci/ngene/Kconfig +2 -0
index 390ed75fe438..c3254f9dc8ad 100644
@@ -8,6 +8,8 @@ config DVB_NGENE
 	select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
+	select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT
 	---help---
 	  Support for Micronas PCI express cards with nGene bridge.
drivers/media/pci/ngene/ngene-cards.c +175 -19

244 lines not on this page · raw message

drivers/media/pci/ngene/ngene-core.c +12 -0
index a63f019fb62f..526d0adfa427 100644
@@ -1408,6 +1408,7 @@ static void release_channel(struct ngene_channel *chan)
 {
 	struct dvb_demux *dvbdemux = &chan->demux;
 	struct ngene *dev = chan->dev;
+	struct i2c_client *client;
 
 	if (chan->running)
 		set_transfer(chan, 0);
@@ -1424,6 +1425,16 @@ static void release_channel(struct ngene_channel *chan)
 
 	if (chan->fe) {
 		dvb_unregister_frontend(chan->fe);
+
+		/* release I2C client (tuner) if needed */
+		client = chan->i2c_client[0];
+		if (chan->i2c_client_fe && client) {
+			module_put(client->dev.driver->owner);
+			i2c_unregister_device(client);
+			chan->i2c_client[0] = NULL;
+			client = NULL;
+		}
+
 		dvb_frontend_detach(chan->fe);
 		chan->fe = NULL;
 	}
@@ -1459,6 +1470,7 @@ static int init_channel(struct ngene_channel *chan)
 	chan->users = 0;
 	chan->type = io;
 	chan->mode = chan->type;	/* for now only one mode */
+	chan->i2c_client_fe = 0;	/* be sure this is set to zero */
 
 	if (io & NGENE_IO_TSIN) {
 		chan->fe = NULL;
drivers/media/pci/ngene/ngene.h +2 -0
index 9724701a3274..1b88a9aa7aac 100644
@@ -53,6 +53,7 @@
 
 #define DEMOD_TYPE_STV090X	0
 #define DEMOD_TYPE_DRXK		1
+#define DEMOD_TYPE_STV0367	2
 
 enum STREAM {
 	STREAM_VIDEOIN1 = 0,        /* ITU656 or TS Input */
@@ -634,6 +635,7 @@ struct ngene_channel {
 	struct device         device;
 	struct i2c_adapter    i2c_adapter;
 	struct i2c_client    *i2c_client[1];
+	int                   i2c_client_fe;
 
 	struct ngene         *dev;
 	int                   number;
Signature · 1 line
2.16.1
#5 lore raw

PATCH 5/12 ngene: add XO2 module support

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

Detect and initialise modules equipped with XO2 interfaces (Lattice MachXO2). This requires a few more I2C transfer functions which this adds aswell. Defines for the different possible (available) module types are added to ngene.h. The support for the actual tuners contained on these addon modules is kept separate from this commit and is being added with the next commits.

The xo2names array is temporarily marked __maybe_unused to silence a corresponding compiler warning at this stage.

---
 drivers/media/pci/ngene/ngene-cards.c | 144 +++++++++++++++++++++++++++++++++-
 drivers/media/pci/ngene/ngene.h       |  12 +++
 2 files changed, 155 insertions(+), 1 deletion(-)
drivers/media/pci/ngene/ngene-cards.c +143 -1
index 7ec5f68b1ec7..05b8e56999ec 100644
@@ -50,6 +50,32 @@
 /* I2C transfer functions used for demod/tuner probing***********************/
 /****************************************************************************/
 
+static int i2c_io(struct i2c_adapter *adapter, u8 adr,
+		  u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
+{
+	struct i2c_msg msgs[2] = {{.addr = adr,  .flags = 0,
+				   .buf  = wbuf, .len   = wlen },
+				  {.addr = adr,  .flags = I2C_M_RD,
+				   .buf  = rbuf,  .len   = rlen } };
+	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+	struct i2c_msg msg = {.addr = adr, .flags = 0,
+			      .buf = data, .len = len};
+
+	return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_write_reg(struct i2c_adapter *adap, u8 adr,
+			 u8 reg, u8 val)
+{
+	u8 msg[2] = {reg, val};
+
+	return i2c_write(adap, adr, msg, 2);
+}
+
 static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
 {
 	struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
@@ -78,6 +104,12 @@ static int i2c_read_regs(struct i2c_adapter *adapter,
 
 	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
 }
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+{
+	return i2c_read_regs(adapter, adr, reg, val, 1);
+}
+
 /****************************************************************************/
 /* Demod/tuner attachment ***************************************************/
 /****************************************************************************/
@@ -390,12 +422,98 @@ static int demod_attach_drxk(struct ngene_channel *chan,
 	return 0;
 }
 
+/****************************************************************************/
+/* XO2 related lists and functions ******************************************/
+/****************************************************************************/
+
+static char __maybe_unused *xo2names[] = {
+	"DUAL DVB-S2",
+	"DUAL DVB-C/T/T2",
+	"DUAL DVB-ISDBT",
+	"DUAL DVB-C/C2/T/T2",
+	"DUAL ATSC",
+	"DUAL DVB-C/C2/T/T2/I",
+};
+
+static int init_xo2(struct ngene_channel *chan, struct i2c_adapter *i2c)
+{
+	struct device *pdev = &chan->dev->pci_dev->dev;
+	u8 addr = 0x10;
+	u8 val, data[2];
+	int res;
+
+	res = i2c_read_regs(i2c, addr, 0x04, data, 2);
+	if (res < 0)
+		return res;
+
+	if (data[0] != 0x01)  {
+		dev_info(pdev, "Invalid XO2 on channel %d\n", chan->number);
+		return -1;
+	}
+
+	i2c_read_reg(i2c, addr, 0x08, &val);
+	if (val != 0) {
+		i2c_write_reg(i2c, addr, 0x08, 0x00);
+		msleep(100);
+	}
+	/* Enable tuner power, disable pll, reset demods */
+	i2c_write_reg(i2c, addr, 0x08, 0x04);
+	usleep_range(2000, 3000);
+	/* Release demod resets */
+	i2c_write_reg(i2c, addr, 0x08, 0x07);
+
+	/*
+	 * speed: 0=55,1=75,2=90,3=104 MBit/s
+	 * Note: The ngene hardware must be run at 75 MBit/s compared
+	 * to more modern ddbridge hardware which runs at 90 MBit/s,
+	 * else there will be issues with the data transport and non-
+	 * working secondary/slave demods/tuners.
+	 */
+	i2c_write_reg(i2c, addr, 0x09, 1);
+
+	i2c_write_reg(i2c, addr, 0x0a, 0x01);
+	i2c_write_reg(i2c, addr, 0x0b, 0x01);
+
+	usleep_range(2000, 3000);
+	/* Start XO2 PLL */
+	i2c_write_reg(i2c, addr, 0x08, 0x87);
+
+	return 0;
+}
+
+static int port_has_xo2(struct i2c_adapter *i2c, u8 *type, u8 *id)
+{
+	u8 probe[1] = { 0x00 }, data[4];
+	u8 addr = 0x10;
+
+	*type = NGENE_XO2_TYPE_NONE;
+
+	if (i2c_io(i2c, addr, probe, 1, data, 4))
+		return 0;
+	if (data[0] == 'D' && data[1] == 'F') {
+		*id = data[2];
+		*type = NGENE_XO2_TYPE_DUOFLEX;
+		return 1;
+	}
+	if (data[0] == 'C' && data[1] == 'I') {
+		*id = data[2];
+		*type = NGENE_XO2_TYPE_CI;
+		return 1;
+	}
+	return 0;
+}
+
+/****************************************************************************/
+/* Probing and port/channel handling ****************************************/
+/****************************************************************************/
+
 static int cineS2_probe(struct ngene_channel *chan)
 {
 	struct device *pdev = &chan->dev->pci_dev->dev;
 	struct i2c_adapter *i2c;
 	struct stv090x_config *fe_conf;
 	u8 buf[3];
+	u8 xo2_type, xo2_id;
 	struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
 	int rc;
 
@@ -405,7 +523,31 @@ static int cineS2_probe(struct ngene_channel *chan)
 	else
 		i2c = &chan->dev->channel[1].i2c_adapter;
 
-	if (port_has_stv0900(i2c, chan->number)) {
+	if (port_has_xo2(i2c, &xo2_type, &xo2_id)) {
+		xo2_id >>= 2;
+		dev_dbg(pdev, "XO2 on channel %d (type %d, id %d)\n",
+			chan->number, xo2_type, xo2_id);
+
+		switch (xo2_type) {
+		case NGENE_XO2_TYPE_DUOFLEX:
+			if (chan->number & 1)
+				dev_dbg(pdev,
+					"skipping XO2 init on odd channel %d",
+					chan->number);
+			else
+				init_xo2(chan, i2c);
+
+			/* TODO: implement support for XO2 module types */
+			dev_warn(pdev, "XO2 not supported\n");
+			return -ENODEV;
+		case NGENE_XO2_TYPE_CI:
+			dev_info(pdev, "DuoFlex CI modules not supported\n");
+			return -ENODEV;
+		default:
+			dev_info(pdev, "Unsupported XO2 module type\n");
+			return -ENODEV;
+		}
+	} else if (port_has_stv0900(i2c, chan->number)) {
 		chan->demod_type = DEMOD_TYPE_STV090X;
 		fe_conf = chan->dev->card_info->fe_config[chan->number];
 		/* demod found, attach it */
drivers/media/pci/ngene/ngene.h +12 -0
index 1b88a9aa7aac..72195f6552b3 100644
@@ -55,6 +55,18 @@
 #define DEMOD_TYPE_DRXK		1
 #define DEMOD_TYPE_STV0367	2
 
+#define DEMOD_TYPE_XO2		32
+#define DEMOD_TYPE_STV0910	(DEMOD_TYPE_XO2 + 0)
+#define DEMOD_TYPE_SONY_CT2	(DEMOD_TYPE_XO2 + 1)
+#define DEMOD_TYPE_SONY_ISDBT	(DEMOD_TYPE_XO2 + 2)
+#define DEMOD_TYPE_SONY_C2T2	(DEMOD_TYPE_XO2 + 3)
+#define DEMOD_TYPE_ST_ATSC	(DEMOD_TYPE_XO2 + 4)
+#define DEMOD_TYPE_SONY_C2T2I	(DEMOD_TYPE_XO2 + 5)
+
+#define NGENE_XO2_TYPE_NONE	0
+#define NGENE_XO2_TYPE_DUOFLEX	1
+#define NGENE_XO2_TYPE_CI	2
+
 enum STREAM {
 	STREAM_VIDEOIN1 = 0,        /* ITU656 or TS Input */
 	STREAM_VIDEOIN2,
Signature · 1 line
2.16.1
#6 lore raw

PATCH 6/12 ngene: add support for Sony CXD28xx-based DuoFlex modules

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

Recognize (probe) and support (attach) all Sony CXD28xx based DuoFlex addon modules/cards, namely the DuoFlex CT2 (CXD2837), ISDB-T (CXD2838), C2T2 (CXD2843) and C2T2I (CXD2854). Since all these modules are equipped with a MachXO2 interface, that support is required for the hardware to work. This functionality utilises the auxiliary cxd2841er and tda18212 drivers.

This also adds autoselection (if MEDIA_SUBDRV_AUTOSELECT) of the CXD2841ER demod driver to Kconfig. The __maybe_unused annotation can now be removed from the xo2names array.

---
 drivers/media/pci/ngene/Kconfig       |  1 +
 drivers/media/pci/ngene/ngene-cards.c | 63 ++++++++++++++++++++++++++++++++---
 2 files changed, 59 insertions(+), 5 deletions(-)
drivers/media/pci/ngene/Kconfig +1 -0
index c3254f9dc8ad..f717567f54a5 100644
@@ -9,6 +9,7 @@ config DVB_NGENE
 	select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT
 	---help---
drivers/media/pci/ngene/ngene-cards.c +58 -5

99 lines not on this page · raw message

Signature · 1 line
2.16.1
#7 lore raw

PATCH 7/12 ngene: add support for DuoFlex S2 V4 addon modules

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

Add support for the STV0910/STV6111/LNBH25 based DuoFlex S2 V4 DVB-S2 addon modules by recognizing them from their XO2 type value and using the auxiliary stv0910, stv6111 and lnbh25 driver to form a complete DVB frontend.

This also adds autoselection (if MEDIA_SUBDRV_AUTOSELECT) of the STV0910, STV6111 and LNBH25 demod/tuner/LNB-IC drivers to Kconfig.

---
 drivers/media/pci/ngene/Kconfig       |  3 ++
 drivers/media/pci/ngene/ngene-cards.c | 83 +++++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+)
drivers/media/pci/ngene/Kconfig +3 -0
index f717567f54a5..e06d019996f3 100644
@@ -11,6 +11,9 @@ config DVB_NGENE
 	select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV0910 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_STV6111 if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT
 	---help---
 	  Support for Micronas PCI express cards with nGene bridge.
drivers/media/pci/ngene/ngene-cards.c +83 -0

113 lines not on this page · raw message

Signature · 1 line
2.16.1
#8 lore raw

PATCH 8/12 ngene: deduplicate I2C adapter evaluation

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

The I2C adapter evaluation (based on chan->number) is duplicated at several places (tuner_attach_() functions, demod_attach_stv0900() and cineS2_probe()). Clean this up by wrapping that construct in a separate function which all users of that can pass the ngene_channel pointer and get the correct I2C adapter from.

---
 drivers/media/pci/ngene/ngene-cards.c | 41 +++++++++++++----------------------
 1 file changed, 15 insertions(+), 26 deletions(-)
drivers/media/pci/ngene/ngene-cards.c +15 -26
index 00b100660784..dff55c7c9f86 100644
@@ -118,17 +118,25 @@ static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
 /* Demod/tuner attachment ***************************************************/
 /****************************************************************************/
 
+static struct i2c_adapter *i2c_adapter_from_chan(struct ngene_channel *chan)
+{
+	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+	if (chan->number < 2)
+		return &chan->dev->channel[0].i2c_adapter;
+
+	return &chan->dev->channel[1].i2c_adapter;
+}
+
 static int tuner_attach_stv6110(struct ngene_channel *chan)
 {
 	struct device *pdev = &chan->dev->pci_dev->dev;
-	struct i2c_adapter *i2c;
+	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
 	struct stv090x_config *feconf = (struct stv090x_config *)
 		chan->dev->card_info->fe_config[chan->number];
 	struct stv6110x_config *tunerconf = (struct stv6110x_config *)
 		chan->dev->card_info->tuner_config[chan->number];
 	const struct stv6110x_devctl *ctl;
 
-	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
 	if (chan->number < 2)
 		i2c = &chan->dev->channel[0].i2c_adapter;
 	else
@@ -158,16 +166,10 @@ static int tuner_attach_stv6110(struct ngene_channel *chan)
 static int tuner_attach_stv6111(struct ngene_channel *chan)
 {
 	struct device *pdev = &chan->dev->pci_dev->dev;
-	struct i2c_adapter *i2c;
+	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
 	struct dvb_frontend *fe;
 	u8 adr = 4 + ((chan->number & 1) ? 0x63 : 0x60);
 
-	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
-	if (chan->number < 2)
-		i2c = &chan->dev->channel[0].i2c_adapter;
-	else
-		i2c = &chan->dev->channel[1].i2c_adapter;
-
 	fe = dvb_attach(stv6111_attach, chan->fe, i2c, adr);
 	if (!fe) {
 		fe = dvb_attach(stv6111_attach, chan->fe, i2c, adr & ~4);
@@ -197,10 +199,9 @@ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
 static int tuner_attach_tda18271(struct ngene_channel *chan)
 {
 	struct device *pdev = &chan->dev->pci_dev->dev;
-	struct i2c_adapter *i2c;
+	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
 	struct dvb_frontend *fe;
 
-	i2c = &chan->dev->channel[0].i2c_adapter;
 	if (chan->fe->ops.i2c_gate_ctrl)
 		chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
 	fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
@@ -240,7 +241,7 @@ static int tuner_tda18212_ping(struct ngene_channel *chan,
 static int tuner_attach_tda18212(struct ngene_channel *chan, u32 dmdtype)
 {
 	struct device *pdev = &chan->dev->pci_dev->dev;
-	struct i2c_adapter *i2c;
+	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
 	struct i2c_client *client;
 	struct tda18212_config config = {
 		.fe = chan->fe,
@@ -262,12 +263,6 @@ static int tuner_attach_tda18212(struct ngene_channel *chan, u32 dmdtype)
 	else
 		board_info.addr = 0x60;
 
-	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
-	if (chan->number < 2)
-		i2c = &chan->dev->channel[0].i2c_adapter;
-	else
-		i2c = &chan->dev->channel[1].i2c_adapter;
-
 	/*
 	 * due to a hardware quirk with the I2C gate on the stv0367+tda18212
 	 * combo, the tda18212 must be probed by reading it's id _twice_ when
@@ -320,7 +315,7 @@ static int tuner_attach_probe(struct ngene_channel *chan)
 static int demod_attach_stv0900(struct ngene_channel *chan)
 {
 	struct device *pdev = &chan->dev->pci_dev->dev;
-	struct i2c_adapter *i2c;
+	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
 	struct stv090x_config *feconf = (struct stv090x_config *)
 		chan->dev->card_info->fe_config[chan->number];
 
@@ -620,7 +615,7 @@ static int port_has_xo2(struct i2c_adapter *i2c, u8 *type, u8 *id)
 static int cineS2_probe(struct ngene_channel *chan)
 {
 	struct device *pdev = &chan->dev->pci_dev->dev;
-	struct i2c_adapter *i2c;
+	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
 	struct stv090x_config *fe_conf;
 	u8 buf[3];
 	u8 xo2_type, xo2_id, xo2_demodtype;
@@ -628,12 +623,6 @@ static int cineS2_probe(struct ngene_channel *chan)
 	struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
 	int rc;
 
-	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
-	if (chan->number < 2)
-		i2c = &chan->dev->channel[0].i2c_adapter;
-	else
-		i2c = &chan->dev->channel[1].i2c_adapter;
-
 	if (port_has_xo2(i2c, &xo2_type, &xo2_id)) {
 		xo2_id >>= 2;
 		dev_dbg(pdev, "XO2 on channel %d (type %d, id %d)\n",
Signature · 1 line
2.16.1
#9 lore raw

PATCH 9/12 ngene: check for CXD2099AR presence before attaching

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

Currently, if there's no CXD2099AR attached to any expansion connector of the ngene hardware, it will complain with this on every module load:

    cxd2099 1-0040: No CXD2099AR detected at 0x40
    cxd2099: probe of 1-0040 failed with error -5
    ngene 0000:02:00.0: CXD2099AR attach failed

This happens due to the logic assuming such hardware is always there and blindly tries to attach the cxd2099 I2C driver. Rather add a probe function (in ngene-cards.c with a prototype in ngene.h) to check for the existence of such hardware before probing, and don't try further if no CXD2099 was found.

---
 drivers/media/pci/ngene/ngene-cards.c | 19 +++++++++++++++++++
 drivers/media/pci/ngene/ngene-core.c  | 14 ++++++++++++++
 drivers/media/pci/ngene/ngene.h       |  3 +++
 3 files changed, 36 insertions(+)
drivers/media/pci/ngene/ngene-cards.c +19 -0
index dff55c7c9f86..d603d0af703e 100644
@@ -505,6 +505,25 @@ static int port_has_stv0367(struct i2c_adapter *i2c)
 	return 1;
 }
 
+int ngene_port_has_cxd2099(struct i2c_adapter *i2c, u8 *type)
+{
+	u8 val;
+	u8 probe[4] = { 0xe0, 0x00, 0x00, 0x00 }, data[4];
+	struct i2c_msg msgs[2] = {{ .addr = 0x40,  .flags = 0,
+				    .buf  = probe, .len   = 4 },
+				  { .addr = 0x40,  .flags = I2C_M_RD,
+				    .buf  = data,  .len   = 4 } };
+	val = i2c_transfer(i2c, msgs, 2);
+	if (val != 2)
+		return 0;
+
+	if (data[0] == 0x02 && data[1] == 0x2b && data[3] == 0x43)
+		*type = 2;
+	else
+		*type = 1;
+	return 1;
+}
+
 static int demod_attach_drxk(struct ngene_channel *chan,
 			     struct i2c_adapter *i2c)
 {
drivers/media/pci/ngene/ngene-core.c +14 -0
index 526d0adfa427..f69a8fc1ec2a 100644
@@ -1589,6 +1589,20 @@ static void cxd_attach(struct ngene *dev)
 		.addr = 0x40,
 		.platform_data = &cxd_cfg,
 	};
+	int ret;
+	u8 type;
+
+	/* check for CXD2099AR presence before attaching */
+	ret = ngene_port_has_cxd2099(&dev->channel[0].i2c_adapter, &type);
+	if (!ret) {
+		dev_dbg(pdev, "No CXD2099AR found\n");
+		return;
+	}
+
+	if (type != 1) {
+		dev_warn(pdev, "CXD2099AR is uninitialized!\n");
+		return;
+	}
 
 	cxd_cfg.en = &ci->en;
 
drivers/media/pci/ngene/ngene.h +3 -0
index 72195f6552b3..66d8eaa28549 100644
@@ -909,6 +909,9 @@ int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level);
 void set_transfer(struct ngene_channel *chan, int state);
 void FillTSBuffer(void *Buffer, int Length, u32 Flags);
 
+/* Provided by ngene-cards.c */
+int ngene_port_has_cxd2099(struct i2c_adapter *i2c, u8 *type);
+
 /* Provided by ngene-i2c.c */
 int ngene_i2c_init(struct ngene *dev, int dev_nr);
 
Signature · 1 line
2.16.1
#10 lore raw

PATCH 10/12 ngene: don't treat non-existing demods as error

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

When probing the I2C busses in cineS2_probe(), it's no error when there's no hardware connected to the probed expansion connector, so print this informal message with info severity.

---
 drivers/media/pci/ngene/ngene-cards.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/pci/ngene/ngene-cards.c +1 -1
index d603d0af703e..37e9f0eb6d20 100644
@@ -728,7 +728,7 @@ static int cineS2_probe(struct ngene_channel *chan)
 		dev_info(pdev, "STV0367 on channel %d\n", chan->number);
 		demod_attach_stv0367(chan, i2c);
 	} else {
-		dev_err(pdev, "No demod found on chan %d\n", chan->number);
+		dev_info(pdev, "No demod found on chan %d\n", chan->number);
 		return -ENODEV;
 	}
 	return 0;
Signature · 1 line
2.16.1
#11 lore raw

PATCH 11/12 ngene: move the tsin_exchange() stripcopy block into a function

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

Move the copy logic that will skip previously inserted TS NULL frames when moving data to the DVB ring buffers into an own function. This is done to not duplicate code all over the place with the following TS offset shift fixup patch.

---
 drivers/media/pci/ngene/ngene-dvb.c | 48 +++++++++++++++++++++----------------
 1 file changed, 27 insertions(+), 21 deletions(-)
drivers/media/pci/ngene/ngene-dvb.c +27 -21
index f71fd41c762c..6d72b9f69418 100644
@@ -123,6 +123,32 @@ static u32 overflow;
 static u32 stripped;
 #endif
 
+static inline void tsin_copy_stripped(struct ngene *dev, void *buf)
+{
+	if (memcmp(buf, fill_ts, sizeof(fill_ts)) != 0) {
+		if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
+			dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
+			wake_up(&dev->tsin_rbuf.queue);
+#ifdef DEBUG_CI_XFER
+			ok++;
+#endif
+		}
+#ifdef DEBUG_CI_XFER
+		else
+			overflow++;
+#endif
+	}
+#ifdef DEBUG_CI_XFER
+	else
+		stripped++;
+
+	if (ok % 100 == 0 && overflow)
+		dev_warn(&dev->pci_dev->dev,
+			 "%s: ok %u overflow %u dropped %u\n",
+			 __func__, ok, overflow, stripped);
+#endif
+}
+
 void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
 {
 	struct ngene_channel *chan = priv;
@@ -134,28 +160,8 @@ void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
 
 	if (dev->ci.en && chan->number == 2) {
 		while (len >= 188) {
-			if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) {
-				if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
-					dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
-					wake_up(&dev->tsin_rbuf.queue);
-#ifdef DEBUG_CI_XFER
-					ok++;
-#endif
-				}
-#ifdef DEBUG_CI_XFER
-				else
-					overflow++;
-#endif
-			}
-#ifdef DEBUG_CI_XFER
-			else
-				stripped++;
+			tsin_copy_stripped(dev, buf);
 
-			if (ok % 100 == 0 && overflow)
-				dev_warn(&dev->pci_dev->dev,
-					 "%s: ok %u overflow %u dropped %u\n",
-					 __func__, ok, overflow, stripped);
-#endif
 			buf += 188;
 			len -= 188;
 		}
Signature · 1 line
2.16.1
#12 lore raw

PATCH 12/12 ngene: compensate for TS buffer offset shifts

In reply to Daniel Scheller

From: Daniel Scheller <[email protected]>

A possible hardware bug was discovered when using CA addon hardware attached to the ngene hardware, in that the TS input buffer much likely will shift and thus become unaligned to 188 byte blocks (a full TS frame) when things like CA module initialisation (which happens via differing communication paths) takes place. This causes the TS NULL removal in tsin_exchange() to fail to detect this previously inserted data and thus causes userspace applications to receive data they didn't sent beforehand and ultimately can cause troubles.

On driver load with an inserted CAM, buffers are fine at first (note that the driver has to keep the communication running from/to the card by inserting TS NULL frames, this is done in tsout_exchange() via FillTSBuffer() - that data is simply sent back by the hardware):

offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 47 1f ff 10 6f 6f .... 47 1f ff 10 6f 6f .... 47

After a few seconds, the CA module is recognised and initialised, which is signalled by

dvb_ca_en50221: dvb_ca adapter X: DVB CAM detected and initialised successfully

This is where the first shift happens (this is always four bytes), buffer becomes like this:

offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 6f 6f 6f 6f 47 1f .... 6f 6f 6f 6f 47 1f .... 6f

Next, VDR, TVHeadend or any other CI aware application is started, buffers will shift by even more bytes. It is believed this is due to the hardware not handling control and data bytes properly distinct, and control data having an influence on the actual data stream, which we cannot properly detect at the driver level.

Workaround this hardware quirk by adding a detection for the TS sync byte 0x47 before each TS frame copy, scan for a new SYNC byte and a TS NULL packet if buffers become unaligned, take note of that offset and apply that when copying data to the DVB ring buffers. The last <188 bytes from the hardware buffers are stored in a temp buffer (tsin_buffer), for which the remainder will be in the beginning of the next hardware buffer (next iteration if tsin_exchange()). That remainder will be appended to the temp buffer and finally sent to the DVB ring buffer. The resulting TS stream is perfectly fine, and the TS NULL packets inserted by the driver which are sent back are properly removed. The resulting offset is being clamped to 188 byte segments (one TS packet). Though this can result in a repeated TS packet if the overall offset grows beyond this (and it will grow only on CA initialisation), this is still way better than unaligned TS frames and data sent to userspace that just isn't supposed to be there.

---
 drivers/media/pci/ngene/ngene-dvb.c | 80 ++++++++++++++++++++++++++++++++++++-
 drivers/media/pci/ngene/ngene.h     |  3 ++
 2 files changed, 82 insertions(+), 1 deletion(-)
drivers/media/pci/ngene/ngene-dvb.c +79 -1
index 6d72b9f69418..5abf69f6bc4b 100644
@@ -38,6 +38,9 @@
 
 #include "ngene.h"
 
+static int ci_tsfix = 1;
+module_param(ci_tsfix, int, 0444);
+MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifs in conjunction with CI expansions (default: 1/enabled)");
 
 /****************************************************************************/
 /* COMMAND API interface ****************************************************/
@@ -123,6 +126,24 @@ static u32 overflow;
 static u32 stripped;
 #endif
 
+static int tsin_find_offset(void *buf, u32 len)
+{
+	int i, l;
+
+	l = len - sizeof(fill_ts);
+	if (l <= 0)
+		return -1;
+
+	for (i = 0; i < l; i++) {
+		if (((char *)buf)[i] == 0x47) {
+			if (!memcmp(buf + i, fill_ts, sizeof(fill_ts)))
+				return i;
+		}
+	}
+
+	return -1;
+}
+
 static inline void tsin_copy_stripped(struct ngene *dev, void *buf)
 {
 	if (memcmp(buf, fill_ts, sizeof(fill_ts)) != 0) {
@@ -153,18 +174,75 @@ void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
 {
 	struct ngene_channel *chan = priv;
 	struct ngene *dev = chan->dev;
-
+	int tsoff;
 
 	if (flags & DF_SWAP32)
 		swap_buffer(buf, len);
 
 	if (dev->ci.en && chan->number == 2) {
+		/* blindly copy buffers if ci_tsfix is disabled */
+		if (!ci_tsfix) {
+			while (len >= 188) {
+				tsin_copy_stripped(dev, buf);
+
+				buf += 188;
+				len -= 188;
+			}
+			return NULL;
+		}
+
+		/* ci_tsfix = 1 */
+
+		/*
+		 * since the remainder of the TS packet which got cut off
+		 * in the previous tsin_exchange() run is at the beginning
+		 * of the new TS buffer, append this to the temp buffer and
+		 * send it to the DVB ringbuffer afterwards.
+		 */
+		if (chan->tsin_offset) {
+			memcpy(&chan->tsin_buffer[(188 - chan->tsin_offset)],
+			       buf, chan->tsin_offset);
+			tsin_copy_stripped(dev, &chan->tsin_buffer);
+
+			buf += chan->tsin_offset;
+			len -= chan->tsin_offset;
+		}
+
+		/*
+		 * copy TS packets to the DVB ringbuffer and detect new offset
+		 * shifts by checking for a valid TS SYNC byte
+		 */
 		while (len >= 188) {
+			if (*((char *)buf) != 0x47) {
+				/* no SYNC header, find new offset */
+				tsoff = tsin_find_offset(buf, len);
+				if (tsoff > 0) {
+					chan->tsin_offset += tsoff;
+					chan->tsin_offset %= 188;
+
+					buf += tsoff;
+					len -= tsoff;
+
+					dev_info(&dev->pci_dev->dev,
+						 "%s(): tsin_offset shift by %d on channel %d\n",
+						 __func__, tsoff,
+						 chan->number);
+				}
+			}
+
 			tsin_copy_stripped(dev, buf);
 
 			buf += 188;
 			len -= 188;
 		}
+
+		/*
+		 * if a fragment is left, copy to temp buffer. The remainder
+		 * will be appended in the next tsin_exchange() iteration.
+		 */
+		if (len > 0 && len < 188)
+			memcpy(&chan->tsin_buffer, buf, len);
+
 		return NULL;
 	}
 
drivers/media/pci/ngene/ngene.h +3 -0
index 66d8eaa28549..01d9f1b58fcb 100644
@@ -732,6 +732,9 @@ struct ngene_channel {
 #endif
 
 	int running;
+
+	int tsin_offset;
+	u8  tsin_buffer[188];
 };
 
 
Signature · 1 line
2.16.1