@@ -37,6 +37,7 @@ EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \
@TOP_BUILDDIR@/include/libcamera/ipa/mali-c55_*.h \
@TOP_BUILDDIR@/include/libcamera/ipa/raspberrypi_*.h \
@TOP_BUILDDIR@/include/libcamera/ipa/rkisp1_*.h \
+ @TOP_BUILDDIR@/include/libcamera/ipa/rppx1_*.h \
@TOP_BUILDDIR@/include/libcamera/ipa/vimc_*.h
EXCLUDE_SYMBOLS = libcamera::BoundMethodArgs \
@@ -66,6 +66,7 @@ pipeline_ipa_mojom_mapping = {
'ipu3': 'ipu3.mojom',
'mali-c55': 'mali-c55.mojom',
'rkisp1': 'rkisp1.mojom',
+ 'rcar-gen4': 'rppx1.mojom',
'rpi/pisp': 'raspberrypi.mojom',
'rpi/vc4': 'raspberrypi.mojom',
'simple': 'soft.mojom',
new file mode 100644
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/*
+ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry.
+ */
+
+module ipa.rppx1;
+
+import "include/libcamera/ipa/core.mojom";
+
+struct IPAConfigInfo {
+ libcamera.IPACameraSensorInfo sensorInfo;
+ libcamera.ControlInfoMap sensorControls;
+};
+
+interface IPARppX1Interface {
+ init(libcamera.IPASettings settings,
+ libcamera.IPACameraSensorInfo sensorInfo,
+ libcamera.ControlInfoMap sensorControls)
+ => (int32 ret, libcamera.ControlInfoMap ipaControls);
+ start() => (int32 ret);
+ stop();
+
+ configure(IPAConfigInfo configInfo,
+ map<uint32, libcamera.IPAStream> streamConfig)
+ => (int32 ret, libcamera.ControlInfoMap ipaControls);
+
+ mapBuffers(array<libcamera.IPABuffer> buffers);
+ unmapBuffers(array<uint32> ids);
+
+ [async] queueRequest(uint32 frame, libcamera.ControlList reqControls);
+ [async] computeParams(uint32 frame, uint32 bufferId);
+ [async] processStats(uint32 frame, uint32 bufferId,
+ libcamera.ControlList sensorControls);
+};
+
+interface IPARppX1EventInterface {
+ paramsComputed(uint32 frame, uint32 bytesused);
+ setSensorControls(uint32 frame, libcamera.ControlList sensorControls);
+ metadataReady(uint32 frame, libcamera.ControlList metadata);
+};
@@ -48,8 +48,8 @@ option('gstreamer',
option('ipas',
type : 'array',
- choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/pisp', 'rpi/vc4', 'simple',
- 'vimc'],
+ choices : ['ipu3', 'mali-c55', 'rkisp1', 'rppx1', 'rpi/pisp', 'rpi/vc4',
+ 'simple', 'vimc'],
description : 'Select which IPA modules to build')
option('lc-compliance',
@@ -27,6 +27,7 @@ ipa_sign = files('ipa-sign.sh')
supported_ipas = {
'ipu3': 'ipu3',
'mali-c55': 'mali-c55',
+ 'rcar-gen4': 'rppx1',
'rkisp1': 'rkisp1',
'rpi/pisp': 'rpi/pisp',
'rpi/vc4': 'rpi/vc4',
new file mode 100644
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ *
+ * RPP-X1 IPA Context
+ */
+
+#include "ipa_context.h"
+
+/**
+ * \file ipa_context.h
+ * \brief Context and state information shared between the algorithms
+ */
+
+namespace libcamera::ipa::rppx1 {
+
+/**
+ * \struct IPASessionConfiguration
+ * \brief Session configuration for the IPA module
+ */
+
+/**
+ * \struct IPAActiveState
+ * \brief Active state for algorithms
+ */
+
+/**
+ * \struct IPAFrameContext
+ * \brief Per-frame context for algorithms
+ */
+
+/**
+ * \struct IPAContext
+ * \brief Global IPA context data shared between all algorithms
+ *
+ * \var IPAContext::sensorInfo
+ * \brief The IPA session sensorInfo, immutable during the session
+ *
+ * \var IPAContext::configuration
+ * \brief The IPA session configuration, immutable during the session
+ *
+ * \var IPAContext::activeState
+ * \brief The IPA active state, storing the latest state for all algorithms
+ *
+ * \var IPAContext::frameContexts
+ * \brief Ring buffer of per-frame contexts
+ *
+ * \var IPAContext::ctrlMap
+ * \brief The IPA map of controls
+ *
+ * \var IPAContext::camHelper
+ * \brief The IPA camera helper
+ */
+
+/**
+ * \fn IPAContext::IPAContext
+ * \brief Construct the IPA context
+ * \param[in] frameContextSize Size of the frame context queue
+ */
+
+} /* namespace libcamera::ipa::rppx1 */
new file mode 100644
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ *
+ * RPP-X1 IPA Context
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <linux/media/dreamchip/rppx1-config.h>
+
+#include <libcamera/controls.h>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+
+#include <libipa/camera_sensor_helper.h>
+#include <libipa/fc_queue.h>
+
+namespace libcamera {
+
+namespace ipa::rppx1 {
+
+struct IPASessionConfiguration {
+};
+
+struct IPAActiveState {
+};
+
+struct IPAFrameContext : public FrameContext {
+};
+
+struct IPAContext {
+ IPAContext(unsigned int frameContextSize)
+ : frameContexts(frameContextSize)
+ {
+ }
+
+ IPACameraSensorInfo sensorInfo;
+ IPASessionConfiguration configuration;
+ IPAActiveState activeState;
+
+ FCQueue<IPAFrameContext> frameContexts;
+
+ ControlInfoMap::Map ctrlMap;
+
+ std::unique_ptr<CameraSensorHelper> camHelper;
+};
+
+} /* namespace ipa::rppx1 */
+
+} /* namespace libcamera*/
new file mode 100644
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: CC0-1.0
+
+ipa_name = 'ipa_rppx1'
+
+rppx1_ipa_sources = files([
+ 'ipa_context.cpp',
+ 'rppx1.cpp',
+])
+
+mod = shared_module(ipa_name, rppx1_ipa_sources,
+ name_prefix : '',
+ include_directories : [ipa_includes],
+ dependencies : [libcamera_private, libipa_dep],
+ install : true,
+ install_dir : ipa_install_dir)
+
+if ipa_sign_module
+ custom_target(ipa_name + '.so.sign',
+ input : mod,
+ output : ipa_name + '.so.sign',
+ command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],
+ install : false,
+ build_by_default : true)
+endif
+
+ipa_names += ipa_name
new file mode 100644
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ *
+ * RPP-X1 IPA Module
+ */
+
+#pragma once
+
+#include <libipa/module.h>
+
+#include "ipa_context.h"
+#include "params.h"
+#include "stats.h"
+
+namespace libcamera {
+
+namespace ipa::rppx1 {
+
+using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,
+ RppX1Params, RppX1Stats>;
+
+} /* namespace ipa::rppx1 */
+
+} /* namespace libcamera*/
new file mode 100644
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ *
+ * RPP-X1 Image Processing Algorithms
+ */
+
+#include <algorithm>
+#include <stdint.h>
+#include <string.h>
+
+#include <libcamera/base/file.h>
+#include <libcamera/base/log.h>
+
+#include <libcamera/controls.h>
+#include <libcamera/framebuffer.h>
+
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libcamera/ipa/rppx1_ipa_interface.h>
+
+#include "libcamera/internal/formats.h"
+#include "libcamera/internal/mapped_framebuffer.h"
+#include "libcamera/internal/yaml_parser.h"
+
+#include "algorithms/algorithm.h"
+
+#include "ipa_context.h"
+#include "params.h"
+#include "stats.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPARppX1)
+
+namespace ipa::rppx1 {
+
+/* Maximum number of frame contexts to be held */
+static constexpr uint32_t kMaxFrameContexts = 16;
+
+class IPARppX1 : public IPARppX1Interface, public Module
+{
+public:
+ IPARppX1();
+
+ int init(const IPASettings &settings,
+ const IPACameraSensorInfo &sensorInfo,
+ const ControlInfoMap &sensorControls,
+ ControlInfoMap *ipaControls) override;
+ int start() override;
+ void stop() override;
+
+ int configure(const IPAConfigInfo &ipaConfig,
+ const std::map<uint32_t, IPAStream> &streamConfig,
+ ControlInfoMap *ipaControls) override;
+ void mapBuffers(const std::vector<IPABuffer> &buffers) override;
+ void unmapBuffers(const std::vector<unsigned int> &ids) override;
+
+ void queueRequest(const uint32_t frame, const ControlList &controls) override;
+ void computeParams(const uint32_t frame, const uint32_t bufferId) override;
+ void processStats(const uint32_t frame, const uint32_t bufferId,
+ const ControlList &sensorControls) override;
+
+protected:
+ std::string logPrefix() const override;
+
+private:
+ void updateControls(ControlInfoMap *ipaControls);
+
+ std::map<unsigned int, FrameBuffer> buffers_;
+ std::map<unsigned int, MappedFrameBuffer> mappedBuffers_;
+
+ /* Local parameter storage */
+ struct IPAContext context_;
+};
+
+IPARppX1::IPARppX1()
+ : context_(kMaxFrameContexts)
+{
+}
+
+std::string IPARppX1::logPrefix() const
+{
+ return "rppx1";
+}
+
+void IPARppX1::updateControls(ControlInfoMap *ipaControls)
+{
+ ControlInfoMap::Map ctrlMap;
+
+ ctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end());
+ *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
+}
+
+int IPARppX1::init(const IPASettings &settings,
+ const IPACameraSensorInfo &sensorInfo,
+ [[maybe_unused]] const ControlInfoMap &sensorControls,
+ [[maybe_unused]] ControlInfoMap *ipaControls)
+{
+ context_.sensorInfo = sensorInfo;
+
+ context_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel);
+ if (!context_.camHelper) {
+ LOG(IPARppX1, Error)
+ << "Failed to create camera sensor helper for "
+ << settings.sensorModel;
+ return -ENODEV;
+ }
+
+ /* Load the tuning data file. */
+ File file(settings.configurationFile);
+ if (!file.open(File::OpenModeFlag::ReadOnly)) {
+ int ret = file.error();
+ LOG(IPARppX1, Error)
+ << "Failed to open configuration file "
+ << settings.configurationFile << ": " << strerror(-ret);
+ return ret;
+ }
+
+ std::unique_ptr<libcamera::ValueNode> data = YamlParser::parse(file);
+ if (!data)
+ return -EINVAL;
+
+ unsigned int version = (*data)["version"].get<uint32_t>(0);
+ if (version != 1) {
+ LOG(IPARppX1, Error)
+ << "Invalid tuning file version " << version;
+ return -EINVAL;
+ }
+
+ if (!data->contains("algorithms")) {
+ LOG(IPARppX1, Error)
+ << "Tuning file doesn't contain any algorithm";
+ return -EINVAL;
+ }
+
+ int ret = createAlgorithms(context_, (*data)["algorithms"]);
+ if (ret)
+ return ret;
+
+ /* Initialize controls. */
+ updateControls(ipaControls);
+
+ return 0;
+}
+
+int IPARppX1::start()
+{
+ /* \todo Properly handle startup controls. */
+ return 0;
+}
+
+void IPARppX1::stop()
+{
+ context_.frameContexts.clear();
+}
+
+int IPARppX1::configure(const IPAConfigInfo &ipaConfig,
+ [[maybe_unused]] const std::map<uint32_t, IPAStream> &streamConfig,
+ [[maybe_unused]] ControlInfoMap *ipaControls)
+{
+ /* Clear the IPA context before the streaming session. */
+ context_.configuration = {};
+ context_.activeState = {};
+ context_.frameContexts.clear();
+
+ const IPACameraSensorInfo &info = ipaConfig.sensorInfo;
+
+ /* Update the camera controls using the new sensor settings. */
+ updateControls(ipaControls);
+
+ for (auto const &a : algorithms()) {
+ Algorithm *algo = static_cast<Algorithm *>(a.get());
+
+ int ret = algo->configure(context_, info);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void IPARppX1::mapBuffers(const std::vector<IPABuffer> &buffers)
+{
+ for (const IPABuffer &buffer : buffers) {
+ auto elem = buffers_.emplace(std::piecewise_construct,
+ std::forward_as_tuple(buffer.id),
+ std::forward_as_tuple(buffer.planes));
+ const FrameBuffer &fb = elem.first->second;
+
+ MappedFrameBuffer mappedBuffer(&fb, MappedFrameBuffer::MapFlag::ReadWrite);
+ if (!mappedBuffer.isValid()) {
+ LOG(IPARppX1, Fatal) << "Failed to mmap buffer: "
+ << strerror(mappedBuffer.error());
+ }
+
+ mappedBuffers_.emplace(buffer.id, std::move(mappedBuffer));
+ }
+}
+
+void IPARppX1::unmapBuffers(const std::vector<unsigned int> &ids)
+{
+ for (unsigned int id : ids) {
+ const auto fb = buffers_.find(id);
+ if (fb == buffers_.end())
+ continue;
+
+ mappedBuffers_.erase(id);
+ buffers_.erase(id);
+ }
+}
+
+void IPARppX1::queueRequest(const uint32_t frame, const ControlList &controls)
+{
+ IPAFrameContext &frameContext = context_.frameContexts.alloc(frame);
+
+ for (auto const &a : algorithms()) {
+ Algorithm *algo = static_cast<Algorithm *>(a.get());
+ if (algo->disabled_)
+ continue;
+
+ algo->queueRequest(context_, frame, frameContext, controls);
+ }
+}
+
+void IPARppX1::computeParams(const uint32_t frame, const uint32_t bufferId)
+{
+ IPAFrameContext &frameContext = context_.frameContexts.get(frame);
+
+ RppX1Params params(mappedBuffers_.at(bufferId).planes()[0]);
+
+ for (auto const &algo : algorithms())
+ algo->prepare(context_, frame, frameContext, ¶ms);
+
+ paramsComputed.emit(frame, params.bytesused());
+}
+
+void IPARppX1::processStats(const uint32_t frame, const uint32_t bufferId,
+ [[maybe_unused]] const ControlList &sensorControls)
+{
+ IPAFrameContext &frameContext = context_.frameContexts.get(frame);
+
+ ControlList metadata(controls::controls);
+
+ auto stats = RppX1Stats(mappedBuffers_.at(bufferId).planes()[0]);
+ if (!stats)
+ return;
+
+ for (auto const &a : algorithms()) {
+ Algorithm *algo = static_cast<Algorithm *>(a.get());
+ if (algo->disabled_)
+ continue;
+ algo->process(context_, frame, frameContext, &stats, metadata);
+ }
+
+ metadataReady.emit(frame, metadata);
+}
+
+} /* namespace ipa::rppx1 */
+
+/*
+ * External IPA module interface
+ */
+
+extern "C" {
+const struct IPAModuleInfo ipaModuleInfo = {
+ IPA_MODULE_API_VERSION,
+ 1,
+ "rppx1",
+};
+
+IPAInterface *ipaCreate()
+{
+ return new ipa::rppx1::IPARppX1();
+}
+}
+
+} /* namespace libcamera */
Add the infrastructure for the RPP-X1 without any registered algorithm but with the definition of the IPA module interface and the IPA context. Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> --- Documentation/Doxyfile-common.in | 1 + include/libcamera/ipa/meson.build | 1 + include/libcamera/ipa/rppx1.mojom | 41 ++++++ meson_options.txt | 4 +- src/ipa/meson.build | 1 + src/ipa/rppx1/ipa_context.cpp | 63 +++++++++ src/ipa/rppx1/ipa_context.h | 55 ++++++++ src/ipa/rppx1/meson.build | 26 ++++ src/ipa/rppx1/module.h | 27 ++++ src/ipa/rppx1/rppx1.cpp | 280 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 497 insertions(+), 2 deletions(-)