media: subdev: Add v4l2_subdev_get_frame_desc_passthrough helper

Add a helper for v4l2_subdev_pad_ops.v4l2_get_frame_desc operation. The
helper can be used when the subdevice directly passes through the
streams.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Tomi Valkeinen
2025-03-24 10:04:44 +02:00
committed by Mauro Carvalho Chehab
parent 291d75952c
commit a564839e63
2 changed files with 135 additions and 0 deletions

View File

@@ -2545,6 +2545,119 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable)
}
EXPORT_SYMBOL_GPL(v4l2_subdev_s_stream_helper);
int v4l2_subdev_get_frame_desc_passthrough(struct v4l2_subdev *sd,
unsigned int pad,
struct v4l2_mbus_frame_desc *fd)
{
const struct media_pad *pads = sd->entity.pads;
struct media_pad *local_sink_pad;
struct v4l2_subdev_route *route;
struct v4l2_subdev_state *state;
struct device *dev = sd->dev;
int ret = 0;
if (WARN_ON(!(pads[pad].flags & MEDIA_PAD_FL_SOURCE)))
return -EINVAL;
state = v4l2_subdev_lock_and_get_active_state(sd);
/* Iterate over sink pads */
media_entity_for_each_pad(&sd->entity, local_sink_pad) {
struct v4l2_mbus_frame_desc source_fd;
bool have_source_fd = false;
if (!(local_sink_pad->flags & MEDIA_PAD_FL_SINK))
continue;
/*
* Copy frame desc entries for the streams going from the sink
* pad to the requested pad
*/
for_each_active_route(&state->routing, route) {
struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
struct media_pad *remote_source_pad;
struct v4l2_subdev *remote_sd;
unsigned int i;
if (route->source_pad != pad ||
route->sink_pad != local_sink_pad->index)
continue;
if (!have_source_fd) {
remote_source_pad = media_pad_remote_pad_unique(local_sink_pad);
if (!remote_source_pad) {
dev_dbg(dev, "Failed to find remote pad for sink pad %u\n",
local_sink_pad->index);
ret = -EINVAL;
goto out_unlock;
}
remote_sd = media_entity_to_v4l2_subdev(remote_source_pad->entity);
if (!remote_sd) {
ret = -EINVAL;
goto out_unlock;
}
ret = v4l2_subdev_call(remote_sd, pad,
get_frame_desc,
remote_source_pad->index,
&source_fd);
if (ret) {
dev_err(dev,
"Failed to get frame desc from remote subdev %s\n",
remote_sd->name);
goto out_unlock;
}
have_source_fd = true;
if (fd->num_entries == 0) {
fd->type = source_fd.type;
} else if (fd->type != source_fd.type) {
dev_err(dev,
"Frame desc type mismatch: %u != %u\n",
fd->type, source_fd.type);
ret = -EPIPE;
goto out_unlock;
}
}
for (i = 0; i < source_fd.num_entries; i++) {
if (source_fd.entry[i].stream == route->sink_stream) {
source_entry = &source_fd.entry[i];
break;
}
}
if (!source_entry) {
dev_dbg(sd->dev,
"Failed to find stream %u from source frame desc\n",
route->sink_stream);
ret = -EPIPE;
goto out_unlock;
}
if (fd->num_entries >= V4L2_FRAME_DESC_ENTRY_MAX) {
dev_dbg(sd->dev, "Frame desc entry limit reached\n");
ret = -ENOSPC;
goto out_unlock;
}
fd->entry[fd->num_entries] = *source_entry;
fd->entry[fd->num_entries].stream = route->source_stream;
fd->num_entries++;
}
}
out_unlock:
v4l2_subdev_unlock_state(state);
return ret;
}
EXPORT_SYMBOL_GPL(v4l2_subdev_get_frame_desc_passthrough);
#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */

View File

@@ -1722,6 +1722,28 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
*/
int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable);
/**
* v4l2_subdev_get_frame_desc_passthrough() - Helper to implement the subdev
* v4l2_get_frame_desc operation in simple passthrough cases
* @sd: The subdevice
* @pad: The source pad index
* @fd: The mbus frame desc
*
* Subdevice drivers that only pass through the streams can use this helper
* to implement the &v4l2_subdev_pad_ops.v4l2_get_frame_desc operation.
*
* The helper will call get_frame_desc on the subdevice's sources, create a new
* frame desc which contains only the streams on the given source pad. The data
* for each frame desc entry is copied directly from the data provided from the
* calls to the subdevice's sources, with the exception of the 'stream' field
* which is set according to the subdevice's routing table.
*
* Return: 0 on success, or a negative error code otherwise.
*/
int v4l2_subdev_get_frame_desc_passthrough(struct v4l2_subdev *sd,
unsigned int pad,
struct v4l2_mbus_frame_desc *fd);
#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */