[PATCH 03/16] media: v4l2-async: Add devlinks between notifier and async subdevices

Daniel Scally dan.scally at ideasonboard.com
Mon Nov 2 23:40:39 PST 2026


When we suspend devices with a live stream running the ordering of
the Power Management .suspend() and .resume() callbacks is important.
We expect that the 'main' driver will stop the stream in its own
.suspend() and restart it in .resume(), which means that it needs to
be first to stop and last to start to ensure that the other devices
in the pipeline are fully available when those callbacks run.

To facilitate this, add device links between the async subdevices and
their associated notifier immediately before calling .complete(). The
new V4L2_DEVICE_FL_IS_OUTPUT flag is checked, and in that case the
consumer and supplier of the device link are reversed.

Signed-off-by: Daniel Scally <dan.scally at ideasonboard.com>
---
 drivers/media/v4l2-core/v4l2-async.c | 107 +++++++++++++++++++++++++++++++++++
 1 file changed, 107 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 460bf3dbbb88264fa63e3808d323d26861c873f5..7199ea244cba6ff1d1ec159ff8d9533418b541e6 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -273,6 +273,107 @@ v4l2_async_nf_can_complete(struct v4l2_async_notifier *notifier)
 	return true;
 }
 
+static void v4l2_async_nf_remove_device_links(struct v4l2_async_notifier *notifier)
+{
+	struct v4l2_async_connection *asc;
+
+	list_for_each_entry(asc, &notifier->done_list, asc_entry) {
+		struct v4l2_async_notifier *subdev_notifier =
+			v4l2_async_find_subdev_notifier(asc->sd);
+		struct device *consumer, *supplier;
+		struct v4l2_device *v4l2_dev;
+
+		v4l2_dev = v4l2_async_nf_find_v4l2_dev(notifier);
+
+		if (v4l2_dev->flags & V4L2_DEVICE_FL_IS_OUTPUT) {
+			consumer = asc->sd->dev;
+			supplier = notifier_dev(notifier);
+		} else {
+			consumer = notifier_dev(notifier);
+			supplier = asc->sd->dev;
+		}
+
+		device_link_remove(consumer, supplier);
+
+		if (subdev_notifier)
+			v4l2_async_nf_remove_device_links(notifier);
+	}
+}
+
+static int __v4l2_async_nf_create_device_links(struct v4l2_async_notifier *notifier)
+{
+	struct v4l2_async_connection *asc;
+	struct device_link *link;
+	int ret;
+
+	list_for_each_entry(asc, &notifier->done_list, asc_entry) {
+		struct v4l2_async_notifier *subdev_notifier =
+			v4l2_async_find_subdev_notifier(asc->sd);
+		struct device *consumer, *supplier;
+		struct v4l2_device *v4l2_dev;
+
+		if (asc->match.type != V4L2_ASYNC_MATCH_TYPE_FWNODE)
+			continue;
+
+		/*
+		 * The following two checks shouldn't be possible, but better
+		 * safe than oopsing.
+		 */
+		if (!asc->sd) {
+			dev_err(notifier_dev(notifier),
+				"async connection with null sd\n");
+			return -EINVAL;
+		}
+
+		if (!asc->sd->dev) {
+			dev_err(notifier_dev(notifier),
+				"async connection with null device\n");
+			return -EINVAL;
+		}
+
+		v4l2_dev = v4l2_async_nf_find_v4l2_dev(notifier);
+
+		if (v4l2_dev->flags & V4L2_DEVICE_FL_IS_OUTPUT) {
+			consumer = asc->sd->dev;
+			supplier = notifier_dev(notifier);
+		} else {
+			consumer = notifier_dev(notifier);
+			supplier = asc->sd->dev;
+		}
+
+		link = device_link_add(consumer, supplier, 0);
+		if (!link)
+			return -EINVAL;
+
+		if (subdev_notifier) {
+			ret = __v4l2_async_nf_create_device_links(subdev_notifier);
+			if (ret) {
+				device_link_del(link);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int v4l2_async_nf_create_device_links(struct v4l2_async_notifier *notifier)
+{
+	int ret;
+
+	if (!notifier->v4l2_dev) {
+		dev_err(notifier_dev(notifier),
+			"v4l2-async: tried to create devlinks for sub-notifier\n");
+		return -EINVAL;
+	}
+
+	ret = __v4l2_async_nf_create_device_links(notifier);
+	if (ret)
+		v4l2_async_nf_remove_device_links(notifier);
+
+	return ret;
+}
+
 /*
  * Complete the master notifier if possible. This is done when all async
  * sub-devices have been bound; v4l2_device is also available then.
@@ -281,6 +382,7 @@ static int
 v4l2_async_nf_try_complete(struct v4l2_async_notifier *notifier)
 {
 	struct v4l2_async_notifier *__notifier = notifier;
+	int ret;
 
 	/* Quick check whether there are still more sub-devices here. */
 	if (!list_empty(&notifier->waiting_list))
@@ -305,6 +407,10 @@ v4l2_async_nf_try_complete(struct v4l2_async_notifier *notifier)
 	if (!v4l2_async_nf_can_complete(notifier))
 		return 0;
 
+	ret = v4l2_async_nf_create_device_links(notifier);
+	if (ret)
+		return ret;
+
 	dev_dbg(notifier_dev(__notifier), "v4l2-async: complete\n");
 
 	return v4l2_async_nf_call_complete(notifier);
@@ -494,6 +600,7 @@ v4l2_async_nf_unbind_all_subdevs(struct v4l2_async_notifier *notifier)
 		if (subdev_notifier)
 			v4l2_async_nf_unbind_all_subdevs(subdev_notifier);
 
+		device_link_remove(notifier_dev(notifier), asc->sd->dev);
 		v4l2_async_unbind_subdev_one(notifier, asc);
 	}
 

-- 
2.43.0




More information about the linux-arm-kernel mailing list