From patchwork Thu Jun 18 10:18:50 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 26926 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 06E76C3308 for ; Thu, 18 Jun 2026 10:19:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B0C5562B2E; Thu, 18 Jun 2026 12:19:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="GdyoZiod"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CCCD1629E1 for ; Thu, 18 Jun 2026 12:19:00 +0200 (CEST) Received: from [192.168.125.177] (mob-109-113-4-199.net.vodafone.it [109.113.4.199]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 34B242908; Thu, 18 Jun 2026 12:18:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781777905; bh=FuZvmNCSrt8DCEum16FJpPu5QVKLW50Nfcxw0/qrA5M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=GdyoZiodwUkP6lTWld0g0Zj3GsF6UTt6h3QWfFoAnJbReDMbnj+ifFYrrH7DgwVeR F/8w6Z7hi4wwjKEQtYd9QHf/kqDScmarK1fDhj+NzUKZqSqJfvWtTCEIIaYjk0+X76 AfaQvbqnJO8Fij032iBfoBRPT/vw45ubu9K0xKCY= From: Jacopo Mondi Date: Thu, 18 Jun 2026 12:18:50 +0200 Subject: [PATCH 11/14] ipa: rppx1: Add RPP-X1 IPA skeleton MIME-Version: 1.0 Message-Id: <20260618-rppx1-ipa-v1-11-32337264cfcd@ideasonboard.com> References: <20260618-rppx1-ipa-v1-0-32337264cfcd@ideasonboard.com> In-Reply-To: <20260618-rppx1-ipa-v1-0-32337264cfcd@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=17071; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=FuZvmNCSrt8DCEum16FJpPu5QVKLW50Nfcxw0/qrA5M=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqM8YKnIYD382Xq+EpMicwe0ueol0wfpqrFE8AO fZtXe14XsyJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCajPGCgAKCRByNAaPFqFW PHGfD/9GoZDbW/Flyyq+HhMYKzyja8MzKdrCi/iPbW3HWkL9eF4ecwZDMo7PKmISFUsG2vVIwSD Px8gxViN2flKJ0np3Ose1Q7Ktd0vT8bZf47TDyrHTAilbEcsDkcscIMDr3TNhR+4Ej2bUWoMGqd EcpCakVCJZb/Wx0q1+E3A9WL+ZKI1WnxQdyWFZ1x95Hhmrfcky3qJYmzUudn05IFBdmYcYwvehz m/ceig2wU4/iLqS9qWG7e49imhDWdhM5b1Pcni9PcSlQ75h5Ufji1+0rbuHvUkuPuLALWTGS9QM A6qZ6uLNEV9jC/vOO81GglcMBMYW5vTdDI2/i9nBehcmcEf2JZ+piCxYJcgetU3EQwVg10Kdpy7 aYY5jO1SvFjNyc3RXDbZoL60mfRWN4kGxS50wTaCwvORKqU9t3hlcIJOkRFNkxucAeH6zhYvgh+ msTbDB6TQJ5kKkHcOD1dSrUYyntb59pM8TkOhvU1BGWvhLacDy6elULMtt4WZSRViUdVLKDMsyE TBFtGftMkjDw4RCW4Z8058UpB0i1XOB9EbI3f9V3JQp/ZU4uj6uRy4pJiPCEqrYFVKkfPtgSvYA xLyKLN5yT9L1iGXQW8t13FMHCPqidU9oJw0naq2QnbC4zNIoM3/izCRHps9IqJuI09QqXs6Hczt 8P7bKQEMy0g5cqg== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 --- 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(-) diff --git a/Documentation/Doxyfile-common.in b/Documentation/Doxyfile-common.in index f11ec593d5ae..131fdcc6083f 100644 --- a/Documentation/Doxyfile-common.in +++ b/Documentation/Doxyfile-common.in @@ -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 \ diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index 3ee3ada303c0..9ac15546102f 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -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', diff --git a/include/libcamera/ipa/rppx1.mojom b/include/libcamera/ipa/rppx1.mojom new file mode 100644 index 000000000000..4abbebb24f85 --- /dev/null +++ b/include/libcamera/ipa/rppx1.mojom @@ -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 streamConfig) + => (int32 ret, libcamera.ControlInfoMap ipaControls); + + mapBuffers(array buffers); + unmapBuffers(array 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); +}; diff --git a/meson_options.txt b/meson_options.txt index 20baacc4fc65..0aa3aef24edd 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -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', diff --git a/src/ipa/meson.build b/src/ipa/meson.build index c583c7efdd35..411d043021f8 100644 --- a/src/ipa/meson.build +++ b/src/ipa/meson.build @@ -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', diff --git a/src/ipa/rppx1/ipa_context.cpp b/src/ipa/rppx1/ipa_context.cpp new file mode 100644 index 000000000000..5ac60dfc7bf6 --- /dev/null +++ b/src/ipa/rppx1/ipa_context.cpp @@ -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 */ diff --git a/src/ipa/rppx1/ipa_context.h b/src/ipa/rppx1/ipa_context.h new file mode 100644 index 000000000000..f268a35081bc --- /dev/null +++ b/src/ipa/rppx1/ipa_context.h @@ -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 + +#include + +#include + +#include + +#include +#include + +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 frameContexts; + + ControlInfoMap::Map ctrlMap; + + std::unique_ptr camHelper; +}; + +} /* namespace ipa::rppx1 */ + +} /* namespace libcamera*/ diff --git a/src/ipa/rppx1/meson.build b/src/ipa/rppx1/meson.build new file mode 100644 index 000000000000..8034fe24d241 --- /dev/null +++ b/src/ipa/rppx1/meson.build @@ -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 diff --git a/src/ipa/rppx1/module.h b/src/ipa/rppx1/module.h new file mode 100644 index 000000000000..0bacff64de8f --- /dev/null +++ b/src/ipa/rppx1/module.h @@ -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 + +#include "ipa_context.h" +#include "params.h" +#include "stats.h" + +namespace libcamera { + +namespace ipa::rppx1 { + +using Module = ipa::Module; + +} /* namespace ipa::rppx1 */ + +} /* namespace libcamera*/ diff --git a/src/ipa/rppx1/rppx1.cpp b/src/ipa/rppx1/rppx1.cpp new file mode 100644 index 000000000000..20ca2d292704 --- /dev/null +++ b/src/ipa/rppx1/rppx1.cpp @@ -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 +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#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 &streamConfig, + ControlInfoMap *ipaControls) override; + void mapBuffers(const std::vector &buffers) override; + void unmapBuffers(const std::vector &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 buffers_; + std::map 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 data = YamlParser::parse(file); + if (!data) + return -EINVAL; + + unsigned int version = (*data)["version"].get(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 &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(a.get()); + + int ret = algo->configure(context_, info); + if (ret) + return ret; + } + + return 0; +} + +void IPARppX1::mapBuffers(const std::vector &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 &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(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(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 */