diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index f5a3c917cb6909f6ef918e5ee8e46cf97ba55010..aa1a90daf3ca7d0041c56000c12fc4d1ab5700eb 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -190,10 +190,10 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
 	context.activeState.agc.meteringMode =
 		static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
 
-	/* Limit the frame duration to match current initialisation */
-	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>());
+	context.activeState.agc.minFrameDuration =
+		context.configuration.sensor.minFrameDuration;
+	context.activeState.agc.maxFrameDuration =
+		context.configuration.sensor.maxFrameDuration;
 
 	context.configuration.agc.measureWindow.h_offs = 0;
 	context.configuration.agc.measureWindow.v_offs = 0;
@@ -320,16 +320,16 @@ void Agc::queueRequest(IPAContext &context,
 
 	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>());
+		/* 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);
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index f85a130d9c23dba7987f388e395239e4b141d776..5fe727bd0b508617d993d226ae785056a3771ce0 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -62,6 +62,8 @@ struct IPASessionConfiguration {
 	struct {
 		utils::Duration minExposureTime;
 		utils::Duration maxExposureTime;
+		utils::Duration minFrameDuration;
+		utils::Duration maxFrameDuration;
 		double minAnalogueGain;
 		double maxAnalogueGain;
 
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index fa22bfc349043002345d275b11a60ac983e329d7..f25e477f0fb77241bd1ccddb7778205e58bdc8a9 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -227,6 +227,7 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,
 			 const std::map<uint32_t, IPAStream> &streamConfig,
 			 ControlInfoMap *ipaControls)
 {
+	const IPACameraSensorInfo &info = ipaConfig.sensorInfo;
 	sensorControls_ = ipaConfig.sensorControls;
 
 	const auto itExp = sensorControls_.find(V4L2_CID_EXPOSURE);
@@ -237,6 +238,12 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,
 	int32_t minGain = itGain->second.min().get<int32_t>();
 	int32_t maxGain = itGain->second.max().get<int32_t>();
 
+	const auto itVBlank = sensorControls_.find(V4L2_CID_VBLANK);
+	std::array<uint32_t, 2> frameHeights{
+		itVBlank->second.min().get<int32_t>() + info.outputSize.height,
+		itVBlank->second.max().get<int32_t>() + info.outputSize.height,
+	};
+
 	LOG(IPARkISP1, Debug)
 		<< "Exposure: [" << minExposure << ", " << maxExposure
 		<< "], gain: [" << minGain << ", " << maxGain << "]";
@@ -248,11 +255,10 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,
 
 	context_.configuration.paramFormat = ipaConfig.paramFormat;
 
-	const IPACameraSensorInfo &info = ipaConfig.sensorInfo;
-	const ControlInfo vBlank = sensorControls_.find(V4L2_CID_VBLANK)->second;
-	context_.configuration.sensor.defVBlank = vBlank.def().get<int32_t>();
+	utils::Duration lineDuration = info.minLineLength * 1.0s / info.pixelRate;
+	context_.configuration.sensor.defVBlank = itVBlank->second.def().get<int32_t>();
 	context_.configuration.sensor.size = info.outputSize;
-	context_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate;
+	context_.configuration.sensor.lineDuration = lineDuration;
 
 	/* Update the camera controls using the new sensor settings. */
 	updateControls(info, sensorControls_, ipaControls);
@@ -261,17 +267,13 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,
 	 * When the AGC computes the new exposure values for a frame, it needs
 	 * to know the limits for exposure time and analogue gain. As it depends
 	 * on the sensor, update it with the controls.
-	 *
-	 * \todo take VBLANK into account for maximum exposure time
 	 */
-	context_.configuration.sensor.minExposureTime =
-		minExposure * context_.configuration.sensor.lineDuration;
-	context_.configuration.sensor.maxExposureTime =
-		maxExposure * context_.configuration.sensor.lineDuration;
-	context_.configuration.sensor.minAnalogueGain =
-		context_.camHelper->gain(minGain);
-	context_.configuration.sensor.maxAnalogueGain =
-		context_.camHelper->gain(maxGain);
+	context_.configuration.sensor.minExposureTime = minExposure * lineDuration;
+	context_.configuration.sensor.maxExposureTime = maxExposure * lineDuration;
+	context_.configuration.sensor.minFrameDuration = frameHeights[0] * lineDuration;
+	context_.configuration.sensor.maxFrameDuration = frameHeights[1] * lineDuration;
+	context_.configuration.sensor.minAnalogueGain = context_.camHelper->gain(minGain);
+	context_.configuration.sensor.maxAnalogueGain = context_.camHelper->gain(maxGain);
 
 	context_.configuration.raw = std::any_of(streamConfig.begin(), streamConfig.end(),
 		[](auto &cfg) -> bool {
@@ -436,12 +438,20 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo,
 		uint64_t frameSize = lineLength * frameHeights[i];
 		frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U);
 	}
-
-	/* \todo Move this (and other agc-related controls) to agc */
-	context_.ctrlMap[&controls::FrameDurationLimits] =
+	ctrlMap[&controls::FrameDurationLimits] =
 		ControlInfo(frameDurations[0], frameDurations[1], frameDurations[2]);
 
-	ctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end());
+	/*
+	 * Store the min/max frame duration in the active context to initialize
+	 * the AGC algorithm.
+	 *
+	 * \todo Move this (and other agc-related controls) to agc
+	 */
+	context_.activeState.agc.minFrameDuration = std::chrono::microseconds(frameDurations[0]);
+	context_.activeState.agc.maxFrameDuration = std::chrono::microseconds(frameDurations[1]);
+
+	ctrlMap.merge(context_.ctrlMap);
+
 	*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
 }
 
