[v11,32/36] media: subdev: add v4l2_subdev_routing_validate() helper
Commit Message
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Add a v4l2_subdev_routing_validate() helper for verifying routing for
common cases like only allowing non-overlapping 1-to-1 streams.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/media/v4l2-core/v4l2-subdev.c | 102 ++++++++++++++++++++++++++
include/media/v4l2-subdev.h | 39 ++++++++++
2 files changed, 141 insertions(+)
Comments
Hi Tomi
On Tue, Mar 01, 2022 at 06:11:52PM +0200, Tomi Valkeinen wrote:
> From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>
> Add a v4l2_subdev_routing_validate() helper for verifying routing for
> common cases like only allowing non-overlapping 1-to-1 streams.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
> drivers/media/v4l2-core/v4l2-subdev.c | 102 ++++++++++++++++++++++++++
> include/media/v4l2-subdev.h | 39 ++++++++++
> 2 files changed, 141 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 1ceee8313246..a19236cf11b8 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -1551,6 +1551,108 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
> }
> EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt);
>
> +int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,
> + const struct v4l2_subdev_krouting *routing,
> + enum v4l2_subdev_routing_restriction disallow)
> +{
> + u32 *remote_pads = NULL;
> + unsigned int i, j;
> + int ret = -EINVAL;
> +
> + if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) {
> + remote_pads = kcalloc(sd->entity.num_pads, sizeof(*remote_pads),
> + GFP_KERNEL);
> + if (!remote_pads)
> + return -ENOMEM;
> +
> + for (i = 0; i < sd->entity.num_pads; ++i)
> + remote_pads[i] = U32_MAX;
> + }
> +
> + for (i = 0; i < routing->num_routes; ++i) {
> + const struct v4l2_subdev_route *route = &routing->routes[i];
I guess validating non-active routes is intentional.
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Thanks
j
> +
> + /* Validate the sink and source pad numbers. */
> + if (route->sink_pad >= sd->entity.num_pads ||
> + !(sd->entity.pads[route->sink_pad].flags & MEDIA_PAD_FL_SINK)) {
> + dev_dbg(sd->dev, "route %u sink (%u) is not a sink pad\n",
> + i, route->sink_pad);
> + goto out;
> + }
> +
> + if (route->source_pad >= sd->entity.num_pads ||
> + !(sd->entity.pads[route->source_pad].flags & MEDIA_PAD_FL_SOURCE)) {
> + dev_dbg(sd->dev, "route %u source (%u) is not a source pad\n",
> + i, route->source_pad);
> + goto out;
> + }
> +
> + /*
> + * V4L2_SUBDEV_ROUTING_NO_STREAM_MIX: Streams on the same pad
> + * may not be routed to streams on different pads.
> + */
> + if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) {
> + if (remote_pads[route->sink_pad] != U32_MAX &&
> + remote_pads[route->sink_pad] != route->source_pad) {
> + dev_dbg(sd->dev,
> + "route %u attempts to mix %s streams\n",
> + i, "sink");
> + goto out;
> + }
> +
> + if (remote_pads[route->source_pad] != U32_MAX &&
> + remote_pads[route->source_pad] != route->sink_pad) {
> + dev_dbg(sd->dev,
> + "route %u attempts to mix %s streams\n",
> + i, "source");
> + goto out;
> + }
> +
> + remote_pads[route->sink_pad] = route->source_pad;
> + remote_pads[route->source_pad] = route->sink_pad;
> + }
> +
> + for (j = i + 1; j < routing->num_routes; ++j) {
> + const struct v4l2_subdev_route *r = &routing->routes[j];
> +
> + /*
> + * V4L2_SUBDEV_ROUTING_NO_1_TO_N: No two routes can
> + * originate from the same (sink) stream.
> + */
> + if ((disallow & V4L2_SUBDEV_ROUTING_NO_1_TO_N) &&
> + route->sink_pad == r->sink_pad &&
> + route->sink_stream == r->sink_stream) {
> + dev_dbg(sd->dev,
> + "routes %u and %u originate from same sink (%u/%u)\n",
> + i, j, route->sink_pad,
> + route->sink_stream);
> + goto out;
> + }
> +
> + /*
> + * V4L2_SUBDEV_ROUTING_NO_N_TO_1: No two routes can end
> + * at the same (source) stream.
> + */
> + if ((disallow & V4L2_SUBDEV_ROUTING_NO_N_TO_1) &&
> + route->source_pad == r->source_pad &&
> + route->source_stream == r->source_stream) {
> + dev_dbg(sd->dev,
> + "routes %u and %u end at same source (%u/%u)\n",
> + i, j, route->source_pad,
> + route->source_stream);
> + goto out;
> + }
> + }
> + }
> +
> + ret = 0;
> +
> +out:
> + kfree(remote_pads);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate);
> +
> #endif /* CONFIG_MEDIA_CONTROLLER */
>
> void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
> index 97db6dfc0b7a..f75e161d646b 100644
> --- a/include/media/v4l2-subdev.h
> +++ b/include/media/v4l2-subdev.h
> @@ -1514,6 +1514,45 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state,
> int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
> struct v4l2_subdev_format *format);
>
> +/**
> + * enum v4l2_subdev_routing_restriction - Subdevice internal routing restrictions
> + *
> + * @V4L2_SUBDEV_ROUTING_NO_1_TO_N:
> + * an input stream may not be routed to multiple output streams (stream
> + * duplication)
> + * @V4L2_SUBDEV_ROUTING_NO_N_TO_1:
> + * multiple input streams may not be routed to the same output stream
> + * (stream merging)
> + * @V4L2_SUBDEV_ROUTING_NO_STREAM_MIX:
> + * streams on the same pad may not be routed to streams on different pads
> + * @V4L2_SUBDEV_ROUTING_ONLY_1_TO_1:
> + * only non-overlapping 1-to-1 stream routing is allowed (a combination of
> + * @V4L2_SUBDEV_ROUTING_NO_1_TO_N and @V4L2_SUBDEV_ROUTING_NO_N_TO_1)
> + */
> +enum v4l2_subdev_routing_restriction {
> + V4L2_SUBDEV_ROUTING_NO_1_TO_N = BIT(0),
> + V4L2_SUBDEV_ROUTING_NO_N_TO_1 = BIT(1),
> + V4L2_SUBDEV_ROUTING_NO_STREAM_MIX = BIT(2),
> + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 =
> + V4L2_SUBDEV_ROUTING_NO_1_TO_N |
> + V4L2_SUBDEV_ROUTING_NO_N_TO_1,
> +};
> +
> +/**
> + * v4l2_subdev_routing_validate() - Verify that routes comply with driver
> + * constraints
> + * @sd: The subdevice
> + * @routing: Routing to verify
> + * @disallow: Restrictions on routes
> + *
> + * This verifies that the given routing complies with the @disallow constraints.
> + *
> + * Returns 0 on success, error value otherwise.
> + */
> +int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,
> + const struct v4l2_subdev_krouting *routing,
> + enum v4l2_subdev_routing_restriction disallow);
> +
> #endif /* CONFIG_MEDIA_CONTROLLER */
>
> /**
> --
> 2.25.1
>
On 16/03/2022 13:10, Jacopo Mondi wrote:
> Hi Tomi
>
> On Tue, Mar 01, 2022 at 06:11:52PM +0200, Tomi Valkeinen wrote:
>> From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>
>> Add a v4l2_subdev_routing_validate() helper for verifying routing for
>> common cases like only allowing non-overlapping 1-to-1 streams.
>>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>> ---
>> drivers/media/v4l2-core/v4l2-subdev.c | 102 ++++++++++++++++++++++++++
>> include/media/v4l2-subdev.h | 39 ++++++++++
>> 2 files changed, 141 insertions(+)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
>> index 1ceee8313246..a19236cf11b8 100644
>> --- a/drivers/media/v4l2-core/v4l2-subdev.c
>> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
>> @@ -1551,6 +1551,108 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
>> }
>> EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt);
>>
>> +int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,
>> + const struct v4l2_subdev_krouting *routing,
>> + enum v4l2_subdev_routing_restriction disallow)
>> +{
>> + u32 *remote_pads = NULL;
>> + unsigned int i, j;
>> + int ret = -EINVAL;
>> +
>> + if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) {
>> + remote_pads = kcalloc(sd->entity.num_pads, sizeof(*remote_pads),
>> + GFP_KERNEL);
>> + if (!remote_pads)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < sd->entity.num_pads; ++i)
>> + remote_pads[i] = U32_MAX;
>> + }
>> +
>> + for (i = 0; i < routing->num_routes; ++i) {
>> + const struct v4l2_subdev_route *route = &routing->routes[i];
>
> I guess validating non-active routes is intentional.
I think it's better to ensure non-active routes are valid too.
Tomi
@@ -1551,6 +1551,108 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
}
EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt);
+int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_krouting *routing,
+ enum v4l2_subdev_routing_restriction disallow)
+{
+ u32 *remote_pads = NULL;
+ unsigned int i, j;
+ int ret = -EINVAL;
+
+ if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) {
+ remote_pads = kcalloc(sd->entity.num_pads, sizeof(*remote_pads),
+ GFP_KERNEL);
+ if (!remote_pads)
+ return -ENOMEM;
+
+ for (i = 0; i < sd->entity.num_pads; ++i)
+ remote_pads[i] = U32_MAX;
+ }
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+
+ /* Validate the sink and source pad numbers. */
+ if (route->sink_pad >= sd->entity.num_pads ||
+ !(sd->entity.pads[route->sink_pad].flags & MEDIA_PAD_FL_SINK)) {
+ dev_dbg(sd->dev, "route %u sink (%u) is not a sink pad\n",
+ i, route->sink_pad);
+ goto out;
+ }
+
+ if (route->source_pad >= sd->entity.num_pads ||
+ !(sd->entity.pads[route->source_pad].flags & MEDIA_PAD_FL_SOURCE)) {
+ dev_dbg(sd->dev, "route %u source (%u) is not a source pad\n",
+ i, route->source_pad);
+ goto out;
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_STREAM_MIX: Streams on the same pad
+ * may not be routed to streams on different pads.
+ */
+ if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) {
+ if (remote_pads[route->sink_pad] != U32_MAX &&
+ remote_pads[route->sink_pad] != route->source_pad) {
+ dev_dbg(sd->dev,
+ "route %u attempts to mix %s streams\n",
+ i, "sink");
+ goto out;
+ }
+
+ if (remote_pads[route->source_pad] != U32_MAX &&
+ remote_pads[route->source_pad] != route->sink_pad) {
+ dev_dbg(sd->dev,
+ "route %u attempts to mix %s streams\n",
+ i, "source");
+ goto out;
+ }
+
+ remote_pads[route->sink_pad] = route->source_pad;
+ remote_pads[route->source_pad] = route->sink_pad;
+ }
+
+ for (j = i + 1; j < routing->num_routes; ++j) {
+ const struct v4l2_subdev_route *r = &routing->routes[j];
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_1_TO_N: No two routes can
+ * originate from the same (sink) stream.
+ */
+ if ((disallow & V4L2_SUBDEV_ROUTING_NO_1_TO_N) &&
+ route->sink_pad == r->sink_pad &&
+ route->sink_stream == r->sink_stream) {
+ dev_dbg(sd->dev,
+ "routes %u and %u originate from same sink (%u/%u)\n",
+ i, j, route->sink_pad,
+ route->sink_stream);
+ goto out;
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_N_TO_1: No two routes can end
+ * at the same (source) stream.
+ */
+ if ((disallow & V4L2_SUBDEV_ROUTING_NO_N_TO_1) &&
+ route->source_pad == r->source_pad &&
+ route->source_stream == r->source_stream) {
+ dev_dbg(sd->dev,
+ "routes %u and %u end at same source (%u/%u)\n",
+ i, j, route->source_pad,
+ route->source_stream);
+ goto out;
+ }
+ }
+ }
+
+ ret = 0;
+
+out:
+ kfree(remote_pads);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate);
+
#endif /* CONFIG_MEDIA_CONTROLLER */
void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
@@ -1514,6 +1514,45 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state,
int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format);
+/**
+ * enum v4l2_subdev_routing_restriction - Subdevice internal routing restrictions
+ *
+ * @V4L2_SUBDEV_ROUTING_NO_1_TO_N:
+ * an input stream may not be routed to multiple output streams (stream
+ * duplication)
+ * @V4L2_SUBDEV_ROUTING_NO_N_TO_1:
+ * multiple input streams may not be routed to the same output stream
+ * (stream merging)
+ * @V4L2_SUBDEV_ROUTING_NO_STREAM_MIX:
+ * streams on the same pad may not be routed to streams on different pads
+ * @V4L2_SUBDEV_ROUTING_ONLY_1_TO_1:
+ * only non-overlapping 1-to-1 stream routing is allowed (a combination of
+ * @V4L2_SUBDEV_ROUTING_NO_1_TO_N and @V4L2_SUBDEV_ROUTING_NO_N_TO_1)
+ */
+enum v4l2_subdev_routing_restriction {
+ V4L2_SUBDEV_ROUTING_NO_1_TO_N = BIT(0),
+ V4L2_SUBDEV_ROUTING_NO_N_TO_1 = BIT(1),
+ V4L2_SUBDEV_ROUTING_NO_STREAM_MIX = BIT(2),
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 =
+ V4L2_SUBDEV_ROUTING_NO_1_TO_N |
+ V4L2_SUBDEV_ROUTING_NO_N_TO_1,
+};
+
+/**
+ * v4l2_subdev_routing_validate() - Verify that routes comply with driver
+ * constraints
+ * @sd: The subdevice
+ * @routing: Routing to verify
+ * @disallow: Restrictions on routes
+ *
+ * This verifies that the given routing complies with the @disallow constraints.
+ *
+ * Returns 0 on success, error value otherwise.
+ */
+int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_krouting *routing,
+ enum v4l2_subdev_routing_restriction disallow);
+
#endif /* CONFIG_MEDIA_CONTROLLER */
/**