Patch Detail
Show a patch.
GET /api/1.1/patches/22449/?format=api
{ "id": 22449, "url": "https://patchwork.libcamera.org/api/1.1/patches/22449/?format=api", "web_url": "https://patchwork.libcamera.org/patch/22449/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20241227105840.159559-2-keke.li@amlogic.com>", "date": "2024-12-27T10:58:39", "name": "[v2,1/2] libcamera: pipeline: Add C3 ISP pipeline handler", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "cbf5e11982c2b798c7db32596b858d0f8fbbe183", "submitter": { "id": 217, "url": "https://patchwork.libcamera.org/api/1.1/people/217/?format=api", "name": "Keke Li", "email": "keke.li@amlogic.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/22449/mbox/", "series": [ { "id": 4926, "url": "https://patchwork.libcamera.org/api/1.1/series/4926/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4926", "date": "2024-12-27T10:58:38", "name": "Add Amlogic C3 ISP pipeline handler and IPA", "version": 2, "mbox": "https://patchwork.libcamera.org/series/4926/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/22449/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/22449/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id E105EC32B5\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 Dec 2024 10:59:07 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6EE22684CE;\n\tFri, 27 Dec 2024 11:59:05 +0100 (CET)", "from mail-sh.amlogic.com (unknown [58.32.228.46])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1FDB5684B9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 Dec 2024 11:58:53 +0100 (CET)", "from droid10.amlogic.com (10.18.11.213) by mail-sh.amlogic.com\n\t(10.18.11.5) with Microsoft SMTP Server id 15.1.2507.39;\n\tFri, 27 Dec 2024 18:58:51 +0800" ], "From": "Keke Li <keke.li@amlogic.com>", "To": "<libcamera-devel@lists.libcamera.org>", "CC": "<kieran.bingham@ideasonboard.com>, <laurent.pinchart@ideasonboard.com>, \n\t<dan.scally@ideasonboard.com>, <jacopo.mondi@ideasonboard.com>, Keke Li\n\t<keke.li@amlogic.com>", "Subject": "[PATCH v2 1/2] libcamera: pipeline: Add C3 ISP pipeline handler", "Date": "Fri, 27 Dec 2024 18:58:39 +0800", "Message-ID": "<20241227105840.159559-2-keke.li@amlogic.com>", "X-Mailer": "git-send-email 2.29.0", "In-Reply-To": "<20241227105840.159559-1-keke.li@amlogic.com>", "References": "<20241227105840.159559-1-keke.li@amlogic.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Content-Type": "text/plain", "X-Originating-IP": "[10.18.11.213]", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "The Amlogic C3 ISP pipeline handler supports\n3-channel image output, 1-channel 3A statistical\ninformation ouput and 1-channel parameters input.\n\nSigned-off-by: Keke Li <keke.li@amlogic.com>\n---\n include/libcamera/ipa/c3isp.mojom | 42 +\n include/libcamera/ipa/meson.build | 1 +\n include/linux/videodev2.h | 4 +\n meson_options.txt | 1 +\n src/libcamera/pipeline/c3-isp/c3-isp.cpp | 1161 +++++++++++++++++++++\n src/libcamera/pipeline/c3-isp/meson.build | 5 +\n 6 files changed, 1214 insertions(+)\n create mode 100644 include/libcamera/ipa/c3isp.mojom\n create mode 100644 src/libcamera/pipeline/c3-isp/c3-isp.cpp\n create mode 100644 src/libcamera/pipeline/c3-isp/meson.build", "diff": "diff --git a/include/libcamera/ipa/c3isp.mojom b/include/libcamera/ipa/c3isp.mojom\nnew file mode 100644\nindex 00000000..e41fb243\n--- /dev/null\n+++ b/include/libcamera/ipa/c3isp.mojom\n@@ -0,0 +1,42 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+\n+/*\n+ * \\todo Document the interface and remove the related EXCLUDE_PATTERNS entry.\n+ */\n+\n+module ipa.c3isp;\n+\n+import \"include/libcamera/ipa/core.mojom\";\n+\n+struct IPAConfigInfo {\n+\tlibcamera.IPACameraSensorInfo sensorInfo;\n+\tlibcamera.ControlInfoMap sensorControls;\n+\tuint32 paramFormat;\n+};\n+\n+interface IPAC3ISPInterface {\n+\tinit(libcamera.IPASettings settings,\n+\t uint32 hwRevision,\n+\t libcamera.IPACameraSensorInfo sensorInfo,\n+\t libcamera.ControlInfoMap sensorControls)\n+\t\t=> (int32 ret, libcamera.ControlInfoMap ipaControls);\n+\tstart() => (int32 ret);\n+\tstop();\n+\n+\tconfigure(IPAConfigInfo configInfo)\n+\t\t=> (int32 ret, libcamera.ControlInfoMap ipaControls);\n+\n+\tmapBuffers(array<libcamera.IPABuffer> buffers);\n+\tunmapBuffers(array<uint32> ids);\n+\n+\t[async] queueRequest(uint32 frame, libcamera.ControlList reqControls);\n+\t[async] fillParamsBuffer(uint32 frame, uint32 bufferId);\n+\t[async] processStatsBuffer(uint32 frame, uint32 bufferId,\n+\t\t\t\t libcamera.ControlList sensorControls);\n+};\n+\n+interface IPAC3ISPEventInterface {\n+\tparamsBufferReady(uint32 frame, uint32 bytesused);\n+\tsetSensorControls(uint32 frame, libcamera.ControlList sensorControls);\n+\tmetadataReady(uint32 frame, libcamera.ControlList metadata);\n+};\ndiff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build\nindex 3129f119..39c55c07 100644\n--- a/include/libcamera/ipa/meson.build\n+++ b/include/libcamera/ipa/meson.build\n@@ -63,6 +63,7 @@ libcamera_ipa_headers += custom_target('core_ipa_serializer_h',\n \n # Mapping from pipeline handler name to mojom file\n pipeline_ipa_mojom_mapping = {\n+ 'c3-isp': 'c3isp.mojom',\n 'ipu3': 'ipu3.mojom',\n 'mali-c55': 'mali-c55.mojom',\n 'rkisp1': 'rkisp1.mojom',\ndiff --git a/include/linux/videodev2.h b/include/linux/videodev2.h\nindex d2653b2e..3227ac1e 100644\n--- a/include/linux/videodev2.h\n+++ b/include/linux/videodev2.h\n@@ -831,6 +831,10 @@ struct v4l2_pix_format {\n #define V4L2_META_FMT_RK_ISP1_STAT_3A\tv4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */\n #define V4L2_META_FMT_RK_ISP1_EXT_PARAMS\tv4l2_fourcc('R', 'K', '1', 'E') /* Rockchip ISP1 3a Extensible Parameters */\n \n+/* Vendor specific - used for C3 ISP */\n+#define V4L2_META_FMT_C3ISP_PARAMS\tv4l2_fourcc('C', '3', 'P', 'M') /* Amlogic C3 ISP Parameters */\n+#define V4L2_META_FMT_C3ISP_STATS\tv4l2_fourcc('C', '3', 'S', 'T') /* Amlogic C3 ISP Statistics */\n+\n /* Vendor specific - used for RaspberryPi PiSP */\n #define V4L2_META_FMT_RPI_BE_CFG\tv4l2_fourcc('R', 'P', 'B', 'C') /* PiSP BE configuration */\n \ndiff --git a/meson_options.txt b/meson_options.txt\nindex 1dc3b4cd..d59f4c04 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -46,6 +46,7 @@ option('pipelines',\n choices : [\n 'all',\n 'auto',\n+ 'c3-isp',\n 'imx8-isi',\n 'ipu3',\n 'mali-c55',\ndiff --git a/src/libcamera/pipeline/c3-isp/c3-isp.cpp b/src/libcamera/pipeline/c3-isp/c3-isp.cpp\nnew file mode 100644\nindex 00000000..524027d4\n--- /dev/null\n+++ b/src/libcamera/pipeline/c3-isp/c3-isp.cpp\n@@ -0,0 +1,1161 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * Pipeline Handler for Amlogic C3 ISP\n+ */\n+\n+#include <algorithm>\n+#include <array>\n+#include <map>\n+#include <memory>\n+#include <queue>\n+#include <set>\n+#include <string>\n+\n+#include <linux/media-bus-format.h>\n+#include <linux/media.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/formats.h>\n+#include <libcamera/geometry.h>\n+#include <libcamera/stream.h>\n+\n+#include <libcamera/ipa/c3isp_ipa_interface.h>\n+#include <libcamera/ipa/c3isp_ipa_proxy.h>\n+\n+#include \"libcamera/internal/bayer_format.h\"\n+#include \"libcamera/internal/camera.h\"\n+#include \"libcamera/internal/camera_sensor.h\"\n+#include \"libcamera/internal/delayed_controls.h\"\n+#include \"libcamera/internal/device_enumerator.h\"\n+#include \"libcamera/internal/framebuffer.h\"\n+#include \"libcamera/internal/ipa_manager.h\"\n+#include \"libcamera/internal/media_device.h\"\n+#include \"libcamera/internal/pipeline_handler.h\"\n+#include \"libcamera/internal/v4l2_subdevice.h\"\n+#include \"libcamera/internal/v4l2_videodevice.h\"\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(C3ISP)\n+\n+class PipelineHandlerC3ISP;\n+class C3ISPCameraData;\n+\n+const std::map<libcamera::PixelFormat, unsigned int> C3ISPFmtToCode = {\n+\t{ formats::R8, MEDIA_BUS_FMT_YUV8_1X24 },\n+\t{ formats::NV12, MEDIA_BUS_FMT_YUV8_1X24 },\n+\t{ formats::NV21, MEDIA_BUS_FMT_YUV8_1X24 },\n+\t{ formats::NV16, MEDIA_BUS_FMT_YUV8_1X24 },\n+\t{ formats::NV61, MEDIA_BUS_FMT_YUV8_1X24 },\n+};\n+\n+constexpr Size kC3ISPMinSize = { 160, 120 };\n+constexpr Size kC3ISPMaxSize = { 2888, 2240 };\n+\n+struct C3ISPFrameInfo {\n+\tunsigned int frame;\n+\tRequest *request;\n+\n+\tFrameBuffer *paramBuffer;\n+\tFrameBuffer *statBuffer;\n+\tFrameBuffer *viewBuffer;\n+\tFrameBuffer *stillBuffer;\n+\tFrameBuffer *videoBuffer;\n+\n+\tbool paramDequeued;\n+\tbool metadataProcessed;\n+};\n+\n+class C3ISPFrames\n+{\n+public:\n+\tC3ISPFrames(PipelineHandler *pipe);\n+\n+\tC3ISPFrameInfo *create(const C3ISPCameraData *data, Request *request);\n+\tint destroy(unsigned int frame);\n+\tvoid clear();\n+\n+\tC3ISPFrameInfo *find(unsigned int frame);\n+\tC3ISPFrameInfo *find(FrameBuffer *buffer);\n+\tC3ISPFrameInfo *find(Request *request);\n+\n+private:\n+\tPipelineHandlerC3ISP *pipe_;\n+\tstd::map<unsigned int, C3ISPFrameInfo *> frameInfo_;\n+};\n+\n+class C3ISPCameraData : public Camera::Private\n+{\n+public:\n+\tC3ISPCameraData(PipelineHandler *pipe, MediaEntity *entity)\n+\t\t: Camera::Private(pipe), entity_(entity), frame_(0), frameInfo_(pipe)\n+\t{\n+\t}\n+\n+\tint init();\n+\tPipelineHandlerC3ISP *pipe();\n+\tint loadIPA(unsigned int hwRevision);\n+\n+\tMediaEntity *entity_;\n+\tstd::unique_ptr<CameraSensor> sensor_;\n+\tstd::unique_ptr<DelayedControls> delayedCtrls_;\n+\tstd::unique_ptr<V4L2Subdevice> csi_;\n+\tstd::unique_ptr<V4L2Subdevice> adap_;\n+\tStream viewStream;\n+\tStream stillStream;\n+\tStream videoStream;\n+\tunsigned int frame_;\n+\tstd::vector<IPABuffer> ipaBuffers_;\n+\tC3ISPFrames frameInfo_;\n+\n+\tstd::unique_ptr<ipa::c3isp::IPAProxyC3ISP> ipa_;\n+\n+private:\n+\tvoid paramFilled(unsigned int frame, unsigned int bytesused);\n+\tvoid setSensorControls(unsigned int frame,\n+\t\t\t const ControlList &sensorControls);\n+\tvoid metadataReady(unsigned int frame, const ControlList &metadata);\n+};\n+\n+class C3ISPCameraConfiguration : public CameraConfiguration\n+{\n+public:\n+\tC3ISPCameraConfiguration(C3ISPCameraData *data)\n+\t\t: CameraConfiguration(), data_(data)\n+\t{\n+\t}\n+\n+\tStatus validate() override;\n+\n+\tV4L2SubdeviceFormat sensorFormat_;\n+\n+private:\n+\tstatic constexpr unsigned int kMaxStreams = 3;\n+\n+\tconst C3ISPCameraData *data_;\n+};\n+\n+class PipelineHandlerC3ISP : public PipelineHandler\n+{\n+public:\n+\tPipelineHandlerC3ISP(CameraManager *manager);\n+\n+\tstd::unique_ptr<CameraConfiguration> generateConfiguration(Camera *camera,\n+\t\t\t\t\t\t\t\t Span<const StreamRole> roles) override;\n+\tint configure(Camera *camera, CameraConfiguration *config) override;\n+\n+\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n+\n+\tint start(Camera *camera, const ControlList *controls) override;\n+\tvoid stopDevice(Camera *camera) override;\n+\n+\tint queueRequestDevice(Camera *camera, Request *request) override;\n+\n+\tvoid bufferReady(FrameBuffer *buffer);\n+\tvoid statReady(FrameBuffer *buffer);\n+\tvoid paramReady(FrameBuffer *buffer);\n+\n+\tbool match(DeviceEnumerator *enumerator) override;\n+\n+private:\n+\tfriend C3ISPCameraData;\n+\tfriend C3ISPFrames;\n+\n+\tstruct C3ISPPipe {\n+\t\tstd::unique_ptr<V4L2Subdevice> resizer;\n+\t\tstd::unique_ptr<V4L2VideoDevice> cap;\n+\t\tStream *stream;\n+\t};\n+\n+\tenum {\n+\t\tC3ISPVIEW,\n+\t\tC3ISPSTILL,\n+\t\tC3ISPVIDEO,\n+\t\tC3ISPNumPipes,\n+\t};\n+\n+\tC3ISPCameraData *cameraData(Camera *camera)\n+\t{\n+\t\treturn static_cast<C3ISPCameraData *>(camera->_d());\n+\t}\n+\n+\tC3ISPPipe *pipeFromStream(C3ISPCameraData *data, Stream *stream)\n+\t{\n+\t\tif (stream == &data->viewStream)\n+\t\t\treturn &pipes_[C3ISPVIEW];\n+\t\telse if (stream == &data->stillStream)\n+\t\t\treturn &pipes_[C3ISPSTILL];\n+\t\telse if (stream == &data->videoStream)\n+\t\t\treturn &pipes_[C3ISPVIDEO];\n+\t\telse\n+\t\t\tLOG(C3ISP, Fatal) << \"Invalid stream: \" << stream;\n+\n+\t\treturn nullptr;\n+\t}\n+\n+\tC3ISPPipe *pipeFromStream(C3ISPCameraData *data, const Stream *stream)\n+\t{\n+\t\treturn pipeFromStream(data, const_cast<Stream *>(stream));\n+\t}\n+\n+\tvoid resetPipes()\n+\t{\n+\t\tfor (C3ISPPipe &pipe : pipes_)\n+\t\t\tpipe.stream = nullptr;\n+\t}\n+\n+\tint pipeStart();\n+\tvoid pipeStop();\n+\n+\tint setConfigStreams(CameraConfiguration *config);\n+\tint configureProcessedStream(C3ISPCameraData *data,\n+\t\t\t\t const StreamConfiguration &config,\n+\t\t\t\t V4L2SubdeviceFormat &subdevFormat);\n+\n+\tbool createCamera(MediaLink *ispLink);\n+\tvoid tryCompleteRequest(C3ISPFrameInfo *info);\n+\tint allocateBuffers(Camera *camera);\n+\tint freeBuffers(Camera *camera);\n+\n+\tMediaDevice *media_;\n+\tstd::unique_ptr<V4L2Subdevice> isp_;\n+\tstd::unique_ptr<V4L2VideoDevice> param_;\n+\tstd::unique_ptr<V4L2VideoDevice> stat_;\n+\n+\tstd::vector<std::unique_ptr<FrameBuffer>> paramBuffers_;\n+\tstd::vector<std::unique_ptr<FrameBuffer>> statBuffers_;\n+\tstd::queue<FrameBuffer *> availableParamBuffers_;\n+\tstd::queue<FrameBuffer *> availableStatBuffers_;\n+\n+\tstd::vector<Stream *> streams_;\n+\tstd::array<C3ISPPipe, C3ISPNumPipes> pipes_;\n+\n+\tCamera *activeCamera_;\n+};\n+\n+C3ISPFrames::C3ISPFrames(PipelineHandler *pipe)\n+\t: pipe_(static_cast<PipelineHandlerC3ISP *>(pipe))\n+{\n+}\n+\n+C3ISPFrameInfo *C3ISPFrames::create(const C3ISPCameraData *data, Request *request)\n+{\n+\tunsigned int frame = data->frame_;\n+\n+\tFrameBuffer *paramBuffer = nullptr;\n+\tFrameBuffer *statBuffer = nullptr;\n+\n+\tif (pipe_->availableParamBuffers_.empty()) {\n+\t\tLOG(C3ISP, Error) << \"Param buffer queue empty\";\n+\t\treturn nullptr;\n+\t}\n+\n+\tif (pipe_->availableStatBuffers_.empty()) {\n+\t\tLOG(C3ISP, Error) << \"Stat buffer queue empty\";\n+\t\treturn nullptr;\n+\t}\n+\n+\tparamBuffer = pipe_->availableParamBuffers_.front();\n+\tpipe_->availableParamBuffers_.pop();\n+\n+\tstatBuffer = pipe_->availableStatBuffers_.front();\n+\tpipe_->availableStatBuffers_.pop();\n+\n+\tFrameBuffer *viewBuffer = request->findBuffer(&data->viewStream);\n+\tFrameBuffer *stillBuffer = request->findBuffer(&data->stillStream);\n+\tFrameBuffer *videoBuffer = request->findBuffer(&data->videoStream);\n+\n+\tC3ISPFrameInfo *info = new C3ISPFrameInfo;\n+\n+\tinfo->frame = frame;\n+\tinfo->request = request;\n+\tinfo->paramBuffer = paramBuffer;\n+\tinfo->statBuffer = statBuffer;\n+\tinfo->viewBuffer = viewBuffer;\n+\tinfo->stillBuffer = stillBuffer;\n+\tinfo->videoBuffer = videoBuffer;\n+\tinfo->paramDequeued = false;\n+\tinfo->metadataProcessed = false;\n+\n+\tframeInfo_[frame] = info;\n+\n+\treturn info;\n+}\n+\n+int C3ISPFrames::destroy(unsigned int frame)\n+{\n+\tC3ISPFrameInfo *info = find(frame);\n+\tif (!info)\n+\t\treturn -ENOENT;\n+\n+\tpipe_->availableParamBuffers_.push(info->paramBuffer);\n+\tpipe_->availableStatBuffers_.push(info->statBuffer);\n+\n+\tframeInfo_.erase(info->frame);\n+\n+\tdelete info;\n+\n+\treturn 0;\n+}\n+\n+void C3ISPFrames::clear()\n+{\n+\tfor (const auto &entry : frameInfo_) {\n+\t\tC3ISPFrameInfo *info = entry.second;\n+\n+\t\tpipe_->availableParamBuffers_.push(info->paramBuffer);\n+\t\tpipe_->availableStatBuffers_.push(info->statBuffer);\n+\n+\t\tdelete info;\n+\t}\n+\n+\tframeInfo_.clear();\n+}\n+\n+C3ISPFrameInfo *C3ISPFrames::find(unsigned int frame)\n+{\n+\tauto itInfo = frameInfo_.find(frame);\n+\n+\tif (itInfo != frameInfo_.end())\n+\t\treturn itInfo->second;\n+\n+\tLOG(C3ISP, Fatal) << \"Can't locate info from frame\";\n+\n+\treturn nullptr;\n+}\n+\n+C3ISPFrameInfo *C3ISPFrames::find(FrameBuffer *buffer)\n+{\n+\tfor (auto &itInfo : frameInfo_) {\n+\t\tC3ISPFrameInfo *info = itInfo.second;\n+\n+\t\tif (info->paramBuffer == buffer ||\n+\t\t info->statBuffer == buffer ||\n+\t\t info->viewBuffer == buffer ||\n+\t\t info->stillBuffer == buffer ||\n+\t\t info->videoBuffer == buffer)\n+\t\t\treturn info;\n+\t}\n+\n+\tLOG(C3ISP, Fatal) << \"Can't locate info from buffer\";\n+\n+\treturn nullptr;\n+}\n+\n+C3ISPFrameInfo *C3ISPFrames::find(Request *request)\n+{\n+\tfor (auto &itInfo : frameInfo_) {\n+\t\tC3ISPFrameInfo *info = itInfo.second;\n+\n+\t\tif (info->request == request)\n+\t\t\treturn info;\n+\t}\n+\n+\tLOG(C3ISP, Fatal) << \"Can't locate info from request\";\n+\n+\treturn nullptr;\n+}\n+\n+int C3ISPCameraData::init()\n+{\n+\tint ret;\n+\n+\t/* Register a CameraSensor */\n+\tsensor_ = CameraSensorFactoryBase::create(entity_);\n+\tif (!sensor_)\n+\t\treturn ret;\n+\n+\tconst MediaPad *sensorSrc = entity_->getPadByIndex(0);\n+\tMediaEntity *csiEntity = sensorSrc->links()[0]->sink()->entity();\n+\n+\tcsi_ = std::make_unique<V4L2Subdevice>(csiEntity);\n+\tif (csi_->open()) {\n+\t\tLOG(C3ISP, Error) << \"Failed to open CSI-2 subdevice\";\n+\t\treturn false;\n+\t}\n+\n+\tconst MediaPad *csiSrc = csiEntity->getPadByIndex(1);\n+\tMediaEntity *adapEntity = csiSrc->links()[0]->sink()->entity();\n+\n+\tadap_ = std::make_unique<V4L2Subdevice>(adapEntity);\n+\tif (adap_->open()) {\n+\t\tLOG(C3ISP, Error) << \"Failed to open adapter subdevice\";\n+\t\treturn false;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+PipelineHandlerC3ISP *C3ISPCameraData::pipe()\n+{\n+\treturn static_cast<PipelineHandlerC3ISP *>(Camera::Private::pipe());\n+}\n+\n+int C3ISPCameraData::loadIPA(unsigned int hwRevision)\n+{\n+\tipa_ = IPAManager::createIPA<ipa::c3isp::IPAProxyC3ISP>(pipe(), 1, 1);\n+\tif (!ipa_)\n+\t\treturn -ENOENT;\n+\n+\tipa_->setSensorControls.connect(this, &C3ISPCameraData::setSensorControls);\n+\tipa_->paramsBufferReady.connect(this, &C3ISPCameraData::paramFilled);\n+\tipa_->metadataReady.connect(this, &C3ISPCameraData::metadataReady);\n+\n+\t/*\n+\t * The API tuning file is made from the sensor name unless the\n+\t * environment variable overrides it.\n+\t */\n+\tstd::string ipaTuningFile;\n+\tchar const *configFromEnv = utils::secure_getenv(\"LIBCAMERA_C3ISP_TUNING_FILE\");\n+\tif (!configFromEnv || *configFromEnv == '\\0') {\n+\t\tipaTuningFile = ipa_->configurationFile(sensor_->model() + \".yaml\");\n+\t\tif (ipaTuningFile.empty())\n+\t\t\tipaTuningFile = ipa_->configurationFile(\"uncalibrated.yaml\");\n+\t} else {\n+\t\tipaTuningFile = std::string(configFromEnv);\n+\t}\n+\n+\tIPACameraSensorInfo sensorInfo{};\n+\tint ret = sensor_->sensorInfo(&sensorInfo);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Invalid semsor information\";\n+\t\treturn ret;\n+\t}\n+\n+\tret = ipa_->init({ ipaTuningFile, sensor_->model() }, hwRevision,\n+\t\t\t sensorInfo, sensor_->controls(), &controlInfo_);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to initialize IPA\";\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void C3ISPCameraData::paramFilled(unsigned int frame, unsigned int bytesused)\n+{\n+\tPipelineHandlerC3ISP *pipe = C3ISPCameraData::pipe();\n+\tC3ISPFrameInfo *info = frameInfo_.find(frame);\n+\tif (!info)\n+\t\treturn;\n+\n+\tinfo->paramBuffer->_d()->metadata().planes()[0].bytesused = bytesused;\n+\tpipe->param_->queueBuffer(info->paramBuffer);\n+\tpipe->stat_->queueBuffer(info->statBuffer);\n+\n+\tif (info->viewBuffer)\n+\t\tpipe->pipes_[PipelineHandlerC3ISP::C3ISPVIEW].cap->queueBuffer(info->viewBuffer);\n+\n+\tif (info->stillBuffer)\n+\t\tpipe->pipes_[PipelineHandlerC3ISP::C3ISPSTILL].cap->queueBuffer(info->stillBuffer);\n+\n+\tif (info->videoBuffer)\n+\t\tpipe->pipes_[PipelineHandlerC3ISP::C3ISPVIDEO].cap->queueBuffer(info->videoBuffer);\n+}\n+\n+void C3ISPCameraData::setSensorControls([[maybe_unused]] unsigned int frame,\n+\t\t\t\t\tconst ControlList &sensorControls)\n+{\n+\tdelayedCtrls_->push(sensorControls);\n+}\n+\n+void C3ISPCameraData::metadataReady(unsigned int frame, const ControlList &metadata)\n+{\n+\tC3ISPFrameInfo *info = frameInfo_.find(frame);\n+\tif (!info)\n+\t\treturn;\n+\n+\tinfo->request->metadata().merge(metadata);\n+\tinfo->metadataProcessed = true;\n+\n+\tpipe()->tryCompleteRequest(info);\n+}\n+\n+namespace {\n+\n+const std::map<PixelFormat, uint32_t> rawFormats = {\n+\t{ formats::SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10 },\n+\t{ formats::SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10 },\n+\t{ formats::SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10 },\n+\t{ formats::SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10 },\n+\t{ formats::SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12 },\n+\t{ formats::SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12 },\n+\t{ formats::SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12 },\n+\t{ formats::SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12 },\n+};\n+\n+};\n+\n+CameraConfiguration::Status C3ISPCameraConfiguration::validate()\n+{\n+\tconst CameraSensor *sensor = data_->sensor_.get();\n+\tStatus status = Valid;\n+\n+\tif (config_.empty())\n+\t\treturn Invalid;\n+\n+\tif (config_.size() > kMaxStreams) {\n+\t\tconfig_.resize(kMaxStreams);\n+\t\tstatus = Adjusted;\n+\t}\n+\n+\tSize maxSize;\n+\n+\tfor (StreamConfiguration &config : config_) {\n+\t\tconst auto it = C3ISPFmtToCode.find(config.pixelFormat);\n+\t\tif (it == C3ISPFmtToCode.end()) {\n+\t\t\tLOG(C3ISP, Debug)\n+\t\t\t\t<< \"Format adjusted to \" << formats::NV12;\n+\t\t\tconfig.pixelFormat = formats::NV12;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tSize size = std::clamp(config.size, kC3ISPMinSize, kC3ISPMaxSize);\n+\t\tif (size != config.size) {\n+\t\t\tLOG(C3ISP, Debug)\n+\t\t\t\t<< \"Size adjusted to \" << size;\n+\t\t\tconfig.size = size;\n+\t\t\tstatus = Adjusted;\n+\t\t}\n+\n+\t\tmaxSize = std::max(maxSize, config.size);\n+\t}\n+\n+\tstd::vector<unsigned int> mbusCodes;\n+\n+\tstd::transform(rawFormats.begin(), rawFormats.end(),\n+\t\t std::back_inserter(mbusCodes),\n+\t\t [](const auto &value) { return value.second; });\n+\n+\tsensorFormat_ = sensor->getFormat(mbusCodes, maxSize);\n+\n+\tif (sensorFormat_.size.isNull())\n+\t\tsensorFormat_.size = sensor->resolution();\n+\n+\treturn status;\n+}\n+\n+PipelineHandlerC3ISP::PipelineHandlerC3ISP(CameraManager *manager)\n+\t: PipelineHandler(manager)\n+{\n+}\n+\n+std::unique_ptr<CameraConfiguration>\n+PipelineHandlerC3ISP::generateConfiguration(Camera *camera,\n+\t\t\t\t\t Span<const StreamRole> roles)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\tstd::unique_ptr<CameraConfiguration> config =\n+\t\tstd::make_unique<C3ISPCameraConfiguration>(data);\n+\n+\tif (roles.empty())\n+\t\treturn config;\n+\n+\tstreams_.clear();\n+\n+\tfor (const StreamRole &role : roles) {\n+\t\tPixelFormat pixelFormat;\n+\t\tSize size = std::min(Size{ 1920, 1080 }, data->sensor_->resolution());\n+\n+\t\tswitch (role) {\n+\t\tcase StreamRole::StillCapture:\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tstreams_.push_back(&data->stillStream);\n+\t\t\tbreak;\n+\n+\t\tcase StreamRole::VideoRecording:\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tstreams_.push_back(&data->videoStream);\n+\t\t\tbreak;\n+\n+\t\tcase StreamRole::Viewfinder:\n+\t\t\tpixelFormat = formats::NV12;\n+\t\t\tstreams_.push_back(&data->viewStream);\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tLOG(C3ISP, Error) << \"Invalid stream role: \" << role;\n+\t\t\treturn nullptr;\n+\t\t}\n+\n+\t\tstd::map<PixelFormat, std::vector<SizeRange>> formats;\n+\t\tfor (const auto &c3Format : C3ISPFmtToCode) {\n+\t\t\tPixelFormat pixFmt = c3Format.first;\n+\t\t\tSize maxSize = std::min(kC3ISPMaxSize, data->sensor_->resolution());\n+\t\t\tformats[pixFmt] = { kC3ISPMinSize, maxSize };\n+\t\t}\n+\n+\t\tStreamFormats streamFormats(formats);\n+\t\tStreamConfiguration cfg(streamFormats);\n+\t\tcfg.pixelFormat = pixelFormat;\n+\t\tcfg.bufferCount = 4;\n+\t\tcfg.size = size;\n+\n+\t\tconfig->addConfiguration(cfg);\n+\t}\n+\n+\tif (config->validate() == CameraConfiguration::Invalid)\n+\t\treturn nullptr;\n+\n+\treturn config;\n+}\n+\n+int PipelineHandlerC3ISP::configureProcessedStream(C3ISPCameraData *data,\n+\t\t\t\t\t\t const StreamConfiguration &config,\n+\t\t\t\t\t\t V4L2SubdeviceFormat &subdevFormat)\n+{\n+\tStream *stream = config.stream();\n+\tC3ISPPipe *pipe = pipeFromStream(data, stream);\n+\tV4L2SubdeviceFormat rszFormat;\n+\n+\tconst MediaEntity *resizerEntity = pipe->resizer->entity();\n+\tint ret = resizerEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = resizerEntity->getPadByIndex(1)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\trszFormat.code = C3ISPFmtToCode.find(config.pixelFormat)->second;\n+\trszFormat.size = subdevFormat.size;\n+\trszFormat.colorSpace = subdevFormat.colorSpace;\n+\n+\tret = pipe->resizer->setFormat(0, &rszFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tRectangle cropRect = { 0, 0, rszFormat.size };\n+\tret = pipe->resizer->setSelection(0, V4L2_SEL_TGT_CROP, &cropRect);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tRectangle scaleRect = { 0, 0, config.size };\n+\tret = pipe->resizer->setSelection(0, V4L2_SEL_TGT_COMPOSE, &scaleRect);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+int PipelineHandlerC3ISP::setConfigStreams(CameraConfiguration *config)\n+{\n+\tif (config->size() != streams_.size()) {\n+\t\tLOG(C3ISP, Error) << \"Invalid configuration size: \" << config->size();\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor (unsigned int i = 0; i < config->size(); i++)\n+\t\tconfig->at(i).setStream(streams_[i]);\n+\n+\treturn 0;\n+}\n+\n+int PipelineHandlerC3ISP::configure(Camera *camera, CameraConfiguration *config)\n+{\n+\tresetPipes();\n+\n+\tint ret = media_->disableLinks();\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * The stream has been set to nullptr in Camera::configure,\n+\t * so need to set stream.\n+\t */\n+\tret = setConfigStreams(config);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Link the graph */\n+\tC3ISPCameraData *data = cameraData(camera);\n+\n+\tconst MediaEntity *csiEntity = data->csi_->entity();\n+\tret = csiEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tconst MediaEntity *adapEntity = data->adap_->entity();\n+\tret = adapEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tconst MediaEntity *ispEntity = isp_->entity();\n+\tret = ispEntity->getPadByIndex(0)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ispEntity->getPadByIndex(1)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ispEntity->getPadByIndex(2)->links()[0]->setEnabled(true);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tC3ISPCameraConfiguration *c3Config = static_cast<C3ISPCameraConfiguration *>(config);\n+\tV4L2SubdeviceFormat subdevFormat = c3Config->sensorFormat_;\n+\n+\tret = data->sensor_->setFormat(&subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = data->csi_->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = data->adap_->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = isp_->setFormat(0, &subdevFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tV4L2SubdeviceFormat ispSrcVideoFormat = subdevFormat;\n+\tispSrcVideoFormat.code = MEDIA_BUS_FMT_YUV8_1X24;\n+\tret = isp_->setFormat(3, &ispSrcVideoFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tV4L2DeviceFormat paramFormat;\n+\tparamFormat.fourcc = V4L2PixelFormat(V4L2_META_FMT_C3ISP_PARAMS);\n+\tret = param_->setFormat(¶mFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tV4L2DeviceFormat statFormat;\n+\tstatFormat.fourcc = V4L2PixelFormat(V4L2_META_FMT_C3ISP_STATS);\n+\tret = stat_->setFormat(&statFormat);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (const StreamConfiguration &streamConfig : *config) {\n+\t\tStream *stream = streamConfig.stream();\n+\t\tC3ISPPipe *pipe = pipeFromStream(data, stream);\n+\n+\t\tret = configureProcessedStream(data, streamConfig, subdevFormat);\n+\t\tif (ret) {\n+\t\t\tLOG(C3ISP, Error) << \"Failed to configure process stream\";\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tV4L2DeviceFormat captureFormat;\n+\t\tcaptureFormat.fourcc = pipe->cap->toV4L2PixelFormat(streamConfig.pixelFormat);\n+\t\tcaptureFormat.size = streamConfig.size;\n+\n+\t\tret = pipe->cap->setFormat(&captureFormat);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tpipe->stream = stream;\n+\t}\n+\n+\t/* Configure IPA module */\n+\tipa::c3isp::IPAConfigInfo ipaConfig{};\n+\n+\tret = data->sensor_->sensorInfo(&ipaConfig.sensorInfo);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tipaConfig.sensorControls = data->sensor_->controls();\n+\n+\tret = data->ipa_->configure(ipaConfig, &data->controlInfo_);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to configure IPA\";\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int PipelineHandlerC3ISP::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n+{\n+\tC3ISPPipe *pipe = pipeFromStream(cameraData(camera), stream);\n+\tunsigned int count = stream->configuration().bufferCount;\n+\n+\treturn pipe->cap->exportBuffers(count, buffers);\n+}\n+\n+int PipelineHandlerC3ISP::allocateBuffers(Camera *camera)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\tunsigned int ipaBufferId = 1;\n+\n+\tint ret = param_->allocateBuffers(4, ¶mBuffers_);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = stat_->allocateBuffers(4, &statBuffers_);\n+\tif (ret < 0) {\n+\t\tparamBuffers_.clear();\n+\t\tstatBuffers_.clear();\n+\t\treturn ret;\n+\t}\n+\n+\tfor (std::unique_ptr<FrameBuffer> &buffer : paramBuffers_) {\n+\t\tbuffer->setCookie(ipaBufferId++);\n+\t\tdata->ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());\n+\t\tavailableParamBuffers_.push(buffer.get());\n+\t}\n+\n+\tfor (std::unique_ptr<FrameBuffer> &buffer : statBuffers_) {\n+\t\tbuffer->setCookie(ipaBufferId++);\n+\t\tdata->ipaBuffers_.emplace_back(buffer->cookie(), buffer->planes());\n+\t\tavailableStatBuffers_.push(buffer.get());\n+\t}\n+\n+\tdata->ipa_->mapBuffers(data->ipaBuffers_);\n+\n+\treturn 0;\n+}\n+\n+int PipelineHandlerC3ISP::freeBuffers(Camera *camera)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\n+\twhile (!availableStatBuffers_.empty())\n+\t\tavailableStatBuffers_.pop();\n+\n+\twhile (!availableParamBuffers_.empty())\n+\t\tavailableParamBuffers_.pop();\n+\n+\tparamBuffers_.clear();\n+\tstatBuffers_.clear();\n+\n+\tstd::vector<unsigned int> ids;\n+\tfor (IPABuffer &ipabuf : data->ipaBuffers_)\n+\t\tids.push_back(ipabuf.id);\n+\n+\tdata->ipa_->unmapBuffers(ids);\n+\tdata->ipaBuffers_.clear();\n+\n+\tif (param_->releaseBuffers())\n+\t\tLOG(C3ISP, Error) << \"Failed to release param buffers\";\n+\n+\tif (stat_->releaseBuffers())\n+\t\tLOG(C3ISP, Error) << \"Failed to release stat buffers\";\n+\n+\treturn 0;\n+}\n+\n+int PipelineHandlerC3ISP::pipeStart()\n+{\n+\tfor (C3ISPPipe &pipe : pipes_) {\n+\t\tif (!pipe.stream)\n+\t\t\tcontinue;\n+\n+\t\tStream *stream = pipe.stream;\n+\n+\t\tint ret = pipe.cap->importBuffers(stream->configuration().bufferCount);\n+\t\tif (ret) {\n+\t\t\tLOG(C3ISP, Error) << \"Failed to import buffers\";\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = pipe.cap->streamOn();\n+\t\tif (ret) {\n+\t\t\tLOG(C3ISP, Error) << \"Failed to start stream\";\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void PipelineHandlerC3ISP::pipeStop()\n+{\n+\tfor (C3ISPPipe &pipe : pipes_) {\n+\t\tif (!pipe.stream)\n+\t\t\tcontinue;\n+\n+\t\tpipe.cap->streamOff();\n+\t\tpipe.cap->releaseBuffers();\n+\t}\n+}\n+\n+int PipelineHandlerC3ISP::start([[maybe_unused]] Camera *camera,\n+\t\t\t\t[[maybe_unused]] const ControlList *controls)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\tint ret;\n+\n+\tret = allocateBuffers(camera);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = data->ipa_->start();\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to start IPA\";\n+\t\tgoto error;\n+\t}\n+\n+\tdata->frame_ = 0;\n+\n+\tret = param_->streamOn();\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to start param\";\n+\t\tgoto error;\n+\t}\n+\n+\tret = stat_->streamOn();\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to start stat\";\n+\t\tgoto error;\n+\t}\n+\n+\tret = pipeStart();\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to start pipe\";\n+\t\tgoto error;\n+\t}\n+\n+\tret = isp_->setFrameStartEnabled(true);\n+\tif (ret) {\n+\t\tLOG(C3ISP, Error) << \"Failed to set frame start\";\n+\t\tgoto error;\n+\t}\n+\n+\tactiveCamera_ = camera;\n+\n+\treturn 0;\n+\n+error:\n+\tpipeStop();\n+\tstat_->streamOff();\n+\tparam_->streamOff();\n+\tdata->ipa_->stop();\n+\tfreeBuffers(camera);\n+\tLOG(C3ISP, Error) << \"Failed to start camera \" << camera->id();\n+\n+\treturn ret;\n+}\n+\n+void PipelineHandlerC3ISP::stopDevice([[maybe_unused]] Camera *camera)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\n+\tisp_->setFrameStartEnabled(false);\n+\n+\tdata->ipa_->stop();\n+\n+\tpipeStop();\n+\n+\tstat_->streamOff();\n+\tparam_->streamOff();\n+\n+\tdata->frameInfo_.clear();\n+\n+\tfreeBuffers(camera);\n+\n+\tactiveCamera_ = nullptr;\n+}\n+\n+int PipelineHandlerC3ISP::queueRequestDevice(Camera *camera, Request *request)\n+{\n+\tC3ISPCameraData *data = cameraData(camera);\n+\n+\tC3ISPFrameInfo *info = data->frameInfo_.create(data, request);\n+\tif (!info)\n+\t\treturn -ENOENT;\n+\n+\tdata->ipa_->queueRequest(data->frame_, request->controls());\n+\n+\tdata->ipa_->fillParamsBuffer(data->frame_, info->paramBuffer->cookie());\n+\n+\tdata->frame_++;\n+\n+\treturn 0;\n+}\n+\n+void PipelineHandlerC3ISP::tryCompleteRequest(C3ISPFrameInfo *info)\n+{\n+\tC3ISPCameraData *data = cameraData(activeCamera_);\n+\tRequest *request = info->request;\n+\n+\tif (request->hasPendingBuffers())\n+\t\treturn;\n+\n+\tif (!info->metadataProcessed)\n+\t\treturn;\n+\n+\tif (!info->paramDequeued)\n+\t\treturn;\n+\n+\tdata->frameInfo_.destroy(info->frame);\n+\n+\tcompleteRequest(request);\n+}\n+\n+void PipelineHandlerC3ISP::bufferReady(FrameBuffer *buffer)\n+{\n+\tC3ISPCameraData *data = cameraData(activeCamera_);\n+\n+\tC3ISPFrameInfo *info = data->frameInfo_.find(buffer);\n+\tif (!info)\n+\t\treturn;\n+\n+\tconst FrameMetadata &metadata = buffer->metadata();\n+\tRequest *request = buffer->request();\n+\n+\tif (metadata.status != FrameMetadata::FrameCancelled) {\n+\t\trequest->metadata().set(controls::SensorTimestamp,\n+\t\t\t\t\tmetadata.timestamp);\n+\t}\n+\n+\tcompleteBuffer(request, buffer);\n+\ttryCompleteRequest(info);\n+}\n+\n+void PipelineHandlerC3ISP::statReady(FrameBuffer *buffer)\n+{\n+\tC3ISPCameraData *data = cameraData(activeCamera_);\n+\n+\tC3ISPFrameInfo *info = data->frameInfo_.find(buffer);\n+\tif (!info)\n+\t\treturn;\n+\n+\tif (buffer->metadata().status == FrameMetadata::FrameCancelled) {\n+\t\tinfo->metadataProcessed = true;\n+\t\ttryCompleteRequest(info);\n+\t\treturn;\n+\t}\n+\n+\tif (data->frame_ <= buffer->metadata().sequence)\n+\t\tdata->frame_ = buffer->metadata().sequence + 1;\n+\n+\tdata->ipa_->processStatsBuffer(info->frame, info->statBuffer->cookie(),\n+\t\t\t\t data->delayedCtrls_->get(buffer->metadata().sequence));\n+}\n+\n+void PipelineHandlerC3ISP::paramReady(FrameBuffer *buffer)\n+{\n+\tC3ISPCameraData *data = cameraData(activeCamera_);\n+\n+\tC3ISPFrameInfo *info = data->frameInfo_.find(buffer);\n+\tif (!info)\n+\t\treturn;\n+\n+\tinfo->paramDequeued = true;\n+\ttryCompleteRequest(info);\n+}\n+\n+bool PipelineHandlerC3ISP::createCamera(MediaLink *ispLink)\n+{\n+\tMediaEntity *adap = ispLink->source()->entity();\n+\tconst MediaPad *adapSink = adap->getPadByIndex(0);\n+\tMediaEntity *csi = adapSink->links()[0]->source()->entity();\n+\tconst MediaPad *csiSink = csi->getPadByIndex(0);\n+\tMediaEntity *sensor = csiSink->links()[0]->source()->entity();\n+\n+\tstd::unique_ptr<C3ISPCameraData> data =\n+\t\tstd::make_unique<C3ISPCameraData>(this, sensor);\n+\n+\tif (data->init())\n+\t\treturn false;\n+\n+\t/* Generic values for sensor */\n+\tstd::unordered_map<uint32_t, DelayedControls::ControlParams> params = {\n+\t\t{ V4L2_CID_ANALOGUE_GAIN, { 1, false } },\n+\t\t{ V4L2_CID_EXPOSURE, { 2, false } },\n+\t};\n+\n+\tdata->delayedCtrls_ = std::make_unique<DelayedControls>(data->sensor_->device(), params);\n+\tisp_->frameStart.connect(data->delayedCtrls_.get(), &DelayedControls::applyControls);\n+\n+\tint ret = data->loadIPA(media_->hwRevision());\n+\tif (ret)\n+\t\treturn false;\n+\n+\tstd::set<Stream *> streams{ &data->viewStream, &data->stillStream, &data->videoStream };\n+\n+\tstd::shared_ptr<Camera> camera = Camera::create(std::move(data), sensor->name(), streams);\n+\n+\tregisterCamera(std::move(camera));\n+\n+\treturn true;\n+}\n+\n+bool PipelineHandlerC3ISP::match(DeviceEnumerator *enumerator)\n+{\n+\tconst MediaPad *ispSink;\n+\n+\tDeviceMatch dm(\"c3-isp\");\n+\tdm.add(\"c3-mipi-csi2\");\n+\tdm.add(\"c3-mipi-adapter\");\n+\tdm.add(\"c3-isp-core\");\n+\n+\tmedia_ = acquireMediaDevice(enumerator, dm);\n+\tif (!media_)\n+\t\treturn false;\n+\n+\tisp_ = V4L2Subdevice::fromEntityName(media_, \"c3-isp-core\");\n+\tif (isp_->open() < 0)\n+\t\treturn false;\n+\n+\tstat_ = V4L2VideoDevice::fromEntityName(media_, \"c3-isp-stats\");\n+\tif (stat_->open() < 0)\n+\t\treturn false;\n+\n+\tstat_->bufferReady.connect(this, &PipelineHandlerC3ISP::statReady);\n+\n+\tparam_ = V4L2VideoDevice::fromEntityName(media_, \"c3-isp-params\");\n+\tif (param_->open() < 0)\n+\t\treturn false;\n+\n+\tparam_->bufferReady.connect(this, &PipelineHandlerC3ISP::paramReady);\n+\n+\tC3ISPPipe *viewPipe = &pipes_[C3ISPVIEW];\n+\tviewPipe->resizer = V4L2Subdevice::fromEntityName(media_, \"c3-isp-resizer0\");\n+\tif (viewPipe->resizer->open() < 0)\n+\t\treturn false;\n+\n+\tviewPipe->cap = V4L2VideoDevice::fromEntityName(media_, \"c3-isp-cap0\");\n+\tif (viewPipe->cap->open() < 0)\n+\t\treturn false;\n+\n+\tviewPipe->cap->bufferReady.connect(this, &PipelineHandlerC3ISP::bufferReady);\n+\n+\tC3ISPPipe *stillPipe = &pipes_[C3ISPSTILL];\n+\tstillPipe->resizer = V4L2Subdevice::fromEntityName(media_, \"c3-isp-resizer1\");\n+\tif (stillPipe->resizer->open() < 0)\n+\t\treturn false;\n+\n+\tstillPipe->cap = V4L2VideoDevice::fromEntityName(media_, \"c3-isp-cap1\");\n+\tif (stillPipe->cap->open() < 0)\n+\t\treturn false;\n+\n+\tstillPipe->cap->bufferReady.connect(this, &PipelineHandlerC3ISP::bufferReady);\n+\n+\tC3ISPPipe *videoPipe = &pipes_[C3ISPVIDEO];\n+\tvideoPipe->resizer = V4L2Subdevice::fromEntityName(media_, \"c3-isp-resizer2\");\n+\tif (videoPipe->resizer->open() < 0)\n+\t\treturn false;\n+\n+\tvideoPipe->cap = V4L2VideoDevice::fromEntityName(media_, \"c3-isp-cap2\");\n+\tif (videoPipe->cap->open() < 0)\n+\t\treturn false;\n+\n+\tvideoPipe->cap->bufferReady.connect(this, &PipelineHandlerC3ISP::bufferReady);\n+\n+\tispSink = isp_->entity()->getPadByIndex(0);\n+\tif (!ispSink || ispSink->links().empty())\n+\t\treturn false;\n+\n+\tif (!createCamera(ispSink->links()[0])) {\n+\t\tLOG(C3ISP, Error) << \"Failed to create camera\";\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+REGISTER_PIPELINE_HANDLER(PipelineHandlerC3ISP, \"c3isp\")\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/pipeline/c3-isp/meson.build b/src/libcamera/pipeline/c3-isp/meson.build\nnew file mode 100644\nindex 00000000..5f8b23f1\n--- /dev/null\n+++ b/src/libcamera/pipeline/c3-isp/meson.build\n@@ -0,0 +1,5 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+libcamera_internal_sources += files([\n+ 'c3-isp.cpp'\n+])\n", "prefixes": [ "v2", "1/2" ] }