diff --git a/src/ipa/ipu3/algorithms/af.cpp b/src/ipa/ipu3/algorithms/af.cpp
index 4835a0345931..e7ed37003217 100644
--- a/src/ipa/ipu3/algorithms/af.cpp
+++ b/src/ipa/ipu3/algorithms/af.cpp
@@ -420,7 +420,8 @@ bool Af::afIsOutOfFocus(IPAContext context)
  *
  * [1] Hill Climbing Algorithm, https://en.wikipedia.org/wiki/Hill_climbing
  */
-void Af::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameContext,
+void Af::process(IPAContext &context,
+		 [[maybe_unused]] IPU3FrameContext &frameContext,
 		 const ipu3_uapi_stats_3a *stats)
 {
 	/* Evaluate the AF buffer length */
diff --git a/src/ipa/ipu3/algorithms/af.h b/src/ipa/ipu3/algorithms/af.h
index ccf015f3f8f5..7a5aeb1b6bf4 100644
--- a/src/ipa/ipu3/algorithms/af.h
+++ b/src/ipa/ipu3/algorithms/af.h
@@ -32,7 +32,7 @@ public:
 
 	void prepare(IPAContext &context, ipu3_uapi_params *params) override;
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, IPU3FrameContext &frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
 
 private:
diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
index ed4809d98007..57bd8a38d22d 100644
--- a/src/ipa/ipu3/algorithms/agc.cpp
+++ b/src/ipa/ipu3/algorithms/agc.cpp
@@ -183,13 +183,13 @@ utils::Duration Agc::filterExposure(utils::Duration exposureValue)
  * \param[in] yGain The gain calculated based on the relative luminance target
  * \param[in] iqMeanGain The gain calculated based on the relative luminance target
  */
-void Agc::computeExposure(IPAContext &context, IPAFrameContext *frameContext,
+void Agc::computeExposure(IPAContext &context, IPU3FrameContext &frameContext,
 			  double yGain, double iqMeanGain)
 {
 	const IPASessionConfiguration &configuration = context.configuration;
 	/* Get the effective exposure and gain applied on the sensor. */
-	uint32_t exposure = frameContext->sensor.exposure;
-	double analogueGain = frameContext->sensor.gain;
+	uint32_t exposure = frameContext.sensor.exposure;
+	double analogueGain = frameContext.sensor.gain;
 
 	/* Use the highest of the two gain estimates. */
 	double evGain = std::max(yGain, iqMeanGain);
@@ -323,7 +323,8 @@ double Agc::estimateLuminance(IPAActiveState &activeState,
  * Identify the current image brightness, and use that to estimate the optimal
  * new exposure and gain for the scene.
  */
-void Agc::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameContext,
+void Agc::process(IPAContext &context,
+		  IPU3FrameContext &frameContext,
 		  const ipu3_uapi_stats_3a *stats)
 {
 	/*
diff --git a/src/ipa/ipu3/algorithms/agc.h b/src/ipa/ipu3/algorithms/agc.h
index 105ae0f2aac6..96585f48933f 100644
--- a/src/ipa/ipu3/algorithms/agc.h
+++ b/src/ipa/ipu3/algorithms/agc.h
@@ -28,14 +28,14 @@ public:
 	~Agc() = default;
 
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, IPU3FrameContext &frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
 
 private:
 	double measureBrightness(const ipu3_uapi_stats_3a *stats,
 				 const ipu3_uapi_grid_config &grid) const;
 	utils::Duration filterExposure(utils::Duration currentExposure);
-	void computeExposure(IPAContext &context, IPAFrameContext *frameContext,
+	void computeExposure(IPAContext &context, IPU3FrameContext &frameContext,
 			     double yGain, double iqMeanGain);
 	double estimateLuminance(IPAActiveState &activeState,
 				 const ipu3_uapi_grid_config &grid,
diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp
index b658ee546b87..35eafd5ec50e 100644
--- a/src/ipa/ipu3/algorithms/awb.cpp
+++ b/src/ipa/ipu3/algorithms/awb.cpp
@@ -387,7 +387,8 @@ void Awb::calculateWBGains(const ipu3_uapi_stats_3a *stats)
 /**
  * \copydoc libcamera::ipa::Algorithm::process
  */
-void Awb::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameContext,
+void Awb::process(IPAContext &context,
+		  [[maybe_unused]] IPU3FrameContext &frameContext,
 		  const ipu3_uapi_stats_3a *stats)
 {
 	calculateWBGains(stats);
diff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h
index 0acd21480845..28e39620fd59 100644
--- a/src/ipa/ipu3/algorithms/awb.h
+++ b/src/ipa/ipu3/algorithms/awb.h
@@ -40,7 +40,7 @@ public:
 
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
 	void prepare(IPAContext &context, ipu3_uapi_params *params) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, IPU3FrameContext &frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
 
 private:
diff --git a/src/ipa/ipu3/algorithms/tone_mapping.cpp b/src/ipa/ipu3/algorithms/tone_mapping.cpp
index 49a5558b6faa..72627ea1e658 100644
--- a/src/ipa/ipu3/algorithms/tone_mapping.cpp
+++ b/src/ipa/ipu3/algorithms/tone_mapping.cpp
@@ -78,7 +78,8 @@ void ToneMapping::prepare([[maybe_unused]] IPAContext &context,
  * The tone mapping look up table is generated as an inverse power curve from
  * our gamma setting.
  */
-void ToneMapping::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameContext,
+void ToneMapping::process(IPAContext &context,
+			  [[maybe_unused]] IPU3FrameContext &frameContext,
 			  [[maybe_unused]] const ipu3_uapi_stats_3a *stats)
 {
 	/*
diff --git a/src/ipa/ipu3/algorithms/tone_mapping.h b/src/ipa/ipu3/algorithms/tone_mapping.h
index d7d4800628f2..8cce38c742a6 100644
--- a/src/ipa/ipu3/algorithms/tone_mapping.h
+++ b/src/ipa/ipu3/algorithms/tone_mapping.h
@@ -20,7 +20,7 @@ public:
 
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
 	void prepare(IPAContext &context, ipu3_uapi_params *params) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, IPU3FrameContext &frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
 
 private:
diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp
index dd139cb4c868..536d76ecd63b 100644
--- a/src/ipa/ipu3/ipa_context.cpp
+++ b/src/ipa/ipu3/ipa_context.cpp
@@ -35,22 +35,6 @@ namespace libcamera::ipa::ipu3 {
  * most recently computed by the IPA algorithms.
  */
 
-/**
- * \struct IPAFrameContext
- * \brief Context for a frame
- *
- * The frame context stores data specific to a single frame processed by the
- * IPA. Each frame processed by the IPA has a context associated with it,
- * accessible through the IPAContext structure.
- *
- * Fields in the frame context should reflect values and controls
- * associated with the specific frame as requested by the application, and
- * as configured by the hardware. Fields can be read by algorithms to
- * determine if they should update any specific action for this frame, and
- * finally to update the metadata control lists when the frame is fully
- * completed.
- */
-
 /**
  * \struct IPAContext
  * \brief Global IPA context data shared between all algorithms
@@ -181,16 +165,16 @@ namespace libcamera::ipa::ipu3 {
  */
 
 /**
- * \var IPAFrameContext::frame
- * \brief The frame number
+ * \struct IPU3FrameContext
+ * \copybrief libcamera::ipa::IPAFrameContext
  *
- * \var IPAFrameContext::sensor
+ * \var IPU3FrameContext::sensor
  * \brief Effective sensor values that were applied for the frame
  *
- * \var IPAFrameContext::sensor.exposure
+ * \var IPU3FrameContext::sensor.exposure
  * \brief Exposure time expressed as a number of lines
  *
- * \var IPAFrameContext::sensor.gain
+ * \var IPU3FrameContext::sensor.gain
  * \brief Analogue gain multiplier
  */
 
diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h
index 193fc44a821a..c4aa4c3f4f6a 100644
--- a/src/ipa/ipu3/ipa_context.h
+++ b/src/ipa/ipu3/ipa_context.h
@@ -73,8 +73,7 @@ struct IPAActiveState {
 	} toneMapping;
 };
 
-struct IPAFrameContext {
-	uint32_t frame;
+struct IPU3FrameContext : public IPAFrameContext {
 
 	struct {
 		uint32_t exposure;
@@ -86,7 +85,7 @@ struct IPAContext {
 	IPASessionConfiguration configuration;
 	IPAActiveState activeState;
 
-	FCQueue<IPAFrameContext> frameContexts;
+	FCQueue<IPU3FrameContext> frameContexts;
 };
 
 } /* namespace ipa::ipu3 */
diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp
index 5396b0704950..13d7c878a242 100644
--- a/src/ipa/ipu3/ipu3.cpp
+++ b/src/ipa/ipu3/ipu3.cpp
@@ -605,7 +605,7 @@ void IPAIPU3::processStatsBuffer(const uint32_t frame,
 	const ipu3_uapi_stats_3a *stats =
 		reinterpret_cast<ipu3_uapi_stats_3a *>(mem.data());
 
-	IPAFrameContext &frameContext = context_.frameContexts.get(frame);
+	IPU3FrameContext &frameContext = context_.frameContexts.get(frame);
 
 	if (frameContext.frame != frame)
 		LOG(IPAIPU3, Warning) << "Frame " << frame << " does not match its frame context";
@@ -618,7 +618,7 @@ void IPAIPU3::processStatsBuffer(const uint32_t frame,
 	ControlList ctrls(controls::controls);
 
 	for (auto const &algo : algorithms())
-		algo->process(context_, &frameContext, stats);
+		algo->process(context_, frameContext, stats);
 
 	setControls(frame);
 
@@ -654,7 +654,7 @@ void IPAIPU3::processStatsBuffer(const uint32_t frame,
 void IPAIPU3::queueRequest(const uint32_t frame, const ControlList &controls)
 {
 	/* \todo Start processing for 'frame' based on 'controls'. */
-	IPAFrameContext &frameContext = context_.frameContexts.initialise(frame);
+	IPU3FrameContext &frameContext = context_.frameContexts.initialise(frame);
 
 	/* \todo Implement queueRequest to each algorithm. */
 	(void)frameContext;
diff --git a/src/ipa/ipu3/module.h b/src/ipa/ipu3/module.h
index d94fc4594871..6d0d50f615d8 100644
--- a/src/ipa/ipu3/module.h
+++ b/src/ipa/ipu3/module.h
@@ -19,7 +19,7 @@ namespace libcamera {
 
 namespace ipa::ipu3 {
 
-using Module = ipa::Module<IPAContext, IPAFrameContext, IPAConfigInfo,
+using Module = ipa::Module<IPAContext, IPU3FrameContext, IPAConfigInfo,
 			   ipu3_uapi_params, ipu3_uapi_stats_3a>;
 
 } /* namespace ipa::ipu3 */
diff --git a/src/ipa/libipa/algorithm.h b/src/ipa/libipa/algorithm.h
index ccc659a63e3b..0fe3d772963a 100644
--- a/src/ipa/libipa/algorithm.h
+++ b/src/ipa/libipa/algorithm.h
@@ -49,7 +49,7 @@ public:
 	}
 
 	virtual void process([[maybe_unused]] typename Module::Context &context,
-			     [[maybe_unused]] typename Module::FrameContext *frameContext,
+			     [[maybe_unused]] typename Module::FrameContext &frameContext,
 			     [[maybe_unused]] const typename Module::Stats *stats)
 	{
 	}
diff --git a/src/ipa/libipa/fc_queue.cpp b/src/ipa/libipa/fc_queue.cpp
index a69579290043..ed058f1cc648 100644
--- a/src/ipa/libipa/fc_queue.cpp
+++ b/src/ipa/libipa/fc_queue.cpp
@@ -66,6 +66,24 @@ namespace ipa {
  * \todo Set an error flag for per-frame control errors.
  */
 
+/**
+ * \struct IPAFrameContext
+ * \brief Context for a frame
+ *
+ * The frame context stores data specific to a single frame processed by the
+ * IPA. Each frame processed by the IPA has a context associated with it,
+ * accessible through the Frame Context Queue.
+ *
+ * Fields in the frame context should reflect values and controls associated
+ * with the specific frame as requested by the application, and as configured by
+ * the hardware. Fields can be read by algorithms to determine if they should
+ * update any specific action for this frame, and finally to update the metadata
+ * control lists when the frame is fully completed.
+ *
+ * \var IPAFrameContext::frame
+ * \brief The frame number
+ */
+
 /**
  * \fn FCQueue::clear()
  * \brief Clear the context queue
diff --git a/src/ipa/libipa/fc_queue.h b/src/ipa/libipa/fc_queue.h
index 5966817ce673..7f0a1f74938d 100644
--- a/src/ipa/libipa/fc_queue.h
+++ b/src/ipa/libipa/fc_queue.h
@@ -24,6 +24,10 @@ namespace ipa {
  */
 static constexpr uint32_t kMaxFrameContexts = 16;
 
+struct IPAFrameContext {
+	unsigned int frame;
+};
+
 template<typename FrameContext>
 class FCQueue : private std::array<FrameContext, kMaxFrameContexts>
 {
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index 483e941fe427..40d27963ef68 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -281,7 +281,7 @@ double Agc::measureBrightness(const rkisp1_cif_isp_hist_stat *hist) const
  * new exposure and gain for the scene.
  */
 void Agc::process(IPAContext &context,
-		  [[maybe_unused]] IPAFrameContext *frameContext,
+		  [[maybe_unused]] RKISP1FrameContext &frameContext,
 		  const rkisp1_stat_buffer *stats)
 {
 	const rkisp1_cif_isp_stat *params = &stats->params;
diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h
index 1c9818b7db2a..af7fe2740908 100644
--- a/src/ipa/rkisp1/algorithms/agc.h
+++ b/src/ipa/rkisp1/algorithms/agc.h
@@ -27,7 +27,7 @@ public:
 
 	int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
 	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, RKISP1FrameContext &frameContext,
 		     const rkisp1_stat_buffer *stats) override;
 
 private:
diff --git a/src/ipa/rkisp1/algorithms/awb.cpp b/src/ipa/rkisp1/algorithms/awb.cpp
index 427aaeb1e955..3beafb73ca33 100644
--- a/src/ipa/rkisp1/algorithms/awb.cpp
+++ b/src/ipa/rkisp1/algorithms/awb.cpp
@@ -120,7 +120,7 @@ void Awb::prepare(IPAContext &context, rkisp1_params_cfg *params)
  * \copydoc libcamera::ipa::Algorithm::process
  */
 void Awb::process([[maybe_unused]] IPAContext &context,
-		  [[maybe_unused]] IPAFrameContext *frameCtx,
+		  [[maybe_unused]] RKISP1FrameContext &frameCtx,
 		  const rkisp1_stat_buffer *stats)
 {
 	const rkisp1_cif_isp_stat *params = &stats->params;
diff --git a/src/ipa/rkisp1/algorithms/awb.h b/src/ipa/rkisp1/algorithms/awb.h
index 667a8beb7689..5954034b8142 100644
--- a/src/ipa/rkisp1/algorithms/awb.h
+++ b/src/ipa/rkisp1/algorithms/awb.h
@@ -21,7 +21,7 @@ public:
 
 	int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
 	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
-	void process(IPAContext &context, IPAFrameContext *frameCtx,
+	void process(IPAContext &context, RKISP1FrameContext &frameCtx,
 		     const rkisp1_stat_buffer *stats) override;
 
 private:
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index a8daeca487ae..c42bcd73b314 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -14,6 +14,8 @@
 
 #include <libcamera/geometry.h>
 
+#include <libipa/fc_queue.h>
+
 namespace libcamera {
 
 namespace ipa::rkisp1 {
@@ -78,7 +80,7 @@ struct IPAActiveState {
 	unsigned int frameCount;
 };
 
-struct IPAFrameContext {
+struct RKISP1FrameContext : public IPAFrameContext {
 };
 
 struct IPAContext {
diff --git a/src/ipa/rkisp1/module.h b/src/ipa/rkisp1/module.h
index 89f83208a75c..2568c624df0f 100644
--- a/src/ipa/rkisp1/module.h
+++ b/src/ipa/rkisp1/module.h
@@ -19,7 +19,7 @@ namespace libcamera {
 
 namespace ipa::rkisp1 {
 
-using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,
+using Module = ipa::Module<IPAContext, RKISP1FrameContext, IPACameraSensorInfo,
 			   rkisp1_params_cfg, rkisp1_stat_buffer>;
 
 } /* namespace ipa::rkisp1 */
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index 88e6a2b23eed..c3aa49329693 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -323,8 +323,11 @@ void IPARkISP1::processStatsBuffer(const uint32_t frame, const uint32_t bufferId
 
 	unsigned int aeState = 0;
 
+	/* \todo Obtain the frame context to pass to process from the FCQueue */
+	RKISP1FrameContext frameContext;
+
 	for (auto const &algo : algorithms())
-		algo->process(context_, nullptr, stats);
+		algo->process(context_, frameContext, stats);
 
 	setControls(frame);
 
