[v2,06/11] ipa: simple: Use libipa CcmAlgorithm
diff mbox series

Message ID 20260623-libipa-algorithms-v2-6-f97433f12e4e@ideasonboard.com
State Superseded
Headers show
Series
  • ipa: libipa: Introduce libipa algorithms
Related show

Commit Message

Jacopo Mondi June 23, 2026, 1:55 p.m. UTC
From: Kieran Bingham <kieran.bingham@ideasonboard.com>

Now that libipa provides a common CCM algorithm implementation,
replace the custom handling with the common Ccm.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 src/ipa/simple/algorithms/ccm.cpp | 69 +++++++++++++++++++++------------------
 src/ipa/simple/algorithms/ccm.h   | 23 ++++++++-----
 src/ipa/simple/ipa_context.h      |  5 +--
 3 files changed, 55 insertions(+), 42 deletions(-)

Comments

Kieran Bingham June 24, 2026, 3:13 p.m. UTC | #1
Quoting Jacopo Mondi (2026-06-23 14:55:01)
> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 

Hello apparently me?!

I don't actually remember writing this patch anymore, so does that make
me viable to review it ?

> Now that libipa provides a common CCM algorithm implementation,
> replace the custom handling with the common Ccm.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> ---
>  src/ipa/simple/algorithms/ccm.cpp | 69 +++++++++++++++++++++------------------
>  src/ipa/simple/algorithms/ccm.h   | 23 ++++++++-----
>  src/ipa/simple/ipa_context.h      |  5 +--
>  3 files changed, 55 insertions(+), 42 deletions(-)
> 
> diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp
> index ff37c718c6e4..6cf55bd93db0 100644
> --- a/src/ipa/simple/algorithms/ccm.cpp
> +++ b/src/ipa/simple/algorithms/ccm.cpp
> @@ -8,54 +8,61 @@
>  
>  #include "ccm.h"
>  
> -#include <libcamera/base/log.h>
> -#include <libcamera/base/utils.h>
> -
> -#include <libcamera/control_ids.h>
> -
>  #include "libcamera/internal/matrix.h"
>  
> -namespace {
> -
> -constexpr unsigned int kTemperatureThreshold = 100;
> -
> -}
> -
>  namespace libcamera {
>  
>  namespace ipa::soft::algorithms {
>  
>  LOG_DEFINE_CATEGORY(IPASoftCcm)
>  
> +/**
> + * \copydoc libcamera::ipa::Algorithm::init
> + */
>  int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
>  {
> -       int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm");
> -       if (ret < 0) {
> -               LOG(IPASoftCcm, Error)
> -                       << "Failed to parse 'ccm' parameter from tuning file.";
> -               return ret;
> -       }
> +       return ccmAlgo_.init(tuningData, context.ctrlMap);
> +}
> +
> +/**
> + * \copydoc libcamera::ipa::Algorithm::configure
> + */
> +int Ccm::configure(IPAContext &context,
> +                  [[maybe_unused]] const IPAConfigInfo &configInfo)
> +{
> +       return ccmAlgo_.configure(context.activeState.ccm,
> +                                 context.activeState.awb.automatic.temperatureK);
> +}
>  
> -       context.ccmEnabled = true;
> +/**
> + * \copydoc libcamera::ipa::Algorithm::queueRequest
> + */
> +void Ccm::queueRequest(IPAContext &context,
> +                      [[maybe_unused]] const uint32_t frame,
> +                      IPAFrameContext &frameContext,
> +                      const ControlList &controls)
> +{
> +       /* Nothing to do here, the ccm will be calculated in prepare() */
> +       if (frameContext.awb.autoEnabled)
> +               return;
>  
> -       return 0;
> +       ccmAlgo_.queueRequest(context.activeState.ccm, frameContext.ccm, controls);
>  }
>  
>  void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
>                   IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
>  {
> -       const unsigned int ct = frameContext.awb.temperatureK;
> -
> -       /* Change CCM only on bigger temperature changes. */
> -       if (!currentCcm_ ||
> -           utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) {
> -               currentCcm_ = ccm_.getInterpolated(ct);
> -               lastCt_ = ct;
> -       }
> -
> +       if (frameContext.awb.autoEnabled)
> +               ccmAlgo_.prepare(context.activeState.ccm, frameContext.ccm,
> +                                frame, frameContext.awb.temperatureK);
> +
> +       /*
> +        * \todo: Split out combined matrix into individual parameters in
> +        * DebayerParams and perform any pre-multiplication combination in the
> +        * SoftISP component directly.
> +        */

Hrm, actually I remember writing this.

anyway this all still looks sane, but I'll hold off a tag as it is my
own code...

Anyone else want to throw weight on this ?

--
Kieran


>         context.activeState.combinedMatrix =
> -               currentCcm_.value() * context.activeState.combinedMatrix;
> -       frameContext.ccm = currentCcm_.value();
> +               frameContext.ccm.ccm * context.activeState.combinedMatrix;
>  }
>  
>  void Ccm::process([[maybe_unused]] IPAContext &context,
> @@ -64,7 +71,7 @@ void Ccm::process([[maybe_unused]] IPAContext &context,
>                   [[maybe_unused]] const SwIspStats *stats,
>                   ControlList &metadata)
>  {
> -       metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.data());
> +       ccmAlgo_.process(frameContext.ccm, metadata);
>  }
>  
>  REGISTER_IPA_ALGORITHM(Ccm, "Ccm")
> diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h
> index b20a7da8aa33..0d35347ea583 100644
> --- a/src/ipa/simple/algorithms/ccm.h
> +++ b/src/ipa/simple/algorithms/ccm.h
> @@ -7,13 +7,15 @@
>  
>  #pragma once
>  
> -#include <optional>
> +#include <libcamera/controls.h>
>  
> -#include "libcamera/internal/matrix.h"
> +#include "libcamera/internal/value_node.h"
>  
> -#include <libipa/interpolator.h>
> +#include "libipa/ccm.h"
> +#include "libipa/fixedpoint.h"
>  
>  #include "algorithm.h"
> +#include "ipa_context.h"
>  
>  namespace libcamera {
>  
> @@ -22,10 +24,15 @@ namespace ipa::soft::algorithms {
>  class Ccm : public Algorithm
>  {
>  public:
> -       Ccm() = default;
> -       ~Ccm() = default;
> -
>         int init(IPAContext &context, const ValueNode &tuningData) override;
> +
> +       int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
> +
> +       void queueRequest(IPAContext &context,
> +                         [[maybe_unused]] const uint32_t frame,
> +                         IPAFrameContext &frameContext,
> +                         const ControlList &controls) override;
> +
>         void prepare(IPAContext &context,
>                      const uint32_t frame,
>                      IPAFrameContext &frameContext,
> @@ -36,9 +43,7 @@ public:
>                      ControlList &metadata) override;
>  
>  private:
> -       unsigned int lastCt_;
> -       Interpolator<Matrix<float, 3, 3>> ccm_;
> -       std::optional<Matrix<float, 3, 3>> currentCcm_;
> +       CcmAlgorithm<Q<4, 16>> ccmAlgo_;
>  };
>  
>  } /* namespace ipa::soft::algorithms */
> diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
> index 29643a655ce1..ff312ae8f4e7 100644
> --- a/src/ipa/simple/ipa_context.h
> +++ b/src/ipa/simple/ipa_context.h
> @@ -17,6 +17,7 @@
>  #include "libcamera/internal/vector.h"
>  
>  #include <libipa/awb.h>
> +#include <libipa/ccm.h>
>  #include <libipa/fc_queue.h>
>  
>  #include "core_ipa_interface.h"
> @@ -38,6 +39,7 @@ struct IPASessionConfiguration {
>  
>  struct IPAActiveState {
>         ipa::awb::ActiveState awb;
> +       ipa::ccm::ActiveState ccm;
>  
>         struct {
>                 int32_t exposure;
> @@ -63,8 +65,7 @@ struct IPAActiveState {
>  
>  struct IPAFrameContext : public FrameContext {
>         ipa::awb::FrameContext awb;
> -
> -       Matrix<float, 3, 3> ccm;
> +       ipa::ccm::FrameContext ccm;
>  
>         struct {
>                 int32_t exposure;
> 
> -- 
> 2.54.0
>
Kieran Bingham June 24, 2026, 4:25 p.m. UTC | #2
Quoting Kieran Bingham (2026-06-24 16:13:05)
> Quoting Jacopo Mondi (2026-06-23 14:55:01)
> > From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > 
> 
> Hello apparently me?!
> 
> I don't actually remember writing this patch anymore, so does that make
> me viable to review it ?

Hi you.

This patch fails to account for context_.ccmEnabled! As such it causes
the Saturation control to stop being initialised or utilised (which is
processed through updating the CCM).

Of course that should be split out separately somehow, but that's for
later. For now - we'll need to keep setting ccmEnabled = true; during init()
still.


> 
> > Now that libipa provides a common CCM algorithm implementation,
> > replace the custom handling with the common Ccm.
> > 
> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > ---
> >  src/ipa/simple/algorithms/ccm.cpp | 69 +++++++++++++++++++++------------------
> >  src/ipa/simple/algorithms/ccm.h   | 23 ++++++++-----
> >  src/ipa/simple/ipa_context.h      |  5 +--
> >  3 files changed, 55 insertions(+), 42 deletions(-)
> > 
> > diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp
> > index ff37c718c6e4..6cf55bd93db0 100644
> > --- a/src/ipa/simple/algorithms/ccm.cpp
> > +++ b/src/ipa/simple/algorithms/ccm.cpp
> > @@ -8,54 +8,61 @@
> >  
> >  #include "ccm.h"
> >  
> > -#include <libcamera/base/log.h>
> > -#include <libcamera/base/utils.h>
> > -
> > -#include <libcamera/control_ids.h>
> > -
> >  #include "libcamera/internal/matrix.h"
> >  
> > -namespace {
> > -
> > -constexpr unsigned int kTemperatureThreshold = 100;
> > -
> > -}
> > -
> >  namespace libcamera {
> >  
> >  namespace ipa::soft::algorithms {
> >  
> >  LOG_DEFINE_CATEGORY(IPASoftCcm)
> >  
> > +/**
> > + * \copydoc libcamera::ipa::Algorithm::init
> > + */
> >  int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
> >  {
> > -       int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm");
> > -       if (ret < 0) {
> > -               LOG(IPASoftCcm, Error)
> > -                       << "Failed to parse 'ccm' parameter from tuning file.";
> > -               return ret;
> > -       }
> > +       return ccmAlgo_.init(tuningData, context.ctrlMap);
> > +}
> > +
> > +/**
> > + * \copydoc libcamera::ipa::Algorithm::configure
> > + */
> > +int Ccm::configure(IPAContext &context,
> > +                  [[maybe_unused]] const IPAConfigInfo &configInfo)
> > +{
> > +       return ccmAlgo_.configure(context.activeState.ccm,
> > +                                 context.activeState.awb.automatic.temperatureK);
> > +}
> >  
> > -       context.ccmEnabled = true;

Setting this is what enables the Saturation control somehow it seems.

This should go before the return statement of course, if there's a
failure - it won't matter as the IPA will fail to load, but continuing
to set the flag will keep Saturation controls working until that
gets reworked and split out of the combined matrix.

The following fixup could be applied to this patch:

diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp
index 6cf55bd93db0..e0cbd453e32f 100644
--- a/src/ipa/simple/algorithms/ccm.cpp
+++ b/src/ipa/simple/algorithms/ccm.cpp
@@ -21,6 +21,9 @@ LOG_DEFINE_CATEGORY(IPASoftCcm)
  */
 int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
 {
+       /* Informs the 'adjust' component that CCM is available to apply Saturation */
+       context.ccmEnabled = true;
+
        return ccmAlgo_.init(tuningData, context.ctrlMap);
 }



--
Kieran


> > +/**
> > + * \copydoc libcamera::ipa::Algorithm::queueRequest
> > + */
> > +void Ccm::queueRequest(IPAContext &context,
> > +                      [[maybe_unused]] const uint32_t frame,
> > +                      IPAFrameContext &frameContext,
> > +                      const ControlList &controls)
> > +{
> > +       /* Nothing to do here, the ccm will be calculated in prepare() */
> > +       if (frameContext.awb.autoEnabled)
> > +               return;
> >  
> > -       return 0;
> > +       ccmAlgo_.queueRequest(context.activeState.ccm, frameContext.ccm, controls);
> >  }
> >  
> >  void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
> >                   IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
> >  {
> > -       const unsigned int ct = frameContext.awb.temperatureK;
> > -
> > -       /* Change CCM only on bigger temperature changes. */
> > -       if (!currentCcm_ ||
> > -           utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) {
> > -               currentCcm_ = ccm_.getInterpolated(ct);
> > -               lastCt_ = ct;
> > -       }
> > -
> > +       if (frameContext.awb.autoEnabled)
> > +               ccmAlgo_.prepare(context.activeState.ccm, frameContext.ccm,
> > +                                frame, frameContext.awb.temperatureK);
> > +
> > +       /*
> > +        * \todo: Split out combined matrix into individual parameters in
> > +        * DebayerParams and perform any pre-multiplication combination in the
> > +        * SoftISP component directly.
> > +        */
> 
> Hrm, actually I remember writing this.
> 
> anyway this all still looks sane, but I'll hold off a tag as it is my
> own code...
> 
> Anyone else want to throw weight on this ?
> 
> --
> Kieran
> 
> 
> >         context.activeState.combinedMatrix =
> > -               currentCcm_.value() * context.activeState.combinedMatrix;
> > -       frameContext.ccm = currentCcm_.value();
> > +               frameContext.ccm.ccm * context.activeState.combinedMatrix;
> >  }
> >  
> >  void Ccm::process([[maybe_unused]] IPAContext &context,
> > @@ -64,7 +71,7 @@ void Ccm::process([[maybe_unused]] IPAContext &context,
> >                   [[maybe_unused]] const SwIspStats *stats,
> >                   ControlList &metadata)
> >  {
> > -       metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.data());
> > +       ccmAlgo_.process(frameContext.ccm, metadata);
> >  }
> >  
> >  REGISTER_IPA_ALGORITHM(Ccm, "Ccm")
> > diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h
> > index b20a7da8aa33..0d35347ea583 100644
> > --- a/src/ipa/simple/algorithms/ccm.h
> > +++ b/src/ipa/simple/algorithms/ccm.h
> > @@ -7,13 +7,15 @@
> >  
> >  #pragma once
> >  
> > -#include <optional>
> > +#include <libcamera/controls.h>
> >  
> > -#include "libcamera/internal/matrix.h"
> > +#include "libcamera/internal/value_node.h"
> >  
> > -#include <libipa/interpolator.h>
> > +#include "libipa/ccm.h"
> > +#include "libipa/fixedpoint.h"
> >  
> >  #include "algorithm.h"
> > +#include "ipa_context.h"
> >  
> >  namespace libcamera {
> >  
> > @@ -22,10 +24,15 @@ namespace ipa::soft::algorithms {
> >  class Ccm : public Algorithm
> >  {
> >  public:
> > -       Ccm() = default;
> > -       ~Ccm() = default;
> > -
> >         int init(IPAContext &context, const ValueNode &tuningData) override;
> > +
> > +       int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
> > +
> > +       void queueRequest(IPAContext &context,
> > +                         [[maybe_unused]] const uint32_t frame,
> > +                         IPAFrameContext &frameContext,
> > +                         const ControlList &controls) override;
> > +
> >         void prepare(IPAContext &context,
> >                      const uint32_t frame,
> >                      IPAFrameContext &frameContext,
> > @@ -36,9 +43,7 @@ public:
> >                      ControlList &metadata) override;
> >  
> >  private:
> > -       unsigned int lastCt_;
> > -       Interpolator<Matrix<float, 3, 3>> ccm_;
> > -       std::optional<Matrix<float, 3, 3>> currentCcm_;
> > +       CcmAlgorithm<Q<4, 16>> ccmAlgo_;
> >  };
> >  
> >  } /* namespace ipa::soft::algorithms */
> > diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
> > index 29643a655ce1..ff312ae8f4e7 100644
> > --- a/src/ipa/simple/ipa_context.h
> > +++ b/src/ipa/simple/ipa_context.h
> > @@ -17,6 +17,7 @@
> >  #include "libcamera/internal/vector.h"
> >  
> >  #include <libipa/awb.h>
> > +#include <libipa/ccm.h>
> >  #include <libipa/fc_queue.h>
> >  
> >  #include "core_ipa_interface.h"
> > @@ -38,6 +39,7 @@ struct IPASessionConfiguration {
> >  
> >  struct IPAActiveState {
> >         ipa::awb::ActiveState awb;
> > +       ipa::ccm::ActiveState ccm;
> >  
> >         struct {
> >                 int32_t exposure;
> > @@ -63,8 +65,7 @@ struct IPAActiveState {
> >  
> >  struct IPAFrameContext : public FrameContext {
> >         ipa::awb::FrameContext awb;
> > -
> > -       Matrix<float, 3, 3> ccm;
> > +       ipa::ccm::FrameContext ccm;
> >  
> >         struct {
> >                 int32_t exposure;
> > 
> > -- 
> > 2.54.0
> >

Patch
diff mbox series

diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp
index ff37c718c6e4..6cf55bd93db0 100644
--- a/src/ipa/simple/algorithms/ccm.cpp
+++ b/src/ipa/simple/algorithms/ccm.cpp
@@ -8,54 +8,61 @@ 
 
 #include "ccm.h"
 
-#include <libcamera/base/log.h>
-#include <libcamera/base/utils.h>
-
-#include <libcamera/control_ids.h>
-
 #include "libcamera/internal/matrix.h"
 
-namespace {
-
-constexpr unsigned int kTemperatureThreshold = 100;
-
-}
-
 namespace libcamera {
 
 namespace ipa::soft::algorithms {
 
 LOG_DEFINE_CATEGORY(IPASoftCcm)
 
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
 int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
 {
-	int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm");
-	if (ret < 0) {
-		LOG(IPASoftCcm, Error)
-			<< "Failed to parse 'ccm' parameter from tuning file.";
-		return ret;
-	}
+	return ccmAlgo_.init(tuningData, context.ctrlMap);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::configure
+ */
+int Ccm::configure(IPAContext &context,
+		   [[maybe_unused]] const IPAConfigInfo &configInfo)
+{
+	return ccmAlgo_.configure(context.activeState.ccm,
+				  context.activeState.awb.automatic.temperatureK);
+}
 
-	context.ccmEnabled = true;
+/**
+ * \copydoc libcamera::ipa::Algorithm::queueRequest
+ */
+void Ccm::queueRequest(IPAContext &context,
+		       [[maybe_unused]] const uint32_t frame,
+		       IPAFrameContext &frameContext,
+		       const ControlList &controls)
+{
+	/* Nothing to do here, the ccm will be calculated in prepare() */
+	if (frameContext.awb.autoEnabled)
+		return;
 
-	return 0;
+	ccmAlgo_.queueRequest(context.activeState.ccm, frameContext.ccm, controls);
 }
 
 void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 		  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
 {
-	const unsigned int ct = frameContext.awb.temperatureK;
-
-	/* Change CCM only on bigger temperature changes. */
-	if (!currentCcm_ ||
-	    utils::abs_diff(ct, lastCt_) >= kTemperatureThreshold) {
-		currentCcm_ = ccm_.getInterpolated(ct);
-		lastCt_ = ct;
-	}
-
+	if (frameContext.awb.autoEnabled)
+		ccmAlgo_.prepare(context.activeState.ccm, frameContext.ccm,
+				 frame, frameContext.awb.temperatureK);
+
+	/*
+	 * \todo: Split out combined matrix into individual parameters in
+	 * DebayerParams and perform any pre-multiplication combination in the
+	 * SoftISP component directly.
+	 */
 	context.activeState.combinedMatrix =
-		currentCcm_.value() * context.activeState.combinedMatrix;
-	frameContext.ccm = currentCcm_.value();
+		frameContext.ccm.ccm * context.activeState.combinedMatrix;
 }
 
 void Ccm::process([[maybe_unused]] IPAContext &context,
@@ -64,7 +71,7 @@  void Ccm::process([[maybe_unused]] IPAContext &context,
 		  [[maybe_unused]] const SwIspStats *stats,
 		  ControlList &metadata)
 {
-	metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.data());
+	ccmAlgo_.process(frameContext.ccm, metadata);
 }
 
 REGISTER_IPA_ALGORITHM(Ccm, "Ccm")
diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h
index b20a7da8aa33..0d35347ea583 100644
--- a/src/ipa/simple/algorithms/ccm.h
+++ b/src/ipa/simple/algorithms/ccm.h
@@ -7,13 +7,15 @@ 
 
 #pragma once
 
-#include <optional>
+#include <libcamera/controls.h>
 
-#include "libcamera/internal/matrix.h"
+#include "libcamera/internal/value_node.h"
 
-#include <libipa/interpolator.h>
+#include "libipa/ccm.h"
+#include "libipa/fixedpoint.h"
 
 #include "algorithm.h"
+#include "ipa_context.h"
 
 namespace libcamera {
 
@@ -22,10 +24,15 @@  namespace ipa::soft::algorithms {
 class Ccm : public Algorithm
 {
 public:
-	Ccm() = default;
-	~Ccm() = default;
-
 	int init(IPAContext &context, const ValueNode &tuningData) override;
+
+	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
+
+	void queueRequest(IPAContext &context,
+			  [[maybe_unused]] const uint32_t frame,
+			  IPAFrameContext &frameContext,
+			  const ControlList &controls) override;
+
 	void prepare(IPAContext &context,
 		     const uint32_t frame,
 		     IPAFrameContext &frameContext,
@@ -36,9 +43,7 @@  public:
 		     ControlList &metadata) override;
 
 private:
-	unsigned int lastCt_;
-	Interpolator<Matrix<float, 3, 3>> ccm_;
-	std::optional<Matrix<float, 3, 3>> currentCcm_;
+	CcmAlgorithm<Q<4, 16>> ccmAlgo_;
 };
 
 } /* namespace ipa::soft::algorithms */
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
index 29643a655ce1..ff312ae8f4e7 100644
--- a/src/ipa/simple/ipa_context.h
+++ b/src/ipa/simple/ipa_context.h
@@ -17,6 +17,7 @@ 
 #include "libcamera/internal/vector.h"
 
 #include <libipa/awb.h>
+#include <libipa/ccm.h>
 #include <libipa/fc_queue.h>
 
 #include "core_ipa_interface.h"
@@ -38,6 +39,7 @@  struct IPASessionConfiguration {
 
 struct IPAActiveState {
 	ipa::awb::ActiveState awb;
+	ipa::ccm::ActiveState ccm;
 
 	struct {
 		int32_t exposure;
@@ -63,8 +65,7 @@  struct IPAActiveState {
 
 struct IPAFrameContext : public FrameContext {
 	ipa::awb::FrameContext awb;
-
-	Matrix<float, 3, 3> ccm;
+	ipa::ccm::FrameContext ccm;
 
 	struct {
 		int32_t exposure;