[DNI,v3,19/19] ipa: mali: Handle FrameDurationLimits
diff mbox series

Message ID 20251114-exposure-limits-v3-19-b7c07feba026@ideasonboard.com
State New
Headers show
Series
  • libipa: agc: Calculate exposure limits
Related show

Commit Message

Jacopo Mondi Nov. 14, 2025, 2:17 p.m. UTC
Handle the FrameDurationLimits control for the Mali C55 platform.

The frame duration is:
1) Adjuted at configure() time to what the AGC algorithm computed
2) Stored at queueRequest() time in the active state and in the
   per-frame context
3) Computed at process() time by the AGC calculateNewEV() function
   and used to compute the desired vblank

The VBLANK control is now handled by both the IPA and the pipeline
handler and correctly programmed on the sensor.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 src/ipa/mali-c55/algorithms/agc.cpp | 50 +++++++++++++++++++++++++++++++++----
 src/ipa/mali-c55/ipa_context.h      |  6 +++++
 src/ipa/mali-c55/mali-c55.cpp       | 10 +++++---
 3 files changed, 58 insertions(+), 8 deletions(-)

Patch
diff mbox series

diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
index a0b55694aad292f8a080d8266470797ac0cc2c25..92a58417f9cdf11aa1cd7ec7167bf4818060c85e 100644
--- a/src/ipa/mali-c55/algorithms/agc.cpp
+++ b/src/ipa/mali-c55/algorithms/agc.cpp
@@ -181,9 +181,10 @@  int Agc::configure(IPAContext &context,
 	sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain;
 	sensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain;
 
-	AgcMeanLuminance::configure(sensorConfig, context.camHelper.get());
-
-	/* \todo Update AGC limits when FrameDurationLimits is passed in */
+	context.activeState.agc.maxFrameDuration =
+		AgcMeanLuminance::configure(sensorConfig, context.camHelper.get());
+	context.activeState.agc.minFrameDuration =
+		context.configuration.sensor.minFrameDuration;
 
 	return 0;
 }
@@ -209,6 +210,27 @@  void Agc::queueRequest(IPAContext &context, const uint32_t frame,
 			<< " AGC";
 	}
 
+	const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits);
+	if (frameDurationLimits) {
+		/* Limit the control value to the sensor constraints. */
+		int64_t sensorMinFrameDuration =
+			context.configuration.sensor.minFrameDuration.get<std::micro>();
+		int64_t sensorMaxFrameDuration =
+			context.configuration.sensor.maxFrameDuration.get<std::micro>();
+
+		int64_t minFrameDuration =
+			std::clamp((*frameDurationLimits).front(),
+				   sensorMinFrameDuration, sensorMaxFrameDuration);
+		int64_t maxFrameDuration =
+			std::clamp((*frameDurationLimits).back(),
+				   sensorMinFrameDuration, sensorMaxFrameDuration);
+
+		agc.minFrameDuration = std::chrono::microseconds(minFrameDuration);
+		agc.maxFrameDuration = std::chrono::microseconds(maxFrameDuration);
+	}
+	frameContext.agc.minFrameDuration = agc.minFrameDuration;
+	frameContext.agc.maxFrameDuration = agc.maxFrameDuration;
+
 	/*
 	 * If the automatic exposure and gain is enabled we have no further work
 	 * to do here...
@@ -372,6 +394,14 @@  void Agc::process(IPAContext &context,
 		return;
 	}
 
+	/*
+	 * Update the AGC limits using the frame duration.
+	 *
+	 * \todo Handle ExposureTime and AnalogueGain controls to support
+	 * manual mode.
+	 */
+	setExposureLimits({}, {}, frameContext.agc.maxFrameDuration, {});
+
 	statistics_.parseStatistics(stats);
 	context.activeState.agc.temperatureK = estimateCCT({ { statistics_.rHist.interQuantileMean(0, 1),
 							       statistics_.gHist.interQuantileMean(0, 1),
@@ -401,11 +431,21 @@  void Agc::process(IPAContext &context,
 		<< "Divided up shutter, analogue gain and digital gain are "
 		<< shutterTime << ", " << aGain << " and " << dGain;
 
-	activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration;
+	/* Use the frame duration to calculate the desired vblank. */
+	utils::Duration lineDuration = configuration.sensor.lineDuration;
+	utils::Duration frameDuration =
+		context.camHelper->minFrameDuration(shutterTime, lineDuration);
+
+	frameContext.agc.vblank = (frameDuration / lineDuration)
+				- context.sensorInfo.outputSize.height;
+
+	/* Populate the active state. */
+	activeState.agc.automatic.exposure = shutterTime / lineDuration;
 	activeState.agc.automatic.sensorGain = aGain;
 	activeState.agc.automatic.ispGain = dGain;
 
-	metadata.set(controls::ExposureTime, currentShutter.get<std::micro>());
+	metadata.set(controls::FrameDuration, frameDuration.get<std::micro>());
+	metadata.set(controls::ExposureTime, shutterTime.get<std::micro>());
 	metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain);
 	metadata.set(controls::DigitalGain, frameContext.agc.ispGain);
 	metadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK);
diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
index 828103f21451d9f7f4998c3faedc8fb6a1e7a2ec..4b76ac25ec4a2e1d2e07642148547303cf4c6031 100644
--- a/src/ipa/mali-c55/ipa_context.h
+++ b/src/ipa/mali-c55/ipa_context.h
@@ -10,6 +10,8 @@ 
 #include <libcamera/base/utils.h>
 #include <libcamera/controls.h>
 
+#include <libcamera/ipa/core_ipa_interface.h>
+
 #include "libcamera/internal/bayer_format.h"
 
 #include <libipa/camera_sensor_helper.h>
@@ -67,6 +69,9 @@  struct IPAFrameContext : public FrameContext {
 		uint32_t exposure;
 		double sensorGain;
 		double ispGain;
+		uint32_t vblank;
+		utils::Duration minFrameDuration;
+		utils::Duration maxFrameDuration;
 	} agc;
 
 	struct {
@@ -81,6 +86,7 @@  struct IPAContext {
 	{
 	}
 
+	IPACameraSensorInfo sensorInfo;
 	IPASessionConfiguration configuration;
 	IPAActiveState activeState;
 
diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp
index 02f5dfb76eae073858ec688746b7e12ec072e567..60b5ee8d3060e9f3a4794550fe4140d58125a925 100644
--- a/src/ipa/mali-c55/mali-c55.cpp
+++ b/src/ipa/mali-c55/mali-c55.cpp
@@ -68,7 +68,7 @@  private:
 	void updateControls(const IPACameraSensorInfo &sensorInfo,
 			    const ControlInfoMap &sensorControls,
 			    ControlInfoMap *ipaControls);
-	void setControls();
+	void setControls(unsigned int frame);
 
 	std::map<unsigned int, MappedFrameBuffer> buffers_;
 
@@ -126,14 +126,17 @@  int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig
 	if (ret)
 		return ret;
 
+	context_.sensorInfo = ipaConfig.sensorInfo;
 	updateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls);
 
 	return 0;
 }
 
-void IPAMaliC55::setControls()
+void IPAMaliC55::setControls(unsigned int frame)
 {
+	IPAFrameContext &frameContext = context_.frameContexts.get(frame);
 	IPAActiveState &activeState = context_.activeState;
+	uint32_t vblank = frameContext.agc.vblank;
 	uint32_t exposure;
 	uint32_t gain;
 
@@ -148,6 +151,7 @@  void IPAMaliC55::setControls()
 	ControlList ctrls(sensorControls_);
 	ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));
 	ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));
+	ctrls.set(V4L2_CID_VBLANK, static_cast<int32_t>(vblank));
 
 	setSensorControls.emit(ctrls);
 }
@@ -375,7 +379,7 @@  void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,
 		algo->process(context_, request, frameContext, stats, metadata);
 	}
 
-	setControls();
+	setControls(request);
 
 	statsProcessed.emit(request, metadata);
 }