]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
media: subdev: Add v4l2_subdev_get_frame_desc_passthrough helper
authorTomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>
Mon, 24 Mar 2025 08:04:44 +0000 (10:04 +0200)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Wed, 11 Mar 2026 00:05:31 +0000 (01:05 +0100)
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>
drivers/media/v4l2-core/v4l2-subdev.c
include/media/v4l2-subdev.h

index 32e6f60e26c71ed04e729c69d399ecd4052ff770..9efd14d4026f22ab743a1a5719e6c87e9ef81860 100644 (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 */
index a37d9a847196b5f75ce6a119a7761be88bd5813f..e754ed3421c5cc7b75ee4092eb2686341fc2a18e 100644 (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 */