media: subdev: add v4l2_subdev_routing_validate() helper
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Tue, 21 Dec 2021 10:23:58 +0000 (11:23 +0100)
committerMauro Carvalho Chehab <mchehab@kernel.org>
Sun, 22 Jan 2023 08:42:43 +0000 (09:42 +0100)
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>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
drivers/media/v4l2-core/v4l2-subdev.c
include/media/v4l2-subdev.h

index ebd6ef9..d7e157c 100644 (file)
@@ -1615,6 +1615,108 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state,
 }
 EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format);
 
+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_VIDEO_V4L2_SUBDEV_API */
 
 #endif /* CONFIG_MEDIA_CONTROLLER */
index 0055211..06c254f 100644 (file)
@@ -1584,6 +1584,45 @@ struct v4l2_mbus_framefmt *
 v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state,
                                             u32 pad, u32 stream);
 
+/**
+ * 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_VIDEO_V4L2_SUBDEV_API */
 
 #endif /* CONFIG_MEDIA_CONTROLLER */