diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp
index 0ae0baa0..fc69f4cb 100644
--- a/src/ipa/raspberrypi/cam_helper.cpp
+++ b/src/ipa/raspberrypi/cam_helper.cpp
@@ -17,6 +17,11 @@
 #include "md_parser.hpp"
 
 using namespace RPiController;
+using namespace libcamera;
+
+namespace libcamera {
+LOG_DECLARE_CATEGORY(IPARPI)
+}
 
 static std::map<std::string, CamHelperCreateFunc> cam_helpers;
 
@@ -45,6 +50,17 @@ CamHelper::~CamHelper()
 	delete parser_;
 }
 
+void CamHelper::Prepare(const Span<uint8_t> &buffer,
+			const ControlList &controls, Metadata &metadata)
+{
+	parseRegisters(buffer, controls, metadata);
+}
+
+void CamHelper::Process([[maybe_unused]] StatisticsPtr &stats,
+			[[maybe_unused]] Metadata &metadata)
+{
+}
+
 uint32_t CamHelper::ExposureLines(double exposure_us) const
 {
 	assert(initialized_);
@@ -139,6 +155,39 @@ unsigned int CamHelper::MistrustFramesModeSwitch() const
 	return 0;
 }
 
+void CamHelper::parseRegisters(const Span<uint8_t> &buffer,
+			       const ControlList &controls, Metadata &metadata)
+{
+	struct DeviceStatus deviceStatus = {};
+	bool success = false;
+	uint32_t exposureLines, gainCode;
+
+	if (buffer.size()) {
+		parser_->SetBufferSize(buffer.size());
+		success = parser_->Parse(buffer.data()) == MdParser::Status::OK &&
+			  parser_->GetExposureLines(exposureLines) == MdParser::Status::OK &&
+			  parser_->GetGainCode(gainCode) == MdParser::Status::OK;
+
+		if (!success)
+			LOG(IPARPI, Error) << "Embedded buffer parsing failed";
+	}
+
+	if (!success) {
+		exposureLines = controls.get(V4L2_CID_EXPOSURE).get<int32_t>();
+		gainCode = controls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
+	}
+
+	deviceStatus.shutter_speed = Exposure(exposureLines);
+	deviceStatus.analogue_gain = Gain(gainCode);
+
+	LOG(IPARPI, Debug) << "Metadata - Exposure : "
+			   << deviceStatus.shutter_speed
+			   << " Gain : "
+			   << deviceStatus.analogue_gain;
+
+	metadata.Set("device.status", deviceStatus);
+}
+
 RegisterCamHelper::RegisterCamHelper(char const *cam_name,
 				     CamHelperCreateFunc create_func)
 {
diff --git a/src/ipa/raspberrypi/cam_helper.hpp b/src/ipa/raspberrypi/cam_helper.hpp
index 1b2d6eec..ce5b82d2 100644
--- a/src/ipa/raspberrypi/cam_helper.hpp
+++ b/src/ipa/raspberrypi/cam_helper.hpp
@@ -8,8 +8,13 @@
 
 #include <string>
 
+#include <libcamera/controls.h>
+#include <libcamera/span.h>
+
 #include "camera_mode.h"
+#include "controller/controller.hpp"
 #include "md_parser.hpp"
+#include "controller/metadata.hpp"
 
 #include "libcamera/internal/v4l2_videodevice.h"
 
@@ -65,7 +70,10 @@ public:
 	CamHelper(MdParser *parser, unsigned int frameIntegrationDiff);
 	virtual ~CamHelper();
 	void SetCameraMode(const CameraMode &mode);
-	MdParser &Parser() const { return *parser_; }
+	virtual void Prepare(const libcamera::Span<uint8_t> &buffer,
+			     const libcamera::ControlList &controls,
+			     Metadata &metadata);
+	virtual void Process(StatisticsPtr &stats, Metadata &metadata);
 	uint32_t ExposureLines(double exposure_us) const;
 	double Exposure(uint32_t exposure_lines) const; // in us
 	virtual uint32_t GetVBlanking(double &exposure_us, double minFrameDuration,
@@ -81,6 +89,10 @@ public:
 	virtual unsigned int MistrustFramesModeSwitch() const;
 
 protected:
+	void parseRegisters(const libcamera::Span<uint8_t> &buffer,
+			    const libcamera::ControlList &controls,
+			    Metadata &metadata);
+
 	MdParser *parser_;
 	CameraMode mode_;
 
diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
index 7904225a..d699540a 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -103,9 +103,6 @@ private:
 	void returnEmbeddedBuffer(unsigned int bufferId);
 	void prepareISP(const ipa::RPi::ISPConfig &data);
 	void reportMetadata();
-	bool parseEmbeddedData(unsigned int bufferId, struct DeviceStatus &deviceStatus);
-	void fillDeviceStatus(uint32_t exposureLines, uint32_t gainCode,
-			      struct DeviceStatus &deviceStatus);
 	void processStats(unsigned int bufferId);
 	void applyFrameDurations(double minFrameDuration, double maxFrameDuration);
 	void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
@@ -913,35 +910,37 @@ void IPARPi::returnEmbeddedBuffer(unsigned int bufferId)
 
 void IPARPi::prepareISP(const ipa::RPi::ISPConfig &data)
 {
-	struct DeviceStatus deviceStatus = {};
-	bool success = false;
+	Span<uint8_t> embeddedBuffer;
+	bool returnEmbedded = false;
+
+	rpiMetadata_.Clear();
 
 	if (data.embeddedBufferPresent) {
 		/*
 		 * Pipeline handler has supplied us with an embedded data buffer,
-		 * so parse it and extract the exposure and gain.
+		 * we must pass it to the CamHelper for parsing.
 		 */
-		success = parseEmbeddedData(data.embeddedBufferId, deviceStatus);
-
-		/* Done with embedded data now, return to pipeline handler asap. */
-		returnEmbeddedBuffer(data.embeddedBufferId);
+		auto it = buffers_.find(data.embeddedBufferId);
+		if (it == buffers_.end())
+			LOG(IPARPI, Error) << "Could not find embedded buffer!";
+		else {
+			embeddedBuffer = it->second.maps()[0];
+			returnEmbedded = true;
+		}
 	}
 
-	if (!success) {
-		/*
-		 * Pipeline handler has not supplied an embedded data buffer,
-		 * or embedded data buffer parsing has failed for some reason,
-		 * so pull the exposure and gain values from the control list.
-		 */
-		int32_t exposureLines = data.controls.get(V4L2_CID_EXPOSURE).get<int32_t>();
-		int32_t gainCode = data.controls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
-		fillDeviceStatus(exposureLines, gainCode, deviceStatus);
-	}
+	/*
+	 * This will add the DeviceStatus to the metadata, and depending on the
+	 * sensor, may do additional custom processing.
+	 */
+	helper_->Prepare(embeddedBuffer, data.controls, rpiMetadata_);
+
+	/* Done with embedded data now, return to pipeline handler asap. */
+	if (returnEmbedded)
+		returnEmbeddedBuffer(data.embeddedBufferId);
 
 	ControlList ctrls(ispCtrls_);
 
-	rpiMetadata_.Clear();
-	rpiMetadata_.Set("device.status", deviceStatus);
 	controller_.Prepare(&rpiMetadata_);
 
 	/* Lock the metadata buffer to avoid constant locks/unlocks. */
@@ -991,50 +990,6 @@ void IPARPi::prepareISP(const ipa::RPi::ISPConfig &data)
 		setIspControls.emit(ctrls);
 }
 
-bool IPARPi::parseEmbeddedData(unsigned int bufferId, struct DeviceStatus &deviceStatus)
-{
-	auto it = buffers_.find(bufferId);
-	if (it == buffers_.end()) {
-		LOG(IPARPI, Error) << "Could not find embedded buffer!";
-		return false;
-	}
-
-	Span<uint8_t> mem = it->second.maps()[0];
-	helper_->Parser().SetBufferSize(mem.size());
-	RPiController::MdParser::Status status = helper_->Parser().Parse(mem.data());
-	if (status != RPiController::MdParser::Status::OK) {
-		LOG(IPARPI, Error) << "Embedded Buffer parsing failed, error " << status;
-		return false;
-	} else {
-		uint32_t exposureLines, gainCode;
-		if (helper_->Parser().GetExposureLines(exposureLines) != RPiController::MdParser::Status::OK) {
-			LOG(IPARPI, Error) << "Exposure time failed";
-			return false;
-		}
-
-		if (helper_->Parser().GetGainCode(gainCode) != RPiController::MdParser::Status::OK) {
-			LOG(IPARPI, Error) << "Gain failed";
-			return false;
-		}
-
-		fillDeviceStatus(exposureLines, gainCode, deviceStatus);
-	}
-
-	return true;
-}
-
-void IPARPi::fillDeviceStatus(uint32_t exposureLines, uint32_t gainCode,
-			      struct DeviceStatus &deviceStatus)
-{
-	deviceStatus.shutter_speed = helper_->Exposure(exposureLines);
-	deviceStatus.analogue_gain = helper_->Gain(gainCode);
-
-	LOG(IPARPI, Debug) << "Metadata - Exposure : "
-			   << deviceStatus.shutter_speed
-			   << " Gain : "
-			   << deviceStatus.analogue_gain;
-}
-
 void IPARPi::processStats(unsigned int bufferId)
 {
 	auto it = buffers_.find(bufferId);
@@ -1046,6 +1001,7 @@ void IPARPi::processStats(unsigned int bufferId)
 	Span<uint8_t> mem = it->second.maps()[0];
 	bcm2835_isp_stats *stats = reinterpret_cast<bcm2835_isp_stats *>(mem.data());
 	RPiController::StatisticsPtr statistics = std::make_shared<bcm2835_isp_stats>(*stats);
+	helper_->Process(statistics, rpiMetadata_);
 	controller_.Process(statistics, &rpiMetadata_);
 
 	struct AgcStatus agcStatus;
