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

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
...@@ -1615,6 +1615,108 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, ...@@ -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); 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_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */ #endif /* CONFIG_MEDIA_CONTROLLER */
......
...@@ -1584,6 +1584,45 @@ struct v4l2_mbus_framefmt * ...@@ -1584,6 +1584,45 @@ struct v4l2_mbus_framefmt *
v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state,
u32 pad, u32 stream); 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_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */ #endif /* CONFIG_MEDIA_CONTROLLER */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment