ipa: rpi: pisp: Add decompand support using PiSP hardware block
diff mbox series

Message ID 20251001091143.2051905-1-naush@raspberrypi.com
State Rejected
Headers show
Series
  • ipa: rpi: pisp: Add decompand support using PiSP hardware block
Related show

Commit Message

Naushir Patuck Oct. 1, 2025, 9:11 a.m. UTC
From: Sena Asotani <aso.fam429@gmail.com>

This patch integrates a new decompand algorithm that utilizes the PiSP
FE hardware block available on Raspberry Pi 5. The implementation
enables conversion of companded sensor data into linear format prior to
ISP processing.

Changes include:
- Implementation of decompand logic for controller and pipe interfaces
- Enabling decompand block by "rpi.decompand" in tuning.json

Signed-off-by: Sena Asotani <aso.fam429@gmail.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
---
 src/ipa/rpi/controller/decompand_status.h |  8 ++++
 src/ipa/rpi/controller/meson.build        |  1 +
 src/ipa/rpi/controller/rpi/decompand.cpp  | 58 +++++++++++++++++++++++
 src/ipa/rpi/controller/rpi/decompand.h    | 31 ++++++++++++
 src/ipa/rpi/pisp/pisp.cpp                 | 40 ++++++++++++++++
 5 files changed, 138 insertions(+)
 create mode 100644 src/ipa/rpi/controller/decompand_status.h
 create mode 100644 src/ipa/rpi/controller/rpi/decompand.cpp
 create mode 100644 src/ipa/rpi/controller/rpi/decompand.h

Comments

Naushir Patuck Oct. 1, 2025, 11:40 a.m. UTC | #1
Hi all,

Please disregard this patch for the time being, we want to add another
feature before reviewing and merging.

Regards,
Naush


On Wed, 1 Oct 2025 at 10:11, Naushir Patuck <naush@raspberrypi.com> wrote:

> From: Sena Asotani <aso.fam429@gmail.com>
>
> This patch integrates a new decompand algorithm that utilizes the PiSP
> FE hardware block available on Raspberry Pi 5. The implementation
> enables conversion of companded sensor data into linear format prior to
> ISP processing.
>
> Changes include:
> - Implementation of decompand logic for controller and pipe interfaces
> - Enabling decompand block by "rpi.decompand" in tuning.json
>
> Signed-off-by: Sena Asotani <aso.fam429@gmail.com>
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> ---
>  src/ipa/rpi/controller/decompand_status.h |  8 ++++
>  src/ipa/rpi/controller/meson.build        |  1 +
>  src/ipa/rpi/controller/rpi/decompand.cpp  | 58 +++++++++++++++++++++++
>  src/ipa/rpi/controller/rpi/decompand.h    | 31 ++++++++++++
>  src/ipa/rpi/pisp/pisp.cpp                 | 40 ++++++++++++++++
>  5 files changed, 138 insertions(+)
>  create mode 100644 src/ipa/rpi/controller/decompand_status.h
>  create mode 100644 src/ipa/rpi/controller/rpi/decompand.cpp
>  create mode 100644 src/ipa/rpi/controller/rpi/decompand.h
>
> diff --git a/src/ipa/rpi/controller/decompand_status.h
> b/src/ipa/rpi/controller/decompand_status.h
> new file mode 100644
> index 000000000000..2d9888dca4f3
> --- /dev/null
> +++ b/src/ipa/rpi/controller/decompand_status.h
> @@ -0,0 +1,8 @@
> +#pragma once
> +
> +#include "libipa/pwl.h"
> +
> +struct DecompandStatus {
> +       uint32_t bitdepth;
> +       libcamera::ipa::Pwl decompandCurve;
> +};
> diff --git a/src/ipa/rpi/controller/meson.build
> b/src/ipa/rpi/controller/meson.build
> index 74b74888bbff..c13c48539d10 100644
> --- a/src/ipa/rpi/controller/meson.build
> +++ b/src/ipa/rpi/controller/meson.build
> @@ -14,6 +14,7 @@ rpi_ipa_controller_sources = files([
>      'rpi/cac.cpp',
>      'rpi/ccm.cpp',
>      'rpi/contrast.cpp',
> +    'rpi/decompand.cpp',
>      'rpi/denoise.cpp',
>      'rpi/dpc.cpp',
>      'rpi/geq.cpp',
> diff --git a/src/ipa/rpi/controller/rpi/decompand.cpp
> b/src/ipa/rpi/controller/rpi/decompand.cpp
> new file mode 100644
> index 000000000000..911b04bc0da0
> --- /dev/null
> +++ b/src/ipa/rpi/controller/rpi/decompand.cpp
> @@ -0,0 +1,58 @@
> +#include "decompand.h"
> +
> +#include <libcamera/base/log.h>
> +
> +#include "../decompand_status.h"
> +#include "../histogram.h"
> +
> +using namespace RPiController;
> +using namespace libcamera;
> +
> +LOG_DEFINE_CATEGORY(RPiDecompand)
> +
> +#define NAME "rpi.decompand"
> +
> +Decompand::Decompand(Controller *controller)
> +       : Algorithm(controller)
> +{
> +}
> +
> +char const *Decompand::name() const
> +{
> +       return NAME;
> +}
> +
> +int Decompand::read(const libcamera::YamlObject &params)
> +{
> +       config_.bitdepth = params["bitdepth"].get<uint32_t>(0);
> +       config_.decompandCurve =
> params["decompand_curve"].get<ipa::Pwl>(ipa::Pwl{});
> +       return config_.decompandCurve.empty() ? -EINVAL : 0;
> +}
> +
> +void Decompand::initialise()
> +{
> +}
> +
> +void Decompand::switchMode([[maybe_unused]] CameraMode const &cameraMode,
> +                          [[maybe_unused]] Metadata *metadata)
> +{
> +       mode_ = cameraMode;
> +}
> +
> +void Decompand::prepare(Metadata *imageMetadata)
> +{
> +       DecompandStatus decompandStatus;
> +
> +       if (config_.bitdepth == 0 || mode_.bitdepth == config_.bitdepth) {
> +               decompandStatus.decompandCurve = config_.decompandCurve;
> +               imageMetadata->set("decompand.status", decompandStatus);
> +       }
> +}
> +
> +/* Register algorithm with the system. */
> +static Algorithm *create(Controller *controller)
> +{
> +       return (Algorithm *)new Decompand(controller);
> +}
> +
> +static RegisterAlgorithm reg(NAME, &create);
> diff --git a/src/ipa/rpi/controller/rpi/decompand.h
> b/src/ipa/rpi/controller/rpi/decompand.h
> new file mode 100644
> index 000000000000..5ef35946efa5
> --- /dev/null
> +++ b/src/ipa/rpi/controller/rpi/decompand.h
> @@ -0,0 +1,31 @@
> +#pragma once
> +
> +#include <libipa/pwl.h>
> +
> +#include "../decompand_status.h"
> +
> +#include "algorithm.h"
> +
> +namespace RPiController {
> +
> +struct DecompandConfig {
> +       uint32_t bitdepth;
> +       libcamera::ipa::Pwl decompandCurve;
> +};
> +
> +class Decompand : public Algorithm
> +{
> +public:
> +       Decompand(Controller *controller = NULL);
> +       char const *name() const override;
> +       int read(const libcamera::YamlObject &params) override;
> +       void initialise() override;
> +       void switchMode(CameraMode const &cameraMode, Metadata *metadata)
> override;
> +       void prepare(Metadata *imageMetadata) override;
> +
> +private:
> +       CameraMode mode_;
> +       DecompandConfig config_;
> +};
> +
> +} /* namespace RPiController */
> diff --git a/src/ipa/rpi/pisp/pisp.cpp b/src/ipa/rpi/pisp/pisp.cpp
> index 829b91258522..e75c87df1924 100644
> --- a/src/ipa/rpi/pisp/pisp.cpp
> +++ b/src/ipa/rpi/pisp/pisp.cpp
> @@ -32,6 +32,7 @@
>  #include "controller/cac_status.h"
>  #include "controller/ccm_status.h"
>  #include "controller/contrast_status.h"
> +#include "controller/decompand_status.h"
>  #include "controller/denoise_algorithm.h"
>  #include "controller/denoise_status.h"
>  #include "controller/dpc_status.h"
> @@ -113,6 +114,25 @@ int generateLut(const ipa::Pwl &pwl, uint32_t *lut,
> std::size_t lutSize,
>         return 0;
>  }
>
> +int generateDecompandLut(const ipa::Pwl &pwl, uint16_t *lut, std::size_t
> lutSize = 65)
> +{
> +       if (pwl.empty())
> +               return -EINVAL;
> +
> +       constexpr int step = 1024;
> +       for (std::size_t i = 0; i < lutSize; ++i) {
> +               int x = i * step;
> +
> +               int y = pwl.eval(x);
> +               if (y < 0)
> +                       return -1;
> +
> +               lut[i] = static_cast<uint16_t>(std::min(y, 65535));
> +       }
> +
> +       return 0;
> +}
> +
>  void packLscLut(uint32_t packed[NumLscVertexes][NumLscVertexes],
>                 double const rgb[3][NumLscVertexes][NumLscVertexes])
>  {
> @@ -236,6 +256,7 @@ private:
>         void applyLensShading(const AlscStatus *alscStatus,
>                               pisp_be_global_config &global);
>         void applyDPC(const DpcStatus *dpcStatus, pisp_be_global_config
> &global);
> +       void applyDecompand(const DecompandStatus *decompandStatus);
>         void applySdn(const SdnStatus *sdnStatus, pisp_be_global_config
> &global);
>         void applyTdn(const TdnStatus *tdnStatus, const DeviceStatus
> *deviceStatus,
>                       pisp_be_global_config &global);
> @@ -351,6 +372,11 @@ void IpaPiSP::platformPrepareIsp([[maybe_unused]]
> const PrepareParams &params,
>                 if (noiseStatus)
>                         applyFocusStats(noiseStatus);
>
> +               DecompandStatus *decompandStatus =
> +
>  rpiMetadata.getLocked<DecompandStatus>("decompand.status");
> +               if (decompandStatus)
> +                       applyDecompand(decompandStatus);
> +
>                 BlackLevelStatus *blackLevelStatus =
>
> rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
>                 if (blackLevelStatus)
> @@ -702,6 +728,20 @@ void IpaPiSP::applyDPC(const DpcStatus *dpcStatus,
> pisp_be_global_config &global
>         be_->SetDpc(dpc);
>  }
>
> +void IpaPiSP::applyDecompand(const DecompandStatus *decompandStatus)
> +{
> +       pisp_fe_global_config feGlobal;
> +       pisp_fe_decompand_config decompand = {};
> +
> +       fe_->GetGlobal(feGlobal);
> +
> +       if (!generateDecompandLut(decompandStatus->decompandCurve,
> decompand.lut, PISP_FE_DECOMPAND_LUT_SIZE)) {
> +               fe_->SetDecompand(decompand);
> +               feGlobal.enables |= PISP_FE_ENABLE_DECOMPAND;
> +               fe_->SetGlobal(feGlobal);
> +       }
> +}
> +
>  void IpaPiSP::applySdn(const SdnStatus *sdnStatus, pisp_be_global_config
> &global)
>  {
>         pisp_be_sdn_config sdn = {};
> --
> 2.43.0
>
>

Patch
diff mbox series

diff --git a/src/ipa/rpi/controller/decompand_status.h b/src/ipa/rpi/controller/decompand_status.h
new file mode 100644
index 000000000000..2d9888dca4f3
--- /dev/null
+++ b/src/ipa/rpi/controller/decompand_status.h
@@ -0,0 +1,8 @@ 
+#pragma once
+
+#include "libipa/pwl.h"
+
+struct DecompandStatus {
+	uint32_t bitdepth;
+	libcamera::ipa::Pwl decompandCurve;
+};
diff --git a/src/ipa/rpi/controller/meson.build b/src/ipa/rpi/controller/meson.build
index 74b74888bbff..c13c48539d10 100644
--- a/src/ipa/rpi/controller/meson.build
+++ b/src/ipa/rpi/controller/meson.build
@@ -14,6 +14,7 @@  rpi_ipa_controller_sources = files([
     'rpi/cac.cpp',
     'rpi/ccm.cpp',
     'rpi/contrast.cpp',
+    'rpi/decompand.cpp',
     'rpi/denoise.cpp',
     'rpi/dpc.cpp',
     'rpi/geq.cpp',
diff --git a/src/ipa/rpi/controller/rpi/decompand.cpp b/src/ipa/rpi/controller/rpi/decompand.cpp
new file mode 100644
index 000000000000..911b04bc0da0
--- /dev/null
+++ b/src/ipa/rpi/controller/rpi/decompand.cpp
@@ -0,0 +1,58 @@ 
+#include "decompand.h"
+
+#include <libcamera/base/log.h>
+
+#include "../decompand_status.h"
+#include "../histogram.h"
+
+using namespace RPiController;
+using namespace libcamera;
+
+LOG_DEFINE_CATEGORY(RPiDecompand)
+
+#define NAME "rpi.decompand"
+
+Decompand::Decompand(Controller *controller)
+	: Algorithm(controller)
+{
+}
+
+char const *Decompand::name() const
+{
+	return NAME;
+}
+
+int Decompand::read(const libcamera::YamlObject &params)
+{
+	config_.bitdepth = params["bitdepth"].get<uint32_t>(0);
+	config_.decompandCurve = params["decompand_curve"].get<ipa::Pwl>(ipa::Pwl{});
+	return config_.decompandCurve.empty() ? -EINVAL : 0;
+}
+
+void Decompand::initialise()
+{
+}
+
+void Decompand::switchMode([[maybe_unused]] CameraMode const &cameraMode,
+			   [[maybe_unused]] Metadata *metadata)
+{
+	mode_ = cameraMode;
+}
+
+void Decompand::prepare(Metadata *imageMetadata)
+{
+	DecompandStatus decompandStatus;
+
+	if (config_.bitdepth == 0 || mode_.bitdepth == config_.bitdepth) {
+		decompandStatus.decompandCurve = config_.decompandCurve;
+		imageMetadata->set("decompand.status", decompandStatus);
+	}
+}
+
+/* Register algorithm with the system. */
+static Algorithm *create(Controller *controller)
+{
+	return (Algorithm *)new Decompand(controller);
+}
+
+static RegisterAlgorithm reg(NAME, &create);
diff --git a/src/ipa/rpi/controller/rpi/decompand.h b/src/ipa/rpi/controller/rpi/decompand.h
new file mode 100644
index 000000000000..5ef35946efa5
--- /dev/null
+++ b/src/ipa/rpi/controller/rpi/decompand.h
@@ -0,0 +1,31 @@ 
+#pragma once
+
+#include <libipa/pwl.h>
+
+#include "../decompand_status.h"
+
+#include "algorithm.h"
+
+namespace RPiController {
+
+struct DecompandConfig {
+	uint32_t bitdepth;
+	libcamera::ipa::Pwl decompandCurve;
+};
+
+class Decompand : public Algorithm
+{
+public:
+	Decompand(Controller *controller = NULL);
+	char const *name() const override;
+	int read(const libcamera::YamlObject &params) override;
+	void initialise() override;
+	void switchMode(CameraMode const &cameraMode, Metadata *metadata) override;
+	void prepare(Metadata *imageMetadata) override;
+
+private:
+	CameraMode mode_;
+	DecompandConfig config_;
+};
+
+} /* namespace RPiController */
diff --git a/src/ipa/rpi/pisp/pisp.cpp b/src/ipa/rpi/pisp/pisp.cpp
index 829b91258522..e75c87df1924 100644
--- a/src/ipa/rpi/pisp/pisp.cpp
+++ b/src/ipa/rpi/pisp/pisp.cpp
@@ -32,6 +32,7 @@ 
 #include "controller/cac_status.h"
 #include "controller/ccm_status.h"
 #include "controller/contrast_status.h"
+#include "controller/decompand_status.h"
 #include "controller/denoise_algorithm.h"
 #include "controller/denoise_status.h"
 #include "controller/dpc_status.h"
@@ -113,6 +114,25 @@  int generateLut(const ipa::Pwl &pwl, uint32_t *lut, std::size_t lutSize,
 	return 0;
 }
 
+int generateDecompandLut(const ipa::Pwl &pwl, uint16_t *lut, std::size_t lutSize = 65)
+{
+	if (pwl.empty())
+		return -EINVAL;
+
+	constexpr int step = 1024;
+	for (std::size_t i = 0; i < lutSize; ++i) {
+		int x = i * step;
+
+		int y = pwl.eval(x);
+		if (y < 0)
+			return -1;
+
+		lut[i] = static_cast<uint16_t>(std::min(y, 65535));
+	}
+
+	return 0;
+}
+
 void packLscLut(uint32_t packed[NumLscVertexes][NumLscVertexes],
 		double const rgb[3][NumLscVertexes][NumLscVertexes])
 {
@@ -236,6 +256,7 @@  private:
 	void applyLensShading(const AlscStatus *alscStatus,
 			      pisp_be_global_config &global);
 	void applyDPC(const DpcStatus *dpcStatus, pisp_be_global_config &global);
+	void applyDecompand(const DecompandStatus *decompandStatus);
 	void applySdn(const SdnStatus *sdnStatus, pisp_be_global_config &global);
 	void applyTdn(const TdnStatus *tdnStatus, const DeviceStatus *deviceStatus,
 		      pisp_be_global_config &global);
@@ -351,6 +372,11 @@  void IpaPiSP::platformPrepareIsp([[maybe_unused]] const PrepareParams &params,
 		if (noiseStatus)
 			applyFocusStats(noiseStatus);
 
+		DecompandStatus *decompandStatus =
+			rpiMetadata.getLocked<DecompandStatus>("decompand.status");
+		if (decompandStatus)
+			applyDecompand(decompandStatus);
+
 		BlackLevelStatus *blackLevelStatus =
 			rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
 		if (blackLevelStatus)
@@ -702,6 +728,20 @@  void IpaPiSP::applyDPC(const DpcStatus *dpcStatus, pisp_be_global_config &global
 	be_->SetDpc(dpc);
 }
 
+void IpaPiSP::applyDecompand(const DecompandStatus *decompandStatus)
+{
+	pisp_fe_global_config feGlobal;
+	pisp_fe_decompand_config decompand = {};
+
+	fe_->GetGlobal(feGlobal);
+
+	if (!generateDecompandLut(decompandStatus->decompandCurve, decompand.lut, PISP_FE_DECOMPAND_LUT_SIZE)) {
+		fe_->SetDecompand(decompand);
+		feGlobal.enables |= PISP_FE_ENABLE_DECOMPAND;
+		fe_->SetGlobal(feGlobal);
+	}
+}
+
 void IpaPiSP::applySdn(const SdnStatus *sdnStatus, pisp_be_global_config &global)
 {
 	pisp_be_sdn_config sdn = {};