[2/4] ipa: rpi: Allow black levels to vary with analogue gain
diff mbox series

Message ID 20260803123012.10175-3-david.plowman@raspberrypi.com
State New
Headers show
Series
  • Black level and other fixes for IMX662
Related show

Commit Message

David Plowman Aug. 3, 2026, 12:07 p.m. UTC
Optionally, a PWL (piecewise linear) function can be specified as the
black level instead of a constant value. This PWL is evaluated at the
current analogue gain for each frame.

Tuning files will also accept "black_level_func" as a shorthand for
all three channels.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 src/ipa/rpi/controller/rpi/black_level.cpp | 33 +++++++++++++++++++---
 src/ipa/rpi/controller/rpi/black_level.h   |  7 +++++
 2 files changed, 36 insertions(+), 4 deletions(-)

Patch
diff mbox series

diff --git a/src/ipa/rpi/controller/rpi/black_level.cpp b/src/ipa/rpi/controller/rpi/black_level.cpp
index 42ea15050..876bc661d 100644
--- a/src/ipa/rpi/controller/rpi/black_level.cpp
+++ b/src/ipa/rpi/controller/rpi/black_level.cpp
@@ -41,12 +41,29 @@  int BlackLevel::read(const libcamera::ValueNode &params)
 		<< " Read black levels red " << blackLevelR_
 		<< " green " << blackLevelG_
 		<< " blue " << blackLevelB_;
+
+	/* Allow "black_level_func" as a shorthand for all 3 colours. */
+	libcamera::ipa::Pwl blackLevelFunc;
+	blackLevelFunc = params["black_level_func"].get<ipa::Pwl>(ipa::Pwl{});
+	blackLevelFuncR_ = params["black_level_func_r"].get<ipa::Pwl>(blackLevelFunc);
+	blackLevelFuncG_ = params["black_level_func_g"].get<ipa::Pwl>(blackLevelFunc);
+	blackLevelFuncB_ = params["black_level_func_b"].get<ipa::Pwl>(blackLevelFunc);
+
 	return 0;
 }
 
 void BlackLevel::initialValues(uint16_t &blackLevelR, uint16_t &blackLevelG,
 			       uint16_t &blackLevelB)
 {
+	if (!blackLevelFuncR_.empty())
+		blackLevelR_ = blackLevelFuncR_.eval(1.0);
+
+	if (!blackLevelFuncG_.empty())
+		blackLevelG_ = blackLevelFuncG_.eval(1.0);
+
+	if (!blackLevelFuncB_.empty())
+		blackLevelB_ = blackLevelFuncB_.eval(1.0);
+
 	blackLevelR = blackLevelR_;
 	blackLevelG = blackLevelG_;
 	blackLevelB = blackLevelB_;
@@ -54,10 +71,18 @@  void BlackLevel::initialValues(uint16_t &blackLevelR, uint16_t &blackLevelG,
 
 void BlackLevel::prepare(Metadata *imageMetadata)
 {
-	/*
-	 * Possibly we should think about doing this in a switchMode or
-	 * something?
-	 */
+	DeviceStatus deviceStatus;
+	if (!imageMetadata->get("device.status", deviceStatus)) {
+		if (!blackLevelFuncR_.empty())
+			blackLevelR_ = blackLevelFuncR_.eval(deviceStatus.analogueGain);
+
+		if (!blackLevelFuncG_.empty())
+			blackLevelG_ = blackLevelFuncG_.eval(deviceStatus.analogueGain);
+
+		if (!blackLevelFuncB_.empty())
+			blackLevelB_ = blackLevelFuncB_.eval(deviceStatus.analogueGain);
+	}
+
 	struct BlackLevelStatus status;
 	status.blackLevelR = blackLevelR_;
 	status.blackLevelG = blackLevelG_;
diff --git a/src/ipa/rpi/controller/rpi/black_level.h b/src/ipa/rpi/controller/rpi/black_level.h
index dbf29b282..34bb81548 100644
--- a/src/ipa/rpi/controller/rpi/black_level.h
+++ b/src/ipa/rpi/controller/rpi/black_level.h
@@ -6,6 +6,8 @@ 
  */
 #pragma once
 
+#include <libipa/pwl.h>
+
 #include "../black_level_algorithm.h"
 #include "../black_level_status.h"
 
@@ -27,6 +29,11 @@  private:
 	double blackLevelR_;
 	double blackLevelG_;
 	double blackLevelB_;
+
+	/* Black levels can vary with analogue gain instead of being constant. */
+	libcamera::ipa::Pwl blackLevelFuncR_;
+	libcamera::ipa::Pwl blackLevelFuncG_;
+	libcamera::ipa::Pwl blackLevelFuncB_;
 };
 
 } /* namespace RPiController */