diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
index 40f78d9e3b3f..325d2d855bc0 100644
--- a/include/libcamera/ipa/raspberrypi.mojom
+++ b/include/libcamera/ipa/raspberrypi.mojom
@@ -37,6 +37,8 @@ struct ISPConfig {
 	uint32 bayerBufferId;
 	bool embeddedBufferPresent;
 	libcamera.ControlList controls;
+	uint32 ipaContext;
+	uint32 delayContext;
 };
 
 struct IPAConfig {
@@ -127,7 +129,7 @@ interface IPARPiInterface {
 	 */
 	unmapBuffers(array<uint32> ids);
 
-	[async] signalStatReady(uint32 bufferId);
+	[async] signalStatReady(uint32 bufferId, uint32 ipaContext);
 	[async] signalQueueRequest(libcamera.ControlList controls);
 	[async] signalIspPrepare(ISPConfig data);
 };
@@ -137,5 +139,5 @@ interface IPARPiEventInterface {
 	runIsp(uint32 bufferId);
 	embeddedComplete(uint32 bufferId);
 	setIspControls(libcamera.ControlList controls);
-	setDelayedControls(libcamera.ControlList controls);
+	setDelayedControls(libcamera.ControlList controls, uint32 delayContext);
 };
diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
index 799a4fe70000..194171a8bc96 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -126,7 +126,7 @@ public:
 		      ControlList *controls, IPAConfigResult *result) override;
 	void mapBuffers(const std::vector<IPABuffer> &buffers) override;
 	void unmapBuffers(const std::vector<unsigned int> &ids) override;
-	void signalStatReady(const uint32_t bufferId) override;
+	void signalStatReady(const uint32_t bufferId, uint32_t ipaContext) override;
 	void signalQueueRequest(const ControlList &controls) override;
 	void signalIspPrepare(const ISPConfig &data) override;
 
@@ -137,9 +137,9 @@ private:
 	void queueRequest(const ControlList &controls);
 	void returnEmbeddedBuffer(unsigned int bufferId);
 	void prepareISP(const ISPConfig &data);
-	void reportMetadata();
-	void fillDeviceStatus(const ControlList &sensorControls);
-	void processStats(unsigned int bufferId);
+	void reportMetadata(unsigned int ipaContext);
+	void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
+	void processStats(unsigned int bufferId, unsigned int ipaContext);
 	void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration);
 	void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
 	void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
@@ -509,14 +509,16 @@ void IPARPi::unmapBuffers(const std::vector<unsigned int> &ids)
 	}
 }
 
-void IPARPi::signalStatReady(uint32_t bufferId)
+void IPARPi::signalStatReady(uint32_t bufferId, uint32_t ipaContext)
 {
+	unsigned int context = ipaContext % rpiMetadata_.size();
+
 	if (++checkCount_ != frameCount_) /* assert here? */
 		LOG(IPARPI, Error) << "WARNING: Prepare/Process mismatch!!!";
 	if (processPending_ && frameCount_ > mistrustCount_)
-		processStats(bufferId);
+		processStats(bufferId, context);
 
-	reportMetadata();
+	reportMetadata(context);
 
 	statsMetadataComplete.emit(bufferId & MaskID, libcameraMetadata_);
 }
@@ -540,9 +542,9 @@ void IPARPi::signalIspPrepare(const ISPConfig &data)
 	runIsp.emit(data.bayerBufferId & MaskID);
 }
 
-void IPARPi::reportMetadata()
+void IPARPi::reportMetadata(unsigned int ipaContext)
 {
-	RPiController::Metadata &rpiMetadata = rpiMetadata_[0];
+	RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
 	std::unique_lock<RPiController::Metadata> lock(rpiMetadata);
 
 	/*
@@ -1009,12 +1011,12 @@ void IPARPi::returnEmbeddedBuffer(unsigned int bufferId)
 void IPARPi::prepareISP(const ISPConfig &data)
 {
 	int64_t frameTimestamp = data.controls.get(controls::SensorTimestamp).value_or(0);
-	RPiController::Metadata lastMetadata;
-	RPiController::Metadata &rpiMetadata = rpiMetadata_[0];
+	unsigned int ipaContext = data.ipaContext % rpiMetadata_.size();
+	RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
 	Span<uint8_t> embeddedBuffer;
 
-	lastMetadata = std::move(rpiMetadata);
-	fillDeviceStatus(data.controls);
+	rpiMetadata.clear();
+	fillDeviceStatus(data.controls, ipaContext);
 
 	if (data.embeddedBufferPresent) {
 		/*
@@ -1046,7 +1048,9 @@ void IPARPi::prepareISP(const ISPConfig &data)
 		 * current frame, or any other bits of metadata that were added
 		 * in helper_->Prepare().
 		 */
-		rpiMetadata.merge(lastMetadata);
+		RPiController::Metadata &lastMetadata =
+			rpiMetadata_[ipaContext ? ipaContext : rpiMetadata_.size()];
+		rpiMetadata.mergeCopy(lastMetadata);
 		processPending_ = false;
 		return;
 	}
@@ -1105,7 +1109,7 @@ void IPARPi::prepareISP(const ISPConfig &data)
 		setIspControls.emit(ctrls);
 }
 
-void IPARPi::fillDeviceStatus(const ControlList &sensorControls)
+void IPARPi::fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext)
 {
 	DeviceStatus deviceStatus = {};
 
@@ -1121,12 +1125,12 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls)
 
 	LOG(IPARPI, Debug) << "Metadata - " << deviceStatus;
 
-	rpiMetadata_[0].set("device.status", deviceStatus);
+	rpiMetadata_[ipaContext].set("device.status", deviceStatus);
 }
 
-void IPARPi::processStats(unsigned int bufferId)
+void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext)
 {
-	RPiController::Metadata &rpiMetadata = rpiMetadata_[0];
+	RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
 
 	auto it = buffers_.find(bufferId);
 	if (it == buffers_.end()) {
@@ -1145,7 +1149,7 @@ void IPARPi::processStats(unsigned int bufferId)
 		ControlList ctrls(sensorCtrls_);
 		applyAGC(&agcStatus, ctrls);
 
-		setDelayedControls.emit(ctrls);
+		setDelayedControls.emit(ctrls, ipaContext);
 	}
 }
 
diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index 2c12ed1fba95..6f560fc2782e 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -206,7 +206,7 @@ public:
 	void runIsp(uint32_t bufferId);
 	void embeddedComplete(uint32_t bufferId);
 	void setIspControls(const ControlList &controls);
-	void setDelayedControls(const ControlList &controls);
+	void setDelayedControls(const ControlList &controls, uint32_t delayContext);
 	void setSensorControls(ControlList &controls);
 	void unicamTimeout();
 
@@ -262,6 +262,7 @@ public:
 	struct BayerFrame {
 		FrameBuffer *buffer;
 		ControlList controls;
+		unsigned int delayContext;
 	};
 
 	std::queue<BayerFrame> bayerQueue_;
@@ -1792,9 +1793,9 @@ void RPiCameraData::setIspControls(const ControlList &controls)
 	handleState();
 }
 
-void RPiCameraData::setDelayedControls(const ControlList &controls)
+void RPiCameraData::setDelayedControls(const ControlList &controls, uint32_t delayContext)
 {
-	if (!delayedCtrls_->push(controls, 0))
+	if (!delayedCtrls_->push(controls, delayContext))
 		LOG(RPI, Error) << "V4L2 DelayedControl set failed";
 	handleState();
 }
@@ -1867,13 +1868,13 @@ void RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer)
 		 * Lookup the sensor controls used for this frame sequence from
 		 * DelayedControl and queue them along with the frame buffer.
 		 */
-		auto [ctrl, cookie] = delayedCtrls_->get(buffer->metadata().sequence);
+		auto [ctrl, delayContext] = delayedCtrls_->get(buffer->metadata().sequence);
 		/*
 		 * Add the frame timestamp to the ControlList for the IPA to use
 		 * as it does not receive the FrameBuffer object.
 		 */
 		ctrl.set(controls::SensorTimestamp, buffer->metadata().timestamp);
-		bayerQueue_.push({ buffer, std::move(ctrl) });
+		bayerQueue_.push({ buffer, std::move(ctrl), delayContext });
 	} else {
 		embeddedQueue_.push(buffer);
 	}
@@ -1923,7 +1924,8 @@ void RPiCameraData::ispOutputDequeue(FrameBuffer *buffer)
 	 * application until after the IPA signals so.
 	 */
 	if (stream == &isp_[Isp::Stats]) {
-		ipa_->signalStatReady(ipa::RPi::MaskStats | static_cast<unsigned int>(index));
+		ipa_->signalStatReady(ipa::RPi::MaskStats | static_cast<unsigned int>(index),
+				      requestQueue_.front()->sequence());
 	} else {
 		/* Any other ISP output can be handed back to the application now. */
 		handleStreamBuffer(buffer, stream);
@@ -2168,6 +2170,8 @@ void RPiCameraData::tryRunPipeline()
 	ipa::RPi::ISPConfig ispPrepare;
 	ispPrepare.bayerBufferId = ipa::RPi::MaskBayerData | bayerId;
 	ispPrepare.controls = std::move(bayerFrame.controls);
+	ispPrepare.ipaContext = request->sequence();
+	ispPrepare.delayContext = bayerFrame.delayContext;
 
 	if (embeddedBuffer) {
 		unsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);
