about summary refs log tree commit diff
path: root/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
From b0c07674abecb05dc0af93a4b749971f057bc3c6 Mon Sep 17 00:00:00 2001
From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
Date: Mon, 11 Mar 2024 15:15:22 +0100
Subject: [PATCH 19/21] libcamera: Soft IPA: use CameraSensorHelper for
 analogue gain

Use CameraSensorHelper to convert the analogue gain code read from the
camera sensor into real analogue gain value. In the future this makes
it possible to use faster AE/AGC algorithm. For now the same AE/AGC
algorithm is used, but even then the CameraSensorHelper lets us use the
full range of analogue gain values.

If there is no CameraSensorHelper for the camera sensor in use, a
warning log message is printed, and the AE/AGC works exactly as before
this change.

Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
---
 .../internal/software_isp/software_isp.h      |  3 +-
 src/ipa/simple/soft_simple.cpp                | 77 ++++++++++++-------
 src/libcamera/pipeline/simple/simple.cpp      |  2 +-
 src/libcamera/software_isp/software_isp.cpp   |  8 +-
 4 files changed, 57 insertions(+), 33 deletions(-)

diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index 8d25e979..2a6db7ba 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -26,6 +26,7 @@
 #include <libcamera/ipa/soft_ipa_interface.h>
 #include <libcamera/ipa/soft_ipa_proxy.h>
 
+#include "libcamera/internal/camera_sensor.h"
 #include "libcamera/internal/dma_heaps.h"
 #include "libcamera/internal/pipeline_handler.h"
 #include "libcamera/internal/shared_mem_object.h"
@@ -43,7 +44,7 @@ LOG_DECLARE_CATEGORY(SoftwareIsp)
 class SoftwareIsp
 {
 public:
-	SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
+	SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor);
 	~SoftwareIsp();
 
 	int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
index ac027568..e4d64762 100644
--- a/src/ipa/simple/soft_simple.cpp
+++ b/src/ipa/simple/soft_simple.cpp
@@ -22,6 +22,8 @@
 #include "libcamera/internal/software_isp/debayer_params.h"
 #include "libcamera/internal/software_isp/swisp_stats.h"
 
+#include "libipa/camera_sensor_helper.h"
+
 #include "black_level.h"
 
 namespace libcamera {
@@ -67,18 +69,27 @@ private:
 	DebayerParams *params_;
 	SwIspStats *stats_;
 	BlackLevel blackLevel_;
+	std::unique_ptr<CameraSensorHelper> camHelper_;
 
 	int32_t exposure_min_, exposure_max_;
-	int32_t again_min_, again_max_;
-	int32_t again_, exposure_;
+	int32_t exposure_;
+	double again_min_, again_max_, againMinStep_;
+	double again_;
 	unsigned int ignore_updates_;
 };
 
-int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
+int IPASoftSimple::init(const IPASettings &settings,
 			const SharedFD &fdStats,
 			const SharedFD &fdParams,
 			const ControlInfoMap &sensorInfoMap)
 {
+	camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
+	if (camHelper_ == nullptr) {
+		LOG(IPASoft, Warning)
+			<< "Failed to create camera sensor helper for "
+			<< settings.sensorModel;
+	}
+
 	fdStats_ = fdStats;
 	if (!fdStats_.isValid()) {
 		LOG(IPASoft, Error) << "Invalid Statistics handle";
@@ -132,25 +143,35 @@ int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)
 		exposure_min_ = 1;
 	}
 
-	again_min_ = gain_info.min().get<int32_t>();
-	again_max_ = gain_info.max().get<int32_t>();
-	/*
-	 * The camera sensor gain (g) is usually not equal to the value written
-	 * into the gain register (x). But the way how the AGC algorithm changes
-	 * the gain value to make the total exposure closer to the optimum assumes
-	 * that g(x) is not too far from linear function. If the minimal gain is 0,
-	 * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
-	 * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
-	 * one edge, and very small near the other) we limit the range of the gain
-	 * values used.
-	 */
-	if (!again_min_) {
-		LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
-		again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
+	int32_t again_min = gain_info.min().get<int32_t>();
+	int32_t again_max = gain_info.max().get<int32_t>();
+
+	if (camHelper_) {
+		again_min_ = camHelper_->gain(again_min);
+		again_max_ = camHelper_->gain(again_max);
+		againMinStep_ = (again_max_ - again_min_) / 100.0;
+	} else {
+		/*
+		 * The camera sensor gain (g) is usually not equal to the value written
+		 * into the gain register (x). But the way how the AGC algorithm changes
+		 * the gain value to make the total exposure closer to the optimum assumes
+		 * that g(x) is not too far from linear function. If the minimal gain is 0,
+		 * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
+		 * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
+		 * one edge, and very small near the other) we limit the range of the gain
+		 * values used.
+		 */
+		again_max_ = again_max;
+		if (!again_min) {
+			LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
+			again_min_ = std::min(100, again_min / 2 + again_max / 2);
+		}
+		againMinStep_ = 1.0;
 	}
 
 	LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
-			   << ", gain " << again_min_ << "-" << again_max_;
+			   << ", gain " << again_min_ << "-" << again_max_
+			   << " (" << againMinStep_ << ")";
 
 	return 0;
 }
@@ -252,12 +273,14 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
 	ControlList ctrls(sensorControls);
 
 	exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();
-	again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
+	int32_t again = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
+	again_ = camHelper_ ? camHelper_->gain(again) : again;
 
 	updateExposure(exposureMSV);
 
 	ctrls.set(V4L2_CID_EXPOSURE, exposure_);
-	ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
+	ctrls.set(V4L2_CID_ANALOGUE_GAIN,
+		  static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(again_) : again_));
 
 	ignore_updates_ = 2;
 
@@ -276,7 +299,7 @@ void IPASoftSimple::updateExposure(double exposureMSV)
 	static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
 	static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
 
-	int next;
+	double next;
 
 	if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
 		next = exposure_ * kExpNumeratorUp / kExpDenominator;
@@ -286,18 +309,18 @@ void IPASoftSimple::updateExposure(double exposureMSV)
 			exposure_ = next;
 		if (exposure_ >= exposure_max_) {
 			next = again_ * kExpNumeratorUp / kExpDenominator;
-			if (next - again_ < 1)
-				again_ += 1;
+			if (next - again_ < againMinStep_)
+				again_ += againMinStep_;
 			else
 				again_ = next;
 		}
 	}
 
 	if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
-		if (exposure_ == exposure_max_ && again_ != again_min_) {
+		if (exposure_ == exposure_max_ && again_ > again_min_) {
 			next = again_ * kExpNumeratorDown / kExpDenominator;
-			if (again_ - next < 1)
-				again_ -= 1;
+			if (again_ - next < againMinStep_)
+				again_ -= againMinStep_;
 			else
 				again_ = next;
 		} else {
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index c3ebb7b7..7e932a14 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -525,7 +525,7 @@ int SimpleCameraData::init()
 	 * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
 	 */
 	if (!converter_ && pipe->swIspEnabled()) {
-		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
+		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get());
 		if (!swIsp_->isValid()) {
 			LOG(SimplePipeline, Warning)
 				<< "Failed to create software ISP, disabling software debayering";
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index 9b49be41..ea4d96e4 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -60,9 +60,9 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
 /**
  * \brief Constructs SoftwareIsp object
  * \param[in] pipe The pipeline handler in use
- * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
+ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline handler
  */
-SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
+SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
 	: debayer_(nullptr),
 	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
 	  dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
@@ -97,10 +97,10 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorCont
 		return;
 	}
 
-	int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
+	int ret = ipa_->init(IPASettings{ "No cfg file", sensor->model() },
 			     debayer_->getStatsFD(),
 			     sharedParams_.fd(),
-			     sensorControls);
+			     sensor->controls());
 	if (ret) {
 		LOG(SoftwareIsp, Error) << "IPA init failed";
 		debayer_.reset();
-- 
2.43.2