[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250717-multicontext-mainline-2025-v1-20-81ac18979c03@ideasonboard.com>
Date: Thu, 17 Jul 2025 12:45:46 +0200
From: Jacopo Mondi <jacopo.mondi@...asonboard.com>
To: Sakari Ailus <sakari.ailus@...ux.intel.com>,
Laurent Pinchart <laurent.pinchart@...asonboard.com>,
Tomi Valkeinen <tomi.valkeinen@...asonboard.com>,
Kieran Bingham <kieran.bingham@...asonboard.com>,
Nicolas Dufresne <nicolas.dufresne@...labora.com>,
Mauro Carvalho Chehab <mchehab@...nel.org>,
Tomasz Figa <tfiga@...omium.org>,
Marek Szyprowski <m.szyprowski@...sung.com>,
Raspberry Pi Kernel Maintenance <kernel-list@...pberrypi.com>,
Florian Fainelli <florian.fainelli@...adcom.com>,
Broadcom internal kernel review list <bcm-kernel-feedback-list@...adcom.com>,
Hans Verkuil <hverkuil@...nel.org>
Cc: linux-kernel@...r.kernel.org, linux-media@...r.kernel.org,
linux-rpi-kernel@...ts.infradead.org, linux-arm-kernel@...ts.infradead.org,
Jacopo Mondi <jacopo.mondi@...asonboard.com>
Subject: [PATCH 20/26] media: v4l2-subdev: Validate media links with
context
The v4l2-subdev.c file provides an helper for subdevs to implement
the media entity .link_validate() operation which can be used by
subdevice drivers.
Provide an 'overload' of the v4l2_subdev_link_validate() function
that supports contexts to be used by context-aware subdev drivers to
implement .link_validate_context().
When v4l2_subdev_link_validate() is used to validate a subdev-to-subdev
link, propagate the media_device_context to all the call chain and
introduce (and use) helpers to get the subdevice state from either the
context or the subdev in case no context is available.
Signed-off-by: Jacopo Mondi <jacopo.mondi@...asonboard.com>
---
drivers/media/v4l2-core/v4l2-subdev.c | 148 +++++++++++++++++++++++++++-------
include/media/v4l2-subdev.h | 34 +++++++-
2 files changed, 151 insertions(+), 31 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 66c539d880127844893620d325a2b05ac4aa9e96..59f42a3a9755a77ea74442605dbbc2af1b67a0ea 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -1460,6 +1460,71 @@ int v4l2_subdev_get_fwnode_pad_1_to_1(struct media_entity *entity,
}
EXPORT_SYMBOL_GPL(v4l2_subdev_get_fwnode_pad_1_to_1);
+/*
+ * Retrieve the subdevice state from the media device context or,
+ * if there is no context, use the active state from the subdevice.
+ *
+ * These three functions wraps the usual subdev state helpers:
+ *
+ * - get_unlocked
+ * - get_locked
+ * - lock_and_get
+ */
+
+static struct v4l2_subdev_state *
+v4l2_subdev_get_unlocked_state_from_mdev_ctx(struct v4l2_subdev *sd,
+ struct media_device_context *mdev_ctx)
+{
+ struct v4l2_subdev_context *ctx = NULL;
+
+ if (mdev_ctx) {
+ ctx = v4l2_subdev_context_get(mdev_ctx, sd);
+ if (WARN_ON(!ctx))
+ return NULL;
+ }
+
+ if (ctx)
+ return v4l2_subdev_get_unlocked_active_state(ctx);
+
+ return v4l2_subdev_get_unlocked_active_state(sd);
+}
+
+static struct v4l2_subdev_state *
+v4l2_subdev_get_locked_state_from_mdev_ctx(struct v4l2_subdev *sd,
+ struct media_device_context *mdev_ctx)
+{
+ struct v4l2_subdev_context *ctx = NULL;
+
+ if (mdev_ctx) {
+ ctx = v4l2_subdev_context_get(mdev_ctx, sd);
+ if (WARN_ON(!ctx))
+ return NULL;
+ }
+
+ if (ctx)
+ return v4l2_subdev_get_locked_active_state(ctx);
+
+ return v4l2_subdev_get_locked_active_state(sd);
+}
+
+static struct v4l2_subdev_state *
+v4l2_subdev_lock_and_get_state_from_mdev_ctx(struct v4l2_subdev *sd,
+ struct media_device_context *mdev_ctx)
+{
+ struct v4l2_subdev_context *ctx = NULL;
+
+ if (mdev_ctx) {
+ ctx = v4l2_subdev_context_get(mdev_ctx, sd);
+ if (WARN_ON(!ctx))
+ return NULL;
+ }
+
+ if (ctx)
+ return v4l2_subdev_lock_and_get_active_state(ctx);
+
+ return v4l2_subdev_lock_and_get_active_state(sd);
+}
+
int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
struct media_link *link,
struct v4l2_subdev_format *source_fmt,
@@ -1518,8 +1583,9 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
static int
-v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream,
- struct v4l2_subdev_format *fmt,
+v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+ struct media_device_context *mdev_context,
+ u32 stream, struct v4l2_subdev_format *fmt,
bool states_locked)
{
struct v4l2_subdev_state *state;
@@ -1533,9 +1599,11 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream,
fmt->stream = stream;
if (states_locked)
- state = v4l2_subdev_get_locked_active_state(sd);
+ state = v4l2_subdev_get_locked_state_from_mdev_ctx(sd,
+ mdev_context);
else
- state = v4l2_subdev_lock_and_get_active_state(sd);
+ state = v4l2_subdev_lock_and_get_state_from_mdev_ctx(sd,
+ mdev_context);
ret = v4l2_subdev_call(sd, pad, get_fmt, state, fmt);
@@ -1548,6 +1616,7 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream,
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
static void __v4l2_link_validate_get_streams(struct media_pad *pad,
+ struct media_device_context *mdev_context,
u64 *streams_mask,
bool states_locked)
{
@@ -1560,10 +1629,11 @@ static void __v4l2_link_validate_get_streams(struct media_pad *pad,
*streams_mask = 0;
if (states_locked)
- state = v4l2_subdev_get_locked_active_state(subdev);
+ state = v4l2_subdev_get_locked_state_from_mdev_ctx(subdev,
+ mdev_context);
else
- state = v4l2_subdev_lock_and_get_active_state(subdev);
-
+ state = v4l2_subdev_lock_and_get_state_from_mdev_ctx(subdev,
+ mdev_context);
if (WARN_ON(!state))
return;
@@ -1592,6 +1662,7 @@ static void __v4l2_link_validate_get_streams(struct media_pad *pad,
#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
static void v4l2_link_validate_get_streams(struct media_pad *pad,
+ struct media_device_context *mdev_context,
u64 *streams_mask,
bool states_locked)
{
@@ -1604,14 +1675,17 @@ static void v4l2_link_validate_get_streams(struct media_pad *pad,
}
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
- __v4l2_link_validate_get_streams(pad, streams_mask, states_locked);
+ __v4l2_link_validate_get_streams(pad, mdev_context, streams_mask,
+ states_locked);
#else
/* This shouldn't happen */
*streams_mask = 0;
#endif
}
-static int v4l2_subdev_link_validate_locked(struct media_link *link, bool states_locked)
+static int v4l2_subdev_link_validate_locked(struct media_link *link,
+ struct media_device_context *mdev_context,
+ bool states_locked)
{
struct v4l2_subdev *sink_subdev =
media_entity_to_v4l2_subdev(link->sink->entity);
@@ -1626,8 +1700,10 @@ static int v4l2_subdev_link_validate_locked(struct media_link *link, bool states
link->source->entity->name, link->source->index,
link->sink->entity->name, link->sink->index);
- v4l2_link_validate_get_streams(link->source, &source_streams_mask, states_locked);
- v4l2_link_validate_get_streams(link->sink, &sink_streams_mask, states_locked);
+ v4l2_link_validate_get_streams(link->source, mdev_context,
+ &source_streams_mask, states_locked);
+ v4l2_link_validate_get_streams(link->sink, mdev_context,
+ &sink_streams_mask, states_locked);
/*
* It is ok to have more source streams than sink streams as extra
@@ -1654,7 +1730,8 @@ static int v4l2_subdev_link_validate_locked(struct media_link *link, bool states
link->source->entity->name, link->source->index, stream,
link->sink->entity->name, link->sink->index, stream);
- ret = v4l2_subdev_link_validate_get_format(link->source, stream,
+ ret = v4l2_subdev_link_validate_get_format(link->source,
+ mdev_context, stream,
&source_fmt, states_locked);
if (ret < 0) {
dev_dbg(dev,
@@ -1664,7 +1741,8 @@ static int v4l2_subdev_link_validate_locked(struct media_link *link, bool states
continue;
}
- ret = v4l2_subdev_link_validate_get_format(link->sink, stream,
+ ret = v4l2_subdev_link_validate_get_format(link->sink,
+ mdev_context, stream,
&sink_fmt, states_locked);
if (ret < 0) {
dev_dbg(dev,
@@ -1693,7 +1771,8 @@ static int v4l2_subdev_link_validate_locked(struct media_link *link, bool states
return 0;
}
-int v4l2_subdev_link_validate(struct media_link *link)
+int __v4l2_subdev_link_validate(struct media_link *link,
+ struct media_device_context *mdev_context)
{
struct v4l2_subdev *source_sd, *sink_sd;
struct v4l2_subdev_state *source_state, *sink_state;
@@ -1716,28 +1795,35 @@ int v4l2_subdev_link_validate(struct media_link *link)
if (is_media_entity_v4l2_video_device(link->source->entity)) {
struct media_entity *source = link->source->entity;
- if (!source->ops || !source->ops->link_validate) {
+ if (!source->ops ||
+ (mdev_context && !source->ops->link_validate_context) ||
+ (!mdev_context && !source->ops->link_validate)) {
/*
- * Many existing drivers do not implement the required
- * .link_validate() operation for their video devices.
- * Print a warning to get the drivers fixed, and return
- * 0 to avoid breaking userspace. This should
- * eventually be turned into a WARN_ON() when all
- * drivers will have been fixed.
+ * Many existing drivers do not implement the correct
+ * .link_validate() or .link_validate_context()
+ * operations for their video devices. Print a warning
+ * to get the drivers fixed, and return 0 to avoid
+ * breaking userspace. This should eventually be turned
+ * into a WARN_ON() when all drivers will have been
+ * fixed.
*/
- pr_warn_once("video device '%s' does not implement .link_validate(), driver bug!\n",
+ pr_warn_once("video device '%s' does not implement the correct .link_validate operation: driver bug!\n",
source->name);
return 0;
}
/*
* Avoid infinite loops in case a video device incorrectly uses
- * this helper function as its .link_validate() handler.
+ * this helper function as its .link_validate[_context]()
+ * handler.
*/
- if (WARN_ON(source->ops->link_validate == v4l2_subdev_link_validate))
+ if (WARN_ON(source->ops->link_validate == v4l2_subdev_link_validate ||
+ source->ops->link_validate_context == v4l2_subdev_link_validate_context))
return -EINVAL;
- return source->ops->link_validate(link);
+ return (mdev_context && source->ops->link_validate_context) ?
+ source->ops->link_validate_context(link, mdev_context) :
+ source->ops->link_validate(link);
}
/*
@@ -1750,22 +1836,24 @@ int v4l2_subdev_link_validate(struct media_link *link)
sink_sd = media_entity_to_v4l2_subdev(link->sink->entity);
source_sd = media_entity_to_v4l2_subdev(link->source->entity);
- sink_state = v4l2_subdev_get_unlocked_active_state(sink_sd);
- source_state = v4l2_subdev_get_unlocked_active_state(source_sd);
-
+ sink_state = v4l2_subdev_get_unlocked_state_from_mdev_ctx(sink_sd,
+ mdev_context);
+ source_state = v4l2_subdev_get_unlocked_state_from_mdev_ctx(source_sd,
+ mdev_context);
states_locked = sink_state && source_state;
if (states_locked)
v4l2_subdev_lock_states(sink_state, source_state);
- ret = v4l2_subdev_link_validate_locked(link, states_locked);
+ ret = v4l2_subdev_link_validate_locked(link, mdev_context, states_locked);
if (states_locked)
v4l2_subdev_unlock_states(sink_state, source_state);
return ret;
+
}
-EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
+EXPORT_SYMBOL_GPL(__v4l2_subdev_link_validate);
bool v4l2_subdev_has_pad_interdep(struct media_entity *entity,
unsigned int pad0, unsigned int pad1)
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 8087c0ae3bc0a0a95512b4b0ff5257522a104ca0..16b6b265e711cf2293ce2478ef90a622beb869e5 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -1223,6 +1223,7 @@ struct v4l2_subdev {
* @vfh: pointer to &struct v4l2_fh
* @state: pointer to &struct v4l2_subdev_state
* @owner: module pointer to the owner of this file handle
+ * @context: pointer to subdevice context associated with the file handle
* @client_caps: bitmask of ``V4L2_SUBDEV_CLIENT_CAP_*``
*/
struct v4l2_subdev_fh {
@@ -1351,6 +1352,9 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
struct v4l2_subdev_format *source_fmt,
struct v4l2_subdev_format *sink_fmt);
+int __v4l2_subdev_link_validate(struct media_link *link,
+ struct media_device_context *mdev_context);
+
/**
* v4l2_subdev_link_validate - validates a media link
*
@@ -1368,7 +1372,35 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
* the video devices also implement their &media_entity_ops.link_validate
* operation.
*/
-int v4l2_subdev_link_validate(struct media_link *link);
+static inline int v4l2_subdev_link_validate(struct media_link *link)
+{
+ return __v4l2_subdev_link_validate(link, NULL);
+}
+
+/**
+ * v4l2_subdev_link_validate_context - validates a media link in a media context
+ *
+ * @link: pointer to &struct media_link
+ * @mdev_context: the media device context
+ *
+ * This function calls the subdev's link_validate_context ops to validate
+ * if a media link is valid for streaming in a media device context. It also
+ * internally calls v4l2_subdev_link_validate_default() to ensure that width,
+ * height and the media bus pixel code are equal on both source and sink of the
+ * link.
+ *
+ * The function can be used as a drop-in &media_entity_ops.link_validate_context
+ * implementation for v4l2_subdev instances. It supports all links between
+ * subdevs, as well as links between subdevs and video devices, provided that
+ * the video devices also implement their
+ * &media_entity_ops.link_validate_context operation.
+ */
+static inline int
+v4l2_subdev_link_validate_context(struct media_link *link,
+ struct media_device_context *mdev_context)
+{
+ return __v4l2_subdev_link_validate(link, mdev_context);
+}
/**
* v4l2_subdev_has_pad_interdep - MC has_pad_interdep implementation for subdevs
--
2.49.0
Powered by blists - more mailing lists