diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
index 1b7e03582e0e..84566648d213 100644
--- a/include/libcamera/ipa/raspberrypi.mojom
+++ b/include/libcamera/ipa/raspberrypi.mojom
@@ -33,6 +33,7 @@ struct BufferIds {
 	uint32 bayer;
 	uint32 embedded;
 	uint32 stats;
+	uint32 params;
 };
 
 struct ConfigParams {
@@ -219,15 +220,16 @@ interface IPARPiEventInterface {
 	/**
 	 * \fn prepareIspComplete()
 	 * \brief Signal completion of \a prepareIsp
-	 * \param[in] buffers Bayer and embedded buffers actioned.
+	 * \param[in] buffers Bayer, embedded and parameter buffers actioned.
 	 * \param[in] stitchSwapBuffers Whether the stitch block buffers need to be swapped.
+	 * \param[in] paramsBytesUsed Number of bytes used in the parameter buffer
 	 *
 	 * This asynchronous event is signalled to the pipeline handler once
 	 * the \a prepareIsp signal has completed, and the ISP is ready to start
 	 * processing the frame. The embedded data buffer may be recycled after
 	 * this event.
 	 */
-	prepareIspComplete(BufferIds buffers, bool stitchSwapBuffers);
+	prepareIspComplete(BufferIds buffers, bool stitchSwapBuffers, uint32 paramsBytesUsed);
 
 	/**
 	 * \fn processStatsComplete()
diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
index 293f68454a84..5af1025650d4 100644
--- a/src/ipa/rpi/common/ipa_base.cpp
+++ b/src/ipa/rpi/common/ipa_base.cpp
@@ -418,6 +418,7 @@ void IpaBase::prepareIsp(const PrepareParams &params)
 	unsigned int ipaContext = params.ipaContext % rpiMetadata_.size();
 	RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
 	std::span<uint8_t> embeddedBuffer;
+	std::span<uint8_t> paramsBuffer;
 
 	rpiMetadata.clear();
 	fillDeviceStatus(params.sensorControls, ipaContext);
@@ -441,6 +442,13 @@ void IpaBase::prepareIsp(const PrepareParams &params)
 		embeddedBuffer = it->second.planes()[0];
 	}
 
+	if (params.buffers.params) {
+		auto it = buffers_.find(params.buffers.params);
+		ASSERT(it != buffers_.end());
+		paramsBuffer = it->second.planes()[0];
+		platformParamsBufferInit(paramsBuffer);
+	}
+
 	/*
 	 * AGC wants to know the algorithm status from the time it actioned the
 	 * sensor exposure/gain changes. So fetch it from the metadata list
@@ -513,7 +521,8 @@ void IpaBase::prepareIsp(const PrepareParams &params)
 		reportMetadata(ipaContext);
 
 	/* Ready to push the input buffer into the ISP. */
-	prepareIspComplete.emit(params.buffers, stitchSwapBuffers_);
+	prepareIspComplete.emit(params.buffers, stitchSwapBuffers_,
+				platformParamsBytesUsed());
 }
 
 void IpaBase::processStats(const ProcessParams &params)
diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
index 1c375669c9f7..88773e1ac759 100644
--- a/src/ipa/rpi/common/ipa_base.h
+++ b/src/ipa/rpi/common/ipa_base.h
@@ -79,6 +79,8 @@ protected:
 	/* Whether the stitch block (if available) needs to swap buffers. */
 	bool stitchSwapBuffers_;
 
+	virtual size_t platformParamsBytesUsed() const { return 0; }
+
 private:
 	/* Number of metadata objects available in the context list. */
 	static constexpr unsigned int numMetadataContexts = 16;
@@ -87,7 +89,9 @@ private:
 	virtual int32_t platformStart(const ControlList &controls, StartResult *result) = 0;
 	virtual int32_t platformConfigure(const ConfigParams &params, ConfigResult *result) = 0;
 
+	virtual void platformParamsBufferInit([[maybe_unused]] std::span<uint8_t> paramsBuffer) {}
 	virtual void platformPrepareIsp(RPiController::Metadata &rpiMetadata) = 0;
+
 	virtual void platformPrepareAgc(RPiController::Metadata &rpiMetadata) = 0;
 	virtual RPiController::StatisticsPtr platformProcessStats(std::span<uint8_t> mem) = 0;
 
diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
index 159c6b05ee7b..d0103adbe17c 100644
--- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp
+++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
@@ -759,7 +759,8 @@ public:
 	void beOutputDequeue(FrameBuffer *buffer);
 
 	void processStatsComplete(const ipa::RPi::BufferIds &buffers);
-	void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers);
+	void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,
+				unsigned int paramsBytesUsed);
 	void setCameraTimeout(uint32_t maxFrameLengthMs);
 
 	/* Array of CFE and ISP device streams and associated buffers/streams. */
@@ -1885,7 +1886,8 @@ void PiSPCameraData::setCameraTimeout(uint32_t maxFrameLengthMs)
 	cfe_[Cfe::Output0].dev()->setDequeueTimeout(timeout);
 }
 
-void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers)
+void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,
+					[[maybe_unused]] unsigned int paramsBytesUsed)
 {
 	unsigned int embeddedId = buffers.embedded & RPi::MaskID;
 	unsigned int bayerId = buffers.bayer & RPi::MaskID;
diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
index 200e3b58724a..dca2c1c12549 100644
--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
@@ -79,7 +79,8 @@ public:
 	void ispOutputDequeue(FrameBuffer *buffer);
 
 	void processStatsComplete(const ipa::RPi::BufferIds &buffers);
-	void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers);
+	void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,
+				unsigned int paramsBytesUsed);
 	void setIspControls(const ControlList &controls);
 	void setCameraTimeout(uint32_t maxFrameLengthMs);
 
@@ -863,7 +864,8 @@ void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)
 }
 
 void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers,
-				       [[maybe_unused]] bool stitchSwapBuffers)
+				       [[maybe_unused]] bool stitchSwapBuffers,
+				       [[maybe_unused]] unsigned int paramsBytesUsed)
 {
 	unsigned int embeddedId = buffers.embedded & RPi::MaskID;
 	unsigned int bayer = buffers.bayer & RPi::MaskID;
