diff --git a/include/libcamera/ipa/rkisp1.mojom b/include/libcamera/ipa/rkisp1.mojom
index 4c29b53cd7f9..56c9fe8ab92a 100644
--- a/include/libcamera/ipa/rkisp1.mojom
+++ b/include/libcamera/ipa/rkisp1.mojom
@@ -17,6 +17,7 @@ struct IPAConfigInfo {
 struct StartResult {
 	libcamera.ControlList controls;
 	int32 code;
+	uint32 paramBufferBytesUsed;
 };
 
 interface IPARkISP1Interface {
@@ -25,7 +26,9 @@ interface IPARkISP1Interface {
 	     libcamera.IPACameraSensorInfo sensorInfo,
 	     libcamera.ControlInfoMap sensorControls)
 		=> (int32 ret, libcamera.ControlInfoMap ipaControls);
-	start(libcamera.ControlList controls) => (StartResult result);
+	start(libcamera.ControlList controls,
+	      uint32 paramBufferId)
+		=> (StartResult result);
 	stop();
 
 	configure(IPAConfigInfo configInfo,
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index d0c753976dd4..8a657a170b0d 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -57,7 +57,8 @@ public:
 		 const IPACameraSensorInfo &sensorInfo,
 		 const ControlInfoMap &sensorControls,
 		 ControlInfoMap *ipaControls) override;
-	void start(const ControlList &controls, StartResult *result) override;
+	void start(const ControlList &controls, const uint32_t paramBufferId,
+		   StartResult *result) override;
 	void stop() override;
 
 	int configure(const IPAConfigInfo &ipaConfig,
@@ -77,6 +78,8 @@ protected:
 	std::string logPrefix() const override;
 
 private:
+	uint32_t computeParamsInternal(IPAFrameContext &frameContext, const uint32_t bufferId);
+
 	void updateControls(const IPACameraSensorInfo &sensorInfo,
 			    const ControlInfoMap &sensorControls,
 			    ControlInfoMap *ipaControls);
@@ -219,9 +222,11 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
 	return 0;
 }
 
-void IPARkISP1::start(const ControlList &controls, StartResult *result)
+void IPARkISP1::start(const ControlList &controls, const uint32_t paramBufferId,
+		      StartResult *result)
 {
 	IPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(0, controls);
+	result->paramBufferBytesUsed = computeParamsInternal(frameContext, paramBufferId);
 	result->controls = getSensorControls(frameContext);
 	result->code = 0;
 }
@@ -353,32 +358,30 @@ void IPARkISP1::initializeFrameContext(IPAFrameContext &fc, const ControlList &c
 	}
 }
 
-void IPARkISP1::computeParams(const uint32_t frame, const uint32_t bufferId)
+uint32_t IPARkISP1::computeParamsInternal(IPAFrameContext &frameContext, const uint32_t bufferId)
 {
-	IPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);
-
-	/*
-	 * \todo: This needs discussion. In raw mode, computeParams is
-	 * called without a params buffer, to trigger the setSensorControls
-	 * signal. Currently our algorithms don't support prepare calls with
-	 * a nullptr. Do we need that or can we safely skip it?
-	 */
-	if (bufferId == 0) {
-		ControlList ctrls = getSensorControls(frameContext);
-		setSensorControls.emit(frame, ctrls);
-		return;
-	}
+	if (bufferId == 0)
+		return 0;
 
 	RkISP1Params params(context_.configuration.paramFormat,
 			    mappedBuffers_.at(bufferId).planes()[0]);
 
 	for (const auto &algo : algorithms())
-		algo->prepare(context_, frame, frameContext, &params);
+		algo->prepare(context_, frameContext.frame(), frameContext, &params);
 
+	return params.bytesused();
+}
+
+void IPARkISP1::computeParams(const uint32_t frame, const uint32_t bufferId)
+{
+	IPAFrameContext &frameContext = context_.frameContexts.getOrInitContext(frame);
+
+	uint32_t size = computeParamsInternal(frameContext, bufferId);
 	ControlList ctrls = getSensorControls(frameContext);
 	setSensorControls.emit(frame, ctrls);
 
-	paramsComputed.emit(frame, params.bytesused());
+	if (bufferId != 0)
+		paramsComputed.emit(frame, size);
 }
 
 void IPARkISP1::processStats(const uint32_t frame, const uint32_t bufferId,
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 46c157526ea1..c2af5ef8897f 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -105,8 +105,8 @@ public:
 	bool canUseDewarper_;
 	bool usesDewarper_;
 
-private:
 	void paramsComputed(unsigned int frame, unsigned int bytesused);
+private:
 	void setSensorControls(unsigned int frame,
 			       const ControlList &sensorControls);
 
@@ -1202,13 +1202,28 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
 	nextStatsToProcess_ = 0;
 	data->frame_ = 0;
 
+	uint32_t paramBufferId = 0;
+	FrameBuffer *paramBuffer = nullptr;
+	if (!isRaw_) {
+		paramBuffer = availableParamBuffers_.front();
+		paramBufferId = paramBuffer->cookie();
+	}
+
 	ipa::rkisp1::StartResult res;
-	data->ipa_->start(ctrls, &res);
+	data->ipa_->start(ctrls, paramBufferId, &res);
 	if (res.code) {
 		LOG(RkISP1, Error)
 			<< "Failed to start IPA " << camera->id();
 		return ret;
 	}
+
+	if (paramBuffer) {
+		availableParamBuffers_.pop();
+		computingParamBuffers_.push({ paramBuffer, nextParamsSequence_++ });
+		paramsSyncHelper_.pushCorrection(0);
+		data->paramsComputed(0, res.paramBufferBytesUsed);
+	}
+
 	actions += [&]() { data->ipa_->stop(); };
 	data->sensor_->setControls(&res.controls);
 	data->delayedCtrls_->reset();
