diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index 2ec14a3c7..30b25cddf 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -88,7 +88,8 @@ public:
 	Signal<const ControlList &> setSensorControls;
 
 private:
-	void saveIspParams();
+	void saveIspParams(uint32_t paramsBufferId);
+	void releaseIspParams(uint32_t paramsBufferId);
 	void setSensorCtrls(const ControlList &sensorControls);
 	void statsReady(uint32_t frame, uint32_t bufferId);
 	void inputReady(FrameBuffer *input);
diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
index 77328c5fd..f37c1e747 100644
--- a/include/libcamera/ipa/soft.mojom
+++ b/include/libcamera/ipa/soft.mojom
@@ -25,7 +25,7 @@ interface IPASoftInterface {
 		=> (int32 ret);
 
 	[async] queueRequest(uint32 frame, libcamera.ControlList sensorControls);
-	[async] computeParams(uint32 frame);
+	[async] computeParams(uint32 frame, uint32 paramsBufferId);
 	[async] processStats(uint32 frame,
 			     uint32 bufferId,
 			     libcamera.ControlList sensorControls);
@@ -33,6 +33,6 @@ interface IPASoftInterface {
 
 interface IPASoftEventInterface {
 	setSensorControls(libcamera.ControlList sensorControls);
-	setIspParams();
+	setIspParams(uint32 paramsBufferId);
 	metadataReady(uint32 frame, libcamera.ControlList metadata);
 };
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
index 6bef597c8..1e8b5334c 100644
--- a/src/ipa/simple/soft_simple.cpp
+++ b/src/ipa/simple/soft_simple.cpp
@@ -64,7 +64,9 @@ public:
 	void stop() override;
 
 	void queueRequest(const uint32_t frame, const ControlList &controls) override;
-	void computeParams(const uint32_t frame) override;
+	void computeParams(const uint32_t frame,
+			   const uint32_t
+				   paramsBufferId) override;
 	void processStats(const uint32_t frame, const uint32_t bufferId,
 			  const ControlList &sensorControls) override;
 
@@ -283,7 +285,8 @@ void IPASoftSimple::queueRequest(const uint32_t frame, const ControlList &contro
 		algo->queueRequest(context_, frame, frameContext, controls);
 }
 
-void IPASoftSimple::computeParams(const uint32_t frame)
+void IPASoftSimple::computeParams(const uint32_t frame,
+				  const uint32_t paramsBufferId)
 {
 	context_.activeState.combinedMatrix = Matrix<float, 3, 3>::identity();
 
@@ -292,7 +295,7 @@ void IPASoftSimple::computeParams(const uint32_t frame)
 		algo->prepare(context_, frame, frameContext, params_);
 	params_->combinedMatrix = context_.activeState.combinedMatrix;
 
-	setIspParams.emit();
+	setIspParams.emit(paramsBufferId);
 }
 
 void IPASoftSimple::processStats(const uint32_t frame,
diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
index dccdd86b4..e2e294d3e 100644
--- a/src/libcamera/software_isp/debayer.cpp
+++ b/src/libcamera/software_isp/debayer.cpp
@@ -105,9 +105,10 @@ Debayer::~Debayer()
  */
 
 /**
- * \fn void Debayer::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params)
+ * \fn void Debayer::process(uint32_t frame, const uint32_t paramsBufferId, FrameBuffer *input, FrameBuffer *output, DebayerParams params)
  * \brief Process the bayer data into the requested format
  * \param[in] frame The frame number
+ * \param[in] paramsBufferId The id of the params buffer in use
  * \param[in] input The input buffer
  * \param[in] output The output buffer
  * \param[in] params The parameters to be used in debayering
@@ -176,6 +177,9 @@ Debayer::~Debayer()
  *
  * \var Debayer::DebayerInputConfig::outputFormats
  * List of pixel formats supported as output for this input configuration.
+ *
+ * \var Signal<uint32_t> Debayer::releaseIspParams
+ * \brief Signals when the processing params are no longer needed
  */
 
 /**
diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h
index ce9350b7b..0c9627d30 100644
--- a/src/libcamera/software_isp/debayer.h
+++ b/src/libcamera/software_isp/debayer.h
@@ -47,7 +47,10 @@ public:
 	virtual std::tuple<unsigned int, unsigned int>
 	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
 
-	virtual void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params) = 0;
+	virtual void process(uint32_t frame,
+			     const uint32_t paramsBufferId,
+			     FrameBuffer *input, FrameBuffer *output,
+			     const DebayerParams &params) = 0;
 	virtual int start() { return 0; }
 	virtual void stop() {}
 
@@ -59,6 +62,7 @@ public:
 
 	Signal<FrameBuffer *> inputBufferReady;
 	Signal<FrameBuffer *> outputBufferReady;
+	Signal<uint32_t> releaseIspParams;
 
 	struct DebayerInputConfig {
 		Size patternSize;
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
index d09883577..5d0e780f7 100644
--- a/src/libcamera/software_isp/debayer_cpu.cpp
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -842,7 +842,9 @@ void DebayerCpu::updateLookupTables(const DebayerParams &params)
 	params_ = params;
 }
 
-void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params)
+void DebayerCpu::process(uint32_t frame, const uint32_t paramsBufferId,
+			 FrameBuffer *input, FrameBuffer *output,
+			 const DebayerParams &params)
 {
 	bench_.startFrame();
 
@@ -852,6 +854,8 @@ void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output
 
 	updateLookupTables(params);
 
+	releaseIspParams.emit(paramsBufferId);
+
 	/* Copy metadata from the input buffer */
 	FrameMetadata &metadata = output->_d()->metadata();
 	metadata.status = input->metadata().status;
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
index 7a6517462..13253f999 100644
--- a/src/libcamera/software_isp/debayer_cpu.h
+++ b/src/libcamera/software_isp/debayer_cpu.h
@@ -39,7 +39,9 @@ public:
 	std::vector<PixelFormat> formats(PixelFormat input);
 	std::tuple<unsigned int, unsigned int>
 	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
-	void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params);
+	void process(uint32_t frame,
+		     const uint32_t paramsBufferId,
+		     FrameBuffer *input, FrameBuffer *output, const DebayerParams &params);
 	SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
 	const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
 
diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 93f7c6946..947c05c07 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -533,7 +533,9 @@ int DebayerEGL::debayerGPU(MappedFrameBuffer &in, int out_fd, const DebayerParam
 	return 0;
 }
 
-void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params)
+void DebayerEGL::process(uint32_t frame, const uint32_t paramsBufferId,
+			 FrameBuffer *input, FrameBuffer *output,
+			 const DebayerParams &params)
 {
 	bench_.startFrame();
 
@@ -557,6 +559,7 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output
 		LOG(Debayer, Error) << "debayerGPU failed";
 		goto error;
 	}
+	releaseIspParams.emit(paramsBufferId);
 
 	bench_.finishFrame();
 
diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
index 59aebcc81..7f29f53e6 100644
--- a/src/libcamera/software_isp/debayer_egl.h
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -50,7 +50,9 @@ public:
 	std::vector<PixelFormat> formats(PixelFormat input);
 	std::tuple<unsigned int, unsigned int> strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
 
-	void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params);
+	void process(uint32_t frame, const uint32_t paramsBufferId,
+		     FrameBuffer *input, FrameBuffer *output,
+		     const DebayerParams &params);
 	int start();
 	void stop();
 
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index a83986b78..0447d2171 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -123,6 +123,7 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
 
 	debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
 	debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady);
+	debayer_->releaseIspParams.connect(this, &SoftwareIsp::releaseIspParams);
 
 	ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
 	if (!ipa_) {
@@ -396,16 +397,22 @@ void SoftwareIsp::stop()
  */
 void SoftwareIsp::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output)
 {
-	ipa_->computeParams(frame);
+	/* \todo Provide a real value */
+	constexpr uint32_t paramsBufferId = 0;
+	ipa_->computeParams(frame, paramsBufferId);
 	debayer_->invokeMethod(&Debayer::process,
-			       ConnectionTypeQueued, frame, input, output, debayerParams_);
+			       ConnectionTypeQueued, frame, paramsBufferId, input, output, debayerParams_);
 }
 
-void SoftwareIsp::saveIspParams()
+void SoftwareIsp::saveIspParams([[maybe_unused]] uint32_t paramsBufferId)
 {
 	debayerParams_ = *sharedParams_;
 }
 
+void SoftwareIsp::releaseIspParams([[maybe_unused]] uint32_t paramsBufferId)
+{
+}
+
 void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
 {
 	setSensorControls.emit(sensorControls);
