Skip to contentSkip to conversation

Some improvements for DVB media graph

PATCH New Mauro Carvalho Chehab 1 participant 7 messages Dec 30, 2015

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

cover Some improvements for DVB media graph

This patch series is the result of some additional tests with the media controller and several different pure digital TV devices, using the dvb-usb, dvb-usb-v2 core and the siano driver.

It addresses some minor issues, and improves the graph representation of those devices. In particular, the DVB USB v2 mxl111sf driver supports one device that has:
- one tuner, provided by the MXL chipset;
- three demodulators (one for ATSC, one for ClearQAM and one for DVB-T), each one using a different chip and different driver (lg2161, lgdt3305 and an internal DVB-T demodulator inside the MXL chipset);
- one demod, provided by the MXL chipset.

The graph for such design is at:
https://mchehab.fedorapeople.org/mc-next-gen/mxl111sf.png
with is generated, after this changeset, using mc-nextgen-test tool, available at:
https://git.linuxtv.org/mchehab/experimental-v4l-utils.git/log/?h=mc-next-gen-v2

The .dot file produced by the tool is at:
https://mchehab.fedorapeople.org/mc-next-gen/mxl111sf.dot

Before this patch series, the RF connector and tuner were not shown. Also, the graph were missing the connections for the frontends 0 and 1.

Mauro Carvalho Chehab (6):
[media] dvbdev: remove two dead functions if
!CONFIG_MEDIA_CONTROLLER_DVB
[media] dvbdev: Add RF connector if needed
[media] dvb-usb-v2: postpone removal of media_device [media] media-entitiy: add a function to create multiple links [media] dvbdev: create links on devices with multiple frontends [media] mxl111sf: Add a tuner entity

 drivers/media/common/siano/smsdvb-main.c    |  2 +-
 drivers/media/dvb-core/dvbdev.c             | 98 +++++++++++++++++++++++++----
 drivers/media/dvb-core/dvbdev.h             | 33 +++++++++-
 drivers/media/media-entity.c                | 65 +++++++++++++++++++
 drivers/media/usb/au0828/au0828-dvb.c       |  2 +-
 drivers/media/usb/cx231xx/cx231xx-dvb.c     |  2 +-
 drivers/media/usb/dvb-usb-v2/dvb_usb_core.c |  4 +-
 drivers/media/usb/dvb-usb-v2/mxl111sf.c     | 20 ++++++
 drivers/media/usb/dvb-usb-v2/mxl111sf.h     |  5 ++
 drivers/media/usb/dvb-usb/dvb-usb-dvb.c     |  2 +-
 include/media/media-entity.h                | 51 +++++++++++++++
 11 files changed, 264 insertions(+), 20 deletions(-)
Signature · 1 line
2.5.0
#1 lore raw

PATCH 1/6 dvbdev: remove two dead functions if !CONFIG_MEDIA_CONTROLLER_DVB

Those functions are used only if CONFIG_MEDIA_CONTROLLER_DVB. Without that, if !CONFIG_MEDIA_CONTROLLER_DVB, it would produce two warnings:

drivers/media/dvb-core/dvbdev.c:219:12: warning: 'dvb_create_tsout_entity' defined but not used [-Wunused-function]
static int dvb_create_tsout_entity(struct dvb_device *dvbdev,

            ^
drivers/media/dvb-core/dvbdev.c:264:12: warning: 'dvb_create_media_entity' defined but not used [-Wunused-function]
 static int dvb_create_media_entity(struct dvb_device *dvbdev,
            ^
---
 drivers/media/dvb-core/dvbdev.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/media/dvb-core/dvbdev.c +2 -4
index b56e00817d3f..860dd7d06b60 100644
@@ -216,10 +216,10 @@ static void dvb_media_device_free(struct dvb_device *dvbdev)
 #endif
 }
 
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
 static int dvb_create_tsout_entity(struct dvb_device *dvbdev,
 				    const char *name, int npads)
 {
-#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
 	int i, ret = 0;
 
 	dvbdev->tsout_pads = kcalloc(npads, sizeof(*dvbdev->tsout_pads),
@@ -254,7 +254,6 @@ static int dvb_create_tsout_entity(struct dvb_device *dvbdev,
 		if (ret < 0)
 			return ret;
 	}
-#endif
 	return 0;
 }
 
@@ -264,7 +263,6 @@ static int dvb_create_tsout_entity(struct dvb_device *dvbdev,
 static int dvb_create_media_entity(struct dvb_device *dvbdev,
 				   int type, int demux_sink_pads)
 {
-#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
 	int i, ret, npads;
 
 	switch (type) {
@@ -352,9 +350,9 @@ static int dvb_create_media_entity(struct dvb_device *dvbdev,
 	printk(KERN_DEBUG "%s: media entity '%s' registered.\n",
 		__func__, dvbdev->entity->name);
 
-#endif
 	return 0;
 }
+#endif
 
 static int dvb_register_media_device(struct dvb_device *dvbdev,
 				     int type, int minor,
Signature · 1 line
2.5.0
#2 lore raw

PATCH 2/6 dvbdev: Add RF connector if needed

In reply to Mauro Carvalho Chehab

Several pure digital TV devices have a frontend with the tuner integrated on it. Add the RF connector when dvb_create_media_graph() is called on such devices.

Tested with siano and dvb_usb_mxl111sf drivers.

---
 drivers/media/common/siano/smsdvb-main.c    |  2 +-
 drivers/media/dvb-core/dvbdev.c             | 49 +++++++++++++++++++++++++++--
 drivers/media/dvb-core/dvbdev.h             | 20 ++++++++++--
 drivers/media/usb/au0828/au0828-dvb.c       |  2 +-
 drivers/media/usb/cx231xx/cx231xx-dvb.c     |  2 +-
 drivers/media/usb/dvb-usb-v2/dvb_usb_core.c |  2 +-
 drivers/media/usb/dvb-usb/dvb-usb-dvb.c     |  2 +-
 7 files changed, 70 insertions(+), 9 deletions(-)
drivers/media/common/siano/smsdvb-main.c +1 -1
index 8a1ea2192439..d31f468830cf 100644
@@ -1184,7 +1184,7 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev,
 	if (smsdvb_debugfs_create(client) < 0)
 		pr_info("failed to create debugfs node\n");
 
-	rc = dvb_create_media_graph(&client->adapter);
+	rc = dvb_create_media_graph(&client->adapter, true);
 	if (rc < 0) {
 		pr_err("dvb_create_media_graph failed %d\n", rc);
 		goto client_error;
drivers/media/dvb-core/dvbdev.c +47 -2

75 lines not on this page · raw message

drivers/media/dvb-core/dvbdev.h +18 -2
index abee18a402e1..b622d6a3b95e 100644
@@ -75,6 +75,9 @@ struct dvb_frontend;
  *			used.
  * @mdev:		pointer to struct media_device, used when the media
  *			controller is used.
+ * @conn:		RF connector. Used only if the device has no separate
+ *			tuner.
+ * @conn_pads:		pointer to struct media_pad associated with @conn;
  */
 struct dvb_adapter {
 	int num;
@@ -94,6 +97,8 @@ struct dvb_adapter {
 
 #if defined(CONFIG_MEDIA_CONTROLLER_DVB)
 	struct media_device *mdev;
+	struct media_entity *conn;
+	struct media_pad *conn_pads;
 #endif
 };
 
@@ -214,7 +219,16 @@ int dvb_register_device(struct dvb_adapter *adap,
 void dvb_unregister_device(struct dvb_device *dvbdev);
 
 #ifdef CONFIG_MEDIA_CONTROLLER_DVB
-__must_check int dvb_create_media_graph(struct dvb_adapter *adap);
+/**
+ * dvb_create_media_graph - Creates media graph for the Digital TV part of the
+ * 				device.
+ *
+ * @adap:			pointer to struct dvb_adapter
+ * @create_rf_connector:	if true, it creates the RF connector too
+ */
+__must_check int dvb_create_media_graph(struct dvb_adapter *adap,
+					bool create_rf_connector);
+
 static inline void dvb_register_media_controller(struct dvb_adapter *adap,
 						 struct media_device *mdev)
 {
@@ -222,7 +236,9 @@ static inline void dvb_register_media_controller(struct dvb_adapter *adap,
 }
 
 #else
-static inline int dvb_create_media_graph(struct dvb_adapter *adap)
+static inline
+int dvb_create_media_graph(struct dvb_adapter *adap,
+			   bool create_rf_connector)
 {
 	return 0;
 };
drivers/media/usb/au0828/au0828-dvb.c +1 -1
index cd542b49a6c2..94363a3ba400 100644
@@ -486,7 +486,7 @@ static int dvb_register(struct au0828_dev *dev)
 	dvb->start_count = 0;
 	dvb->stop_count = 0;
 
-	result = dvb_create_media_graph(&dvb->adapter);
+	result = dvb_create_media_graph(&dvb->adapter, false);
 	if (result < 0)
 		goto fail_create_graph;
 
drivers/media/usb/cx231xx/cx231xx-dvb.c +1 -1
index b7552d20ebdb..b8d5b2be9293 100644
@@ -551,7 +551,7 @@ static int register_dvb(struct cx231xx_dvb *dvb,
 
 	/* register network adapter */
 	dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
-	result = dvb_create_media_graph(&dvb->adapter);
+	result = dvb_create_media_graph(&dvb->adapter, false);
 	if (result < 0)
 		goto fail_create_graph;
 
drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +1 -1
index 0fa2c45917b0..e8491f73c0d9 100644
@@ -706,7 +706,7 @@ static int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap)
 		}
 	}
 
-	ret = dvb_create_media_graph(&adap->dvb_adap);
+	ret = dvb_create_media_graph(&adap->dvb_adap, true);
 	if (ret < 0)
 		goto err_dvb_unregister_frontend;
 
drivers/media/usb/dvb-usb/dvb-usb-dvb.c +1 -1
index 241463ef631e..9ddfcab268be 100644
@@ -330,7 +330,7 @@ int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
 	if (ret)
 		return ret;
 
-	ret = dvb_create_media_graph(&adap->dvb_adap);
+	ret = dvb_create_media_graph(&adap->dvb_adap, true);
 	if (ret)
 		return ret;
 
Signature · 1 line
2.5.0
#3 lore raw

PATCH 3/6 dvb-usb-v2: postpone removal of media_device

In reply to Mauro Carvalho Chehab

We should not remove the media_device until its last usage, or we may have use after free troubles.

So, move the per-adapter media_device removal to happen at the end of the adapter removal code.

---
 drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +1 -1
index e8491f73c0d9..f0565bf3673e 100644
@@ -542,7 +542,6 @@ static int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap)
 		adap->demux.dmx.close(&adap->demux.dmx);
 		dvb_dmxdev_release(&adap->dmxdev);
 		dvb_dmx_release(&adap->demux);
-		dvb_usbv2_media_device_unregister(adap);
 		dvb_unregister_adapter(&adap->dvb_adap);
 	}
 
@@ -852,6 +851,7 @@ static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d)
 			dvb_usbv2_adapter_dvb_exit(&d->adapter[i]);
 			dvb_usbv2_adapter_stream_exit(&d->adapter[i]);
 			dvb_usbv2_adapter_frontend_exit(&d->adapter[i]);
+			dvb_usbv2_media_device_unregister(&d->adapter[i]);
 		}
 	}
 
Signature · 1 line
2.5.0
#4 lore raw

PATCH 4/6 media-entitiy: add a function to create multiple links

In reply to Mauro Carvalho Chehab

Sometimes, it is desired to create 1:n and n:1 or even n:n links between different entities with the same function.

This is actually needed to support DVB devices that have multiple frontends. While we could do a function like that internally at the DVB core, such function is generic enough to be at media-entity, and it could be useful on some other places.

So, add such function.

---
 drivers/media/media-entity.c | 65 ++++++++++++++++++++++++++++++++++++++++++++
 include/media/media-entity.h | 51 ++++++++++++++++++++++++++++++++++
 2 files changed, 116 insertions(+)
drivers/media/media-entity.c +65 -0
index eb38bc35320a..e89d85a7d31b 100644
@@ -625,6 +625,71 @@ media_create_pad_link(struct media_entity *source, u16 source_pad,
 }
 EXPORT_SYMBOL_GPL(media_create_pad_link);
 
+int media_create_pad_links(const struct media_device *mdev,
+			   const u32 source_function,
+			   struct media_entity *source,
+			   const u16 source_pad,
+			   const u32 sink_function,
+			   struct media_entity *sink,
+			   const u16 sink_pad,
+			   u32 flags,
+			   const bool allow_both_undefined)
+{
+	struct media_entity *entity;
+	unsigned function;
+	int ret;
+
+	/* Trivial case: 1:1 relation */
+	if (source && sink)
+		return media_create_pad_link(source, source_pad,
+					     sink, sink_pad, flags);
+
+	/* Worse case scenario: n:n relation */
+	if (!source && !sink) {
+		if (!allow_both_undefined)
+			return 0;
+		media_device_for_each_entity(source, mdev) {
+			if (source->function != source_function)
+				continue;
+			media_device_for_each_entity(sink, mdev) {
+				if (sink->function != sink_function)
+					continue;
+				ret = media_create_pad_link(source, source_pad,
+							    sink, sink_pad,
+							    flags);
+				if (ret)
+					return ret;
+				flags &= ~(MEDIA_LNK_FL_ENABLED |
+					   MEDIA_LNK_FL_IMMUTABLE);
+			}
+		}
+		return 0;
+	}
+
+	/* Handle 1:n and n:1 cases */
+	if (source)
+		function = sink_function;
+	else
+		function = source_function;
+
+	media_device_for_each_entity(entity, mdev) {
+		if (entity->function != function)
+			continue;
+
+		if (source)
+			ret = media_create_pad_link(source, source_pad,
+						    entity, sink_pad, flags);
+		else
+			ret = media_create_pad_link(entity, source_pad,
+						    sink, sink_pad, flags);
+		if (ret)
+			return ret;
+		flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(media_create_pad_links);
+
 void __media_entity_remove_links(struct media_entity *entity)
 {
 	struct media_link *link, *tmp;
include/media/media-entity.h +51 -0

57 lines not on this page · raw message

Signature · 1 line
2.5.0
#5 lore raw

PATCH 5/6 dvbdev: create links on devices with multiple frontends

In reply to Mauro Carvalho Chehab

Devices like mxl111sf-based WinTV Aero-m have multiple frontends, all linked on the same demod. Currently, the dvb_create_graph() function is not smart enough to create multiple links. Fix it.

---
 drivers/media/dvb-core/dvbdev.c | 57 +++++++++++++++++++++++++++++++----------
 drivers/media/dvb-core/dvbdev.h |  7 +++++
 2 files changed, 51 insertions(+), 13 deletions(-)
drivers/media/dvb-core/dvbdev.c +44 -13
index 28e340583ede..560450a0b32a 100644
@@ -576,6 +576,7 @@ int dvb_create_media_graph(struct dvb_adapter *adap,
 	struct media_interface *intf;
 	unsigned demux_pad = 0;
 	unsigned dvr_pad = 0;
+	unsigned ntuner = 0, ndemod = 0;
 	int ret;
 	static const char *connector_name = "Television";
 
@@ -586,9 +587,11 @@ int dvb_create_media_graph(struct dvb_adapter *adap,
 		switch (entity->function) {
 		case MEDIA_ENT_F_TUNER:
 			tuner = entity;
+			ntuner++;
 			break;
 		case MEDIA_ENT_F_DTV_DEMOD:
 			demod = entity;
+			ndemod++;
 			break;
 		case MEDIA_ENT_F_TS_DEMUX:
 			demux = entity;
@@ -599,6 +602,18 @@ int dvb_create_media_graph(struct dvb_adapter *adap,
 		}
 	}
 
+	/*
+	 * Prepare to signalize to media_create_pad_links() that multiple
+	 * entities of the same type exists and a 1:n or n:1 links need to be
+	 * created.
+	 * NOTE: if both tuner and demod have multiple instances, it is up
+	 * to the caller driver to create such links.
+	 */
+	if (ntuner > 1)
+		tuner = NULL;
+	if (ndemod > 1)
+		demod = NULL;
+
 	if (create_rf_connector) {
 		conn = kzalloc(sizeof(*conn), GFP_KERNEL);
 		if (!conn)
@@ -623,28 +638,44 @@ int dvb_create_media_graph(struct dvb_adapter *adap,
 		if (ret)
 			return ret;
 
-		if (!tuner)
-			ret = media_create_pad_link(conn, 0,
-						    demod, 0,
-						    MEDIA_LNK_FL_ENABLED);
+		if (!ntuner)
+			ret = media_create_pad_links(mdev,
+						     MEDIA_ENT_F_CONN_RF,
+						     conn, 0,
+						     MEDIA_ENT_F_DTV_DEMOD,
+						     demod, 0,
+						     MEDIA_LNK_FL_ENABLED,
+						     false);
 		else
-			ret = media_create_pad_link(conn, 0,
-						    tuner, TUNER_PAD_RF_INPUT,
-						    MEDIA_LNK_FL_ENABLED);
+			ret = media_create_pad_links(mdev,
+						     MEDIA_ENT_F_CONN_RF,
+						     conn, 0,
+						     MEDIA_ENT_F_TUNER,
+						     tuner, TUNER_PAD_RF_INPUT,
+						     MEDIA_LNK_FL_ENABLED,
+						     false);
 		if (ret)
 			return ret;
 	}
 
-	if (tuner && demod) {
-		ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT,
-					    demod, 0, MEDIA_LNK_FL_ENABLED);
+	if (ntuner && ndemod) {
+		ret = media_create_pad_links(mdev,
+					     MEDIA_ENT_F_TUNER,
+					     tuner, TUNER_PAD_IF_OUTPUT,
+					     MEDIA_ENT_F_DTV_DEMOD,
+					     demod, 0, MEDIA_LNK_FL_ENABLED,
+					     false);
 		if (ret)
 			return ret;
 	}
 
-	if (demod && demux) {
-		ret = media_create_pad_link(demod, 1, demux,
-					    0, MEDIA_LNK_FL_ENABLED);
+	if (ndemod && demux) {
+		ret = media_create_pad_links(mdev,
+					     MEDIA_ENT_F_DTV_DEMOD,
+					     demod, 1,
+					     MEDIA_ENT_F_TS_DEMUX,
+					     demux, 0, MEDIA_LNK_FL_ENABLED,
+					     false);
 		if (ret)
 			return -ENOMEM;
 	}
drivers/media/dvb-core/dvbdev.h +7 -0
index b622d6a3b95e..d7c67baa885e 100644
@@ -225,6 +225,13 @@ void dvb_unregister_device(struct dvb_device *dvbdev);
  *
  * @adap:			pointer to struct dvb_adapter
  * @create_rf_connector:	if true, it creates the RF connector too
+ *
+ * This function checks all DVB-related functions at the media controller
+ * entities and creates the needed links for the media graph. It is
+ * capable of working with multiple tuners or multiple frontends, but it
+ * won't create links if the device has multiple tuners and multiple frontends
+ * or if the device has multiple muxes. In such case, the caller driver should
+ * manually create the remaining links.
  */
 __must_check int dvb_create_media_graph(struct dvb_adapter *adap,
 					bool create_rf_connector);
Signature · 1 line
2.5.0
#6 lore raw

PATCH 6/6 mxl111sf: Add a tuner entity

In reply to Mauro Carvalho Chehab

While mxl111sf may have multiple frontends, it has just one tuner. Reflect that on the media graph.

---
 drivers/media/dvb-core/dvbdev.h         |  6 ++++++
 drivers/media/usb/dvb-usb-v2/mxl111sf.c | 20 ++++++++++++++++++++
 drivers/media/usb/dvb-usb-v2/mxl111sf.h |  5 +++++
 3 files changed, 31 insertions(+)
drivers/media/dvb-core/dvbdev.h +6 -0
index d7c67baa885e..4aff7bd3dea8 100644
@@ -242,6 +242,11 @@ static inline void dvb_register_media_controller(struct dvb_adapter *adap,
 	adap->mdev = mdev;
 }
 
+static inline struct media_device
+*dvb_get_media_controller(struct dvb_adapter *adap)
+{
+	return adap->mdev;
+}
 #else
 static inline
 int dvb_create_media_graph(struct dvb_adapter *adap,
@@ -250,6 +255,7 @@ int dvb_create_media_graph(struct dvb_adapter *adap,
 	return 0;
 };
 #define dvb_register_media_controller(a, b) {}
+#define dvb_get_media_controller(a) NULL
 #endif
 
 int dvb_generic_open (struct inode *inode, struct file *file);
drivers/media/usb/dvb-usb-v2/mxl111sf.c +20 -0
index 1710f9038d75..b669deccc34c 100644
@@ -10,6 +10,7 @@
 
 #include <linux/vmalloc.h>
 #include <linux/i2c.h>
+#include <media/tuner.h>
 
 #include "mxl111sf.h"
 #include "mxl111sf-reg.h"
@@ -868,6 +869,10 @@ static struct mxl111sf_tuner_config mxl_tuner_config = {
 static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
 {
 	struct mxl111sf_state *state = adap_to_priv(adap);
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	struct media_device *mdev = dvb_get_media_controller(&adap->dvb_adap);
+	int ret;
+#endif
 	int i;
 
 	pr_debug("%s()\n", __func__);
@@ -879,6 +884,21 @@ static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
 		adap->fe[i]->ops.read_signal_strength = adap->fe[i]->ops.tuner_ops.get_rf_strength;
 	}
 
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	state->tuner.function = MEDIA_ENT_F_TUNER;
+	state->tuner.name = "mxl111sf tuner";
+	state->tuner_pads[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
+	state->tuner_pads[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&state->tuner,
+				     TUNER_NUM_PADS, state->tuner_pads);
+	if (ret)
+		return ret;
+
+	ret = media_device_register_entity(mdev, &state->tuner);
+	if (ret)
+		return ret;
+#endif
 	return 0;
 }
 
drivers/media/usb/dvb-usb-v2/mxl111sf.h +5 -0
index ee70df1f1e94..846260e0eec0 100644
@@ -17,6 +17,7 @@
 #define DVB_USB_LOG_PREFIX "mxl111sf"
 #include "dvb_usb.h"
 #include <media/tveeprom.h>
+#include <media/media-entity.h>
 
 #define MXL_EP1_REG_READ     1
 #define MXL_EP2_REG_WRITE    2
@@ -85,6 +86,10 @@ struct mxl111sf_state {
 	struct mutex fe_lock;
 	u8 num_frontends;
 	struct mxl111sf_adap_state adap_state[3];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+	struct media_entity tuner;
+	struct media_pad tuner_pads[2];
+#endif
 };
 
 int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data);
Signature · 1 line
2.5.0