Commit 69c0fe7a authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab
Browse files

media: subdev: add v4l2_subdev_routing_validate() helper



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: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Reviewed-by: default avatarJacopo Mondi <jacopo@jmondi.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent 5b0d85b3
Loading
Loading
Loading
Loading
+102 −0
Original line number Diff line number Diff line
@@ -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 */
+39 −0
Original line number Diff line number Diff line
@@ -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 */