Add a declarative MC topology builder for CAMSS offline ISP drivers.
Drivers describe their entire media graph, entities (video devices,
subdevs, or base entities), their pads, and the links between them
in a static descriptor table. The builder validates the table,
allocates and registers all entities, and creates all MC pad links.

Signed-off-by: Loic Poulain <[email protected]>
---
 drivers/media/platform/qcom/camss/Makefile         |   3 +-
 .../media/platform/qcom/camss/camss-isp-pipeline.c | 361 +++++++++++++++++++++
 .../media/platform/qcom/camss/camss-isp-pipeline.h | 228 +++++++++++++
 3 files changed, 591 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/qcom/camss/Makefile 
b/drivers/media/platform/qcom/camss/Makefile
index 
f13c9f326cf81962bd165dc8dd2bb60207cd54a7..f3acb1b54b6c1455d72e2d947c860f0c337648de
 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -31,7 +31,8 @@ qcom-camss-objs += \
 obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o
 
 qcom-camss-isp-objs := camss-isp-bufq.o \
-               camss-isp-sched.o
+               camss-isp-sched.o \
+               camss-isp-pipeline.o
 
 obj-$(CONFIG_VIDEO_QCOM_CAMSS_ISP) += qcom-camss-isp.o
 
diff --git a/drivers/media/platform/qcom/camss/camss-isp-pipeline.c 
b/drivers/media/platform/qcom/camss/camss-isp-pipeline.c
new file mode 100644
index 
0000000000000000000000000000000000000000..8e44bedb0a41e3cf4fc7e3a138c1f48854f5efc8
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-isp-pipeline.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CAMSS ISP pipeline helper — declarative MC topology builder
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/slab.h>
+
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include "camss-isp-pipeline.h"
+
+#if !IS_ENABLED(CONFIG_MEDIA_CONTROLLER)
+static inline int media_entity_pads_init(struct media_entity *e, u16 n,
+                                        struct media_pad *p) { return 0; }
+static inline void media_entity_remove_links(struct media_entity *e) {}
+static inline int media_create_pad_link(struct media_entity *src, u16 sp,
+                                       struct media_entity *sink, u16 dp,
+                                       u32 flags) { return 0; }
+#endif
+
+/* -------- Internal elpers -------- */
+
+static enum vfl_devnode_direction isp_caps_to_vfl_dir(u32 caps)
+{
+       if (caps & (V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE))
+               return VFL_DIR_M2M;
+       if (caps & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+                   V4L2_CAP_META_OUTPUT | V4L2_CAP_VBI_OUTPUT | 
V4L2_CAP_SDR_OUTPUT))
+               return VFL_DIR_TX;
+       return VFL_DIR_RX;
+}
+
+static unsigned int isp_count_pads(const struct camss_isp_pad_desc *pads)
+{
+       unsigned int n = 0;
+
+       if (!pads)
+               return 0;
+       while (pads[n].flags)
+               n++;
+       return n;
+}
+
+static struct media_entity *isp_pipeline_media_entity(struct 
camss_isp_pipeline *pipeline,
+                                                     unsigned int idx)
+{
+       struct camss_isp_pipeline_entity *slot = &pipeline->entities[idx];
+
+       switch (slot->obj_type) {
+       case MEDIA_ENTITY_TYPE_VIDEO_DEVICE:
+               return &slot->vdev.entity;
+       case MEDIA_ENTITY_TYPE_V4L2_SUBDEV:
+               return &slot->subdev.entity;
+       default:
+               return &slot->entity;
+       }
+}
+
+/* -------- Validation -------- */
+
+static int isp_pipeline_validate(struct device *dev,
+                                const struct camss_isp_entity_desc *descs,
+                                unsigned int num_entities)
+{
+       unsigned int i, pi;
+
+       for (i = 0; i < num_entities; i++) {
+               const struct camss_isp_pad_desc *pads = descs[i].pads;
+               unsigned int num_pads = isp_count_pads(pads);
+
+               for (pi = 0; pi < num_pads; pi++) {
+                       const struct camss_isp_pad_desc *pad = &pads[pi];
+                       const struct camss_isp_pad_desc *peer_pad;
+                       unsigned int peer_num_pads;
+                       int peer_ent = pad->peer_entity;
+
+                       if (peer_ent < 0)
+                               continue;
+
+                       if ((unsigned int)peer_ent >= num_entities) {
+                               dev_err(dev, "entity[%u].p%u: peer_entity %d 
out of range\n",
+                                       i, pi, peer_ent);
+                               return -EINVAL;
+                       }
+
+                       peer_num_pads = isp_count_pads(descs[peer_ent].pads);
+                       if (pad->peer_pad >= peer_num_pads) {
+                               dev_err(dev, "entity[%u].p%u: peer_pad %u out 
of range\n",
+                                       i, pi, pad->peer_pad);
+                               return -EINVAL;
+                       }
+
+                       peer_pad = &descs[peer_ent].pads[pad->peer_pad];
+
+                       /* Links are SOURCE->SINK; reject SOURCE->SOURCE or 
SINK->SINK */
+                       if (((pad->flags & MEDIA_PAD_FL_SOURCE) &&
+                            (peer_pad->flags & MEDIA_PAD_FL_SOURCE)) ||
+                           ((pad->flags & MEDIA_PAD_FL_SINK) &&
+                            (peer_pad->flags & MEDIA_PAD_FL_SINK))) {
+                               dev_err(dev, "entity[%u].p%u -> entity[%d].p%u: 
invalid\n",
+                                       i, pi, peer_ent, pad->peer_pad);
+                               return -EINVAL;
+                       }
+
+                       /* Verify back-reference consistency */
+                       if (peer_pad->peer_entity >= 0 &&
+                           ((unsigned int)peer_pad->peer_entity != i ||
+                            peer_pad->peer_pad != pi)) {
+                               dev_err(dev, "entity[%u].p%u <-> 
entity[%d].p%u: mismatch\n",
+                                       i, pi, peer_ent, pad->peer_pad);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* -------- Allocation / Release -------- */
+
+struct camss_isp_pipeline *camss_isp_pipeline_alloc(unsigned int num_entities)
+{
+       struct camss_isp_pipeline *pipeline;
+
+       pipeline = kzalloc(struct_size(pipeline, entities, num_entities),
+                          GFP_KERNEL);
+       if (!pipeline)
+               return ERR_PTR(-ENOMEM);
+
+       pipeline->num_entities = num_entities;
+       return pipeline;
+}
+EXPORT_SYMBOL_GPL(camss_isp_pipeline_alloc);
+
+void camss_isp_pipeline_free(struct camss_isp_pipeline *pipeline)
+{
+       kfree(pipeline);
+}
+EXPORT_SYMBOL_GPL(camss_isp_pipeline_free);
+
+/* -------- Registration -------- */
+
+void camss_isp_pipeline_unregister(struct camss_isp_pipeline *pipeline)
+{
+       int i;
+
+       /* Unregister entities in reverse order */
+       for (i = (int)pipeline->num_entities - 1; i >= 0; i--) {
+               struct camss_isp_pipeline_entity *slot = &pipeline->entities[i];
+
+               switch (slot->obj_type) {
+               case MEDIA_ENTITY_TYPE_VIDEO_DEVICE:
+                       if (slot->vdev.name[0])
+                               video_unregister_device(&slot->vdev);
+                       break;
+               case MEDIA_ENTITY_TYPE_V4L2_SUBDEV:
+                       if (slot->subdev.name[0])
+                               v4l2_device_unregister_subdev(&slot->subdev);
+                       break;
+               case MEDIA_ENTITY_TYPE_BASE:
+                       if (slot->entity.name) {
+                               media_entity_remove_links(&slot->entity);
+                               media_device_unregister_entity(&slot->entity);
+                       }
+                       break;
+               }
+
+               kfree(slot->pads);
+               slot->pads = NULL;
+       }
+
+       pipeline->v4l2_dev = NULL;
+}
+EXPORT_SYMBOL_GPL(camss_isp_pipeline_unregister);
+
+static int isp_register_vdev(struct camss_isp_pipeline_entity *slot,
+                            const struct camss_isp_entity_desc *desc,
+                            struct v4l2_device *v4l2_dev)
+{
+       struct video_device *vdev = &slot->vdev;
+       int ret;
+
+       strscpy(vdev->name, desc->name, sizeof(vdev->name));
+       vdev->vfl_dir     = isp_caps_to_vfl_dir(desc->vdev.caps);
+       vdev->v4l2_dev    = v4l2_dev;
+       vdev->device_caps = desc->vdev.caps;
+       vdev->release     = video_device_release_empty;
+       if (desc->vdev.fops)
+               vdev->fops = desc->vdev.fops;
+       if (desc->vdev.ioctl_ops)
+               vdev->ioctl_ops = desc->vdev.ioctl_ops;
+
+       vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
+       vdev->entity.function = desc->function ? desc->function : 
MEDIA_ENT_F_IO_V4L;
+
+       ret = media_entity_pads_init(&vdev->entity, slot->num_pads, slot->pads);
+       if (ret)
+               return ret;
+
+       ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+       if (ret)
+               return ret;
+
+       video_set_drvdata(vdev, desc->vdev.drvdata);
+
+       return 0;
+}
+
+static int isp_register_subdev(struct camss_isp_pipeline_entity *slot,
+                              const struct camss_isp_entity_desc *desc,
+                              struct v4l2_device *v4l2_dev)
+{
+       struct v4l2_subdev *sd = &slot->subdev;
+       int ret;
+
+       v4l2_subdev_init(sd, desc->subdev.ops);
+       strscpy(sd->name, desc->name, sizeof(sd->name));
+       sd->entity.function = desc->function ?
+                             desc->function : MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
+
+       ret = media_entity_pads_init(&sd->entity, slot->num_pads, slot->pads);
+       if (ret)
+               return ret;
+
+       return v4l2_device_register_subdev(v4l2_dev, sd);
+}
+
+static int isp_register_base_entity(struct camss_isp_pipeline_entity *slot,
+                                   const struct camss_isp_entity_desc *desc,
+                                   struct v4l2_device *v4l2_dev)
+{
+       struct media_entity *entity = &slot->entity;
+       int ret;
+
+       entity->obj_type = MEDIA_ENTITY_TYPE_BASE;
+       entity->name     = desc->name;
+       entity->function = desc->function;
+
+       ret = media_entity_pads_init(entity, slot->num_pads, slot->pads);
+       if (ret)
+               return ret;
+
+       return media_device_register_entity(v4l2_dev->mdev, entity);
+}
+
+static int isp_alloc_pads(struct camss_isp_pipeline_entity *slot,
+                         const struct camss_isp_entity_desc *desc)
+{
+       unsigned int num_pads = isp_count_pads(desc->pads);
+       unsigned int i;
+
+       if (!num_pads)
+               goto done;
+
+       slot->pads = kcalloc(num_pads, sizeof(*slot->pads), GFP_KERNEL);
+       if (!slot->pads)
+               return -ENOMEM;
+
+       for (i = 0; i < num_pads; i++)
+               slot->pads[i].flags = desc->pads[i].flags;
+done:
+       slot->num_pads = num_pads;
+       return 0;
+}
+
+int camss_isp_pipeline_register(struct camss_isp_pipeline *pipeline,
+                               struct v4l2_device *v4l2_dev,
+                               const struct camss_isp_entity_desc *descs,
+                               unsigned int num_entities)
+{
+       unsigned int i, pi;
+       int ret;
+
+       if (WARN_ON(num_entities != pipeline->num_entities))
+               return -EINVAL;
+
+       if (WARN_ON(!v4l2_dev || !v4l2_dev->mdev))
+               return -EINVAL;
+
+       ret = isp_pipeline_validate(v4l2_dev->dev, descs, num_entities);
+       if (ret)
+               return ret;
+
+       pipeline->v4l2_dev = v4l2_dev;
+
+       /* Register each entity */
+       for (i = 0; i < num_entities; i++) {
+               const struct camss_isp_entity_desc *desc = &descs[i];
+               struct camss_isp_pipeline_entity *slot = &pipeline->entities[i];
+
+               slot->obj_type = desc->obj_type;
+
+               ret = isp_alloc_pads(slot, desc);
+               if (ret)
+                       goto err_unregister;
+
+               switch (desc->obj_type) {
+               case MEDIA_ENTITY_TYPE_VIDEO_DEVICE:
+                       ret = isp_register_vdev(slot, desc, v4l2_dev);
+                       break;
+               case MEDIA_ENTITY_TYPE_V4L2_SUBDEV:
+                       ret = isp_register_subdev(slot, desc, v4l2_dev);
+                       break;
+               case MEDIA_ENTITY_TYPE_BASE:
+               default:
+                       ret = isp_register_base_entity(slot, desc, v4l2_dev);
+                       break;
+               }
+               if (ret)
+                       goto err_unregister;
+       }
+
+       /* Create links — only from SOURCE side to avoid duplicates */
+       for (i = 0; i < num_entities; i++) {
+               const struct camss_isp_entity_desc *desc = &descs[i];
+               unsigned int num_pads = isp_count_pads(desc->pads);
+
+               for (pi = 0; pi < num_pads; pi++) {
+                       const struct camss_isp_pad_desc *pad = &desc->pads[pi];
+                       struct media_entity *src_entity, *sink_entity;
+                       unsigned int src_pad_idx, sink_pad_idx;
+                       u32 lflags;
+
+                       if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
+                               continue;
+                       if (pad->peer_entity < 0)
+                               continue;
+
+                       src_entity   = isp_pipeline_media_entity(pipeline, i);
+                       sink_entity  = isp_pipeline_media_entity(pipeline,
+                                                                (unsigned 
int)pad->peer_entity);
+                       src_pad_idx  = pi;
+                       sink_pad_idx = pad->peer_pad;
+
+                       lflags = pad->link_flags ?
+                                pad->link_flags :
+                                (MEDIA_LNK_FL_IMMUTABLE | 
MEDIA_LNK_FL_ENABLED);
+
+                       ret = media_create_pad_link(src_entity,  src_pad_idx,
+                                                   sink_entity, sink_pad_idx,
+                                                   lflags);
+                       if (ret)
+                               goto err_unregister;
+               }
+       }
+
+       return 0;
+
+err_unregister:
+       camss_isp_pipeline_unregister(pipeline);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(camss_isp_pipeline_register);
+
+MODULE_DESCRIPTION("CAMSS ISP pipeline topology builder");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/qcom/camss/camss-isp-pipeline.h 
b/drivers/media/platform/qcom/camss/camss-isp-pipeline.h
new file mode 100644
index 
0000000000000000000000000000000000000000..5dfa32dcafc0a944ca2c160fb5846a2c73214acc
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-isp-pipeline.h
@@ -0,0 +1,228 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * CAMSS ISP pipeline helper — declarative MC topology builder
+ *
+ * Drivers describe their entire media graph — entities (video devices,
+ * subdevs, or base entities), their pads, and the links between them —
+ * in a single static descriptor table.  The builder validates the table,
+ * allocates and registers all entities, and creates all MC links.
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef _CAMSS_ISP_PIPELINE_H
+#define _CAMSS_ISP_PIPELINE_H
+
+#include <linux/mutex.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+/**
+ * struct camss_isp_pad_desc - descriptor for one pad and its optional link
+ *
+ * @flags:       Pad flags: MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
+ *               MEDIA_PAD_FL_MUST_CONNECT.  A zero @flags value acts as
+ *               the sentinel that terminates the pad list.
+ * @peer_entity: Index of the peer entity in the descriptor array, or -1
+ *               if this pad has no link.
+ * @peer_pad:    Pad index on the peer entity to link to.
+ * @link_flags:  MC link flags (MEDIA_LNK_FL_*).  Defaults to
+ *               MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED when zero.
+ *
+ * Links are described from both sides (each endpoint references the other),
+ * but the builder only creates each link once — from the SOURCE side.
+ */
+struct camss_isp_pad_desc {
+       u32          flags;
+       int          peer_entity;
+       unsigned int peer_pad;
+       u32          link_flags;
+};
+
+/**
+ * struct camss_isp_entity_desc - descriptor for one entity in the pipeline
+ *
+ * @name:      Human-readable entity name (also used as video device name
+ *             suffix when @obj_type is MEDIA_ENTITY_TYPE_VIDEO_DEVICE).
+ * @obj_type:  MEDIA_ENTITY_TYPE_VIDEO_DEVICE, MEDIA_ENTITY_TYPE_V4L2_SUBDEV,
+ *             or MEDIA_ENTITY_TYPE_BASE.
+ * @function:  MEDIA_ENT_F_* function identifier.
+ * @pads:      Sentinel-terminated (flags == 0) array of pad descriptors.
+ *
+ * Fields used only for MEDIA_ENTITY_TYPE_VIDEO_DEVICE:
+ * @vdev.caps: V4L2_CAP_* device capabilities.
+ *             The video device direction (VFL_DIR_RX/TX/M2M) is derived
+ *             automatically from @caps by the builder.
+ * @vdev.drvdata: Opaque pointer set via video_set_drvdata() after 
registration.
+ * @vdev.fops:      File operations (may be NULL to use kernel defaults).
+ * @vdev.ioctl_ops: ioctl operations (may be NULL).
+ *
+ * Fields used only for MEDIA_ENTITY_TYPE_V4L2_SUBDEV:
+ * @subdev.ops: Subdev operations (may be NULL).
+ */
+struct camss_isp_entity_desc {
+       const char                              *name;
+       u32                                     obj_type;
+       u32                                     function;
+       const struct camss_isp_pad_desc         *pads;
+
+       union {
+               /* MEDIA_ENTITY_TYPE_VIDEO_DEVICE */
+               struct {
+                       u32                                     caps;
+                       void                                    *drvdata;
+                       const struct v4l2_file_operations       *fops;
+                       const struct v4l2_ioctl_ops             *ioctl_ops;
+               } vdev;
+               /* MEDIA_ENTITY_TYPE_V4L2_SUBDEV */
+               struct {
+                       const struct v4l2_subdev_ops            *ops;
+               } subdev;
+       };
+};
+
+/**
+ * struct camss_isp_pipeline_entity - one registered entity slot
+ *
+ * Internal to the pipeline; drivers access entities via the accessor helpers.
+ *
+ * @obj_type: mirrors the descriptor's @obj_type.
+ * @pads:     allocated pad array for this entity.
+ * @num_pads: number of entries in @pads.
+ * @vdev:     valid when @obj_type == MEDIA_ENTITY_TYPE_VIDEO_DEVICE.
+ * @subdev:   valid when @obj_type == MEDIA_ENTITY_TYPE_V4L2_SUBDEV.
+ * @entity:   valid when @obj_type == MEDIA_ENTITY_TYPE_BASE.
+ */
+struct camss_isp_pipeline_entity {
+       u32                      obj_type;
+       struct media_pad        *pads;
+       unsigned int             num_pads;
+       union {
+               struct video_device  vdev;
+               struct v4l2_subdev   subdev;
+               struct media_entity  entity;
+       };
+};
+
+/**
+ * struct camss_isp_pipeline - registered ISP pipeline topology
+ *
+ * Allocate with camss_isp_pipeline_alloc(), register with
+ * camss_isp_pipeline_register(), tear down with
+ * camss_isp_pipeline_unregister(), free with camss_isp_pipeline_free().
+ *
+ * @v4l2_dev:     Pointer to the caller-provided V4L2 device.
+ * @drv_priv:     Driver-private pointer; not touched by the framework.
+ * @num_entities: Number of entries in @entities.
+ * @entities:     Per-entity state; flexible array.
+ */
+struct camss_isp_pipeline {
+       struct v4l2_device      *v4l2_dev;
+       void                    *drv_priv;
+
+       unsigned int             num_entities;
+       struct camss_isp_pipeline_entity entities[] __counted_by(num_entities);
+};
+
+/**
+ * camss_isp_pipeline_alloc() - allocate a pipeline for @num_entities entities
+ *
+ * Returns a pointer to the new pipeline or ERR_PTR on failure.
+ * Free with camss_isp_pipeline_free() if never registered, or call
+ * camss_isp_pipeline_unregister() followed by camss_isp_pipeline_free().
+ */
+struct camss_isp_pipeline *camss_isp_pipeline_alloc(unsigned int num_entities);
+
+/**
+ * camss_isp_pipeline_free() - free an unregistered pipeline
+ * @pipeline: pipeline to free (may be NULL)
+ */
+void camss_isp_pipeline_free(struct camss_isp_pipeline *pipeline);
+
+/**
+ * camss_isp_pipeline_register() - validate descriptors and register the graph
+ * @pipeline:    pipeline (allocated with camss_isp_pipeline_alloc())
+ * @v4l2_dev:    caller-owned and already-registered V4L2 device; its
+ *               associated media_device (v4l2_dev->mdev) must also be
+ *               initialised and registered before this call.
+ * @descs:       array of @num_entities entity descriptors
+ * @num_entities: number of entities; must equal pipeline->num_entities
+ *
+ * Validates the descriptor table (link direction consistency, index bounds),
+ * then registers all entities into the provided v4l2_device / media_device
+ * and creates all MC pad links.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int camss_isp_pipeline_register(struct camss_isp_pipeline *pipeline,
+                               struct v4l2_device *v4l2_dev,
+                               const struct camss_isp_entity_desc *descs,
+                               unsigned int num_entities);
+
+/**
+ * camss_isp_pipeline_unregister() - tear down a registered pipeline
+ * @pipeline: pipeline to unregister
+ */
+void camss_isp_pipeline_unregister(struct camss_isp_pipeline *pipeline);
+
+/**
+ * camss_isp_pipeline_get_vdev() - return the video_device for entity @idx
+ * @pipeline: registered pipeline
+ * @idx:      entity index (must be MEDIA_ENTITY_TYPE_VIDEO_DEVICE)
+ *
+ * Returns NULL if @idx is out of range or the entity is not a video device.
+ */
+static inline struct video_device *
+camss_isp_pipeline_get_vdev(struct camss_isp_pipeline *pipeline,
+                           unsigned int idx)
+{
+       if (WARN_ON(idx >= pipeline->num_entities))
+               return NULL;
+       if (WARN_ON(pipeline->entities[idx].obj_type !=
+                   MEDIA_ENTITY_TYPE_VIDEO_DEVICE))
+               return NULL;
+       return &pipeline->entities[idx].vdev;
+}
+
+/**
+ * camss_isp_pipeline_get_subdev() - return the v4l2_subdev for entity @idx
+ * @pipeline: registered pipeline
+ * @idx:      entity index (must be MEDIA_ENTITY_TYPE_V4L2_SUBDEV)
+ *
+ * Returns NULL if @idx is out of range or the entity is not a subdev.
+ */
+static inline struct v4l2_subdev *
+camss_isp_pipeline_get_subdev(struct camss_isp_pipeline *pipeline,
+                             unsigned int idx)
+{
+       if (WARN_ON(idx >= pipeline->num_entities))
+               return NULL;
+       if (WARN_ON(pipeline->entities[idx].obj_type !=
+                   MEDIA_ENTITY_TYPE_V4L2_SUBDEV))
+               return NULL;
+       return &pipeline->entities[idx].subdev;
+}
+
+/**
+ * camss_isp_pipeline_get_entity() - return the media_entity for entity @idx
+ * @pipeline: registered pipeline
+ * @idx:      entity index (must be MEDIA_ENTITY_TYPE_BASE)
+ *
+ * Returns NULL if @idx is out of range or the entity is not a base entity.
+ */
+static inline struct media_entity *
+camss_isp_pipeline_get_entity(struct camss_isp_pipeline *pipeline,
+                             unsigned int idx)
+{
+       if (WARN_ON(idx >= pipeline->num_entities))
+               return NULL;
+       if (WARN_ON(pipeline->entities[idx].obj_type !=
+                   MEDIA_ENTITY_TYPE_BASE))
+               return NULL;
+       return &pipeline->entities[idx].entity;
+}
+
+#endif /* _CAMSS_ISP_PIPELINE_H */

-- 
2.34.1


Reply via email to