[v3,12/21] ipa: rppx1: Add
diff mbox series

Message ID 20260918120949.191668-13-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • libcamera: rcar-gen4 + rpp-x1
Related show

Commit Message

Barnabás Pőcze Sept. 18, 2026, 12:09 p.m. UTC
From: Jai Luthra <jai.luthra@ideasonboard.com>

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 <jacopo.mondi@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
---
 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

Comments

Jacopo Mondi Sept. 24, 2026, 2:44 p.m. UTC | #1
Hi Barnabas,
   the commit title in this and the next patches seems to be broken at
"Add"

On Fri, Sep 18, 2026 at 02:09:40PM +0200, Barnabás Pőcze wrote:
> From: Jai Luthra <jai.luthra@ideasonboard.com>
>
> 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.

Well, that's what might have originally happened but there is nothing
that relates to rkisp1 anymore here, and this could actually be
classified as new code.

Should we reword the commit message to simply introduce the rppx1
support as new code ?

>
> Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
> ---
>  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 <libipa/algorithm.h>
> +
> +#include "module.h"
> +
> +namespace libcamera {
> +
> +namespace ipa::rppx1 {
> +
> +using Algorithm = libcamera::ipa::Algorithm<Module>;
> +
> +} /* 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 <memory>
> +
> +#include <libcamera/base/utils.h>
> +
> +#include <libcamera/control_ids.h>
> +#include <libcamera/controls.h>
> +#include <libcamera/geometry.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;
> +
> +	ControlInfoMap sensorControls;
> +
> +	/* Interface to the Camera Helper */
> +	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 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 <linux/media/dreamchip/rppx1-config.h>
> +
> +#include <libcamera/ipa/rppx1_ipa_interface.h>
> +
> +#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/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 <linux/media/dreamchip/rppx1-config.h>
> +
> +#include <libipa/v4l2_params.h>
> +
> +namespace libcamera {
> +
> +namespace ipa::rppx1 {
> +
> +enum class BlockType : uint16_t {
> +};
> +
> +namespace details {
> +
> +template<BlockType B>
> +struct block_type {
> +};
> +
> +#define RPPX1_DEFINE_BLOCK_TYPE(blkType, cfgType, id)			\
> +	template<>							\
> +	struct block_type<BlockType::blkType> {				\
> +		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<id_type Id>
> +	using id_to_details = block_type<Id>;
> +};
> +
> +} /* namespace details */
> +
> +template<typename T>
> +class RppX1ParamsBlock final : public V4L2ParamsBlock<T>
> +{
> +public:
> +	RppX1ParamsBlock(const std::span<uint8_t> data)
> +		: V4L2ParamsBlock<T>(data),
> +		  configData_(data.subspan(sizeof(v4l2_isp_block_header)))
> +	{
> +	}
> +
> +	const T *operator->() const override
> +	{
> +		return reinterpret_cast<const T *>(configData_.data());
> +	}
> +
> +	T *operator->() override
> +	{
> +		return reinterpret_cast<T *>(configData_.data());
> +	}
> +
> +	const T &operator*() const override
> +	{
> +		return *reinterpret_cast<const T *>(configData_.data());
> +	}
> +
> +	T &operator*() override
> +	{
> +		return *reinterpret_cast<T *>(configData_.data());
> +	}
> +
> +private:
> +	std::span<uint8_t> configData_;
> +};
> +
> +class RppX1Params : public V4L2Params<details::params_traits>
> +{
> +public:
> +	RppX1Params(std::span<uint8_t> 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.

Please update this one :)

> + *
> + * RPP-X1 Image Processing Algorithms
> + */
> +
> +#include <map>
> +#include <stdint.h>
> +#include <string.h>
> +#include <utility>
> +
> +#include <linux/v4l2-controls.h>
> +
> +#include <libcamera/base/file.h>
> +#include <libcamera/base/log.h>
> +
> +#include <libcamera/control_ids.h>
> +#include <libcamera/controls.h>
> +#include <libcamera/framebuffer.h>
> +#include <libcamera/request.h>
> +
> +#include <libcamera/ipa/core_ipa_interface.h>
> +#include <libcamera/ipa/ipa_module_info.h>
> +#include <libcamera/ipa/rppx1_ipa_interface.h>
> +
> +#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"

Just for fun I've run iwyu

The full include-list for ../src/ipa/rppx1/rppx1.cpp:

#include <errno.h>                                  // for EINVAL, ENODEV
#include <libcamera/base/file.h>                    // for File
#include <libcamera/base/log.h>                     // for LogSeverity, isLo...
#include <libcamera/control_ids.h>                  // for controls
#include <libcamera/controls.h>                     // for ControlInfoMap
#include <libcamera/framebuffer.h>                  // for FrameBuffer
#include <libcamera/ipa/core_ipa_interface.h>       // for IPASettings, IPAB...
#include <libcamera/ipa/ipa_module_info.h>          // for IPAModuleInfo
#include <libcamera/ipa/rppx1_ipa_interface.h>      // for IPAConfigInfo
#include <libipa/agc.h>                             // for extractControls
#include <linux/v4l2-controls.h>                    // for V4L2_CID_VBLANK
#include <stdint.h>                                 // for uint32_t, int32_t
#include <string.h>                                 // for strerror
#include <list>                                     // for _List_const_iterator
#include <map>                                      // for map
#include <memory>                                   // for unique_ptr
#include <ostream>                                  // for basic_ostream
#include <span>                                     // for span
#include <string>                                   // for char_traits, basi...
#include <tuple>                                    // for tie, tuple
#include <utility>                                  // for get, pair, move
#include <vector>                                   // for vector
#include "algorithms/algorithm.h"                   // for Algorithm
#include "ipa_context.h"                            // for IPAFrameContext
#include "libcamera/base/signal.h"                  // for Signal
#include "libcamera/base/utils.h"                   // for Duration
#include "libcamera/geometry.h"                     // for Size
#include "libcamera/internal/mapped_framebuffer.h"  // for MappedFrameBuffer
#include "libcamera/internal/yaml_parser.h"         // for YamlParser
#include "libcamera/ipa/ipa_interface.h"            // for IPAInterface (ptr...
#include "libipa/algorithm.h"                       // for Algorithm
#include "libipa/camera_sensor_helper.h"            // for CameraSensorHelper
#include "libipa/fc_queue.h"                        // for FCQueue
#include "libipa/pwl.h"                             // for ValueNode, Matrix
#include "module.h"                                 // for Module
#include "params.h"                                 // for RppX1Params
#include "stats.h"                                  // for RppX1Stats

> +
> +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<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);
> +	void setControls(unsigned int frame);
> +
> +	std::map<unsigned int, MappedFrameBuffer> 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<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, 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<Algorithm *>(a.get());
> +
> +		int ret = algo->configure(context_, context_.sensorInfo);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	updateControls(ipaControls);
> +
> +	return 0;
> +}
> +
> +void IPARppX1::mapBuffers(const std::vector<IPABuffer> &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<unsigned int> &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<Algorithm *>(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, &params);
> +
> +	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<Algorithm *>(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 <linux/media/dreamchip/rppx1-config.h>
> +
> +#include <libipa/v4l2_stats.h>
> +
> +namespace libcamera {
> +
> +namespace ipa::rppx1 {
> +
> +enum class StatsType : uint16_t {
> +};
> +
> +namespace details {
> +
> +template<StatsType B>
> +struct stats_type {
> +};
> +
> +#define RPPX1_DEFINE_STATS_TYPE(blkType, cfgType, id)			\
> +	template<>							\
> +	struct stats_type<StatsType::blkType> {				\
> +		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<id_type Id>
> +	using id_to_details = stats_type<Id>;
> +};
> +
> +} /* namespace details */
> +
> +class RppX1Stats : public V4L2Stats<details::stats_traits>
> +{
> +public:
> +	RppX1Stats(std::span<uint8_t> data)
> +		: V4L2Stats(data, V4L2_ISP_VERSION_V1)
> +	{
> +	}
> +};
> +
> +} /* namespace ipa::rppx1 */
> +
> +} /* namespace libcamera */

Seems a pretty regular IPA module so far ;)

Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Thanks
  j

> --
> 2.55.0
>
Barnabás Pőcze Sept. 24, 2026, 3:09 p.m. UTC | #2
2026. 09. 24. 16:44 keltezéssel, Jacopo Mondi írta:
> Hi Barnabas,
>     the commit title in this and the next patches seems to be broken at
> "Add"

Sadly no, my imagination concluded that "Add" would be a good title.


> 
> On Fri, Sep 18, 2026 at 02:09:40PM +0200, Barnabás Pőcze wrote:
>> From: Jai Luthra <jai.luthra@ideasonboard.com>
>>
>> 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.
> 
> Well, that's what might have originally happened but there is nothing
> that relates to rkisp1 anymore here, and this could actually be
> classified as new code.
> 
> Should we reword the commit message to simply introduce the rppx1
> support as new code ?

Well, true, but it is still effectively a copy of it, so I think
it is fair to mention that.


> 
>>
>> Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>> Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
>> ---
>>   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 <libipa/algorithm.h>
>> +
>> +#include "module.h"
>> +
>> +namespace libcamera {
>> +
>> +namespace ipa::rppx1 {
>> +
>> +using Algorithm = libcamera::ipa::Algorithm<Module>;
>> +
>> +} /* 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 <memory>
>> +
>> +#include <libcamera/base/utils.h>
>> +
>> +#include <libcamera/control_ids.h>
>> +#include <libcamera/controls.h>
>> +#include <libcamera/geometry.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;
>> +
>> +	ControlInfoMap sensorControls;
>> +
>> +	/* Interface to the Camera Helper */
>> +	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 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 <linux/media/dreamchip/rppx1-config.h>
>> +
>> +#include <libcamera/ipa/rppx1_ipa_interface.h>
>> +
>> +#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/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 <linux/media/dreamchip/rppx1-config.h>
>> +
>> +#include <libipa/v4l2_params.h>
>> +
>> +namespace libcamera {
>> +
>> +namespace ipa::rppx1 {
>> +
>> +enum class BlockType : uint16_t {
>> +};
>> +
>> +namespace details {
>> +
>> +template<BlockType B>
>> +struct block_type {
>> +};
>> +
>> +#define RPPX1_DEFINE_BLOCK_TYPE(blkType, cfgType, id)			\
>> +	template<>							\
>> +	struct block_type<BlockType::blkType> {				\
>> +		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<id_type Id>
>> +	using id_to_details = block_type<Id>;
>> +};
>> +
>> +} /* namespace details */
>> +
>> +template<typename T>
>> +class RppX1ParamsBlock final : public V4L2ParamsBlock<T>
>> +{
>> +public:
>> +	RppX1ParamsBlock(const std::span<uint8_t> data)
>> +		: V4L2ParamsBlock<T>(data),
>> +		  configData_(data.subspan(sizeof(v4l2_isp_block_header)))
>> +	{
>> +	}
>> +
>> +	const T *operator->() const override
>> +	{
>> +		return reinterpret_cast<const T *>(configData_.data());
>> +	}
>> +
>> +	T *operator->() override
>> +	{
>> +		return reinterpret_cast<T *>(configData_.data());
>> +	}
>> +
>> +	const T &operator*() const override
>> +	{
>> +		return *reinterpret_cast<const T *>(configData_.data());
>> +	}
>> +
>> +	T &operator*() override
>> +	{
>> +		return *reinterpret_cast<T *>(configData_.data());
>> +	}
>> +
>> +private:
>> +	std::span<uint8_t> configData_;
>> +};
>> +
>> +class RppX1Params : public V4L2Params<details::params_traits>
>> +{
>> +public:
>> +	RppX1Params(std::span<uint8_t> 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.
> 
> Please update this one :)
> [...]

So what should happen with the google copyright? It's effectively a copy.
Jacopo Mondi Sept. 24, 2026, 4:31 p.m. UTC | #3
Hi Barnabás

On Thu, Sep 24, 2026 at 05:09:01PM +0200, Barnabás Pőcze wrote:
> 2026. 09. 24. 16:44 keltezéssel, Jacopo Mondi írta:
> > Hi Barnabas,
> >     the commit title in this and the next patches seems to be broken at
> > "Add"
>
> Sadly no, my imagination concluded that "Add" would be a good title.

Ah, now I understand what you meant now. I still think it could be
improved :)

>
>
> >
> > On Fri, Sep 18, 2026 at 02:09:40PM +0200, Barnabás Pőcze wrote:
> > > From: Jai Luthra <jai.luthra@ideasonboard.com>
> > >
> > > 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.
> >
> > Well, that's what might have originally happened but there is nothing
> > that relates to rkisp1 anymore here, and this could actually be
> > classified as new code.
> >
> > Should we reword the commit message to simply introduce the rppx1
> > support as new code ?
>
> Well, true, but it is still effectively a copy of it, so I think
> it is fair to mention that.
>

Well, many times I had copied a file, renamed the common prefixes and
then removed all code just to get a skeleton in place, just to avoid
typing out all the boilerplate, but if not much remains from the
original file, it hardly is a copy anymore.

I would argue that looking at thing this way even mali is a copy of
rkisp1, and possibily all other IPA modules are a copy of an existing
one :)

>
> >
> > >
> > > Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
> > > ---
> > >   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 <libipa/algorithm.h>
> > > +
> > > +#include "module.h"
> > > +
> > > +namespace libcamera {
> > > +
> > > +namespace ipa::rppx1 {
> > > +
> > > +using Algorithm = libcamera::ipa::Algorithm<Module>;
> > > +
> > > +} /* 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 <memory>
> > > +
> > > +#include <libcamera/base/utils.h>
> > > +
> > > +#include <libcamera/control_ids.h>
> > > +#include <libcamera/controls.h>
> > > +#include <libcamera/geometry.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;
> > > +
> > > +	ControlInfoMap sensorControls;
> > > +
> > > +	/* Interface to the Camera Helper */
> > > +	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 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 <linux/media/dreamchip/rppx1-config.h>
> > > +
> > > +#include <libcamera/ipa/rppx1_ipa_interface.h>
> > > +
> > > +#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/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 <linux/media/dreamchip/rppx1-config.h>
> > > +
> > > +#include <libipa/v4l2_params.h>
> > > +
> > > +namespace libcamera {
> > > +
> > > +namespace ipa::rppx1 {
> > > +
> > > +enum class BlockType : uint16_t {
> > > +};
> > > +
> > > +namespace details {
> > > +
> > > +template<BlockType B>
> > > +struct block_type {
> > > +};
> > > +
> > > +#define RPPX1_DEFINE_BLOCK_TYPE(blkType, cfgType, id)			\
> > > +	template<>							\
> > > +	struct block_type<BlockType::blkType> {				\
> > > +		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<id_type Id>
> > > +	using id_to_details = block_type<Id>;
> > > +};
> > > +
> > > +} /* namespace details */
> > > +
> > > +template<typename T>
> > > +class RppX1ParamsBlock final : public V4L2ParamsBlock<T>
> > > +{
> > > +public:
> > > +	RppX1ParamsBlock(const std::span<uint8_t> data)
> > > +		: V4L2ParamsBlock<T>(data),
> > > +		  configData_(data.subspan(sizeof(v4l2_isp_block_header)))
> > > +	{
> > > +	}
> > > +
> > > +	const T *operator->() const override
> > > +	{
> > > +		return reinterpret_cast<const T *>(configData_.data());
> > > +	}
> > > +
> > > +	T *operator->() override
> > > +	{
> > > +		return reinterpret_cast<T *>(configData_.data());
> > > +	}
> > > +
> > > +	const T &operator*() const override
> > > +	{
> > > +		return *reinterpret_cast<const T *>(configData_.data());
> > > +	}
> > > +
> > > +	T &operator*() override
> > > +	{
> > > +		return *reinterpret_cast<T *>(configData_.data());
> > > +	}
> > > +
> > > +private:
> > > +	std::span<uint8_t> configData_;
> > > +};
> > > +
> > > +class RppX1Params : public V4L2Params<details::params_traits>
> > > +{
> > > +public:
> > > +	RppX1Params(std::span<uint8_t> 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.
> >
> > Please update this one :)
> > [...]
>
> So what should happen with the google copyright? It's effectively a copy.

Even if you still think it's a copy and want to retain it, you should
add the IoB one on top.

In linux, when this happens for example with sensor drivers what
usually happens is

        Copyright 2026 ...

        Based on someotherfile.c
        Copyright 2021 ...

Patch
diff mbox series

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 <libipa/algorithm.h>
+
+#include "module.h"
+
+namespace libcamera {
+
+namespace ipa::rppx1 {
+
+using Algorithm = libcamera::ipa::Algorithm<Module>;
+
+} /* 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 <memory>
+
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/geometry.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;
+
+	ControlInfoMap sensorControls;
+
+	/* Interface to the Camera Helper */
+	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 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 <linux/media/dreamchip/rppx1-config.h>
+
+#include <libcamera/ipa/rppx1_ipa_interface.h>
+
+#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/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 <linux/media/dreamchip/rppx1-config.h>
+
+#include <libipa/v4l2_params.h>
+
+namespace libcamera {
+
+namespace ipa::rppx1 {
+
+enum class BlockType : uint16_t {
+};
+
+namespace details {
+
+template<BlockType B>
+struct block_type {
+};
+
+#define RPPX1_DEFINE_BLOCK_TYPE(blkType, cfgType, id)			\
+	template<>							\
+	struct block_type<BlockType::blkType> {				\
+		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<id_type Id>
+	using id_to_details = block_type<Id>;
+};
+
+} /* namespace details */
+
+template<typename T>
+class RppX1ParamsBlock final : public V4L2ParamsBlock<T>
+{
+public:
+	RppX1ParamsBlock(const std::span<uint8_t> data)
+		: V4L2ParamsBlock<T>(data),
+		  configData_(data.subspan(sizeof(v4l2_isp_block_header)))
+	{
+	}
+
+	const T *operator->() const override
+	{
+		return reinterpret_cast<const T *>(configData_.data());
+	}
+
+	T *operator->() override
+	{
+		return reinterpret_cast<T *>(configData_.data());
+	}
+
+	const T &operator*() const override
+	{
+		return *reinterpret_cast<const T *>(configData_.data());
+	}
+
+	T &operator*() override
+	{
+		return *reinterpret_cast<T *>(configData_.data());
+	}
+
+private:
+	std::span<uint8_t> configData_;
+};
+
+class RppX1Params : public V4L2Params<details::params_traits>
+{
+public:
+	RppX1Params(std::span<uint8_t> 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 <map>
+#include <stdint.h>
+#include <string.h>
+#include <utility>
+
+#include <linux/v4l2-controls.h>
+
+#include <libcamera/base/file.h>
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/framebuffer.h>
+#include <libcamera/request.h>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libcamera/ipa/rppx1_ipa_interface.h>
+
+#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<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);
+	void setControls(unsigned int frame);
+
+	std::map<unsigned int, MappedFrameBuffer> 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<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, 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<Algorithm *>(a.get());
+
+		int ret = algo->configure(context_, context_.sensorInfo);
+		if (ret)
+			return ret;
+	}
+
+	updateControls(ipaControls);
+
+	return 0;
+}
+
+void IPARppX1::mapBuffers(const std::vector<IPABuffer> &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<unsigned int> &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<Algorithm *>(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, &params);
+
+	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<Algorithm *>(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 <linux/media/dreamchip/rppx1-config.h>
+
+#include <libipa/v4l2_stats.h>
+
+namespace libcamera {
+
+namespace ipa::rppx1 {
+
+enum class StatsType : uint16_t {
+};
+
+namespace details {
+
+template<StatsType B>
+struct stats_type {
+};
+
+#define RPPX1_DEFINE_STATS_TYPE(blkType, cfgType, id)			\
+	template<>							\
+	struct stats_type<StatsType::blkType> {				\
+		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<id_type Id>
+	using id_to_details = stats_type<Id>;
+};
+
+} /* namespace details */
+
+class RppX1Stats : public V4L2Stats<details::stats_traits>
+{
+public:
+	RppX1Stats(std::span<uint8_t> data)
+		: V4L2Stats(data, V4L2_ISP_VERSION_V1)
+	{
+	}
+};
+
+} /* namespace ipa::rppx1 */
+
+} /* namespace libcamera */