| Message ID | 20260618-rppx1-ipa-v1-11-32337264cfcd@ideasonboard.com |
|---|---|
| State | New |
| Headers | show |
| Series |
|
| Related | show |
Hi Jacopo, Thanks for your work. On 2026-06-18 12:18:50 +0200, Jacopo Mondi wrote: > 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(-) > > 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<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); > +}; > 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'], nit: rppx1 should be sorted after rpi/*, no? With this considered, Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> > 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 <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*/ > 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 <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*/ > 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 <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 */ > > -- > 2.54.0 >
Hi Niklas On Fri, Jun 19, 2026 at 05:09:55PM +0200, Niklas Söderlund wrote: > Hi Jacopo, > > Thanks for your work. > > On 2026-06-18 12:18:50 +0200, Jacopo Mondi wrote: > > 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(-) > > > > 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<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); > > +}; > > 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'], > > nit: rppx1 should be sorted after rpi/*, no? Only if you know the alphabet, something I don't seem to really grasp Thanks for the catch > > With this considered, > > Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> > > > 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 <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*/ > > 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 <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*/ > > 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 <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 */ > > > > -- > > 2.54.0 > > > > -- > Kind Regards, > Niklas Söderlund
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<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); +}; 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 <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*/ 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 <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*/ 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 <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(-)