{"id":27164,"url":"https://patchwork.libcamera.org/api/patches/27164/?format=json","web_url":"https://patchwork.libcamera.org/patch/27164/","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":"<20260703122543.1991189-6-paul.elder@ideasonboard.com>","date":"2026-07-03T12:25:11","name":"[RFC,05/19] ipa: rkisp2: Add rkisp2 ipa","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"659b9ae57a95f895edf3a6b79fe9e56fd9b3225b","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/?format=json","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/27164/mbox/","series":[{"id":6035,"url":"https://patchwork.libcamera.org/api/series/6035/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=6035","date":"2026-07-03T12:25:06","name":"Add support for rkisp2","version":1,"mbox":"https://patchwork.libcamera.org/series/6035/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/27164/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27164/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 58D71C328C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Jul 2026 12:26:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 16AD965FCF;\n\tFri,  3 Jul 2026 14:26:19 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2993465FC3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Jul 2026 14:26:18 +0200 (CEST)","from neptunite.hamster-moth.ts.net (unknown\n\t[IPv6:2404:7a81:160:2100:a2cc:2f45:3bd7:2589])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0C4C08E0;\n\tFri,  3 Jul 2026 14:25:28 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"hwnV1OTj\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783081532;\n\tbh=+fLjhxaR5bCNOB2VrVIIuGV3Gcz1NpGvuAXUHsjk3xU=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=hwnV1OTj3WNoR8T2du0cUdK1yi2C6LF494FHWs38phLu4+p9tadR3FzmUkHlJtXcg\n\tF4driUbpp4RJemFF43X5VyGpl7Gc61W0rD1Iq6qhCPl3uP3YwYE8CKt23jEnJBkKmF\n\tEZL0OW7haWzxcrjB2lWsMAFB2V9ermFEVLJO/Rrs=","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"laurent.pinchart@ideasonboard.com","Cc":"Paul Elder <paul.elder@ideasonboard.com>, michael.riesch@collabora.com, \n\txuhf@rock-chips.com, stefan.klug@ideasonboard.com,\n\tkieran.bingham@ideasonboard.com, dan.scally@ideasonboard.com,\n\tjacopo.mondi@ideasonboard.com, nicolas.dufresne@collabora.com,\n\tlibcamera-devel@lists.libcamera.org","Subject":"[RFC PATCH 05/19] ipa: rkisp2: Add rkisp2 ipa","Date":"Fri,  3 Jul 2026 21:25:11 +0900","Message-ID":"<20260703122543.1991189-6-paul.elder@ideasonboard.com>","X-Mailer":"git-send-email 2.47.2","In-Reply-To":"<20260703122543.1991189-1-paul.elder@ideasonboard.com>","References":"<20260703122543.1991189-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","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":"Implement the rkisp2 IPA. It is based on the rkisp1 IPA. No control\nalgorithms are implemented yet, but this lays the foundation and\nplumbing for the IPA to function. The control algorithms will be added\nin later patches.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n\n---\nThis is also split from other patches to make them all easier to review.\nI put this patch before the pipeline handler patch so that the pipeline\nhandler doesn't error out when it fails to find an IPA.\n---\n meson_options.txt                     |   4 +-\n src/ipa/rkisp2/algorithms/meson.build |   5 +\n src/ipa/rkisp2/algorithms/module.h    |  28 ++\n src/ipa/rkisp2/ipa_context.h          | 201 ++++++++++++\n src/ipa/rkisp2/meson.build            |  29 ++\n src/ipa/rkisp2/rkisp2.cpp             | 429 ++++++++++++++++++++++++++\n 6 files changed, 694 insertions(+), 2 deletions(-)\n create mode 100644 src/ipa/rkisp2/algorithms/meson.build\n create mode 100644 src/ipa/rkisp2/algorithms/module.h\n create mode 100644 src/ipa/rkisp2/ipa_context.h\n create mode 100644 src/ipa/rkisp2/meson.build\n create mode 100644 src/ipa/rkisp2/rkisp2.cpp","diff":"diff --git a/meson_options.txt b/meson_options.txt\nindex 20baacc4fc65..2638c77cb8b0 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -48,8 +48,8 @@ option('gstreamer',\n \n option('ipas',\n         type : 'array',\n-        choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/pisp', 'rpi/vc4', 'simple',\n-                   'vimc'],\n+        choices : ['ipu3', 'mali-c55', 'rkisp1', 'rkisp2', 'rpi/pisp',\n+                   'rpi/vc4', 'simple', 'vimc'],\n         description : 'Select which IPA modules to build')\n \n option('lc-compliance',\ndiff --git a/src/ipa/rkisp2/algorithms/meson.build b/src/ipa/rkisp2/algorithms/meson.build\nnew file mode 100644\nindex 000000000000..4bb81e48eb01\n--- /dev/null\n+++ b/src/ipa/rkisp2/algorithms/meson.build\n@@ -0,0 +1,5 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+rkisp2_ipa_algorithms = files([\n+])\n+\ndiff --git a/src/ipa/rkisp2/algorithms/module.h b/src/ipa/rkisp2/algorithms/module.h\nnew file mode 100644\nindex 000000000000..23507e1db81f\n--- /dev/null\n+++ b/src/ipa/rkisp2/algorithms/module.h\n@@ -0,0 +1,28 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas on Board Oy.\n+ *\n+ * RkISP2 IPA Module\n+ */\n+\n+#pragma once\n+\n+#include <linux/rkisp2-config.h>\n+\n+#include <libcamera/ipa/rkisp2_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::rkisp2 {\n+\n+using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,\n+\t\t\t   RkISP2Params, rkisp2_stats_buffer>;\n+\n+} /* namespace ipa::rkisp2 */\n+\n+} /* namespace libcamera*/\ndiff --git a/src/ipa/rkisp2/ipa_context.h b/src/ipa/rkisp2/ipa_context.h\nnew file mode 100644\nindex 000000000000..c806f1fe1bfa\n--- /dev/null\n+++ b/src/ipa/rkisp2/ipa_context.h\n@@ -0,0 +1,201 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas on Board Oy.\n+ *\n+ * RkISP2 IPA Context\n+ *\n+ */\n+\n+#pragma once\n+\n+#include <memory>\n+\n+#include <linux/rkisp2-config.h>\n+\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/control_ids.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/geometry.h>\n+\n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n+#include \"libcamera/internal/debug_controls.h\"\n+#include \"libcamera/internal/matrix.h\"\n+#include \"libcamera/internal/vector.h\"\n+\n+#include \"libipa/agc_mean_luminance.h\"\n+#include \"libipa/awb.h\"\n+#include \"libipa/camera_sensor_helper.h\"\n+#include \"libipa/ccm.h\"\n+#include \"libipa/fc_queue.h\"\n+#include \"libipa/fixedpoint.h\"\n+#include \"libipa/lsc.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::rkisp2 {\n+\n+struct IPAHwSettings {\n+\tunsigned int numAeCells;\n+\tunsigned int numHistogramBins;\n+\tunsigned int numHistogramWeights;\n+\tunsigned int numGammaOutSamples;\n+\tuint32_t supportedBlocks;\n+\tbool compand;\n+};\n+\n+struct RKISP2AwbSession {\n+\tstruct rkisp2_isp_window measureWindow;\n+\tbool enabled;\n+};\n+\n+struct IPASessionConfiguration {\n+\tstruct {\n+\t\tstruct rkisp2_isp_window measureWindow;\n+\t\tstruct rkisp2_isp_window measureWindow15;\n+\t} agc;\n+\n+\tstruct RKISP2AwbSession awb;\n+\n+\tstruct {\n+\t\tutils::Duration minExposureTime;\n+\t\tutils::Duration maxExposureTime;\n+\t\tdouble minAnalogueGain;\n+\t\tdouble maxAnalogueGain;\n+\n+\t\tint32_t defVBlank;\n+\t\tutils::Duration lineDuration;\n+\t\tSize size;\n+\t} sensor;\n+\n+\tstruct {\n+\t\tint32_t colorSpaceEncoding;\n+\t\tint32_t colorSpaceRange;\n+\t} csm;\n+\n+\tbool raw;\n+};\n+\n+struct IPAActiveState {\n+\tstruct {\n+\t\tstruct {\n+\t\t\tuint32_t exposure;\n+\t\t\tdouble gain;\n+\t\t} manual;\n+\t\tstruct {\n+\t\t\tuint32_t exposure;\n+\t\t\tdouble gain;\n+\t\t\tdouble quantizationGain;\n+\t\t\tdouble yTarget;\n+\t\t} automatic;\n+\n+\t\tbool autoExposureEnabled;\n+\t\tbool autoGainEnabled;\n+\t\tdouble exposureValue;\n+\t\tcontrols::AeConstraintModeEnum constraintMode;\n+\t\tcontrols::AeExposureModeEnum exposureMode;\n+\t\tcontrols::AeMeteringModeEnum meteringMode;\n+\t\tutils::Duration minFrameDuration;\n+\t\tutils::Duration maxFrameDuration;\n+\t} agc;\n+\n+\tipa::awb::ActiveState awb;\n+\n+\tstruct {\n+\t\tdouble gamma;\n+\t} goc;\n+\n+\tipa::ccm::ActiveState ccm;\n+\n+\tstruct {\n+\t\tdouble lux;\n+\t} lux;\n+\n+\tstruct {\n+\t\tcontrols::WdrModeEnum mode;\n+\t\tAgcMeanLuminance::AgcConstraint constraint;\n+\t\tdouble gain;\n+\t\tdouble strength;\n+\t} wdr;\n+\n+\tipa::lsc::ActiveState lsc;\n+\n+\tstruct {\n+\t\tMatrix<uint16_t, 3, 3> csm;\n+\t\tbool update;\n+\t} csm;\n+};\n+\n+struct IPAFrameContext : public FrameContext {\n+\tstruct {\n+\t\tuint32_t exposure;\n+\t\tdouble gain;\n+\t\tdouble exposureValue;\n+\t\tdouble quantizationGain;\n+\t\tuint32_t vblank;\n+\t\tdouble yTarget;\n+\t\tbool autoExposureEnabled;\n+\t\tbool autoGainEnabled;\n+\t\tcontrols::AeConstraintModeEnum constraintMode;\n+\t\tcontrols::AeExposureModeEnum exposureMode;\n+\t\tcontrols::AeMeteringModeEnum meteringMode;\n+\t\tutils::Duration minFrameDuration;\n+\t\tutils::Duration maxFrameDuration;\n+\t\tutils::Duration frameDuration;\n+\t\tbool updateMetering;\n+\t\tbool autoExposureModeChange;\n+\t\tbool autoGainModeChange;\n+\t} agc;\n+\n+\tipa::awb::FrameContext awb;\n+\n+\tstruct {\n+\t\tdouble gamma;\n+\t\tbool update;\n+\t} goc;\n+\n+\tstruct {\n+\t\tuint32_t exposure;\n+\t\tdouble gain;\n+\t} sensor;\n+\n+\tipa::ccm::FrameContext ccm;\n+\n+\tstruct {\n+\t\tdouble lux;\n+\t} lux;\n+\n+\tstruct {\n+\t\tcontrols::WdrModeEnum mode;\n+\t\tdouble strength;\n+\t\tdouble gain;\n+\t} wdr;\n+\n+\tipa::lsc::FrameContext lsc;\n+};\n+\n+struct IPAContext {\n+\tIPAContext(unsigned int frameContextSize)\n+\t\t: frameContexts(frameContextSize)\n+\t{\n+\t}\n+\n+\tIPAHwSettings hw;\n+\tIPACameraSensorInfo sensorInfo;\n+\tIPASessionConfiguration configuration;\n+\tIPAActiveState activeState;\n+\n+\tFCQueue<IPAFrameContext> frameContexts;\n+\n+\tControlInfoMap::Map ctrlMap;\n+\n+\tDebugMetadata debugMetadata;\n+\n+\t/* Interface to the Camera Helper */\n+\tstd::unique_ptr<CameraSensorHelper> camHelper;\n+};\n+\n+} /* namespace ipa::rkisp2 */\n+\n+} /* namespace libcamera*/\ndiff --git a/src/ipa/rkisp2/meson.build b/src/ipa/rkisp2/meson.build\nnew file mode 100644\nindex 000000000000..f2f435c2d66f\n--- /dev/null\n+++ b/src/ipa/rkisp2/meson.build\n@@ -0,0 +1,29 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+subdir('algorithms')\n+\n+ipa_name = 'ipa_rkisp2'\n+\n+rkisp2_ipa_sources = files([\n+    'rkisp2.cpp',\n+])\n+\n+rkisp2_ipa_sources += rkisp2_ipa_algorithms\n+\n+mod = shared_module(ipa_name, rkisp2_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/rkisp2/rkisp2.cpp b/src/ipa/rkisp2/rkisp2.cpp\nnew file mode 100644\nindex 000000000000..18053c7d802a\n--- /dev/null\n+++ b/src/ipa/rkisp2/rkisp2.cpp\n@@ -0,0 +1,429 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas on Board Oy.\n+ *\n+ * RkISP2 Image Processing Algorithms\n+ */\n+\n+#include <algorithm>\n+#include <array>\n+#include <chrono>\n+#include <functional>\n+#include <stdint.h>\n+#include <string.h>\n+\n+#include <linux/rkisp2-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/ipa_interface.h>\n+#include <libcamera/ipa/ipa_module_info.h>\n+#include <libcamera/ipa/rkisp2_ipa_interface.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+\n+#include \"ipa_context.h\"\n+#include \"params.h\"\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(IPARkISP2)\n+\n+using namespace std::literals::chrono_literals;\n+\n+namespace ipa::rkisp2 {\n+\n+/* Maximum number of frame contexts to be held */\n+static constexpr uint32_t kMaxFrameContexts = 16;\n+\n+class IPARkISP2 : public IPARkISP2Interface, public Module\n+{\n+public:\n+\tIPARkISP2();\n+\n+\tint init(const IPASettings &settings,\n+\t\t const IPACameraSensorInfo &sensorInfo,\n+\t\t const ControlInfoMap &sensorControls,\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) override;\n+\tvoid unmapBuffers(const std::vector<unsigned int> &ids) override;\n+\n+\tvoid queueRequest(const uint32_t frame, const ControlList &controls) override;\n+\tvoid computeParams(const uint32_t frame, const uint32_t bufferId) override;\n+\tvoid initializeFrameContext(IPAFrameContext &frameContext,\n+\t\t\t\t    const ControlList &controls);\n+\tvoid processStats(const uint32_t frame, 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 updateControls(const IPACameraSensorInfo &sensorInfo,\n+\t\t\t    const ControlInfoMap &sensorControls,\n+\t\t\t    ControlInfoMap *ipaControls);\n+\n+\tvoid setControls(unsigned int frame, const IPAFrameContext &frameContext);\n+\n+\tstd::map<unsigned int, FrameBuffer> buffers_;\n+\tstd::map<unsigned int, MappedFrameBuffer> mappedBuffers_;\n+\n+\tControlInfoMap sensorControls_;\n+\n+\t/* Local parameter storage */\n+\tstruct IPAContext context_;\n+};\n+\n+namespace {\n+\n+/* List of controls handled by the RkISP2 IPA */\n+const ControlInfoMap::Map rkisp2Controls{\n+\t{ &controls::DebugMetadataEnable, ControlInfo(false, true, false) },\n+};\n+\n+} /* namespace */\n+\n+IPARkISP2::IPARkISP2()\n+\t: context_(kMaxFrameContexts)\n+{\n+\tcontext_.frameContexts.setInitCallback(\n+\t\t[this](IPAFrameContext &fc, const ControlList &c) {\n+\t\t\tthis->initializeFrameContext(fc, c);\n+\t\t});\n+}\n+\n+std::string IPARkISP2::logPrefix() const\n+{\n+\treturn \"rkisp2\";\n+}\n+\n+int IPARkISP2::init(const IPASettings &settings,\n+\t\t    const IPACameraSensorInfo &sensorInfo,\n+\t\t    const ControlInfoMap &sensorControls,\n+\t\t    ControlInfoMap *ipaControls)\n+{\n+\tcontext_.sensorInfo = sensorInfo;\n+\n+\tcontext_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n+\tif (!context_.camHelper) {\n+\t\tLOG(IPARkISP2, 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+\tcontext_.configuration.sensor.lineDuration =\n+\t\tsensorInfo.minLineLength * 1.0s / sensorInfo.pixelRate;\n+\n+\t/* Load the tuning data file. */\n+\tFile file(settings.configurationFile);\n+\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n+\t\tint ret = file.error();\n+\t\tLOG(IPARkISP2, 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::ValueNode> data = YamlParser::parse(file);\n+\tif (!data)\n+\t\treturn -EINVAL;\n+\n+\tunsigned int version = (*data)[\"version\"].get<uint32_t>(0);\n+\tif (version != 1) {\n+\t\tLOG(IPARkISP2, Error)\n+\t\t\t<< \"Invalid tuning file version \" << version;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!data->contains(\"algorithms\")) {\n+\t\tLOG(IPARkISP2, 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+\t/* Initialize controls. */\n+\tupdateControls(sensorInfo, sensorControls, ipaControls);\n+\n+\treturn 0;\n+}\n+\n+int IPARkISP2::start()\n+{\n+\t/* \\todo Properly handle startup controls. */\n+\treturn 0;\n+}\n+\n+void IPARkISP2::stop()\n+{\n+\tcontext_.frameContexts.clear();\n+}\n+\n+int IPARkISP2::configure(const IPAConfigInfo &ipaConfig,\n+\t\t\t ControlInfoMap *ipaControls)\n+{\n+\tsensorControls_ = ipaConfig.sensorControls;\n+\n+\tconst auto itExp = sensorControls_.find(V4L2_CID_EXPOSURE);\n+\tint32_t minExposure = itExp->second.min().get<int32_t>();\n+\tint32_t maxExposure = itExp->second.max().get<int32_t>();\n+\n+\tconst auto itGain = sensorControls_.find(V4L2_CID_ANALOGUE_GAIN);\n+\tint32_t minGain = itGain->second.min().get<int32_t>();\n+\tint32_t maxGain = itGain->second.max().get<int32_t>();\n+\n+\tLOG(IPARkISP2, Debug)\n+\t\t<< \"Exposure: [\" << minExposure << \", \" << maxExposure\n+\t\t<< \"], gain: [\" << minGain << \", \" << maxGain << \"]\";\n+\n+\t/* Clear the IPA context before the streaming session. */\n+\tcontext_.configuration = {};\n+\tcontext_.activeState = {};\n+\tcontext_.frameContexts.clear();\n+\n+\tconst IPACameraSensorInfo &info = ipaConfig.sensorInfo;\n+\tconst ControlInfo vBlank = sensorControls_.find(V4L2_CID_VBLANK)->second;\n+\tcontext_.configuration.sensor.defVBlank = vBlank.def().get<int32_t>();\n+\tcontext_.configuration.sensor.size = info.outputSize;\n+\tcontext_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate;\n+\n+\t/* Update the camera controls using the new sensor settings. */\n+\tupdateControls(info, sensorControls_, ipaControls);\n+\n+\t/*\n+\t * When the AGC computes the new exposure values for a frame, it needs\n+\t * to know the limits for exposure time and analogue gain. As it depends\n+\t * on the sensor, update it with the controls.\n+\t *\n+\t * \\todo take VBLANK into account for maximum exposure time\n+\t */\n+\tcontext_.configuration.sensor.minExposureTime =\n+\t\tminExposure * context_.configuration.sensor.lineDuration;\n+\tcontext_.configuration.sensor.maxExposureTime =\n+\t\tmaxExposure * context_.configuration.sensor.lineDuration;\n+\tcontext_.configuration.sensor.minAnalogueGain =\n+\t\tcontext_.camHelper->gain(minGain);\n+\tcontext_.configuration.sensor.maxAnalogueGain =\n+\t\tcontext_.camHelper->gain(maxGain);\n+\n+\tcontext_.configuration.csm.colorSpaceEncoding = ipaConfig.colorSpaceEncoding;\n+\tcontext_.configuration.csm.colorSpaceRange = ipaConfig.colorSpaceRange;\n+\n+\tfor (const auto &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 IPARkISP2::mapBuffers(const std::vector<IPABuffer> &buffers)\n+{\n+\tfor (const IPABuffer &buffer : buffers) {\n+\t\tauto elem = buffers_.emplace(std::piecewise_construct,\n+\t\t\t\t\t     std::forward_as_tuple(buffer.id),\n+\t\t\t\t\t     std::forward_as_tuple(buffer.planes));\n+\t\tconst FrameBuffer &fb = elem.first->second;\n+\n+\t\tMappedFrameBuffer mappedBuffer(&fb, MappedFrameBuffer::MapFlag::ReadWrite);\n+\t\tif (!mappedBuffer.isValid()) {\n+\t\t\tLOG(IPARkISP2, Fatal) << \"Failed to mmap buffer: \"\n+\t\t\t\t\t      << strerror(mappedBuffer.error());\n+\t\t}\n+\n+\t\tmappedBuffers_.emplace(buffer.id, std::move(mappedBuffer));\n+\t}\n+}\n+\n+void IPARkISP2::unmapBuffers(const std::vector<unsigned int> &ids)\n+{\n+\tfor (unsigned int id : ids) {\n+\t\tconst auto fb = buffers_.find(id);\n+\t\tif (fb == buffers_.end())\n+\t\t\tcontinue;\n+\n+\t\tmappedBuffers_.erase(id);\n+\t\tbuffers_.erase(id);\n+\t}\n+}\n+\n+void IPARkISP2::queueRequest(const uint32_t frame, const ControlList &controls)\n+{\n+\tcontext_.debugMetadata.enableByControl(controls);\n+\n+\tcontext_.frameContexts.getOrInitContext(frame, controls);\n+}\n+\n+void IPARkISP2::initializeFrameContext(IPAFrameContext &frameContext,\n+\t\t\t\t       const ControlList &controls)\n+{\n+\tfor (const auto &a : algorithms()) {\n+\t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n+\t\talgo->queueRequest(context_, frameContext.frame(), frameContext, controls);\n+\t}\n+}\n+\n+void IPARkISP2::computeParams(const uint32_t frame, const uint32_t bufferId)\n+{\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);\n+\n+\tRkISP2Params params(mappedBuffers_.at(bufferId).planes()[0]);\n+\n+\tfor (const auto &algo : algorithms())\n+\t\talgo->prepare(context_, frame, frameContext, &params);\n+\n+\tparamsComputed.emit(frame, bufferId, params.bytesused());\n+}\n+\n+void IPARkISP2::processStats(const uint32_t frame, const uint32_t bufferId,\n+\t\t\t     const ControlList &sensorControls)\n+{\n+\tIPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);\n+\n+\tconst rkisp2_stats_buffer *stats = reinterpret_cast<rkisp2_stats_buffer *>(\n+\t\tmappedBuffers_.at(bufferId).planes()[0].data());\n+\n+\tframeContext.sensor.exposure =\n+\t\tsensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n+\tframeContext.sensor.gain =\n+\t\tcontext_.camHelper->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());\n+\n+\tControlList metadata(controls::controls);\n+\n+\tfor (const auto &algo : algorithms())\n+\t\talgo->process(context_, frame, frameContext, stats, metadata);\n+\n+\tsetControls(frame, frameContext);\n+\n+\tmetadataReady.emit(metadata);\n+}\n+\n+void IPARkISP2::setControls(unsigned int frame, const IPAFrameContext &frameContext)\n+{\n+\t/*\n+\t * \\todo The frame number is most likely wrong here, we need to take\n+\t * internal sensor delays and other timing parameters into account.\n+\t */\n+\n+\tuint32_t exposure = frameContext.agc.exposure;\n+\tuint32_t gain = context_.camHelper->gainCode(frameContext.agc.gain);\n+\tuint32_t vblank = frameContext.agc.vblank;\n+\n+\tLOG(IPARkISP2, Debug)\n+\t\t<< \"Set controls for frame \" << frame << \": exposure \" << exposure\n+\t\t<< \", gain \" << frameContext.agc.gain << \", vblank \" << vblank;\n+\n+\tControlList ctrls(sensorControls_);\n+\tif (frameContext.agc.exposure * frameContext.agc.gain > 0) {\n+\t\tctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));\n+\t\tctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));\n+\t}\n+\tctrls.set(V4L2_CID_VBLANK, static_cast<int32_t>(vblank));\n+\n+\tsetSensorControls.emit(frame, ctrls);\n+}\n+\n+void IPARkISP2::updateControls(const IPACameraSensorInfo &sensorInfo,\n+\t\t\t       const ControlInfoMap &sensorControls,\n+\t\t\t       ControlInfoMap *ipaControls)\n+{\n+\tControlInfoMap::Map ctrlMap = rkisp2Controls;\n+\n+\t/*\n+\t * Compute exposure time limits from the V4L2_CID_EXPOSURE control\n+\t * limits and the line duration.\n+\t */\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.emplace(std::piecewise_construct,\n+\t\t\tstd::forward_as_tuple(&controls::ExposureTime),\n+\t\t\tstd::forward_as_tuple(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 = context_.camHelper->gain(v4l2Gain.min().get<int32_t>());\n+\tfloat maxGain = context_.camHelper->gain(v4l2Gain.max().get<int32_t>());\n+\tfloat defGain = context_.camHelper->gain(v4l2Gain.def().get<int32_t>());\n+\tctrlMap.emplace(std::piecewise_construct,\n+\t\t\tstd::forward_as_tuple(&controls::AnalogueGain),\n+\t\t\tstd::forward_as_tuple(minGain, maxGain, defGain));\n+\n+\t/*\n+\t * Compute the frame duration limits.\n+\t *\n+\t * The frame length is computed assuming a fixed line length combined\n+\t * with the vertical frame sizes.\n+\t */\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+\t/* \\todo Move this (and other agc-related controls) to agc */\n+\tcontext_.ctrlMap[&controls::FrameDurationLimits] =\n+\t\tControlInfo(frameDurations[0], frameDurations[1],\n+\t\t\t    ControlValue(Span<const int64_t, 2>{ { frameDurations[2], frameDurations[2] } }));\n+\n+\tctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end());\n+\t*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);\n+}\n+\n+} /* namespace ipa::rkisp2 */\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\"rkisp2\",\n+\t\"rkisp2\",\n+};\n+\n+IPAInterface *ipaCreate()\n+{\n+\treturn new ipa::rkisp2::IPARkISP2();\n+}\n+}\n+\n+} /* namespace libcamera */\n","prefixes":["RFC","05/19"]}