From 93802f600cf4c3bf15c9d044b980927615e800f4 Mon Sep 17 00:00:00 2001
From: Jean-Michel Hautbois <jeanmichel.hautbois@ideasonboard.com>
Date: Wed, 18 Aug 2021 17:54:03 +0200
Subject: ipa: ipu3: Move IPU3 agc into algorithms

Now that the interface is properly used by the AGC class, move it into
ipa::ipu3::algorithms and let the loops do the calls.
As we need to exchange the exposure_ and gain_ by passing them through the
FrameContext, use the calculated values in setControls() function to
ease the reading.

Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/ipa/ipu3/algorithms/agc.cpp     | 200 ++++++++++++++++++++++++++++++++++++
 src/ipa/ipu3/algorithms/agc.h       |  60 +++++++++++
 src/ipa/ipu3/algorithms/meson.build |   1 +
 src/ipa/ipu3/ipu3.cpp               |  20 ++--
 src/ipa/ipu3/ipu3_agc.cpp           | 200 ------------------------------------
 src/ipa/ipu3/ipu3_agc.h             |  60 -----------
 src/ipa/ipu3/meson.build            |   1 -
 7 files changed, 269 insertions(+), 273 deletions(-)
 create mode 100644 src/ipa/ipu3/algorithms/agc.cpp
 create mode 100644 src/ipa/ipu3/algorithms/agc.h
 delete mode 100644 src/ipa/ipu3/ipu3_agc.cpp
 delete mode 100644 src/ipa/ipu3/ipu3_agc.h

(limited to 'src')

diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
new file mode 100644
index 00000000..e4598b2e
--- /dev/null
+++ b/src/ipa/ipu3/algorithms/agc.cpp
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Ideas On Board
+ *
+ * ipu3_agc.cpp - AGC/AEC control algorithm
+ */
+
+#include "agc.h"
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+
+#include "libipa/histogram.h"
+
+namespace libcamera {
+
+using namespace std::literals::chrono_literals;
+
+namespace ipa::ipu3::algorithms {
+
+LOG_DEFINE_CATEGORY(IPU3Agc)
+
+/* Number of frames to wait before calculating stats on minimum exposure */
+static constexpr uint32_t kInitialFrameMinAECount = 4;
+/* Number of frames to wait between new gain/exposure estimations */
+static constexpr uint32_t kFrameSkipCount = 6;
+
+/* Maximum ISO value for analogue gain */
+static constexpr uint32_t kMinISO = 100;
+static constexpr uint32_t kMaxISO = 1500;
+
+/* Maximum analogue gain value
+ * \todo grab it from a camera helper */
+static constexpr uint32_t kMinGain = kMinISO / 100;
+static constexpr uint32_t kMaxGain = kMaxISO / 100;
+
+/* \todo use calculated value based on sensor */
+static constexpr uint32_t kMinExposure = 1;
+static constexpr uint32_t kMaxExposure = 1976;
+
+/* Histogram constants */
+static constexpr uint32_t knumHistogramBins = 256;
+static constexpr double kEvGainTarget = 0.5;
+
+/* A cell is 8 bytes and contains averages for RGB values and saturation ratio */
+static constexpr uint8_t kCellSize = 8;
+
+Agc::Agc()
+	: frameCount_(0), lastFrame_(0), iqMean_(0.0), lineDuration_(0s),
+	  maxExposureTime_(0s), prevExposure_(0s), prevExposureNoDg_(0s),
+	  currentExposure_(0s), currentExposureNoDg_(0s)
+{
+}
+
+int Agc::configure(IPAContext &context, const IPAConfigInfo &configInfo)
+{
+	aeGrid_ = context.configuration.grid.bdsGrid;
+
+	lineDuration_ = configInfo.sensorInfo.lineLength * 1.0s
+		      / configInfo.sensorInfo.pixelRate;
+	maxExposureTime_ = kMaxExposure * lineDuration_;
+
+	return 0;
+}
+
+void Agc::processBrightness(const ipu3_uapi_stats_3a *stats)
+{
+	const struct ipu3_uapi_grid_config statsAeGrid = stats->stats_4a_config.awb_config.grid;
+	Rectangle aeRegion = { statsAeGrid.x_start,
+			       statsAeGrid.y_start,
+			       static_cast<unsigned int>(statsAeGrid.x_end - statsAeGrid.x_start) + 1,
+			       static_cast<unsigned int>(statsAeGrid.y_end - statsAeGrid.y_start) + 1 };
+	Point topleft = aeRegion.topLeft();
+	int topleftX = topleft.x >> aeGrid_.block_width_log2;
+	int topleftY = topleft.y >> aeGrid_.block_height_log2;
+
+	/* Align to the grid cell width and height */
+	uint32_t startX = topleftX << aeGrid_.block_width_log2;
+	uint32_t startY = topleftY * aeGrid_.width << aeGrid_.block_width_log2;
+	uint32_t endX = (startX + (aeRegion.size().width >> aeGrid_.block_width_log2)) << aeGrid_.block_width_log2;
+	uint32_t i, j;
+	uint32_t count = 0;
+
+	uint32_t hist[knumHistogramBins] = { 0 };
+	for (j = topleftY;
+	     j < topleftY + (aeRegion.size().height >> aeGrid_.block_height_log2);
+	     j++) {
+		for (i = startX + startY; i < endX + startY; i += kCellSize) {
+			/*
+			 * The grid width (and maybe height) is not reliable.
+			 * We observed a bit shift which makes the value 160 to be 32 in the stats grid.
+			 * Use the one passed at init time.
+			 */
+			if (stats->awb_raw_buffer.meta_data[i + 4 + j * aeGrid_.width] == 0) {
+				uint8_t Gr = stats->awb_raw_buffer.meta_data[i + 0 + j * aeGrid_.width];
+				uint8_t Gb = stats->awb_raw_buffer.meta_data[i + 3 + j * aeGrid_.width];
+				hist[(Gr + Gb) / 2]++;
+				count++;
+			}
+		}
+	}
+
+	/* Estimate the quantile mean of the top 2% of the histogram */
+	iqMean_ = Histogram(Span<uint32_t>(hist)).interQuantileMean(0.98, 1.0);
+}
+
+void Agc::filterExposure()
+{
+	double speed = 0.2;
+	if (prevExposure_ == 0s) {
+		/* DG stands for digital gain.*/
+		prevExposure_ = currentExposure_;
+		prevExposureNoDg_ = currentExposureNoDg_;
+	} else {
+		/*
+		 * If we are close to the desired result, go faster to avoid making
+		 * multiple micro-adjustments.
+		 * \ todo: Make this customisable?
+		 */
+		if (prevExposure_ < 1.2 * currentExposure_ &&
+		    prevExposure_ > 0.8 * currentExposure_)
+			speed = sqrt(speed);
+
+		prevExposure_ = speed * currentExposure_ +
+				prevExposure_ * (1.0 - speed);
+		prevExposureNoDg_ = speed * currentExposureNoDg_ +
+				prevExposureNoDg_ * (1.0 - speed);
+	}
+	/*
+	 * We can't let the no_dg exposure deviate too far below the
+	 * total exposure, as there might not be enough digital gain available
+	 * in the ISP to hide it (which will cause nasty oscillation).
+	 */
+	double fastReduceThreshold = 0.4;
+	if (prevExposureNoDg_ <
+	    prevExposure_ * fastReduceThreshold)
+		prevExposureNoDg_ = prevExposure_ * fastReduceThreshold;
+	LOG(IPU3Agc, Debug) << "After filtering, total_exposure " << prevExposure_;
+}
+
+void Agc::lockExposureGain(uint32_t &exposure, double &gain)
+{
+	/* Algorithm initialization should wait for first valid frames */
+	/* \todo - have a number of frames given by DelayedControls ?
+	 * - implement a function for IIR */
+	if ((frameCount_ < kInitialFrameMinAECount) || (frameCount_ - lastFrame_ < kFrameSkipCount))
+		return;
+
+	/* Are we correctly exposed ? */
+	if (std::abs(iqMean_ - kEvGainTarget * knumHistogramBins) <= 1) {
+		LOG(IPU3Agc, Debug) << "!!! Good exposure with iqMean = " << iqMean_;
+	} else {
+		double newGain = kEvGainTarget * knumHistogramBins / iqMean_;
+
+		/* extracted from Rpi::Agc::computeTargetExposure */
+		libcamera::utils::Duration currentShutter = exposure * lineDuration_;
+		currentExposureNoDg_ = currentShutter * gain;
+		LOG(IPU3Agc, Debug) << "Actual total exposure " << currentExposureNoDg_
+				    << " Shutter speed " << currentShutter
+				    << " Gain " << gain;
+		currentExposure_ = currentExposureNoDg_ * newGain;
+		libcamera::utils::Duration maxTotalExposure = maxExposureTime_ * kMaxGain;
+		currentExposure_ = std::min(currentExposure_, maxTotalExposure);
+		LOG(IPU3Agc, Debug) << "Target total exposure " << currentExposure_;
+
+		/* \todo: estimate if we need to desaturate */
+		filterExposure();
+
+		libcamera::utils::Duration newExposure = 0.0s;
+		if (currentShutter < maxExposureTime_) {
+			exposure = std::clamp(static_cast<uint32_t>(exposure * currentExposure_ / currentExposureNoDg_), kMinExposure, kMaxExposure);
+			newExposure = currentExposure_ / exposure;
+			gain = std::clamp(static_cast<uint32_t>(gain * currentExposure_ / newExposure), kMinGain, kMaxGain);
+		} else if (currentShutter >= maxExposureTime_) {
+			gain = std::clamp(static_cast<uint32_t>(gain * currentExposure_ / currentExposureNoDg_), kMinGain, kMaxGain);
+			newExposure = currentExposure_ / gain;
+			exposure = std::clamp(static_cast<uint32_t>(exposure * currentExposure_ / newExposure), kMinExposure, kMaxExposure);
+		}
+		LOG(IPU3Agc, Debug) << "Adjust exposure " << exposure * lineDuration_ << " and gain " << gain;
+	}
+	lastFrame_ = frameCount_;
+}
+
+void Agc::process(IPAContext &context, const ipu3_uapi_stats_3a *stats)
+{
+	uint32_t &exposure = context.frameContext.agc.exposure;
+	double &gain = context.frameContext.agc.gain;
+	processBrightness(stats);
+	lockExposureGain(exposure, gain);
+	frameCount_++;
+}
+
+} /* namespace ipa::ipu3::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/ipu3/algorithms/agc.h b/src/ipa/ipu3/algorithms/agc.h
new file mode 100644
index 00000000..1739d2fd
--- /dev/null
+++ b/src/ipa/ipu3/algorithms/agc.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Ideas On Board
+ *
+ * agc.h - IPU3 AGC/AEC control algorithm
+ */
+#ifndef __LIBCAMERA_IPU3_ALGORITHMS_AGC_H__
+#define __LIBCAMERA_IPU3_ALGORITHMS_AGC_H__
+
+#include <linux/intel-ipu3.h>
+
+#include <libcamera/base/utils.h>
+
+#include <libcamera/geometry.h>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+struct IPACameraSensorInfo;
+
+namespace ipa::ipu3::algorithms {
+
+using utils::Duration;
+
+class Agc : public Algorithm
+{
+public:
+	Agc();
+	~Agc() = default;
+
+	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
+	void process(IPAContext &context, const ipu3_uapi_stats_3a *stats) override;
+
+private:
+	void processBrightness(const ipu3_uapi_stats_3a *stats);
+	void filterExposure();
+	void lockExposureGain(uint32_t &exposure, double &gain);
+
+	struct ipu3_uapi_grid_config aeGrid_;
+
+	uint64_t frameCount_;
+	uint64_t lastFrame_;
+
+	double iqMean_;
+
+	Duration lineDuration_;
+	Duration maxExposureTime_;
+
+	Duration prevExposure_;
+	Duration prevExposureNoDg_;
+	Duration currentExposure_;
+	Duration currentExposureNoDg_;
+};
+
+} /* namespace ipa::ipu3::algorithms */
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_IPU3_ALGORITHMS_AGC_H__ */
diff --git a/src/ipa/ipu3/algorithms/meson.build b/src/ipa/ipu3/algorithms/meson.build
index fa004df4..deae225b 100644
--- a/src/ipa/ipu3/algorithms/meson.build
+++ b/src/ipa/ipu3/algorithms/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: CC0-1.0
 
 ipu3_ipa_algorithms = files([
+    'agc.cpp',
     'algorithm.cpp',
     'awb.cpp',
     'tone_mapping.cpp',
diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp
index 5fc358fa..0ed0a6f1 100644
--- a/src/ipa/ipu3/ipu3.cpp
+++ b/src/ipa/ipu3/ipu3.cpp
@@ -29,10 +29,10 @@
 
 #include "libcamera/internal/mapped_framebuffer.h"
 
+#include "algorithms/agc.h"
 #include "algorithms/algorithm.h"
 #include "algorithms/awb.h"
 #include "algorithms/tone_mapping.h"
-#include "ipu3_agc.h"
 #include "libipa/camera_sensor_helper.h"
 
 /**
@@ -187,8 +187,6 @@ private:
 	uint32_t minGain_;
 	uint32_t maxGain_;
 
-	/* Interface to the AEC/AGC algorithm */
-	std::unique_ptr<IPU3Agc> agcAlgo_;
 	/* Interface to the Camera Helper */
 	std::unique_ptr<CameraSensorHelper> camHelper_;
 
@@ -268,6 +266,7 @@ int IPAIPU3::init(const IPASettings &settings,
 	*ipaControls = ControlInfoMap(std::move(controls), controls::controls);
 
 	/* Construct our Algorithms */
+	algorithms_.push_back(std::make_unique<algorithms::Agc>());
 	algorithms_.push_back(std::make_unique<algorithms::Awb>());
 	algorithms_.push_back(std::make_unique<algorithms::ToneMapping>());
 
@@ -386,9 +385,6 @@ int IPAIPU3::configure(const IPAConfigInfo &configInfo)
 			return ret;
 	}
 
-	agcAlgo_ = std::make_unique<IPU3Agc>();
-	agcAlgo_->configure(context_, configInfo);
-
 	return 0;
 }
 
@@ -476,15 +472,12 @@ void IPAIPU3::parseStatistics(unsigned int frame,
 {
 	ControlList ctrls(controls::controls);
 
-	for (auto const &algo : algorithms_)
-		algo->process(context_, stats);
-
 	/* \todo These fields should not be written by the IPAIPU3 layer */
 	context_.frameContext.agc.gain = camHelper_->gain(gain_);
 	context_.frameContext.agc.exposure = exposure_;
-	agcAlgo_->process(context_, stats);
-	exposure_ = context_.frameContext.agc.exposure;
-	gain_ = camHelper_->gainCode(context_.frameContext.agc.gain);
+
+	for (auto const &algo : algorithms_)
+		algo->process(context_, stats);
 
 	setControls(frame);
 
@@ -505,6 +498,9 @@ void IPAIPU3::setControls(unsigned int frame)
 	IPU3Action op;
 	op.op = ActionSetSensorControls;
 
+	exposure_ = context_.frameContext.agc.exposure;
+	gain_ = camHelper_->gainCode(context_.frameContext.agc.gain);
+
 	ControlList ctrls(ctrls_);
 	ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure_));
 	ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain_));
diff --git a/src/ipa/ipu3/ipu3_agc.cpp b/src/ipa/ipu3/ipu3_agc.cpp
deleted file mode 100644
index 1c156b89..00000000
--- a/src/ipa/ipu3/ipu3_agc.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2021, Ideas On Board
- *
- * ipu3_agc.cpp - AGC/AEC control algorithm
- */
-
-#include "ipu3_agc.h"
-
-#include <algorithm>
-#include <chrono>
-#include <cmath>
-
-#include <libcamera/base/log.h>
-
-#include <libcamera/ipa/core_ipa_interface.h>
-
-#include "libipa/histogram.h"
-
-namespace libcamera {
-
-using namespace std::literals::chrono_literals;
-
-namespace ipa::ipu3 {
-
-LOG_DEFINE_CATEGORY(IPU3Agc)
-
-/* Number of frames to wait before calculating stats on minimum exposure */
-static constexpr uint32_t kInitialFrameMinAECount = 4;
-/* Number of frames to wait between new gain/exposure estimations */
-static constexpr uint32_t kFrameSkipCount = 6;
-
-/* Maximum ISO value for analogue gain */
-static constexpr uint32_t kMinISO = 100;
-static constexpr uint32_t kMaxISO = 1500;
-
-/* Maximum analogue gain value
- * \todo grab it from a camera helper */
-static constexpr uint32_t kMinGain = kMinISO / 100;
-static constexpr uint32_t kMaxGain = kMaxISO / 100;
-
-/* \todo use calculated value based on sensor */
-static constexpr uint32_t kMinExposure = 1;
-static constexpr uint32_t kMaxExposure = 1976;
-
-/* Histogram constants */
-static constexpr uint32_t knumHistogramBins = 256;
-static constexpr double kEvGainTarget = 0.5;
-
-/* A cell is 8 bytes and contains averages for RGB values and saturation ratio */
-static constexpr uint8_t kCellSize = 8;
-
-IPU3Agc::IPU3Agc()
-	: frameCount_(0), lastFrame_(0), iqMean_(0.0), lineDuration_(0s),
-	  maxExposureTime_(0s), prevExposure_(0s), prevExposureNoDg_(0s),
-	  currentExposure_(0s), currentExposureNoDg_(0s)
-{
-}
-
-int IPU3Agc::configure(IPAContext &context, const IPAConfigInfo &configInfo)
-{
-	aeGrid_ = context.configuration.grid.bdsGrid;
-
-	lineDuration_ = configInfo.sensorInfo.lineLength * 1.0s
-		      / configInfo.sensorInfo.pixelRate;
-	maxExposureTime_ = kMaxExposure * lineDuration_;
-
-	return 0;
-}
-
-void IPU3Agc::processBrightness(const ipu3_uapi_stats_3a *stats)
-{
-	const struct ipu3_uapi_grid_config statsAeGrid = stats->stats_4a_config.awb_config.grid;
-	Rectangle aeRegion = { statsAeGrid.x_start,
-			       statsAeGrid.y_start,
-			       static_cast<unsigned int>(statsAeGrid.x_end - statsAeGrid.x_start) + 1,
-			       static_cast<unsigned int>(statsAeGrid.y_end - statsAeGrid.y_start) + 1 };
-	Point topleft = aeRegion.topLeft();
-	int topleftX = topleft.x >> aeGrid_.block_width_log2;
-	int topleftY = topleft.y >> aeGrid_.block_height_log2;
-
-	/* Align to the grid cell width and height */
-	uint32_t startX = topleftX << aeGrid_.block_width_log2;
-	uint32_t startY = topleftY * aeGrid_.width << aeGrid_.block_width_log2;
-	uint32_t endX = (startX + (aeRegion.size().width >> aeGrid_.block_width_log2)) << aeGrid_.block_width_log2;
-	uint32_t i, j;
-	uint32_t count = 0;
-
-	uint32_t hist[knumHistogramBins] = { 0 };
-	for (j = topleftY;
-	     j < topleftY + (aeRegion.size().height >> aeGrid_.block_height_log2);
-	     j++) {
-		for (i = startX + startY; i < endX + startY; i += kCellSize) {
-			/*
-			 * The grid width (and maybe height) is not reliable.
-			 * We observed a bit shift which makes the value 160 to be 32 in the stats grid.
-			 * Use the one passed at init time.
-			 */
-			if (stats->awb_raw_buffer.meta_data[i + 4 + j * aeGrid_.width] == 0) {
-				uint8_t Gr = stats->awb_raw_buffer.meta_data[i + 0 + j * aeGrid_.width];
-				uint8_t Gb = stats->awb_raw_buffer.meta_data[i + 3 + j * aeGrid_.width];
-				hist[(Gr + Gb) / 2]++;
-				count++;
-			}
-		}
-	}
-
-	/* Estimate the quantile mean of the top 2% of the histogram */
-	iqMean_ = Histogram(Span<uint32_t>(hist)).interQuantileMean(0.98, 1.0);
-}
-
-void IPU3Agc::filterExposure()
-{
-	double speed = 0.2;
-	if (prevExposure_ == 0s) {
-		/* DG stands for digital gain.*/
-		prevExposure_ = currentExposure_;
-		prevExposureNoDg_ = currentExposureNoDg_;
-	} else {
-		/*
-		 * If we are close to the desired result, go faster to avoid making
-		 * multiple micro-adjustments.
-		 * \ todo: Make this customisable?
-		 */
-		if (prevExposure_ < 1.2 * currentExposure_ &&
-		    prevExposure_ > 0.8 * currentExposure_)
-			speed = sqrt(speed);
-
-		prevExposure_ = speed * currentExposure_ +
-				prevExposure_ * (1.0 - speed);
-		prevExposureNoDg_ = speed * currentExposureNoDg_ +
-				prevExposureNoDg_ * (1.0 - speed);
-	}
-	/*
-	 * We can't let the no_dg exposure deviate too far below the
-	 * total exposure, as there might not be enough digital gain available
-	 * in the ISP to hide it (which will cause nasty oscillation).
-	 */
-	double fastReduceThreshold = 0.4;
-	if (prevExposureNoDg_ <
-	    prevExposure_ * fastReduceThreshold)
-		prevExposureNoDg_ = prevExposure_ * fastReduceThreshold;
-	LOG(IPU3Agc, Debug) << "After filtering, total_exposure " << prevExposure_;
-}
-
-void IPU3Agc::lockExposureGain(uint32_t &exposure, double &gain)
-{
-	/* Algorithm initialization should wait for first valid frames */
-	/* \todo - have a number of frames given by DelayedControls ?
-	 * - implement a function for IIR */
-	if ((frameCount_ < kInitialFrameMinAECount) || (frameCount_ - lastFrame_ < kFrameSkipCount))
-		return;
-
-	/* Are we correctly exposed ? */
-	if (std::abs(iqMean_ - kEvGainTarget * knumHistogramBins) <= 1) {
-		LOG(IPU3Agc, Debug) << "!!! Good exposure with iqMean = " << iqMean_;
-	} else {
-		double newGain = kEvGainTarget * knumHistogramBins / iqMean_;
-
-		/* extracted from Rpi::Agc::computeTargetExposure */
-		libcamera::utils::Duration currentShutter = exposure * lineDuration_;
-		currentExposureNoDg_ = currentShutter * gain;
-		LOG(IPU3Agc, Debug) << "Actual total exposure " << currentExposureNoDg_
-				    << " Shutter speed " << currentShutter
-				    << " Gain " << gain;
-		currentExposure_ = currentExposureNoDg_ * newGain;
-		libcamera::utils::Duration maxTotalExposure = maxExposureTime_ * kMaxGain;
-		currentExposure_ = std::min(currentExposure_, maxTotalExposure);
-		LOG(IPU3Agc, Debug) << "Target total exposure " << currentExposure_;
-
-		/* \todo: estimate if we need to desaturate */
-		filterExposure();
-
-		libcamera::utils::Duration newExposure = 0.0s;
-		if (currentShutter < maxExposureTime_) {
-			exposure = std::clamp(static_cast<uint32_t>(exposure * currentExposure_ / currentExposureNoDg_), kMinExposure, kMaxExposure);
-			newExposure = currentExposure_ / exposure;
-			gain = std::clamp(static_cast<uint32_t>(gain * currentExposure_ / newExposure), kMinGain, kMaxGain);
-		} else if (currentShutter >= maxExposureTime_) {
-			gain = std::clamp(static_cast<uint32_t>(gain * currentExposure_ / currentExposureNoDg_), kMinGain, kMaxGain);
-			newExposure = currentExposure_ / gain;
-			exposure = std::clamp(static_cast<uint32_t>(exposure * currentExposure_ / newExposure), kMinExposure, kMaxExposure);
-		}
-		LOG(IPU3Agc, Debug) << "Adjust exposure " << exposure * lineDuration_ << " and gain " << gain;
-	}
-	lastFrame_ = frameCount_;
-}
-
-void IPU3Agc::process(IPAContext &context, const ipu3_uapi_stats_3a *stats)
-{
-	uint32_t &exposure = context.frameContext.agc.exposure;
-	double &gain = context.frameContext.agc.gain;
-	processBrightness(stats);
-	lockExposureGain(exposure, gain);
-	frameCount_++;
-}
-
-} /* namespace ipa::ipu3 */
-
-} /* namespace libcamera */
diff --git a/src/ipa/ipu3/ipu3_agc.h b/src/ipa/ipu3/ipu3_agc.h
deleted file mode 100644
index 0e922664..00000000
--- a/src/ipa/ipu3/ipu3_agc.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2021, Ideas On Board
- *
- * ipu3_agc.h - IPU3 AGC/AEC control algorithm
- */
-#ifndef __LIBCAMERA_IPU3_AGC_H__
-#define __LIBCAMERA_IPU3_AGC_H__
-
-#include <linux/intel-ipu3.h>
-
-#include <libcamera/base/utils.h>
-
-#include <libcamera/geometry.h>
-
-#include "algorithms/algorithm.h"
-
-namespace libcamera {
-
-struct IPACameraSensorInfo;
-
-namespace ipa::ipu3 {
-
-using utils::Duration;
-
-class IPU3Agc : public Algorithm
-{
-public:
-	IPU3Agc();
-	~IPU3Agc() = default;
-
-	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
-	void process(IPAContext &context, const ipu3_uapi_stats_3a *stats) override;
-
-private:
-	void processBrightness(const ipu3_uapi_stats_3a *stats);
-	void filterExposure();
-	void lockExposureGain(uint32_t &exposure, double &gain);
-
-	struct ipu3_uapi_grid_config aeGrid_;
-
-	uint64_t frameCount_;
-	uint64_t lastFrame_;
-
-	double iqMean_;
-
-	Duration lineDuration_;
-	Duration maxExposureTime_;
-
-	Duration prevExposure_;
-	Duration prevExposureNoDg_;
-	Duration currentExposure_;
-	Duration currentExposureNoDg_;
-};
-
-} /* namespace ipa::ipu3 */
-
-} /* namespace libcamera */
-
-#endif /* __LIBCAMERA_IPU3_AGC_H__ */
diff --git a/src/ipa/ipu3/meson.build b/src/ipa/ipu3/meson.build
index d1126947..39320980 100644
--- a/src/ipa/ipu3/meson.build
+++ b/src/ipa/ipu3/meson.build
@@ -6,7 +6,6 @@ ipa_name = 'ipa_ipu3'
 
 ipu3_ipa_sources = files([
     'ipu3.cpp',
-    'ipu3_agc.cpp',
 ])
 
 ipu3_ipa_sources += ipu3_ipa_algorithms
-- 
cgit v1.2.1