From patchwork Fri Sep 18 12:09:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 28350 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 E7F7AC335E for ; Fri, 18 Sep 2026 12:10:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5F1E26876E; Fri, 18 Sep 2026 14:10:12 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="l8VHmJAp"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5805D6873B for ; Fri, 18 Sep 2026 14:09:56 +0200 (CEST) Received: from pb-laptop.local (185.221.142.0.nat.pool.zt.hu [185.221.142.0]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6DC574C2F; Fri, 18 Sep 2026 14:08:13 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1789733293; bh=khd9j0p48MUFq+NxvLZjqZTHESW3+rivAxkDwiP6zrw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=l8VHmJApFJCBghVmlLbEg3QOSsY0NyR2kQTc6vxAdXFM9tinf08ZyfhxxnAXqOyji 6NeBy+5uPhmteJEz/mrj0MIglIizVCv+hU0bt1ZjgMuSnCZTWmvTnKrqwxk2r/FAoZ NmIphmNwpNA2KHowqPd83Hl9yLXbDC2PvecgPOGE= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Cc: Jai Luthra , Jacopo Mondi Subject: [PATCH v3 12/21] ipa: rppx1: Add Date: Fri, 18 Sep 2026 14:09:40 +0200 Message-ID: <20260918120949.191668-13-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260918120949.191668-1-barnabas.pocze@ideasonboard.com> References: <20260918120949.191668-1-barnabas.pocze@ideasonboard.com> MIME-Version: 1.0 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" From: Jai Luthra Copy the rkisp1 IPA module and remove the unnecessary parts, and adjust it as necessary to create the base for the new rppx1 ipa module. Co-developed-by: Jacopo Mondi Signed-off-by: Jai Luthra --- meson_options.txt | 2 +- src/ipa/meson.build | 1 + src/ipa/rppx1/algorithms/algorithm.h | 22 +++ src/ipa/rppx1/algorithms/meson.build | 4 + src/ipa/rppx1/data/meson.build | 8 + src/ipa/rppx1/ipa_context.cpp | 37 ++++ src/ipa/rppx1/ipa_context.h | 58 ++++++ src/ipa/rppx1/meson.build | 31 +++ src/ipa/rppx1/module.h | 29 +++ src/ipa/rppx1/params.h | 89 +++++++++ src/ipa/rppx1/rppx1.cpp | 278 +++++++++++++++++++++++++++ src/ipa/rppx1/stats.h | 55 ++++++ 12 files changed, 613 insertions(+), 1 deletion(-) create mode 100644 src/ipa/rppx1/algorithms/algorithm.h create mode 100644 src/ipa/rppx1/algorithms/meson.build create mode 100644 src/ipa/rppx1/data/meson.build create mode 100644 src/ipa/rppx1/ipa_context.cpp create mode 100644 src/ipa/rppx1/ipa_context.h create mode 100644 src/ipa/rppx1/meson.build create mode 100644 src/ipa/rppx1/module.h create mode 100644 src/ipa/rppx1/params.h create mode 100644 src/ipa/rppx1/rppx1.cpp create mode 100644 src/ipa/rppx1/stats.h diff --git a/meson_options.txt b/meson_options.txt index 3c4bc9f8e7..2d98955dcf 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -49,7 +49,7 @@ option('gstreamer', option('ipas', type : 'array', choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/pisp', 'rpi/vc4', - 'softisp', 'vimc'], + 'rppx1', 'softisp', 'vimc'], description : 'Select which IPA modules to build') option('lc-compliance', diff --git a/src/ipa/meson.build b/src/ipa/meson.build index 1c785deb58..e692e6c60e 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/algorithms/algorithm.h b/src/ipa/rppx1/algorithms/algorithm.h new file mode 100644 index 0000000000..f5f002a0af --- /dev/null +++ b/src/ipa/rppx1/algorithms/algorithm.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * RPP-X1 control algorithm interface + */ + +#pragma once + +#include + +#include "module.h" + +namespace libcamera { + +namespace ipa::rppx1 { + +using Algorithm = libcamera::ipa::Algorithm; + +} /* namespace ipa::rppx1 */ + +} /* namespace libcamera */ diff --git a/src/ipa/rppx1/algorithms/meson.build b/src/ipa/rppx1/algorithms/meson.build new file mode 100644 index 0000000000..5fed9e9d50 --- /dev/null +++ b/src/ipa/rppx1/algorithms/meson.build @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: CC0-1.0 + +rppx1_ipa_algorithms = files([ +]) diff --git a/src/ipa/rppx1/data/meson.build b/src/ipa/rppx1/data/meson.build new file mode 100644 index 0000000000..d1eb321758 --- /dev/null +++ b/src/ipa/rppx1/data/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: CC0-1.0 + +conf_files = files([ +]) + +install_data(conf_files, + install_dir : ipa_data_dir / 'rppx1', + install_tag : 'runtime') diff --git a/src/ipa/rppx1/ipa_context.cpp b/src/ipa/rppx1/ipa_context.cpp new file mode 100644 index 0000000000..2cd435d980 --- /dev/null +++ b/src/ipa/rppx1/ipa_context.cpp @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * 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 + */ + +} /* 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 0000000000..079fcab782 --- /dev/null +++ b/src/ipa/rppx1/ipa_context.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * RPP-X1 IPA Context + */ + +#pragma once + +#include + +#include + +#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; + + ControlInfoMap sensorControls; + + /* Interface to the Camera Helper */ + 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 0000000000..98499c3f78 --- /dev/null +++ b/src/ipa/rppx1/meson.build @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: CC0-1.0 + +subdir('algorithms') +subdir('data') + +ipa_name = 'ipa_rppx1' + +rppx1_ipa_sources = files([ + 'ipa_context.cpp', + 'rppx1.cpp', +]) + +rppx1_ipa_sources += rppx1_ipa_algorithms + +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 0000000000..04902eae10 --- /dev/null +++ b/src/ipa/rppx1/module.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * RPP-X1 IPA Module + */ + +#pragma once + +#include + +#include + +#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/params.h b/src/ipa/rppx1/params.h new file mode 100644 index 0000000000..315c8a9785 --- /dev/null +++ b/src/ipa/rppx1/params.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * RPP-X1 ISP Parameters + */ + +#pragma once + +#include + +#include + +namespace libcamera { + +namespace ipa::rppx1 { + +enum class BlockType : uint16_t { +}; + +namespace details { + +template +struct block_type { +}; + +#define RPPX1_DEFINE_BLOCK_TYPE(blkType, cfgType, id) \ + template<> \ + struct block_type { \ + using type = struct rppx1_##cfgType##_params; \ + static constexpr rppx1_params_block_type blockType = \ + RPPX1_PARAMS_BLOCK_TYPE_##id; \ + }; + +struct params_traits { + using id_type = BlockType; + + template + using id_to_details = block_type; +}; + +} /* namespace details */ + +template +class RppX1ParamsBlock final : public V4L2ParamsBlock +{ +public: + RppX1ParamsBlock(const std::span data) + : V4L2ParamsBlock(data), + configData_(data.subspan(sizeof(v4l2_isp_block_header))) + { + } + + const T *operator->() const override + { + return reinterpret_cast(configData_.data()); + } + + T *operator->() override + { + return reinterpret_cast(configData_.data()); + } + + const T &operator*() const override + { + return *reinterpret_cast(configData_.data()); + } + + T &operator*() override + { + return *reinterpret_cast(configData_.data()); + } + +private: + std::span configData_; +}; + +class RppX1Params : public V4L2Params +{ +public: + RppX1Params(std::span data) + : V4L2Params(data, V4L2_ISP_PARAMS_VERSION_V1) + { + } +}; + +} /* namespace ipa::rppx1 */ + +} /* namespace libcamera */ diff --git a/src/ipa/rppx1/rppx1.cpp b/src/ipa/rppx1/rppx1.cpp new file mode 100644 index 0000000000..23ab8391b3 --- /dev/null +++ b/src/ipa/rppx1/rppx1.cpp @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * RPP-X1 Image Processing Algorithms + */ + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "libcamera/internal/mapped_framebuffer.h" +#include "libcamera/internal/yaml_parser.h" + +#include "algorithms/algorithm.h" + +#include "ipa_context.h" +#include "module.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 final : 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, 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); + void setControls(unsigned int frame); + + std::map mappedBuffers_; + + /* Local parameter storage */ + struct IPAContext context_; +}; + +IPARppX1::IPARppX1() + : context_(kMaxFrameContexts) +{ +} + +std::string IPARppX1::logPrefix() const +{ + return "rppx1"; +} + +int IPARppX1::init(const IPASettings &settings, + const IPACameraSensorInfo &sensorInfo, + const ControlInfoMap &sensorControls, + ControlInfoMap *ipaControls) +{ + context_.sensorInfo = sensorInfo; + context_.sensorControls = sensorControls; + + 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, ControlInfoMap *ipaControls) +{ + context_.sensorInfo = ipaConfig.sensorInfo; + context_.sensorControls = ipaConfig.sensorControls; + + /* Clear the IPA context before the streaming session. */ + context_.configuration = {}; + context_.activeState = {}; + context_.frameContexts.clear(); + + for (const auto &a : algorithms()) { + Algorithm *algo = static_cast(a.get()); + + int ret = algo->configure(context_, context_.sensorInfo); + if (ret) + return ret; + } + + updateControls(ipaControls); + + return 0; +} + +void IPARppX1::mapBuffers(const std::vector &buffers) +{ + for (const IPABuffer &buffer : buffers) { + FrameBuffer fb(buffer.planes); + + auto [it, inserted] = mappedBuffers_.try_emplace( + buffer.id, &fb, MappedFrameBuffer::MapFlag::ReadWrite); + ASSERT(inserted); + + if (!it->second.isValid()) { + LOG(IPARppX1, Fatal) + << "Failed to mmap buffer: " + << strerror(it->second.error()); + } + } +} + +void IPARppX1::unmapBuffers(const std::vector &ids) +{ + for (unsigned int id : ids) + mappedBuffers_.erase(id); +} + +void IPARppX1::queueRequest(const uint32_t frame, const ControlList &controls) +{ + IPAFrameContext &frameContext = context_.frameContexts.alloc(frame); + + for (const auto &a : algorithms()) { + Algorithm *algo = static_cast(a.get()); + 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 (const auto &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) +{ + auto stats = RppX1Stats(mappedBuffers_.at(bufferId).planes()[0]); + if (!stats) + return; + + IPAFrameContext &frameContext = context_.frameContexts.get(frame); + ControlList metadata(controls::controls); + + for (auto const &a : algorithms()) { + Algorithm *algo = static_cast(a.get()); + algo->process(context_, frame, frameContext, &stats, metadata); + } + + setControls(frame); + + metadataReady.emit(frame, metadata); +} + +void IPARppX1::updateControls(ControlInfoMap *ipaControls) +{ + ControlInfoMap::Map ctrlMap = {}; + + ctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end()); + *ipaControls = { std::move(ctrlMap), controls::controls }; +} + +void IPARppX1::setControls(unsigned int frame) +{ + [[maybe_unused]] IPAFrameContext &frameContext = context_.frameContexts.get(frame); + + ControlList ctrls(context_.sensorControls); + + setSensorControls.emit(frame, ctrls); +} + +} /* 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 */ diff --git a/src/ipa/rppx1/stats.h b/src/ipa/rppx1/stats.h new file mode 100644 index 0000000000..39dc132bac --- /dev/null +++ b/src/ipa/rppx1/stats.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * RPP-X1 ISP Statistics + */ + +#pragma once + +#include + +#include + +namespace libcamera { + +namespace ipa::rppx1 { + +enum class StatsType : uint16_t { +}; + +namespace details { + +template +struct stats_type { +}; + +#define RPPX1_DEFINE_STATS_TYPE(blkType, cfgType, id) \ + template<> \ + struct stats_type { \ + using type = struct rppx1_##cfgType##_stats; \ + static constexpr rppx1_stats_block_type blockType = \ + RPPX1_STATS_BLOCK_TYPE_##id; \ + }; + +struct stats_traits { + using id_type = StatsType; + + template + using id_to_details = stats_type; +}; + +} /* namespace details */ + +class RppX1Stats : public V4L2Stats +{ +public: + RppX1Stats(std::span data) + : V4L2Stats(data, V4L2_ISP_VERSION_V1) + { + } +}; + +} /* namespace ipa::rppx1 */ + +} /* namespace libcamera */