diff --git a/include/libcamera/ipa/vimc.mojom b/include/libcamera/ipa/vimc.mojom
index 8cb240d3..85a2d8e4 100644
--- a/include/libcamera/ipa/vimc.mojom
+++ b/include/libcamera/ipa/vimc.mojom
@@ -29,8 +29,20 @@ interface IPAVimcInterface {
 
 	mapBuffers(array<libcamera.IPABuffer> buffers);
 	unmapBuffers(array<uint32> ids);
+
+	/*
+	 * VIMC being a virtual test driver does not have actual parameters
+	 * and statistics buffers. However, VIMC pipeline creates mock IPA
+	 * buffers and map it to IPA.
+	 *
+	 * Since, we are not working with actual IPA buffers here,
+	 * following are mostly stub functions, introduced to leverage IPA
+	 * IPC tests.
+	 */
+	[async] fillParams(uint32 frame, uint32 bufferId);
+	[async] processControls(uint32 frame, libcamera.ControlList controls);
 };
 
 interface IPAVimcEventInterface {
-	dummyEvent(uint32 val);
+	paramsFilled(uint32 bufferId);
 };
diff --git a/src/ipa/vimc/vimc.cpp b/src/ipa/vimc/vimc.cpp
index 082b2db7..be189181 100644
--- a/src/ipa/vimc/vimc.cpp
+++ b/src/ipa/vimc/vimc.cpp
@@ -43,6 +43,9 @@ public:
 	void mapBuffers(const std::vector<IPABuffer> &buffers) override;
 	void unmapBuffers(const std::vector<unsigned int> &ids) override;
 
+	void fillParams(uint32_t frame, uint32_t bufferId) override;
+	void processControls(uint32_t frame, const ControlList &controls) override;
+
 private:
 	void initTrace();
 	void trace(enum ipa::vimc::IPAOperationCode operation);
@@ -125,6 +128,22 @@ void IPAVimc::unmapBuffers(const std::vector<unsigned int> &ids)
 	}
 }
 
+void IPAVimc::fillParams([[maybe_unused]] uint32_t frame, uint32_t bufferId)
+{
+	auto it = buffers_.find(bufferId);
+	if (it == buffers_.end()) {
+		LOG(IPAVimc, Error) << "Could not find parameter buffer";
+		return;
+	}
+
+	paramsFilled.emit(bufferId);
+}
+
+void IPAVimc::processControls([[maybe_unused]] uint32_t frame,
+			      [[maybe_unused]] const ControlList &controls)
+{
+}
+
 void IPAVimc::initTrace()
 {
 	struct stat fifoStat;
diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
index c44a6242..bfd545c8 100644
--- a/src/libcamera/pipeline/vimc/vimc.cpp
+++ b/src/libcamera/pipeline/vimc/vimc.cpp
@@ -52,6 +52,7 @@ public:
 	int init();
 	int allocateMockIPABuffers();
 	void bufferReady(FrameBuffer *buffer);
+	void paramsFilled(unsigned int id);
 
 	MediaDevice *media_;
 	std::unique_ptr<CameraSensor> sensor_;
@@ -433,6 +434,8 @@ int PipelineHandlerVimc::queueRequestDevice(Camera *camera, Request *request)
 	if (ret < 0)
 		return ret;
 
+	data->ipa_->processControls(request->sequence(), request->controls());
+
 	return 0;
 }
 
@@ -466,6 +469,8 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
 		return false;
 	}
 
+	data->ipa_->paramsFilled.connect(data.get(), &VimcCameraData::paramsFilled);
+
 	std::string conf = data->ipa_->configurationFile("vimc.conf");
 	data->ipa_->init(IPASettings{ conf, data->sensor_->model() });
 
@@ -570,6 +575,8 @@ void VimcCameraData::bufferReady(FrameBuffer *buffer)
 
 	pipe_->completeBuffer(request, buffer);
 	pipe_->completeRequest(request);
+
+	ipa_->fillParams(request->sequence(), mockIPABufs_[0]->cookie());
 }
 
 int VimcCameraData::allocateMockIPABuffers()
@@ -591,6 +598,10 @@ int VimcCameraData::allocateMockIPABuffers()
 	return video_->exportBuffers(bufCount, &mockIPABufs_);
 }
 
+void VimcCameraData::paramsFilled([[maybe_unused]] unsigned int id)
+{
+}
+
 REGISTER_PIPELINE_HANDLER(PipelineHandlerVimc)
 
 } /* namespace libcamera */
