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

Message ID 20260918120949.191668-14-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: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Add the algorithm to the rppx1 ipa module based on the corresponding
algorithm in the rkisp1 ipa module.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 src/ipa/rppx1/algorithms/blc.cpp     | 111 +++++++++++++++++++++++++++
 src/ipa/rppx1/algorithms/blc.h       |  41 ++++++++++
 src/ipa/rppx1/algorithms/meson.build |   1 +
 src/ipa/rppx1/params.h               |   3 +
 4 files changed, 156 insertions(+)
 create mode 100644 src/ipa/rppx1/algorithms/blc.cpp
 create mode 100644 src/ipa/rppx1/algorithms/blc.h

Comments

Jacopo Mondi Sept. 24, 2026, 2:47 p.m. UTC | #1
On Fri, Sep 18, 2026 at 02:09:41PM +0200, Barnabás Pőcze wrote:
> From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>
> Add the algorithm to the rppx1 ipa module based on the corresponding
> algorithm in the rkisp1 ipa module.
>
> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Does it even make sense to have my tag here ?

> ---
>  src/ipa/rppx1/algorithms/blc.cpp     | 111 +++++++++++++++++++++++++++
>  src/ipa/rppx1/algorithms/blc.h       |  41 ++++++++++
>  src/ipa/rppx1/algorithms/meson.build |   1 +
>  src/ipa/rppx1/params.h               |   3 +
>  4 files changed, 156 insertions(+)
>  create mode 100644 src/ipa/rppx1/algorithms/blc.cpp
>  create mode 100644 src/ipa/rppx1/algorithms/blc.h
>
> diff --git a/src/ipa/rppx1/algorithms/blc.cpp b/src/ipa/rppx1/algorithms/blc.cpp
> new file mode 100644
> index 0000000000..a56b834fb6
> --- /dev/null
> +++ b/src/ipa/rppx1/algorithms/blc.cpp
> @@ -0,0 +1,111 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2026, Ideas On Board
> + *
> + * RPP-X1 Black Level Correction control
> + */
> +
> +#include "blc.h"
> +
> +#include <libcamera/base/log.h>
> +
> +#include <libcamera/control_ids.h>
> +
> +#include "libcamera/internal/value_node.h"
> +
> +/**
> + * \file blc.h
> + */
> +
> +namespace libcamera {
> +
> +namespace ipa::rppx1::algorithms {
> +
> +LOG_DEFINE_CATEGORY(RppX1Blc)
> +
> +/**
> + * \copydoc libcamera::ipa::Algorithm::init
> + */
> +int BlackLevelCorrection::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData)
> +{
> +	auto blackLevel = context.camHelper->blackLevel();

Do you this is too strict or is it fine ? Other pipelines like rkisp1
support parsing black level from tuning file...

As we guarantee an helper for the sensor is present, I would be fine
keeping the implementation as it is now

Thanks
  j

> +	if (!blackLevel) {
> +		LOG(RppX1Blc, Error)
> +			<< "No black levels provided by camera sensor helper";
> +		return -ENOTSUP;
> +	}
> +
> +	blackLevelRed_ = *blackLevel;
> +	blackLevelGreenR_ = *blackLevel;
> +	blackLevelGreenB_ = *blackLevel;
> +	blackLevelBlue_ = *blackLevel;
> +
> +	LOG(RppX1Blc, Debug)
> +		<< "Black levels: red " << blackLevelRed_
> +		<< ", green (red) " << blackLevelGreenR_
> +		<< ", green (blue) " << blackLevelGreenB_
> +		<< ", blue " << blackLevelBlue_;
> +
> +	return 0;
> +}
> +
> +int BlackLevelCorrection::configure([[maybe_unused]] IPAContext &context,
> +				    [[maybe_unused]] const IPACameraSensorInfo &configInfo)
> +{
> +	return 0;
> +}
> +
> +/**
> + * \copydoc libcamera::ipa::Algorithm::prepare
> + */
> +void BlackLevelCorrection::prepare([[maybe_unused]] IPAContext &context,
> +				   const uint32_t frame,
> +				   [[maybe_unused]] IPAFrameContext &frameContext,
> +				   RppX1Params *params)
> +{
> +	if (frame > 0)
> +		return;
> +
> +	auto config = params->block<BlockType::BlsPre1>();
> +	config.setEnabled(true);
> +
> +	config->mode = RPPX1_BLS_MODE_FIXED;
> +	config->en_windows = RPPX1_BLS_WIN_EN_OFF;
> +
> +	/*
> +	 * RPP-X1: the rppx1 specifies fixed BLS values per-color component
> +	 * matching on the input port cropping configuration.
> +	 * We have the same BLS value for all channels, so this is not relevant
> +	 * for now, but should probably be handled properly.
> +	 *
> +	 * Black level values are 16 bits, scale to the RPP-X1 PRE1 pipe
> +	 * 24-bit-domain.
> +	 */
> +
> +	config->fixed.a = static_cast<int32_t>(blackLevelRed_) << 8;
> +	config->fixed.b = static_cast<int32_t>(blackLevelGreenR_) << 8;
> +	config->fixed.c = static_cast<int32_t>(blackLevelGreenB_) << 8;
> +	config->fixed.d = static_cast<int32_t>(blackLevelBlue_) << 8;
> +}
> +
> +/**
> + * \copydoc libcamera::ipa::Algorithm::process
> + */
> +void BlackLevelCorrection::process([[maybe_unused]] IPAContext &context,
> +				   [[maybe_unused]] const uint32_t frame,
> +				   [[maybe_unused]] IPAFrameContext &frameContext,
> +				   [[maybe_unused]] const RppX1Stats *stats,
> +				   ControlList &metadata)
> +{
> +	metadata.set(controls::SensorBlackLevels,
> +		     { static_cast<int32_t>(blackLevelRed_),
> +		       static_cast<int32_t>(blackLevelGreenR_),
> +		       static_cast<int32_t>(blackLevelGreenB_),
> +		       static_cast<int32_t>(blackLevelBlue_) });
> +}
> +
> +REGISTER_IPA_ALGORITHM(BlackLevelCorrection, "BlackLevelCorrection")
> +
> +} /* namespace ipa::rppx1::algorithms */
> +
> +} /* namespace libcamera */
> diff --git a/src/ipa/rppx1/algorithms/blc.h b/src/ipa/rppx1/algorithms/blc.h
> new file mode 100644
> index 0000000000..3752166d78
> --- /dev/null
> +++ b/src/ipa/rppx1/algorithms/blc.h
> @@ -0,0 +1,41 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2026, Ideas On Board
> + *
> + * RPP-X1 Black Level Correction control
> + */
> +
> +#pragma once
> +
> +#include <stdint.h>
> +
> +#include "algorithm.h"
> +
> +namespace libcamera {
> +
> +namespace ipa::rppx1::algorithms {
> +
> +class BlackLevelCorrection : public Algorithm
> +{
> +public:
> +	int init(IPAContext &context, const ValueNode &tuningData) override;
> +	int configure(IPAContext &context,
> +		      const IPACameraSensorInfo &configInfo) override;
> +	void prepare(IPAContext &context, const uint32_t frame,
> +		     IPAFrameContext &frameContext,
> +		     RppX1Params *params) override;
> +	void process(IPAContext &context, const uint32_t frame,
> +		     IPAFrameContext &frameContext,
> +		     const RppX1Stats *stats,
> +		     ControlList &metadata) override;
> +
> +private:
> +	int16_t blackLevelRed_;
> +	int16_t blackLevelGreenR_;
> +	int16_t blackLevelGreenB_;
> +	int16_t blackLevelBlue_;
> +};
> +
> +} /* namespace ipa::rppx1::algorithms */
> +
> +} /* namespace libcamera */
> diff --git a/src/ipa/rppx1/algorithms/meson.build b/src/ipa/rppx1/algorithms/meson.build
> index 5fed9e9d50..165f0ce054 100644
> --- a/src/ipa/rppx1/algorithms/meson.build
> +++ b/src/ipa/rppx1/algorithms/meson.build
> @@ -1,4 +1,5 @@
>  # SPDX-License-Identifier: CC0-1.0
>
>  rppx1_ipa_algorithms = files([
> +    'blc.cpp',
>  ])
> diff --git a/src/ipa/rppx1/params.h b/src/ipa/rppx1/params.h
> index 315c8a9785..05db309914 100644
> --- a/src/ipa/rppx1/params.h
> +++ b/src/ipa/rppx1/params.h
> @@ -16,6 +16,7 @@ namespace libcamera {
>  namespace ipa::rppx1 {
>
>  enum class BlockType : uint16_t {
> +	BlsPre1,
>  };
>
>  namespace details {
> @@ -32,6 +33,8 @@ struct block_type {
>  			RPPX1_PARAMS_BLOCK_TYPE_##id;			\
>  	};
>
> +RPPX1_DEFINE_BLOCK_TYPE(BlsPre1, bls, BLS_PRE1)
> +
>  struct params_traits {
>  	using id_type = BlockType;
>
> --
> 2.55.0
>
Barnabás Pőcze Sept. 24, 2026, 3:09 p.m. UTC | #2
2026. 09. 24. 16:47 keltezéssel, Jacopo Mondi írta:
> On Fri, Sep 18, 2026 at 02:09:41PM +0200, Barnabás Pőcze wrote:
>> From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>>
>> Add the algorithm to the rppx1 ipa module based on the corresponding
>> algorithm in the rkisp1 ipa module.
>>
>> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> 
> Does it even make sense to have my tag here ?

Well, this is effectively from the commit that I based this on,
I don't really care either way.


> 
>> ---
>>   src/ipa/rppx1/algorithms/blc.cpp     | 111 +++++++++++++++++++++++++++
>>   src/ipa/rppx1/algorithms/blc.h       |  41 ++++++++++
>>   src/ipa/rppx1/algorithms/meson.build |   1 +
>>   src/ipa/rppx1/params.h               |   3 +
>>   4 files changed, 156 insertions(+)
>>   create mode 100644 src/ipa/rppx1/algorithms/blc.cpp
>>   create mode 100644 src/ipa/rppx1/algorithms/blc.h
>>
>> diff --git a/src/ipa/rppx1/algorithms/blc.cpp b/src/ipa/rppx1/algorithms/blc.cpp
>> new file mode 100644
>> index 0000000000..a56b834fb6
>> --- /dev/null
>> +++ b/src/ipa/rppx1/algorithms/blc.cpp
>> @@ -0,0 +1,111 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2026, Ideas On Board
>> + *
>> + * RPP-X1 Black Level Correction control
>> + */
>> +
>> +#include "blc.h"
>> +
>> +#include <libcamera/base/log.h>
>> +
>> +#include <libcamera/control_ids.h>
>> +
>> +#include "libcamera/internal/value_node.h"
>> +
>> +/**
>> + * \file blc.h
>> + */
>> +
>> +namespace libcamera {
>> +
>> +namespace ipa::rppx1::algorithms {
>> +
>> +LOG_DEFINE_CATEGORY(RppX1Blc)
>> +
>> +/**
>> + * \copydoc libcamera::ipa::Algorithm::init
>> + */
>> +int BlackLevelCorrection::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData)
>> +{
>> +	auto blackLevel = context.camHelper->blackLevel();
> 
> Do you this is too strict or is it fine ? Other pipelines like rkisp1
> support parsing black level from tuning file...

My reading of the rkisp1 code suggested that using the tuning file is
effectively considered a deprecated feature, so I didn't want to copy it.


> 
> As we guarantee an helper for the sensor is present, I would be fine
> keeping the implementation as it is now
> 
> Thanks
>    j
> 
>> +	if (!blackLevel) {
>> +		LOG(RppX1Blc, Error)
>> +			<< "No black levels provided by camera sensor helper";
>> +		return -ENOTSUP;
>> +	}
>> +
>> +	blackLevelRed_ = *blackLevel;
>> +	blackLevelGreenR_ = *blackLevel;
>> +	blackLevelGreenB_ = *blackLevel;
>> +	blackLevelBlue_ = *blackLevel;
>> +
>> +	LOG(RppX1Blc, Debug)
>> +		<< "Black levels: red " << blackLevelRed_
>> +		<< ", green (red) " << blackLevelGreenR_
>> +		<< ", green (blue) " << blackLevelGreenB_
>> +		<< ", blue " << blackLevelBlue_;
>> +
>> +	return 0;
>> +}
>> +
>> +int BlackLevelCorrection::configure([[maybe_unused]] IPAContext &context,
>> +				    [[maybe_unused]] const IPACameraSensorInfo &configInfo)
>> +{
>> +	return 0;
>> +}
>> +
>> +/**
>> + * \copydoc libcamera::ipa::Algorithm::prepare
>> + */
>> +void BlackLevelCorrection::prepare([[maybe_unused]] IPAContext &context,
>> +				   const uint32_t frame,
>> +				   [[maybe_unused]] IPAFrameContext &frameContext,
>> +				   RppX1Params *params)
>> +{
>> +	if (frame > 0)
>> +		return;
>> +
>> +	auto config = params->block<BlockType::BlsPre1>();
>> +	config.setEnabled(true);
>> +
>> +	config->mode = RPPX1_BLS_MODE_FIXED;
>> +	config->en_windows = RPPX1_BLS_WIN_EN_OFF;
>> +
>> +	/*
>> +	 * RPP-X1: the rppx1 specifies fixed BLS values per-color component
>> +	 * matching on the input port cropping configuration.
>> +	 * We have the same BLS value for all channels, so this is not relevant
>> +	 * for now, but should probably be handled properly.
>> +	 *
>> +	 * Black level values are 16 bits, scale to the RPP-X1 PRE1 pipe
>> +	 * 24-bit-domain.
>> +	 */
>> +
>> +	config->fixed.a = static_cast<int32_t>(blackLevelRed_) << 8;
>> +	config->fixed.b = static_cast<int32_t>(blackLevelGreenR_) << 8;
>> +	config->fixed.c = static_cast<int32_t>(blackLevelGreenB_) << 8;
>> +	config->fixed.d = static_cast<int32_t>(blackLevelBlue_) << 8;
>> +}
>> +
>> +/**
>> + * \copydoc libcamera::ipa::Algorithm::process
>> + */
>> +void BlackLevelCorrection::process([[maybe_unused]] IPAContext &context,
>> +				   [[maybe_unused]] const uint32_t frame,
>> +				   [[maybe_unused]] IPAFrameContext &frameContext,
>> +				   [[maybe_unused]] const RppX1Stats *stats,
>> +				   ControlList &metadata)
>> +{
>> +	metadata.set(controls::SensorBlackLevels,
>> +		     { static_cast<int32_t>(blackLevelRed_),
>> +		       static_cast<int32_t>(blackLevelGreenR_),
>> +		       static_cast<int32_t>(blackLevelGreenB_),
>> +		       static_cast<int32_t>(blackLevelBlue_) });
>> +}
>> +
>> +REGISTER_IPA_ALGORITHM(BlackLevelCorrection, "BlackLevelCorrection")
>> +
>> +} /* namespace ipa::rppx1::algorithms */
>> +
>> +} /* namespace libcamera */
>> diff --git a/src/ipa/rppx1/algorithms/blc.h b/src/ipa/rppx1/algorithms/blc.h
>> new file mode 100644
>> index 0000000000..3752166d78
>> --- /dev/null
>> +++ b/src/ipa/rppx1/algorithms/blc.h
>> @@ -0,0 +1,41 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2026, Ideas On Board
>> + *
>> + * RPP-X1 Black Level Correction control
>> + */
>> +
>> +#pragma once
>> +
>> +#include <stdint.h>
>> +
>> +#include "algorithm.h"
>> +
>> +namespace libcamera {
>> +
>> +namespace ipa::rppx1::algorithms {
>> +
>> +class BlackLevelCorrection : public Algorithm
>> +{
>> +public:
>> +	int init(IPAContext &context, const ValueNode &tuningData) override;
>> +	int configure(IPAContext &context,
>> +		      const IPACameraSensorInfo &configInfo) override;
>> +	void prepare(IPAContext &context, const uint32_t frame,
>> +		     IPAFrameContext &frameContext,
>> +		     RppX1Params *params) override;
>> +	void process(IPAContext &context, const uint32_t frame,
>> +		     IPAFrameContext &frameContext,
>> +		     const RppX1Stats *stats,
>> +		     ControlList &metadata) override;
>> +
>> +private:
>> +	int16_t blackLevelRed_;
>> +	int16_t blackLevelGreenR_;
>> +	int16_t blackLevelGreenB_;
>> +	int16_t blackLevelBlue_;
>> +};
>> +
>> +} /* namespace ipa::rppx1::algorithms */
>> +
>> +} /* namespace libcamera */
>> diff --git a/src/ipa/rppx1/algorithms/meson.build b/src/ipa/rppx1/algorithms/meson.build
>> index 5fed9e9d50..165f0ce054 100644
>> --- a/src/ipa/rppx1/algorithms/meson.build
>> +++ b/src/ipa/rppx1/algorithms/meson.build
>> @@ -1,4 +1,5 @@
>>   # SPDX-License-Identifier: CC0-1.0
>>
>>   rppx1_ipa_algorithms = files([
>> +    'blc.cpp',
>>   ])
>> diff --git a/src/ipa/rppx1/params.h b/src/ipa/rppx1/params.h
>> index 315c8a9785..05db309914 100644
>> --- a/src/ipa/rppx1/params.h
>> +++ b/src/ipa/rppx1/params.h
>> @@ -16,6 +16,7 @@ namespace libcamera {
>>   namespace ipa::rppx1 {
>>
>>   enum class BlockType : uint16_t {
>> +	BlsPre1,
>>   };
>>
>>   namespace details {
>> @@ -32,6 +33,8 @@ struct block_type {
>>   			RPPX1_PARAMS_BLOCK_TYPE_##id;			\
>>   	};
>>
>> +RPPX1_DEFINE_BLOCK_TYPE(BlsPre1, bls, BLS_PRE1)
>> +
>>   struct params_traits {
>>   	using id_type = BlockType;
>>
>> --
>> 2.55.0
>>
Jacopo Mondi Sept. 24, 2026, 4:10 p.m. UTC | #3
On Thu, Sep 24, 2026 at 05:09:49PM +0200, Barnabás Pőcze wrote:
> 2026. 09. 24. 16:47 keltezéssel, Jacopo Mondi írta:
> > On Fri, Sep 18, 2026 at 02:09:41PM +0200, Barnabás Pőcze wrote:
> > > From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > >
> > > Add the algorithm to the rppx1 ipa module based on the corresponding
> > > algorithm in the rkisp1 ipa module.
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> >
> > Does it even make sense to have my tag here ?
>
> Well, this is effectively from the commit that I based this on,
> I don't really care either way.

Sorry, I wasn't clear.

What I meant is if it made any sense for me to add a Reviewed-by tag
to my own patches :)

>
>
> >
> > > ---
> > >   src/ipa/rppx1/algorithms/blc.cpp     | 111 +++++++++++++++++++++++++++
> > >   src/ipa/rppx1/algorithms/blc.h       |  41 ++++++++++
> > >   src/ipa/rppx1/algorithms/meson.build |   1 +
> > >   src/ipa/rppx1/params.h               |   3 +
> > >   4 files changed, 156 insertions(+)
> > >   create mode 100644 src/ipa/rppx1/algorithms/blc.cpp
> > >   create mode 100644 src/ipa/rppx1/algorithms/blc.h
> > >
> > > diff --git a/src/ipa/rppx1/algorithms/blc.cpp b/src/ipa/rppx1/algorithms/blc.cpp
> > > new file mode 100644
> > > index 0000000000..a56b834fb6
> > > --- /dev/null
> > > +++ b/src/ipa/rppx1/algorithms/blc.cpp
> > > @@ -0,0 +1,111 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2026, Ideas On Board
> > > + *
> > > + * RPP-X1 Black Level Correction control
> > > + */
> > > +
> > > +#include "blc.h"
> > > +
> > > +#include <libcamera/base/log.h>
> > > +
> > > +#include <libcamera/control_ids.h>
> > > +
> > > +#include "libcamera/internal/value_node.h"
> > > +
> > > +/**
> > > + * \file blc.h
> > > + */
> > > +
> > > +namespace libcamera {
> > > +
> > > +namespace ipa::rppx1::algorithms {
> > > +
> > > +LOG_DEFINE_CATEGORY(RppX1Blc)
> > > +
> > > +/**
> > > + * \copydoc libcamera::ipa::Algorithm::init
> > > + */
> > > +int BlackLevelCorrection::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData)
> > > +{
> > > +	auto blackLevel = context.camHelper->blackLevel();
> >
> > Do you this is too strict or is it fine ? Other pipelines like rkisp1
> > support parsing black level from tuning file...
>
> My reading of the rkisp1 code suggested that using the tuning file is
> effectively considered a deprecated feature, so I didn't want to copy it.
>
>
> >
> > As we guarantee an helper for the sensor is present, I would be fine
> > keeping the implementation as it is now
> >
> > Thanks
> >    j
> >
> > > +	if (!blackLevel) {
> > > +		LOG(RppX1Blc, Error)
> > > +			<< "No black levels provided by camera sensor helper";
> > > +		return -ENOTSUP;
> > > +	}
> > > +
> > > +	blackLevelRed_ = *blackLevel;
> > > +	blackLevelGreenR_ = *blackLevel;
> > > +	blackLevelGreenB_ = *blackLevel;
> > > +	blackLevelBlue_ = *blackLevel;
> > > +
> > > +	LOG(RppX1Blc, Debug)
> > > +		<< "Black levels: red " << blackLevelRed_
> > > +		<< ", green (red) " << blackLevelGreenR_
> > > +		<< ", green (blue) " << blackLevelGreenB_
> > > +		<< ", blue " << blackLevelBlue_;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +int BlackLevelCorrection::configure([[maybe_unused]] IPAContext &context,
> > > +				    [[maybe_unused]] const IPACameraSensorInfo &configInfo)
> > > +{
> > > +	return 0;
> > > +}
> > > +
> > > +/**
> > > + * \copydoc libcamera::ipa::Algorithm::prepare
> > > + */
> > > +void BlackLevelCorrection::prepare([[maybe_unused]] IPAContext &context,
> > > +				   const uint32_t frame,
> > > +				   [[maybe_unused]] IPAFrameContext &frameContext,
> > > +				   RppX1Params *params)
> > > +{
> > > +	if (frame > 0)
> > > +		return;
> > > +
> > > +	auto config = params->block<BlockType::BlsPre1>();
> > > +	config.setEnabled(true);
> > > +
> > > +	config->mode = RPPX1_BLS_MODE_FIXED;
> > > +	config->en_windows = RPPX1_BLS_WIN_EN_OFF;
> > > +
> > > +	/*
> > > +	 * RPP-X1: the rppx1 specifies fixed BLS values per-color component
> > > +	 * matching on the input port cropping configuration.
> > > +	 * We have the same BLS value for all channels, so this is not relevant
> > > +	 * for now, but should probably be handled properly.
> > > +	 *
> > > +	 * Black level values are 16 bits, scale to the RPP-X1 PRE1 pipe
> > > +	 * 24-bit-domain.
> > > +	 */
> > > +
> > > +	config->fixed.a = static_cast<int32_t>(blackLevelRed_) << 8;
> > > +	config->fixed.b = static_cast<int32_t>(blackLevelGreenR_) << 8;
> > > +	config->fixed.c = static_cast<int32_t>(blackLevelGreenB_) << 8;
> > > +	config->fixed.d = static_cast<int32_t>(blackLevelBlue_) << 8;
> > > +}
> > > +
> > > +/**
> > > + * \copydoc libcamera::ipa::Algorithm::process
> > > + */
> > > +void BlackLevelCorrection::process([[maybe_unused]] IPAContext &context,
> > > +				   [[maybe_unused]] const uint32_t frame,
> > > +				   [[maybe_unused]] IPAFrameContext &frameContext,
> > > +				   [[maybe_unused]] const RppX1Stats *stats,
> > > +				   ControlList &metadata)
> > > +{
> > > +	metadata.set(controls::SensorBlackLevels,
> > > +		     { static_cast<int32_t>(blackLevelRed_),
> > > +		       static_cast<int32_t>(blackLevelGreenR_),
> > > +		       static_cast<int32_t>(blackLevelGreenB_),
> > > +		       static_cast<int32_t>(blackLevelBlue_) });
> > > +}
> > > +
> > > +REGISTER_IPA_ALGORITHM(BlackLevelCorrection, "BlackLevelCorrection")
> > > +
> > > +} /* namespace ipa::rppx1::algorithms */
> > > +
> > > +} /* namespace libcamera */
> > > diff --git a/src/ipa/rppx1/algorithms/blc.h b/src/ipa/rppx1/algorithms/blc.h
> > > new file mode 100644
> > > index 0000000000..3752166d78
> > > --- /dev/null
> > > +++ b/src/ipa/rppx1/algorithms/blc.h
> > > @@ -0,0 +1,41 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2026, Ideas On Board
> > > + *
> > > + * RPP-X1 Black Level Correction control
> > > + */
> > > +
> > > +#pragma once
> > > +
> > > +#include <stdint.h>
> > > +
> > > +#include "algorithm.h"
> > > +
> > > +namespace libcamera {
> > > +
> > > +namespace ipa::rppx1::algorithms {
> > > +
> > > +class BlackLevelCorrection : public Algorithm
> > > +{
> > > +public:
> > > +	int init(IPAContext &context, const ValueNode &tuningData) override;
> > > +	int configure(IPAContext &context,
> > > +		      const IPACameraSensorInfo &configInfo) override;
> > > +	void prepare(IPAContext &context, const uint32_t frame,
> > > +		     IPAFrameContext &frameContext,
> > > +		     RppX1Params *params) override;
> > > +	void process(IPAContext &context, const uint32_t frame,
> > > +		     IPAFrameContext &frameContext,
> > > +		     const RppX1Stats *stats,
> > > +		     ControlList &metadata) override;
> > > +
> > > +private:
> > > +	int16_t blackLevelRed_;
> > > +	int16_t blackLevelGreenR_;
> > > +	int16_t blackLevelGreenB_;
> > > +	int16_t blackLevelBlue_;
> > > +};
> > > +
> > > +} /* namespace ipa::rppx1::algorithms */
> > > +
> > > +} /* namespace libcamera */
> > > diff --git a/src/ipa/rppx1/algorithms/meson.build b/src/ipa/rppx1/algorithms/meson.build
> > > index 5fed9e9d50..165f0ce054 100644
> > > --- a/src/ipa/rppx1/algorithms/meson.build
> > > +++ b/src/ipa/rppx1/algorithms/meson.build
> > > @@ -1,4 +1,5 @@
> > >   # SPDX-License-Identifier: CC0-1.0
> > >
> > >   rppx1_ipa_algorithms = files([
> > > +    'blc.cpp',
> > >   ])
> > > diff --git a/src/ipa/rppx1/params.h b/src/ipa/rppx1/params.h
> > > index 315c8a9785..05db309914 100644
> > > --- a/src/ipa/rppx1/params.h
> > > +++ b/src/ipa/rppx1/params.h
> > > @@ -16,6 +16,7 @@ namespace libcamera {
> > >   namespace ipa::rppx1 {
> > >
> > >   enum class BlockType : uint16_t {
> > > +	BlsPre1,
> > >   };
> > >
> > >   namespace details {
> > > @@ -32,6 +33,8 @@ struct block_type {
> > >   			RPPX1_PARAMS_BLOCK_TYPE_##id;			\
> > >   	};
> > >
> > > +RPPX1_DEFINE_BLOCK_TYPE(BlsPre1, bls, BLS_PRE1)
> > > +
> > >   struct params_traits {
> > >   	using id_type = BlockType;
> > >
> > > --
> > > 2.55.0
> > >
>

Patch
diff mbox series

diff --git a/src/ipa/rppx1/algorithms/blc.cpp b/src/ipa/rppx1/algorithms/blc.cpp
new file mode 100644
index 0000000000..a56b834fb6
--- /dev/null
+++ b/src/ipa/rppx1/algorithms/blc.cpp
@@ -0,0 +1,111 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Ideas On Board
+ *
+ * RPP-X1 Black Level Correction control
+ */
+
+#include "blc.h"
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+
+#include "libcamera/internal/value_node.h"
+
+/**
+ * \file blc.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rppx1::algorithms {
+
+LOG_DEFINE_CATEGORY(RppX1Blc)
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int BlackLevelCorrection::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData)
+{
+	auto blackLevel = context.camHelper->blackLevel();
+	if (!blackLevel) {
+		LOG(RppX1Blc, Error)
+			<< "No black levels provided by camera sensor helper";
+		return -ENOTSUP;
+	}
+
+	blackLevelRed_ = *blackLevel;
+	blackLevelGreenR_ = *blackLevel;
+	blackLevelGreenB_ = *blackLevel;
+	blackLevelBlue_ = *blackLevel;
+
+	LOG(RppX1Blc, Debug)
+		<< "Black levels: red " << blackLevelRed_
+		<< ", green (red) " << blackLevelGreenR_
+		<< ", green (blue) " << blackLevelGreenB_
+		<< ", blue " << blackLevelBlue_;
+
+	return 0;
+}
+
+int BlackLevelCorrection::configure([[maybe_unused]] IPAContext &context,
+				    [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+	return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void BlackLevelCorrection::prepare([[maybe_unused]] IPAContext &context,
+				   const uint32_t frame,
+				   [[maybe_unused]] IPAFrameContext &frameContext,
+				   RppX1Params *params)
+{
+	if (frame > 0)
+		return;
+
+	auto config = params->block<BlockType::BlsPre1>();
+	config.setEnabled(true);
+
+	config->mode = RPPX1_BLS_MODE_FIXED;
+	config->en_windows = RPPX1_BLS_WIN_EN_OFF;
+
+	/*
+	 * RPP-X1: the rppx1 specifies fixed BLS values per-color component
+	 * matching on the input port cropping configuration.
+	 * We have the same BLS value for all channels, so this is not relevant
+	 * for now, but should probably be handled properly.
+	 *
+	 * Black level values are 16 bits, scale to the RPP-X1 PRE1 pipe
+	 * 24-bit-domain.
+	 */
+
+	config->fixed.a = static_cast<int32_t>(blackLevelRed_) << 8;
+	config->fixed.b = static_cast<int32_t>(blackLevelGreenR_) << 8;
+	config->fixed.c = static_cast<int32_t>(blackLevelGreenB_) << 8;
+	config->fixed.d = static_cast<int32_t>(blackLevelBlue_) << 8;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void BlackLevelCorrection::process([[maybe_unused]] IPAContext &context,
+				   [[maybe_unused]] const uint32_t frame,
+				   [[maybe_unused]] IPAFrameContext &frameContext,
+				   [[maybe_unused]] const RppX1Stats *stats,
+				   ControlList &metadata)
+{
+	metadata.set(controls::SensorBlackLevels,
+		     { static_cast<int32_t>(blackLevelRed_),
+		       static_cast<int32_t>(blackLevelGreenR_),
+		       static_cast<int32_t>(blackLevelGreenB_),
+		       static_cast<int32_t>(blackLevelBlue_) });
+}
+
+REGISTER_IPA_ALGORITHM(BlackLevelCorrection, "BlackLevelCorrection")
+
+} /* namespace ipa::rppx1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rppx1/algorithms/blc.h b/src/ipa/rppx1/algorithms/blc.h
new file mode 100644
index 0000000000..3752166d78
--- /dev/null
+++ b/src/ipa/rppx1/algorithms/blc.h
@@ -0,0 +1,41 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Ideas On Board
+ *
+ * RPP-X1 Black Level Correction control
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::rppx1::algorithms {
+
+class BlackLevelCorrection : public Algorithm
+{
+public:
+	int init(IPAContext &context, const ValueNode &tuningData) override;
+	int configure(IPAContext &context,
+		      const IPACameraSensorInfo &configInfo) override;
+	void prepare(IPAContext &context, const uint32_t frame,
+		     IPAFrameContext &frameContext,
+		     RppX1Params *params) override;
+	void process(IPAContext &context, const uint32_t frame,
+		     IPAFrameContext &frameContext,
+		     const RppX1Stats *stats,
+		     ControlList &metadata) override;
+
+private:
+	int16_t blackLevelRed_;
+	int16_t blackLevelGreenR_;
+	int16_t blackLevelGreenB_;
+	int16_t blackLevelBlue_;
+};
+
+} /* namespace ipa::rppx1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rppx1/algorithms/meson.build b/src/ipa/rppx1/algorithms/meson.build
index 5fed9e9d50..165f0ce054 100644
--- a/src/ipa/rppx1/algorithms/meson.build
+++ b/src/ipa/rppx1/algorithms/meson.build
@@ -1,4 +1,5 @@ 
 # SPDX-License-Identifier: CC0-1.0
 
 rppx1_ipa_algorithms = files([
+    'blc.cpp',
 ])
diff --git a/src/ipa/rppx1/params.h b/src/ipa/rppx1/params.h
index 315c8a9785..05db309914 100644
--- a/src/ipa/rppx1/params.h
+++ b/src/ipa/rppx1/params.h
@@ -16,6 +16,7 @@  namespace libcamera {
 namespace ipa::rppx1 {
 
 enum class BlockType : uint16_t {
+	BlsPre1,
 };
 
 namespace details {
@@ -32,6 +33,8 @@  struct block_type {
 			RPPX1_PARAMS_BLOCK_TYPE_##id;			\
 	};
 
+RPPX1_DEFINE_BLOCK_TYPE(BlsPre1, bls, BLS_PRE1)
+
 struct params_traits {
 	using id_type = BlockType;