{"id":22887,"url":"https://patchwork.libcamera.org/api/patches/22887/?format=json","web_url":"https://patchwork.libcamera.org/patch/22887/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20250227105733.187611-5-keke.li@amlogic.com>","date":"2025-02-27T10:57:26","name":"[v3,04/11] ipa: c3-isp: Add C3 ISP IPA module","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"5d89bc2e1da4f96249a73472dca927cdc693c0d7","submitter":{"id":217,"url":"https://patchwork.libcamera.org/api/people/217/?format=json","name":"Keke Li","email":"keke.li@amlogic.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/22887/mbox/","series":[{"id":5027,"url":"https://patchwork.libcamera.org/api/series/5027/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5027","date":"2025-02-27T10:57:22","name":"Add Amlogic C3 ISP pipeline handler and IPA","version":3,"mbox":"https://patchwork.libcamera.org/series/5027/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/22887/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/22887/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 7DC08BF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 27 Feb 2025 10:57:51 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0B3396875B;\n\tThu, 27 Feb 2025 11:57:50 +0100 (CET)","from mail-sh.amlogic.com (unknown [58.32.228.46])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A513C686E7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 27 Feb 2025 11:57:42 +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\tThu, 27 Feb 2025 18:57:38 +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>, Keke Li <keke.li@amlogic.com>","Subject":"[PATCH v3 04/11] ipa: c3-isp: Add C3 ISP IPA module","Date":"Thu, 27 Feb 2025 18:57:26 +0800","Message-ID":"<20250227105733.187611-5-keke.li@amlogic.com>","X-Mailer":"git-send-email 2.29.0","In-Reply-To":"<20250227105733.187611-1-keke.li@amlogic.com>","References":"<20250227105733.187611-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":"Add a barebones IPA module for the C3 ISP. In this initial\nimplementation only basic interfaces are implemented.\n\nSigned-off-by: Keke Li <keke.li@amlogic.com>\n---\n meson_options.txt                     |   2 +-\n src/ipa/c3-isp/algorithms/algorithm.h |  28 ++\n src/ipa/c3-isp/algorithms/meson.build |   5 +\n src/ipa/c3-isp/c3-isp.cpp             | 370 ++++++++++++++++++++++++++\n src/ipa/c3-isp/data/meson.build       |   9 +\n src/ipa/c3-isp/ipa_context.cpp        | 101 +++++++\n src/ipa/c3-isp/ipa_context.h          |  87 ++++++\n src/ipa/c3-isp/meson.build            |  32 +++\n src/ipa/c3-isp/module.h               |  28 ++\n src/ipa/c3-isp/params.cpp             | 127 +++++++++\n src/ipa/c3-isp/params.h               | 133 +++++++++\n 11 files changed, 921 insertions(+), 1 deletion(-)\n create mode 100644 src/ipa/c3-isp/algorithms/algorithm.h\n create mode 100644 src/ipa/c3-isp/algorithms/meson.build\n create mode 100644 src/ipa/c3-isp/c3-isp.cpp\n create mode 100644 src/ipa/c3-isp/data/meson.build\n create mode 100644 src/ipa/c3-isp/ipa_context.cpp\n create mode 100644 src/ipa/c3-isp/ipa_context.h\n create mode 100644 src/ipa/c3-isp/meson.build\n create mode 100644 src/ipa/c3-isp/module.h\n create mode 100644 src/ipa/c3-isp/params.cpp\n create mode 100644 src/ipa/c3-isp/params.h","diff":"diff --git a/meson_options.txt b/meson_options.txt\nindex 998a4463..7c666d34 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -32,7 +32,7 @@ option('gstreamer',\n \n option('ipas',\n         type : 'array',\n-        choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n+        choices : ['c3-isp', 'ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],\n         description : 'Select which IPA modules to build')\n \n option('lc-compliance',\ndiff --git a/src/ipa/c3-isp/algorithms/algorithm.h b/src/ipa/c3-isp/algorithms/algorithm.h\nnew file mode 100644\nindex 00000000..68c311d7\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/algorithm.h\n@@ -0,0 +1,28 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP control algorithm interface\n+ */\n+\n+#pragma once\n+\n+#include <libipa/algorithm.h>\n+\n+#include \"module.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp {\n+\n+class Algorithm : public libcamera::ipa::Algorithm<Module>\n+{\n+public:\n+\tAlgorithm()\n+\t{\n+\t}\n+};\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/algorithms/meson.build b/src/ipa/c3-isp/algorithms/meson.build\nnew file mode 100644\nindex 00000000..1e00af33\n--- /dev/null\n+++ b/src/ipa/c3-isp/algorithms/meson.build\n@@ -0,0 +1,5 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+c3isp_ipa_algorithms = files([\n+\n+])\ndiff --git a/src/ipa/c3-isp/c3-isp.cpp b/src/ipa/c3-isp/c3-isp.cpp\nnew file mode 100644\nindex 00000000..abb56d15\n--- /dev/null\n+++ b/src/ipa/c3-isp/c3-isp.cpp\n@@ -0,0 +1,370 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * c3-isp.cpp - Amlogic Image Processing Algorithms\n+ */\n+\n+#include <algorithm>\n+#include <array>\n+#include <chrono>\n+#include <stdint.h>\n+#include <string.h>\n+\n+#include <linux/c3-isp-config.h>\n+#include <linux/v4l2-controls.h>\n+\n+#include <libcamera/base/file.h>\n+#include <libcamera/base/log.h>\n+\n+#include <libcamera/control_ids.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/framebuffer.h>\n+#include <libcamera/request.h>\n+\n+#include <libcamera/ipa/c3-isp_ipa_interface.h>\n+#include <libcamera/ipa/ipa_interface.h>\n+#include <libcamera/ipa/ipa_module_info.h>\n+\n+#include \"libcamera/internal/formats.h\"\n+#include \"libcamera/internal/mapped_framebuffer.h\"\n+#include \"libcamera/internal/yaml_parser.h\"\n+\n+#include \"algorithms/algorithm.h\"\n+#include \"libipa/camera_sensor_helper.h\"\n+\n+#include \"ipa_context.h\"\n+#include \"params.h\"\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(IPAC3ISP)\n+\n+using namespace std::literals::chrono_literals;\n+\n+namespace ipa::c3isp {\n+\n+static constexpr uint32_t kMaxFrameContexts = 16;\n+\n+class IPAC3ISP : public IPAC3ISPInterface, public Module\n+{\n+public:\n+\tIPAC3ISP();\n+\n+\tint init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,\n+\t\t ControlInfoMap *ipaControls) override;\n+\tint start() override;\n+\tvoid stop() override;\n+\n+\tint configure(const IPAConfigInfo &ipaConfig,\n+\t\t      ControlInfoMap *ipaControls) override;\n+\tvoid mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) override;\n+\tvoid unmapBuffers(const std::vector<IPABuffer> &buffers) override;\n+\n+\tvoid queueRequest(const uint32_t request, const ControlList &controls) override;\n+\tvoid computeParams(const uint32_t request, const uint32_t bufferId) override;\n+\tvoid processStats(const uint32_t request, const uint32_t bufferId,\n+\t\t\t  const ControlList &sensorControls) override;\n+\n+protected:\n+\tstd::string logPrefix() const override;\n+\n+private:\n+\tvoid updateSessionConfiguration(const IPACameraSensorInfo &info,\n+\t\t\t\t\tconst ControlInfoMap &sensorControls);\n+\tvoid updateControls(const IPACameraSensorInfo &sensorInfo,\n+\t\t\t    const ControlInfoMap &sensorControls,\n+\t\t\t    ControlInfoMap *ipaControls);\n+\tvoid setControls();\n+\n+\tstd::map<unsigned int, MappedFrameBuffer> buffers_;\n+\n+\tControlInfoMap sensorControls_;\n+\n+\t/* Interface to the Camera Helper */\n+\tstd::unique_ptr<CameraSensorHelper> camHelper_;\n+\n+\t/* Local parameter storage */\n+\tstruct IPAContext context_;\n+};\n+\n+IPAC3ISP::IPAC3ISP()\n+\t: context_(kMaxFrameContexts)\n+{\n+}\n+\n+std::string IPAC3ISP::logPrefix() const\n+{\n+\treturn \"c3isp\";\n+}\n+\n+int IPAC3ISP::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,\n+\t\t   ControlInfoMap *ipaControls)\n+{\n+\tcamHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n+\tif (!camHelper_) {\n+\t\tLOG(IPAC3ISP, Error)\n+\t\t\t<< \"Failed to create camera sensor helper for \"\n+\t\t\t<< settings.sensorModel;\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tFile file(settings.configurationFile);\n+\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n+\t\tint ret = file.error();\n+\t\tLOG(IPAC3ISP, Error)\n+\t\t\t<< \"Failed to open configuration file \"\n+\t\t\t<< settings.configurationFile << \": \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\tstd::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file);\n+\tif (!data)\n+\t\treturn -EINVAL;\n+\n+\tif (!data->contains(\"algorithms\")) {\n+\t\tLOG(IPAC3ISP, Error)\n+\t\t\t<< \"Tuning file doesn't contain any algorithm\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tint ret = createAlgorithms(context_, (*data)[\"algorithms\"]);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tupdateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls);\n+\n+\treturn 0;\n+}\n+\n+int IPAC3ISP::start()\n+{\n+\treturn 0;\n+}\n+\n+void IPAC3ISP::stop()\n+{\n+\tcontext_.frameContexts.clear();\n+}\n+\n+void IPAC3ISP::updateSessionConfiguration(const IPACameraSensorInfo &info,\n+\t\t\t\t\t  const ControlInfoMap &sensorControls)\n+{\n+\tconst ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;\n+\tint32_t minExposure = v4l2Exposure.min().get<int32_t>();\n+\tint32_t maxExposure = v4l2Exposure.max().get<int32_t>();\n+\tint32_t defExposure = v4l2Exposure.def().get<int32_t>();\n+\n+\tconst ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n+\tint32_t minGain = v4l2Gain.min().get<int32_t>();\n+\tint32_t maxGain = v4l2Gain.max().get<int32_t>();\n+\n+\tcontext_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate;\n+\tcontext_.configuration.agc.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration;\n+\tcontext_.configuration.agc.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration;\n+\tcontext_.configuration.agc.defaultExposure = defExposure;\n+\tcontext_.configuration.agc.minAnalogueGain = camHelper_->gain(minGain);\n+\tcontext_.configuration.agc.maxAnalogueGain = camHelper_->gain(maxGain);\n+\n+\tcontext_.configuration.sensor.size = info.outputSize;\n+\n+\tif (camHelper_->blackLevel().has_value())\n+\t\t/*\n+\t\t * The black level from CameraSensorHelper is a 16-bit value.\n+\t\t * The C3 ISP expects 20-bit settings, so we shift it to the\n+\t\t * appropriate width\n+\t\t */\n+\t\tcontext_.configuration.sensor.blackLevel = camHelper_->blackLevel().value() << 4;\n+}\n+\n+void IPAC3ISP::updateControls(const IPACameraSensorInfo &sensorInfo,\n+\t\t\t      const ControlInfoMap &sensorControls,\n+\t\t\t      ControlInfoMap *ipaControls)\n+{\n+\tControlInfoMap::Map ctrlMap;\n+\n+\t/* Compute the frame duration limits. */\n+\tconst ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second;\n+\tuint32_t hblank = v4l2HBlank.def().get<int32_t>();\n+\tuint32_t lineLength = sensorInfo.outputSize.width + hblank;\n+\n+\tconst ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second;\n+\tstd::array<uint32_t, 3> frameHeights{\n+\t\tv4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height,\n+\t\tv4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height,\n+\t\tv4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height,\n+\t};\n+\n+\tstd::array<int64_t, 3> frameDurations;\n+\tfor (unsigned int i = 0; i < frameHeights.size(); ++i) {\n+\t\tuint64_t frameSize = lineLength * frameHeights[i];\n+\t\tframeDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U);\n+\t}\n+\n+\tctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0],\n+\t\t\t\t\t\t\t      frameDurations[1],\n+\t\t\t\t\t\t\t      frameDurations[2]);\n+\n+\t/* Compute the exposure time limits */\n+\tdouble lineDuration = context_.configuration.sensor.lineDuration.get<std::micro>();\n+\tconst ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;\n+\tint32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;\n+\tint32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration;\n+\tint32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration;\n+\tctrlMap[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure, defExposure);\n+\n+\t/* Compute the analogue gain limits. */\n+\tconst ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n+\tfloat minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>());\n+\tfloat maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>());\n+\tfloat defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>());\n+\tctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain);\n+\n+\t/* Merge the cotrols */\n+\tctrlMap.merge(context_.ctrlMap);\n+\n+\t*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);\n+}\n+\n+int IPAC3ISP::configure(const IPAConfigInfo &ipaConfig,\n+\t\t\tControlInfoMap *ipaControls)\n+{\n+\tsensorControls_ = ipaConfig.sensorControls;\n+\n+\tcontext_.configuration = {};\n+\tcontext_.activeState = {};\n+\tcontext_.frameContexts.clear();\n+\n+\tconst IPACameraSensorInfo &info = ipaConfig.sensorInfo;\n+\n+\tupdateSessionConfiguration(info, ipaConfig.sensorControls);\n+\n+\tupdateControls(info, ipaConfig.sensorControls, ipaControls);\n+\n+\tfor (auto const &a : algorithms()) {\n+\t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n+\n+\t\tint ret = algo->configure(context_, info);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void IPAC3ISP::mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly)\n+{\n+\tfor (const IPABuffer &buffer : buffers) {\n+\t\tconst FrameBuffer fb(buffer.planes);\n+\t\tbuffers_.emplace(\n+\t\t\tbuffer.id,\n+\t\t\tMappedFrameBuffer(\n+\t\t\t\t&fb,\n+\t\t\t\treadOnly ? MappedFrameBuffer::MapFlag::Read\n+\t\t\t\t\t : MappedFrameBuffer::MapFlag::ReadWrite));\n+\t}\n+}\n+\n+void IPAC3ISP::unmapBuffers(const std::vector<IPABuffer> &buffers)\n+{\n+\tfor (const IPABuffer &buffer : buffers) {\n+\t\tauto it = buffers_.find(buffer.id);\n+\t\tif (it == buffers_.end())\n+\t\t\tcontinue;\n+\n+\t\tbuffers_.erase(buffer.id);\n+\t}\n+}\n+\n+void IPAC3ISP::queueRequest(const uint32_t request, const ControlList &controls)\n+{\n+\tIPAFrameContext &frameContext = context_.frameContexts.alloc(request);\n+\n+\tfor (auto const &a : algorithms()) {\n+\t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n+\n+\t\talgo->queueRequest(context_, request, frameContext, controls);\n+\t}\n+}\n+\n+void IPAC3ISP::computeParams(const uint32_t request, const uint32_t bufferId)\n+{\n+\tIPAFrameContext &frameContext = context_.frameContexts.get(request);\n+\n+\tC3ISPParams params(buffers_.at(bufferId).planes()[0]);\n+\n+\tfor (auto const &algo : algorithms())\n+\t\talgo->prepare(context_, request, frameContext, &params);\n+\n+\tparamsComputed.emit(request, params.size());\n+}\n+\n+void IPAC3ISP::processStats(const uint32_t request, const uint32_t bufferId,\n+\t\t\t    const ControlList &sensorControls)\n+{\n+\tIPAFrameContext &frameContext = context_.frameContexts.get(request);\n+\tconst c3_isp_stats_info *stats = nullptr;\n+\n+\tstats = reinterpret_cast<c3_isp_stats_info *>(\n+\t\tbuffers_.at(bufferId).planes()[0].data());\n+\n+\tframeContext.agc.exposure =\n+\t\tsensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n+\tframeContext.agc.sensorGain =\n+\t\tcamHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());\n+\n+\tControlList metadata(controls::controls);\n+\n+\tfor (auto const &a : algorithms()) {\n+\t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n+\n+\t\talgo->process(context_, request, frameContext, stats, metadata);\n+\t}\n+\n+\tsetControls();\n+\n+\tstatsProcessed.emit(request, metadata);\n+}\n+\n+void IPAC3ISP::setControls()\n+{\n+\tIPAActiveState &activeState = context_.activeState;\n+\tuint32_t exposure;\n+\tuint32_t gain;\n+\n+\tif (activeState.agc.autoEnabled) {\n+\t\texposure = activeState.agc.automatic.exposure;\n+\t\tgain = camHelper_->gainCode(activeState.agc.automatic.sensorGain);\n+\t} else {\n+\t\texposure = activeState.agc.manual.exposure;\n+\t\tgain = camHelper_->gainCode(activeState.agc.manual.sensorGain);\n+\t}\n+\n+\tControlList ctrls(sensorControls_);\n+\tctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));\n+\tctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));\n+\n+\tsetSensorControls.emit(ctrls);\n+}\n+\n+} /* namespace ipa::c3isp */\n+\n+/*\n+ * External IPA module interface\n+ */\n+\n+extern \"C\" {\n+const struct IPAModuleInfo ipaModuleInfo = {\n+\tIPA_MODULE_API_VERSION,\n+\t1,\n+\t\"c3isp\",\n+\t\"c3isp\",\n+};\n+\n+IPAInterface *ipaCreate()\n+{\n+\treturn new ipa::c3isp::IPAC3ISP();\n+}\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/data/meson.build b/src/ipa/c3-isp/data/meson.build\nnew file mode 100644\nindex 00000000..75e44bac\n--- /dev/null\n+++ b/src/ipa/c3-isp/data/meson.build\n@@ -0,0 +1,9 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+conf_files = files([\n+\n+])\n+\n+install_data(conf_files,\n+             install_dir : ipa_data_dir / 'c3isp',\n+             install_tag : 'runtime')\ndiff --git a/src/ipa/c3-isp/ipa_context.cpp b/src/ipa/c3-isp/ipa_context.cpp\nnew file mode 100644\nindex 00000000..8f69bbfb\n--- /dev/null\n+++ b/src/ipa/c3-isp/ipa_context.cpp\n@@ -0,0 +1,101 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * C3ISP IPA Context\n+ */\n+\n+#include \"ipa_context.h\"\n+\n+/**\n+ * \\file ipa_context.h\n+ * \\brief Context and state information shared between the algorithms\n+ */\n+\n+namespace libcamera::ipa::c3isp {\n+\n+/**\n+ * \\struct IPASessionConfiguration\n+ * \\brief Session configuration for the IPA module\n+ *\n+ * The session configuration contains all IPA configuration parameters that\n+ * remain constant during the capture session, from IPA module start to stop.\n+ * It is typically set during the configure() operation of the IPA module, but\n+ * may also be updated in the start() operation.\n+ */\n+\n+/**\n+ * \\struct IPAActiveState\n+ * \\brief Active state for algorithms\n+ *\n+ * The active state contains all algorithm-specific data that needs to be\n+ * maintained by algorithms across frames. Unlike the session configuration,\n+ * the active state is mutable and constantly updated by algorithms. The active\n+ * state is accessible through the IPAContext structure.\n+ *\n+ * The active state stores two distinct categories of information:\n+ *\n+ *  - The consolidated value of all algorithm controls. Requests passed to\n+ *    the queueRequest() function store values for controls that the\n+ *    application wants to modify for that particular frame, and the\n+ *    queueRequest() function updates the active state with those values.\n+ *    The active state thus contains a consolidated view of the value of all\n+ *    controls handled by the algorithm.\n+ *\n+ *  - The value of parameters computed by the algorithm when running in auto\n+ *    mode. Algorithms running in auto mode compute new parameters every\n+ *    time statistics buffers are received (either synchronously, or\n+ *    possibly in a background thread). The latest computed value of those\n+ *    parameters is stored in the active state in the process() function.\n+ *\n+ * Each of the members in the active state belongs to a specific algorithm. A\n+ * member may be read by any algorithm, but shall only be written by its owner.\n+ */\n+\n+/**\n+ * \\struct IPAFrameContext\n+ * \\brief Per-frame context for algorithms\n+ *\n+ * The frame context stores two distinct categories of information:\n+ *\n+ * - The value of the controls to be applied to the frame. These values are\n+ *   typically set in the queueRequest() function, from the consolidated\n+ *   control values stored in the active state. The frame context thus stores\n+ *   values for all controls related to the algorithm, not limited to the\n+ *   controls specified in the corresponding request, but consolidated from all\n+ *   requests that have been queued so far.\n+ *\n+ *   For controls that can be set manually or computed by an algorithm\n+ *   (depending on the algorithm operation mode), such as for instance the\n+ *   colour gains for the AWB algorithm, the control value will be stored in\n+ *   the frame context in the queueRequest() function only when operating in\n+ *   manual mode. When operating in auto mode, the values are computed by the\n+ *   algorithm in process(), stored in the active state, and copied to the\n+ *   frame context in prepare(), just before being stored in the ISP parameters\n+ *   buffer.\n+ *\n+ *   The queueRequest() function can also store ancillary data in the frame\n+ *   context, such as flags to indicate if (and what) control values have\n+ *   changed compared to the previous request.\n+ *\n+ * - Status information computed by the algorithm for a frame. For instance,\n+ *   the colour temperature estimated by the AWB algorithm from ISP statistics\n+ *   calculated on a frame is stored in the frame context for that frame in\n+ *   the process() function.\n+ */\n+\n+/**\n+ * \\struct IPAContext\n+ * \\brief Global IPA context data shared between all algorithms\n+ *\n+ * \\var IPAContext::configuration\n+ * \\brief The IPA session configuration, immutable during the session\n+ *\n+ * \\var IPAContext::activeState\n+ * \\brief The IPA active state, storing the latest state for all algorithms\n+ *\n+ * \\var IPAContext::frameContexts\n+ * \\brief Ring buffer of per-frame contexts\n+ */\n+\n+} /* namespace libcamera::ipa::c3isp */\ndiff --git a/src/ipa/c3-isp/ipa_context.h b/src/ipa/c3-isp/ipa_context.h\nnew file mode 100644\nindex 00000000..227d657a\n--- /dev/null\n+++ b/src/ipa/c3-isp/ipa_context.h\n@@ -0,0 +1,87 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * C3ISP IPA Context\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/base/utils.h>\n+#include <libcamera/controls.h>\n+\n+#include <libipa/fc_queue.h>\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp {\n+\n+struct IPASessionConfiguration {\n+\tstruct {\n+\t\tutils::Duration minShutterSpeed;\n+\t\tutils::Duration maxShutterSpeed;\n+\t\tuint32_t defaultExposure;\n+\t\tdouble minAnalogueGain;\n+\t\tdouble maxAnalogueGain;\n+\t} agc;\n+\n+\tstruct {\n+\t\tutils::Duration lineDuration;\n+\t\tuint32_t blackLevel;\n+\t\tSize size;\n+\t} sensor;\n+};\n+\n+struct IPAActiveState {\n+\tstruct {\n+\t\tstruct {\n+\t\t\tuint32_t exposure;\n+\t\t\tdouble sensorGain;\n+\t\t\tdouble ispGain;\n+\t\t} automatic;\n+\t\tstruct {\n+\t\t\tuint32_t exposure;\n+\t\t\tdouble sensorGain;\n+\t\t\tdouble ispGain;\n+\t\t} manual;\n+\t\tbool autoEnabled;\n+\t\tuint32_t constraintMode;\n+\t\tuint32_t exposureMode;\n+\t} agc;\n+\n+\tstruct {\n+\t\tdouble rGain;\n+\t\tdouble bGain;\n+\t\tuint32_t temperatureK;\n+\t} awb;\n+};\n+\n+struct IPAFrameContext : public FrameContext {\n+\tstruct {\n+\t\tuint32_t exposure;\n+\t\tdouble sensorGain;\n+\t} agc;\n+\n+\tstruct {\n+\t\tdouble rGain;\n+\t\tdouble bGain;\n+\t} awb;\n+};\n+\n+struct IPAContext {\n+\tIPAContext(unsigned int frameContextSize)\n+\t\t: frameContexts(frameContextSize)\n+\t{\n+\t}\n+\n+\tIPASessionConfiguration configuration;\n+\tIPAActiveState activeState;\n+\n+\tFCQueue<IPAFrameContext> frameContexts;\n+\n+\tControlInfoMap::Map ctrlMap;\n+};\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera*/\ndiff --git a/src/ipa/c3-isp/meson.build b/src/ipa/c3-isp/meson.build\nnew file mode 100644\nindex 00000000..fa5c6be0\n--- /dev/null\n+++ b/src/ipa/c3-isp/meson.build\n@@ -0,0 +1,32 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+subdir('algorithms')\n+subdir('data')\n+\n+ipa_name = 'ipa_c3isp'\n+\n+c3isp_ipa_sources = files([\n+    'ipa_context.cpp',\n+    'params.cpp',\n+    'c3-isp.cpp',\n+])\n+\n+c3isp_ipa_sources += c3isp_ipa_algorithms\n+\n+mod = shared_module(ipa_name, c3isp_ipa_sources,\n+                    name_prefix : '',\n+                    include_directories : [ipa_includes],\n+                    dependencies : [libcamera_private, libipa_dep],\n+                    install : true,\n+                    install_dir : ipa_install_dir)\n+\n+if ipa_sign_module\n+    custom_target(ipa_name + '.so.sign',\n+                  input : mod,\n+                  output : ipa_name + '.so.sign',\n+                  command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],\n+                  install : false,\n+                  build_by_default : true)\n+endif\n+\n+ipa_names += ipa_name\ndiff --git a/src/ipa/c3-isp/module.h b/src/ipa/c3-isp/module.h\nnew file mode 100644\nindex 00000000..a1116396\n--- /dev/null\n+++ b/src/ipa/c3-isp/module.h\n@@ -0,0 +1,28 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic\n+ *\n+ * C3ISP IPA Module\n+ */\n+\n+#pragma once\n+\n+#include <linux/c3-isp-config.h>\n+\n+#include <libcamera/ipa/c3-isp_ipa_interface.h>\n+\n+#include <libipa/module.h>\n+\n+#include \"ipa_context.h\"\n+#include \"params.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp {\n+\n+using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,\n+\t\t\t   C3ISPParams, c3_isp_stats_info>;\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera*/\ndiff --git a/src/ipa/c3-isp/params.cpp b/src/ipa/c3-isp/params.cpp\nnew file mode 100644\nindex 00000000..d5c57da3\n--- /dev/null\n+++ b/src/ipa/c3-isp/params.cpp\n@@ -0,0 +1,127 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * C3ISP ISP Parameters\n+ */\n+\n+#include \"params.h\"\n+\n+#include <map>\n+#include <stddef.h>\n+#include <string.h>\n+\n+#include <linux/c3-isp-config.h>\n+#include <linux/videodev2.h>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(C3ISPParams)\n+\n+namespace ipa::c3isp {\n+\n+namespace {\n+\n+struct BlockTypeInfo {\n+\tenum c3_isp_params_block_type type;\n+\tsize_t size;\n+};\n+\n+#define C3ISP_BLOCK_TYPE_ENTRY(block, id, type)                      \\\n+\t{                                                            \\\n+\t\tBlockType::block,                                    \\\n+\t\t{                                                    \\\n+\t\t\tC3_ISP_PARAMS_BLOCK_##id,                    \\\n+\t\t\t\tsizeof(struct c3_isp_params_##type), \\\n+\t\t}                                                    \\\n+\t}\n+\n+const std::map<BlockType, BlockTypeInfo> kBlockTypeInfo = {\n+\tC3ISP_BLOCK_TYPE_ENTRY(AWBGains, AWB_GAINS, awb_gains),\n+\tC3ISP_BLOCK_TYPE_ENTRY(AWBConfig, AWB_CONFIG, awb_config),\n+\tC3ISP_BLOCK_TYPE_ENTRY(AEConfig, AE_CONFIG, ae_config),\n+\tC3ISP_BLOCK_TYPE_ENTRY(AFConfig, AF_CONFIG, af_config),\n+\tC3ISP_BLOCK_TYPE_ENTRY(PostGamma, PST_GAMMA, pst_gamma),\n+\tC3ISP_BLOCK_TYPE_ENTRY(Ccm, CCM, ccm),\n+\tC3ISP_BLOCK_TYPE_ENTRY(Csc, CSC, csc),\n+\tC3ISP_BLOCK_TYPE_ENTRY(Blc, BLC, blc),\n+};\n+\n+} /* namespace */\n+\n+C3ISPParamsBlockBase::C3ISPParamsBlockBase(BlockType type,\n+\t\t\t\t\t   const Span<uint8_t> &data)\n+\t: type_(type), data_(data)\n+{\n+\theader_ = data.subspan(0, sizeof(c3_isp_params_block_header));\n+}\n+\n+void C3ISPParamsBlockBase::setEnabled(uint16_t flags)\n+{\n+\tstruct c3_isp_params_block_header *header =\n+\t\treinterpret_cast<struct c3_isp_params_block_header *>(header_.data());\n+\n+\theader->flags = flags;\n+}\n+\n+C3ISPParams::C3ISPParams(Span<uint8_t> data)\n+\t: data_(data), used_(0)\n+{\n+\tstruct c3_isp_params_cfg *buffer =\n+\t\treinterpret_cast<struct c3_isp_params_cfg *>(data.data());\n+\n+\tbuffer->version = C3_ISP_PARAMS_BUFFER_V0;\n+\tbuffer->data_size = 0;\n+\n+\tused_ += offsetof(struct c3_isp_params_cfg, data);\n+}\n+\n+Span<uint8_t> C3ISPParams::block(BlockType type)\n+{\n+\tauto infoIt = kBlockTypeInfo.find(type);\n+\tif (infoIt == kBlockTypeInfo.end()) {\n+\t\tLOG(C3ISPParams, Error)\n+\t\t\t<< \"Invalid parameters type \"\n+\t\t\t<< utils::to_underlying(type);\n+\t\treturn {};\n+\t}\n+\n+\tconst BlockTypeInfo &info = infoIt->second;\n+\n+\tauto cacheIt = blocks_.find(type);\n+\tif (cacheIt != blocks_.end())\n+\t\treturn cacheIt->second;\n+\n+\tsize_t size = info.size;\n+\tif (size > data_.size() - used_) {\n+\t\tLOG(C3ISPParams, Error)\n+\t\t\t<< \"No enough remaining space \"\n+\t\t\t<< utils::to_underlying(type);\n+\t\treturn {};\n+\t}\n+\n+\tSpan<uint8_t> block = data_.subspan(used_, info.size);\n+\tused_ += block.size();\n+\n+\tstruct c3_isp_params_cfg *buffer =\n+\t\treinterpret_cast<struct c3_isp_params_cfg *>(data_.data());\n+\tbuffer->data_size += block.size();\n+\n+\tmemset(block.data(), 0, block.size());\n+\n+\tstruct c3_isp_params_block_header *header =\n+\t\treinterpret_cast<struct c3_isp_params_block_header *>(block.data());\n+\theader->type = info.type;\n+\theader->size = block.size();\n+\n+\tblocks_[type] = block;\n+\n+\treturn block;\n+}\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/c3-isp/params.h b/src/ipa/c3-isp/params.h\nnew file mode 100644\nindex 00000000..9bb3877b\n--- /dev/null\n+++ b/src/ipa/c3-isp/params.h\n@@ -0,0 +1,133 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024, Amlogic Inc.\n+ *\n+ * C3ISP ISP Parameters\n+ */\n+\n+#pragma once\n+\n+#include <map>\n+#include <stdint.h>\n+\n+#include <linux/c3-isp-config.h>\n+\n+#include <libcamera/base/class.h>\n+#include <libcamera/base/span.h>\n+\n+namespace libcamera {\n+\n+namespace ipa::c3isp {\n+\n+enum class BlockType {\n+\tAWBGains,\n+\tAWBConfig,\n+\tAEConfig,\n+\tAFConfig,\n+\tPostGamma,\n+\tCcm,\n+\tCsc,\n+\tBlc,\n+};\n+\n+namespace details {\n+\n+template<BlockType B>\n+struct block_type {\n+};\n+\n+#define C3ISP_DEFINE_BLOCK_TYPE(blocktype, blockStruct)          \\\n+\ttemplate<>                                               \\\n+\tstruct block_type<BlockType::blocktype> {                \\\n+\t\tusing type = struct c3_isp_params_##blockStruct; \\\n+\t};\n+\n+C3ISP_DEFINE_BLOCK_TYPE(AWBGains, awb_gains)\n+C3ISP_DEFINE_BLOCK_TYPE(AWBConfig, awb_config)\n+C3ISP_DEFINE_BLOCK_TYPE(AEConfig, ae_config)\n+C3ISP_DEFINE_BLOCK_TYPE(AFConfig, af_config)\n+C3ISP_DEFINE_BLOCK_TYPE(PostGamma, pst_gamma)\n+C3ISP_DEFINE_BLOCK_TYPE(Ccm, ccm)\n+C3ISP_DEFINE_BLOCK_TYPE(Csc, csc)\n+C3ISP_DEFINE_BLOCK_TYPE(Blc, blc)\n+\n+} /* namespace details */\n+\n+class C3ISPParams;\n+\n+class C3ISPParamsBlockBase\n+{\n+public:\n+\tC3ISPParamsBlockBase(BlockType type, const Span<uint8_t> &data);\n+\n+\tSpan<uint8_t> data() const { return data_; }\n+\n+\tvoid setEnabled(uint16_t flags);\n+\n+private:\n+\tLIBCAMERA_DISABLE_COPY(C3ISPParamsBlockBase)\n+\n+\tBlockType type_;\n+\tSpan<uint8_t> header_;\n+\tSpan<uint8_t> data_;\n+};\n+\n+template<BlockType B>\n+class C3ISPParamsBlock : public C3ISPParamsBlockBase\n+{\n+public:\n+\tusing Type = typename details::block_type<B>::type;\n+\n+\tC3ISPParamsBlock(const Span<uint8_t> &data)\n+\t\t: C3ISPParamsBlockBase(B, data)\n+\t{\n+\t}\n+\n+\tconst Type *operator->() const\n+\t{\n+\t\treturn reinterpret_cast<const Type *>(data().data());\n+\t}\n+\n+\tType *operator->()\n+\t{\n+\t\treturn reinterpret_cast<Type *>(data().data());\n+\t}\n+\n+\tconst Type &operator*() const &\n+\t{\n+\t\treturn *reinterpret_cast<const Type *>(data().data());\n+\t}\n+\n+\tconst Type &operator*() &\n+\t{\n+\t\treturn *reinterpret_cast<Type *>(data().data());\n+\t}\n+};\n+\n+class C3ISPParams\n+{\n+public:\n+\tC3ISPParams(Span<uint8_t> data);\n+\n+\ttemplate<BlockType B>\n+\tC3ISPParamsBlock<B> block()\n+\t{\n+\t\treturn C3ISPParamsBlock<B>(block(B));\n+\t}\n+\n+\tsize_t size() const { return used_; }\n+\n+private:\n+\tfriend class C3ISPParamsBlockBase;\n+\n+\tSpan<uint8_t> block(BlockType type);\n+\n+\tSpan<uint8_t> data_;\n+\tsize_t used_;\n+\n+\tstd::map<BlockType, Span<uint8_t>> blocks_;\n+};\n+\n+} /* namespace ipa::c3isp */\n+\n+} /* namespace libcamera */\n","prefixes":["v3","04/11"]}