[DNI,v2,10/10] ipa: mali: Handle FrameDurationLimits
diff mbox series

Message ID 20251028-exposure-limits-v2-10-a8b5a318323e@ideasonboard.com
State New
Headers show
Series
  • libipa: agc: Calculate exposure limits
Related show

Commit Message

Jacopo Mondi Oct. 28, 2025, 9:31 a.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 | 63 +++++++++++++++++++++++++++++++------
 src/ipa/mali-c55/ipa_context.h      |  6 ++++
 src/ipa/mali-c55/mali-c55.cpp       | 10 ++++--
 3 files changed, 67 insertions(+), 12 deletions(-)

Patch
diff mbox series

diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
index 6ecb18d959c7f978966d619beb43e1c2cbfeec5f..60224cc0e261709000901ab9a91c3566cbace6c5 100644
--- a/src/ipa/mali-c55/algorithms/agc.cpp
+++ b/src/ipa/mali-c55/algorithms/agc.cpp
@@ -170,21 +170,22 @@  int Agc::configure(IPAContext &context,
 	context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first;
 
 	ControlInfo &frameDurationLimits = context.ctrlMap[&controls::FrameDurationLimits];
+	context.activeState.agc.minFrameDuration =
+		std::chrono::microseconds(frameDurationLimits.min().get<int64_t>());
+	context.activeState.agc.maxFrameDuration =
+		std::chrono::microseconds(frameDurationLimits.max().get<int64_t>());
 
 	AgcMeanLuminance::AgcSensorConfiguration sensorConfig;
 	sensorConfig.lineDuration = context.configuration.sensor.lineDuration;
 	sensorConfig.minExposureTime = context.configuration.agc.minShutterSpeed;
 	sensorConfig.maxExposureTime = context.configuration.agc.maxShutterSpeed;
-	sensorConfig.minFrameDuration =
-		std::chrono::microseconds(frameDurationLimits.min().get<int64_t>());
-	sensorConfig.maxFrameDuration =
-		std::chrono::microseconds(frameDurationLimits.max().get<int64_t>());
+	sensorConfig.minFrameDuration = context.activeState.agc.minFrameDuration;
+	sensorConfig.maxFrameDuration = context.activeState.agc.maxFrameDuration;
 	sensorConfig.minAnalogueGain = context.configuration.agc.minAnalogueGain;
 	sensorConfig.maxAnalogueGain = context.configuration.agc.maxAnalogueGain;
 
 	AgcMeanLuminance::configure(&sensorConfig, context.camHelper.get());
-
-	/* \todo Update AGC limits when FrameDurationLimits is passed in */
+	context.activeState.agc.maxFrameDuration = sensorConfig.maxFrameDuration;
 
 	resetFrameCount();
 
@@ -212,6 +213,25 @@  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 limits in ControlInfo */
+		ControlInfo &limits = context.ctrlMap[&controls::FrameDurationLimits];
+		int64_t minFrameDuration =
+			std::clamp((*frameDurationLimits).front(),
+				   limits.min().get<int64_t>(),
+				   limits.max().get<int64_t>());
+		int64_t maxFrameDuration =
+			std::clamp((*frameDurationLimits).back(),
+				   limits.min().get<int64_t>(),
+				   limits.max().get<int64_t>());
+
+		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...
@@ -375,6 +395,21 @@  void Agc::process(IPAContext &context,
 		return;
 	}
 
+	/*
+	 * Update the AGC limits using the frame duration.
+	 *
+	 * \todo Handle ExposureTime and AnalogueGain controls to support
+	 * manual mode.
+	 */
+	utils::Duration minExposureTime = context.configuration.agc.minShutterSpeed;
+	utils::Duration maxExposureTime = context.configuration.agc.maxShutterSpeed;
+	utils::Duration maxFrameDuration = frameContext.agc.maxFrameDuration;
+
+	setLimits(minExposureTime, maxExposureTime, maxFrameDuration,
+		  context.configuration.agc.minAnalogueGain,
+		  context.configuration.agc.maxAnalogueGain,
+		  {});
+
 	statistics_.parseStatistics(stats);
 	context.activeState.agc.temperatureK = estimateCCT({ { statistics_.rHist.interQuantileMean(0, 1),
 							       statistics_.gHist.interQuantileMean(0, 1),
@@ -402,13 +437,23 @@  void Agc::process(IPAContext &context,
 	dGain = std::clamp(dGain, kMinDigitalGain, kMaxDigitalGain);
 
 	LOG(MaliC55Agc, Debug)
-		<< "Divided up shutter, analogue gain and digital gain are "
-		<< shutterTime << ", " << aGain << " and " << dGain;
+		<< "Divided up shutter, frame duration, analogue gain and digital gain are "
+		<< shutterTime << ", " << frameDuration << ", " << aGain
+		<< " and " << dGain;
+
+	/* Use the frame duration to calculate the desired vblank. */
+	utils::Duration lineDuration = configuration.sensor.lineDuration;
+	IPACameraSensorInfo &sensorInfo = context.sensorInfo;
+
+	frameContext.agc.vblank = (frameDuration / lineDuration)
+				- sensorInfo.outputSize.height;
 
-	activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration;
+	/* Populate the active state. */
+	activeState.agc.automatic.exposure = shutterTime / lineDuration;
 	activeState.agc.automatic.sensorGain = aGain;
 	activeState.agc.automatic.ispGain = dGain;
 
+	metadata.set(controls::FrameDuration, frameDuration.get<std::micro>());
 	metadata.set(controls::ExposureTime, currentShutter.get<std::micro>());
 	metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain);
 	metadata.set(controls::DigitalGain, frameContext.agc.ispGain);
diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
index ecb2f79c0dca0e41166b88f2608c22aa72adcf8a..107e55a2dab325e8d360da1281b212aa052ec724 100644
--- a/src/ipa/mali-c55/ipa_context.h
+++ b/src/ipa/mali-c55/ipa_context.h
@@ -53,6 +53,8 @@  struct IPAActiveState {
 		uint32_t constraintMode;
 		uint32_t exposureMode;
 		uint32_t temperatureK;
+		utils::Duration minFrameDuration;
+		utils::Duration maxFrameDuration;
 	} agc;
 
 	struct {
@@ -66,6 +68,9 @@  struct IPAFrameContext : public FrameContext {
 		uint32_t exposure;
 		double sensorGain;
 		double ispGain;
+		uint32_t vblank;
+		utils::Duration minFrameDuration;
+		utils::Duration maxFrameDuration;
 	} agc;
 
 	struct {
@@ -80,6 +85,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 12cad7374520db8c6fa5ca233a0ef33dc7b2f287..7454e4b0d7c8703398cdb716f9a52ea551784371 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);
 }
@@ -368,7 +372,7 @@  void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,
 		algo->process(context_, request, frameContext, stats, metadata);
 	}
 
-	setControls();
+	setControls(request);
 
 	statsProcessed.emit(request, metadata);
 }