[{"id":39186,"web_url":"https://patchwork.libcamera.org/comment/39186/","msgid":"<178181540656.288350.10204281687047403407@ping.linuxembedded.co.uk>","date":"2026-06-18T20:43:26","subject":"Re: [PATCH 14/14] ipa: rppx1: Add Ccm algorithm","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Jacopo Mondi (2026-06-18 11:18:53)\n> Add Ccm algorithm to the RPP-X1 IPA.\n> \n> The implementation is based on libIPA CcmAlgorithm.\n> \n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> ---\n>  src/ipa/rppx1/algorithms/awb.cpp     |   3 +-\n>  src/ipa/rppx1/algorithms/ccm.cpp     | 124 +++++++++++++++++++++++++++++++++++\n>  src/ipa/rppx1/algorithms/ccm.h       |  54 +++++++++++++++\n>  src/ipa/rppx1/algorithms/meson.build |   1 +\n>  src/ipa/rppx1/ipa_context.cpp        |  10 +++\n>  src/ipa/rppx1/ipa_context.h          |   3 +\n>  6 files changed, 193 insertions(+), 2 deletions(-)\n> \n> diff --git a/src/ipa/rppx1/algorithms/awb.cpp b/src/ipa/rppx1/algorithms/awb.cpp\n> index 6316606b4b0f..598e4503513c 100644\n> --- a/src/ipa/rppx1/algorithms/awb.cpp\n> +++ b/src/ipa/rppx1/algorithms/awb.cpp\n> @@ -275,11 +275,10 @@ RppX1AwbStats Awb::calculateRgbMeans(const IPAFrameContext &frameContext,\n>         rgbMeans = rgbMeans.max(0.0);\n>  \n>         /*\n> -        * \\todo\n>          * The ISP computes the AWB means after applying the CCM. Apply\n>          * the inverse as we want to get the raw means before the colour gains.\n> -        * rgbMeans = frameContext.ccm.ccm.inverse() * rgbMeans;\n>          */\n> +       rgbMeans = frameContext.ccm.ccm.inverse() * rgbMeans;\n>  \n>         /*\n>          * The ISP computes the AWB means after applying the colour gains,\n> diff --git a/src/ipa/rppx1/algorithms/ccm.cpp b/src/ipa/rppx1/algorithms/ccm.cpp\n> new file mode 100644\n> index 000000000000..4b485d1cff0e\n> --- /dev/null\n> +++ b/src/ipa/rppx1/algorithms/ccm.cpp\n> @@ -0,0 +1,124 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2026, Ideas On Board\n> + *\n> + * RPP-X1 color correction matrix control algorithm\n> + */\n> +\n> +#include \"ccm.h\"\n> +\n> +#include <linux/media/dreamchip/rppx1-config.h>\n> +\n> +#include <libcamera/base/log.h>\n> +#include <libcamera/base/utils.h>\n> +\n> +#include <libcamera/control_ids.h>\n> +\n> +#include <libcamera/ipa/core_ipa_interface.h>\n> +\n> +/**\n> + * \\file ccm.h\n> + */\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa::rppx1::algorithms {\n> +\n> +LOG_DEFINE_CATEGORY(RppX1Ccm)\n> +\n> +/**\n> + * \\class Ccm\n> + * \\brief Color correction matrix algorithm\n> + */\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::init\n> + */\n> +int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)\n> +{\n> +       return ccmAlgo_.init(tuningData, context.ctrlMap);\n> +}\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::configure\n> + */\n> +int Ccm::configure(IPAContext &context,\n> +                  [[maybe_unused]] const IPACameraSensorInfo &configInfo)\n> +{\n> +       return ccmAlgo_.configure(context.activeState.ccm,\n> +                                 context.activeState.awb.automatic.temperatureK);\n> +}\n> +\n> +void Ccm::queueRequest(IPAContext &context,\n> +                      [[maybe_unused]] const uint32_t frame,\n> +                      IPAFrameContext &frameContext,\n> +                      const ControlList &controls)\n> +{\n> +       /* Nothing to do here, the ccm will be calculated in prepare() */\n> +       if (frameContext.awb.autoEnabled)\n> +               return;\n> +\n> +       ccmAlgo_.queueRequest(context.activeState.ccm, frameContext.ccm, controls);\n> +}\n\nAll as short as it could be ;-)\n\n> +\n> +void Ccm::setParameters(RppX1Params *params, IPAFrameContext &context)\n> +{\n> +       const Matrix<float, 3, 3> &matrix = context.ccm.ccm;\n> +       const Matrix<int16_t, 3, 1> &offsets = context.ccm.offsets;\n\nCurious, isn't a Matrix of 1 column (or row) just a Vector?\n\nOr is that because we only have an interpolator for the Matrix and it\ndoesn't match to a Vector ?\n\n\n> +\n> +       auto config = params->block<BlockType::CcorPost>();\n> +       config.setEnabled(true);\n> +\n> +       /*\n> +        * RPP-X1 coefficients are 16 bits Q4.12 signed fixed-point ranging from\n> +        * -8 (0x8000) to +7.9996 (0x7fff). x1 = 0x1000.\n> +        */\n> +       for (unsigned int i = 0; i < 3; i++) {\n> +               for (unsigned int j = 0; j < 3; j++)\n> +                       config->coeff[i][j] = Q<4, 12>(matrix[i][j]).quantized();\n> +       }\n> +\n> +       /*\n> +        * RPP-X1 offsets are 25 bits 2's complement while the CcmAlgorithm\n> +        * class uses int16_t.\n> +        *\n> +        * \\todo: Better investigate how negative offsets are handled in the\n> +        * offsets interpolation.\n> +        */\n> +       for (unsigned int i = 0; i < 3; i++)\n> +               config->offset[i] = static_cast<int32_t>(offsets[i][0]);\n> +\n> +       LOG(RppX1Ccm, Debug) << \"Setting matrix \" << matrix;\n> +       LOG(RppX1Ccm, Debug) << \"Setting offsets \" << offsets;\n> +}\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::prepare\n> + */\n> +void Ccm::prepare(IPAContext &context, const uint32_t frame,\n> +                 IPAFrameContext &frameContext, RppX1Params *params)\n> +{\n> +       if (frameContext.awb.autoEnabled)\n> +               ccmAlgo_.prepare(context.activeState.ccm, frameContext.ccm,\n> +                                frame, frameContext.awb.temperatureK);\n> +\n> +       setParameters(params, frameContext);\n> +}\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::process\n> + */\n> +void Ccm::process([[maybe_unused]] IPAContext &context,\n> +                 [[maybe_unused]] const uint32_t frame,\n> +                 IPAFrameContext &frameContext,\n> +                 [[maybe_unused]] const RppX1Stats *stats,\n> +                 ControlList &metadata)\n> +{\n> +       ccmAlgo_.process(frameContext.ccm, metadata);\n> +}\n> +\n> +REGISTER_IPA_ALGORITHM(Ccm, \"Ccm\")\n> +\n> +} /* namespace ipa::rppx1::algorithms */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/rppx1/algorithms/ccm.h b/src/ipa/rppx1/algorithms/ccm.h\n> new file mode 100644\n> index 000000000000..5e183d255a48\n> --- /dev/null\n> +++ b/src/ipa/rppx1/algorithms/ccm.h\n> @@ -0,0 +1,54 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2026, Ideas On Board\n> + *\n> + * RPP-X1 color correction matrix control algorithm\n> + */\n> +\n> +#pragma once\n> +\n> +#include \"libcamera/internal/matrix.h\"\n> +\n> +#include <libipa/ccm.h>\n> +\n> +#include \"algorithm.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa::rppx1::algorithms {\n> +\n> +class Ccm : public Algorithm\n> +{\n> +public:\n> +       Ccm() {}\n> +       ~Ccm() = default;\n> +\n> +       int init(IPAContext &context, const ValueNode &tuningData) override;\n> +       int configure(IPAContext &context,\n> +                     const IPACameraSensorInfo &configInfo) override;\n> +       void queueRequest(IPAContext &context,\n> +                         const uint32_t frame,\n> +                         IPAFrameContext &frameContext,\n> +                         const ControlList &controls) override;\n> +       void prepare(IPAContext &context, const uint32_t frame,\n> +                    IPAFrameContext &frameContext,\n> +                    RppX1Params *params) override;\n> +       void process(IPAContext &context, const uint32_t frame,\n> +                    IPAFrameContext &frameContext,\n> +                    const RppX1Stats *stats,\n> +                    ControlList &metadata) override;\n> +\n> +private:\n> +       void parseYaml(const ValueNode &tuningData);\n> +       void setParameters(RppX1Params *params, IPAFrameContext &context);\n> +\n> +       unsigned int ct_;\n> +       Interpolator<Matrix<float, 3, 3>> ccm_;\n> +       Interpolator<Matrix<int16_t, 3, 1>> offsets_;\n\nSo only still curious if offsets_ should be a Vector - but it's the same\n... so I don't think it makes much difference except conceptually?\n\nPerhaps this is actually a discussion for the common libipa ccm part not\nhere...\n\nso ... \n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> +\n> +       CcmAlgorithm<Q<4, 12>> ccmAlgo_;\n> +};\n> +\n> +} /* namespace ipa::rppx1::algorithms */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/rppx1/algorithms/meson.build b/src/ipa/rppx1/algorithms/meson.build\n> index 70368ad9bac9..ddebbfbecbee 100644\n> --- a/src/ipa/rppx1/algorithms/meson.build\n> +++ b/src/ipa/rppx1/algorithms/meson.build\n> @@ -2,4 +2,5 @@\n>  \n>  rppx1_ipa_algorithms = files([\n>      'awb.cpp',\n> +    'ccm.cpp'\n>  ])\n> diff --git a/src/ipa/rppx1/ipa_context.cpp b/src/ipa/rppx1/ipa_context.cpp\n> index ea7008f5685d..57a2b56774bc 100644\n> --- a/src/ipa/rppx1/ipa_context.cpp\n> +++ b/src/ipa/rppx1/ipa_context.cpp\n> @@ -51,6 +51,11 @@ namespace libcamera::ipa::rppx1 {\n>   * \\copydoc ipa::awb::ActiveState\n>   */\n>  \n> +/**\n> + * \\var IPAActiveState::ccm\n> + * \\copydoc ipa::ccm::ActiveState\n> + */\n> +\n>  /**\n>   * \\struct IPAFrameContext\n>   * \\brief Per-frame context for algorithms\n> @@ -61,6 +66,11 @@ namespace libcamera::ipa::rppx1 {\n>   * \\copydoc ipa::awb::FrameContext\n>   */\n>  \n> +/**\n> + * \\var IPAFrameContext::ccm\n> + * \\copydoc ipa::ccm::FrameContext\n> + */\n> +\n>  /**\n>   * \\struct IPAContext\n>   * \\brief Global IPA context data shared between all algorithms\n> diff --git a/src/ipa/rppx1/ipa_context.h b/src/ipa/rppx1/ipa_context.h\n> index 57197d865d3f..bba1d10da73f 100644\n> --- a/src/ipa/rppx1/ipa_context.h\n> +++ b/src/ipa/rppx1/ipa_context.h\n> @@ -21,6 +21,7 @@\n>  #include <libipa/fc_queue.h>\n>  \n>  #include \"libipa/awb.h\"\n> +#include \"libipa/ccm.h\"\n>  \n>  namespace libcamera {\n>  \n> @@ -37,10 +38,12 @@ struct IPASessionConfiguration {\n>  \n>  struct IPAActiveState {\n>         ipa::awb::ActiveState awb;\n> +       ipa::ccm::ActiveState ccm;\n>  };\n>  \n>  struct IPAFrameContext : public FrameContext {\n>         ipa::awb::FrameContext awb;\n> +       ipa::ccm::FrameContext ccm;\n>  };\n>  \n>  struct IPAContext {\n> \n> -- \n> 2.54.0\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 5C271C3261\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 18 Jun 2026 20:43:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 14A076301A;\n\tThu, 18 Jun 2026 22:43:30 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 42AB861754\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 18 Jun 2026 22:43:29 +0200 (CEST)","from monstersaurus.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0AD6E8E0;\n\tThu, 18 Jun 2026 22:42:54 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"uw1M3c2R\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1781815374;\n\tbh=6VjBcLyOaFRvhovHGdDKIU32kMgEpnjzxSFaPmvBw1U=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=uw1M3c2RYW9RC9kxZsEGeosxGGibOosebAwpRx1SfkQT4KNxr++X0DC9Vig/ccdJX\n\ta5vBFsnj1WYOKdtU8gUgWcayIm8wzCn8xpQ9qMCakGawb3qpm46aDXuxkmRmq/4zo7\n\tcgaYQFswg1Q6ee06wHxAwDZMWI720gETfREDsvCQ=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260618-rppx1-ipa-v1-14-32337264cfcd@ideasonboard.com>","References":"<20260618-rppx1-ipa-v1-0-32337264cfcd@ideasonboard.com>\n\t<20260618-rppx1-ipa-v1-14-32337264cfcd@ideasonboard.com>","Subject":"Re: [PATCH 14/14] ipa: rppx1: Add Ccm algorithm","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>, Niklas =?utf-8?b?U8O2?=\n\t=?utf-8?q?derlund?= <niklas.soderlund+renesas@ragnatech.se>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Thu, 18 Jun 2026 21:43:26 +0100","Message-ID":"<178181540656.288350.10204281687047403407@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":39198,"web_url":"https://patchwork.libcamera.org/comment/39198/","msgid":"<20260620093039.GC3631325@ragnatech.se>","date":"2026-06-20T09:30:39","subject":"Re: [PATCH 14/14] ipa: rppx1: Add Ccm algorithm","submitter":{"id":230,"url":"https://patchwork.libcamera.org/api/people/230/","name":"Niklas Söderlund","email":"niklas.soderlund+renesas@ragnatech.se"},"content":"Hi Jacopo,\n\nThanks for your work.\n\nOn 2026-06-18 12:18:53 +0200, Jacopo Mondi wrote:\n> Add Ccm algorithm to the RPP-X1 IPA.\n> \n> The implementation is based on libIPA CcmAlgorithm.\n> \n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n\nReviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>\n\n> ---\n>  src/ipa/rppx1/algorithms/awb.cpp     |   3 +-\n>  src/ipa/rppx1/algorithms/ccm.cpp     | 124 +++++++++++++++++++++++++++++++++++\n>  src/ipa/rppx1/algorithms/ccm.h       |  54 +++++++++++++++\n>  src/ipa/rppx1/algorithms/meson.build |   1 +\n>  src/ipa/rppx1/ipa_context.cpp        |  10 +++\n>  src/ipa/rppx1/ipa_context.h          |   3 +\n>  6 files changed, 193 insertions(+), 2 deletions(-)\n> \n> diff --git a/src/ipa/rppx1/algorithms/awb.cpp b/src/ipa/rppx1/algorithms/awb.cpp\n> index 6316606b4b0f..598e4503513c 100644\n> --- a/src/ipa/rppx1/algorithms/awb.cpp\n> +++ b/src/ipa/rppx1/algorithms/awb.cpp\n> @@ -275,11 +275,10 @@ RppX1AwbStats Awb::calculateRgbMeans(const IPAFrameContext &frameContext,\n>  \trgbMeans = rgbMeans.max(0.0);\n>  \n>  \t/*\n> -\t * \\todo\n>  \t * The ISP computes the AWB means after applying the CCM. Apply\n>  \t * the inverse as we want to get the raw means before the colour gains.\n> -\t * rgbMeans = frameContext.ccm.ccm.inverse() * rgbMeans;\n>  \t */\n> +\trgbMeans = frameContext.ccm.ccm.inverse() * rgbMeans;\n>  \n>  \t/*\n>  \t * The ISP computes the AWB means after applying the colour gains,\n> diff --git a/src/ipa/rppx1/algorithms/ccm.cpp b/src/ipa/rppx1/algorithms/ccm.cpp\n> new file mode 100644\n> index 000000000000..4b485d1cff0e\n> --- /dev/null\n> +++ b/src/ipa/rppx1/algorithms/ccm.cpp\n> @@ -0,0 +1,124 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2026, Ideas On Board\n> + *\n> + * RPP-X1 color correction matrix control algorithm\n> + */\n> +\n> +#include \"ccm.h\"\n> +\n> +#include <linux/media/dreamchip/rppx1-config.h>\n> +\n> +#include <libcamera/base/log.h>\n> +#include <libcamera/base/utils.h>\n> +\n> +#include <libcamera/control_ids.h>\n> +\n> +#include <libcamera/ipa/core_ipa_interface.h>\n> +\n> +/**\n> + * \\file ccm.h\n> + */\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa::rppx1::algorithms {\n> +\n> +LOG_DEFINE_CATEGORY(RppX1Ccm)\n> +\n> +/**\n> + * \\class Ccm\n> + * \\brief Color correction matrix algorithm\n> + */\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::init\n> + */\n> +int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)\n> +{\n> +\treturn ccmAlgo_.init(tuningData, context.ctrlMap);\n> +}\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::configure\n> + */\n> +int Ccm::configure(IPAContext &context,\n> +\t\t   [[maybe_unused]] const IPACameraSensorInfo &configInfo)\n> +{\n> +\treturn ccmAlgo_.configure(context.activeState.ccm,\n> +\t\t\t\t  context.activeState.awb.automatic.temperatureK);\n> +}\n> +\n> +void Ccm::queueRequest(IPAContext &context,\n> +\t\t       [[maybe_unused]] const uint32_t frame,\n> +\t\t       IPAFrameContext &frameContext,\n> +\t\t       const ControlList &controls)\n> +{\n> +\t/* Nothing to do here, the ccm will be calculated in prepare() */\n> +\tif (frameContext.awb.autoEnabled)\n> +\t\treturn;\n> +\n> +\tccmAlgo_.queueRequest(context.activeState.ccm, frameContext.ccm, controls);\n> +}\n> +\n> +void Ccm::setParameters(RppX1Params *params, IPAFrameContext &context)\n> +{\n> +\tconst Matrix<float, 3, 3> &matrix = context.ccm.ccm;\n> +\tconst Matrix<int16_t, 3, 1> &offsets = context.ccm.offsets;\n> +\n> +\tauto config = params->block<BlockType::CcorPost>();\n> +\tconfig.setEnabled(true);\n> +\n> +\t/*\n> +\t * RPP-X1 coefficients are 16 bits Q4.12 signed fixed-point ranging from\n> +\t * -8 (0x8000) to +7.9996 (0x7fff). x1 = 0x1000.\n> +\t */\n> +\tfor (unsigned int i = 0; i < 3; i++) {\n> +\t\tfor (unsigned int j = 0; j < 3; j++)\n> +\t\t\tconfig->coeff[i][j] = Q<4, 12>(matrix[i][j]).quantized();\n> +\t}\n> +\n> +\t/*\n> +\t * RPP-X1 offsets are 25 bits 2's complement while the CcmAlgorithm\n> +\t * class uses int16_t.\n> +\t *\n> +\t * \\todo: Better investigate how negative offsets are handled in the\n> +\t * offsets interpolation.\n> +\t */\n> +\tfor (unsigned int i = 0; i < 3; i++)\n> +\t\tconfig->offset[i] = static_cast<int32_t>(offsets[i][0]);\n> +\n> +\tLOG(RppX1Ccm, Debug) << \"Setting matrix \" << matrix;\n> +\tLOG(RppX1Ccm, Debug) << \"Setting offsets \" << offsets;\n> +}\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::prepare\n> + */\n> +void Ccm::prepare(IPAContext &context, const uint32_t frame,\n> +\t\t  IPAFrameContext &frameContext, RppX1Params *params)\n> +{\n> +\tif (frameContext.awb.autoEnabled)\n> +\t\tccmAlgo_.prepare(context.activeState.ccm, frameContext.ccm,\n> +\t\t\t\t frame, frameContext.awb.temperatureK);\n> +\n> +\tsetParameters(params, frameContext);\n> +}\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::process\n> + */\n> +void Ccm::process([[maybe_unused]] IPAContext &context,\n> +\t\t  [[maybe_unused]] const uint32_t frame,\n> +\t\t  IPAFrameContext &frameContext,\n> +\t\t  [[maybe_unused]] const RppX1Stats *stats,\n> +\t\t  ControlList &metadata)\n> +{\n> +\tccmAlgo_.process(frameContext.ccm, metadata);\n> +}\n> +\n> +REGISTER_IPA_ALGORITHM(Ccm, \"Ccm\")\n> +\n> +} /* namespace ipa::rppx1::algorithms */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/rppx1/algorithms/ccm.h b/src/ipa/rppx1/algorithms/ccm.h\n> new file mode 100644\n> index 000000000000..5e183d255a48\n> --- /dev/null\n> +++ b/src/ipa/rppx1/algorithms/ccm.h\n> @@ -0,0 +1,54 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2026, Ideas On Board\n> + *\n> + * RPP-X1 color correction matrix control algorithm\n> + */\n> +\n> +#pragma once\n> +\n> +#include \"libcamera/internal/matrix.h\"\n> +\n> +#include <libipa/ccm.h>\n> +\n> +#include \"algorithm.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa::rppx1::algorithms {\n> +\n> +class Ccm : public Algorithm\n> +{\n> +public:\n> +\tCcm() {}\n> +\t~Ccm() = default;\n> +\n> +\tint init(IPAContext &context, const ValueNode &tuningData) override;\n> +\tint configure(IPAContext &context,\n> +\t\t      const IPACameraSensorInfo &configInfo) override;\n> +\tvoid queueRequest(IPAContext &context,\n> +\t\t\t  const uint32_t frame,\n> +\t\t\t  IPAFrameContext &frameContext,\n> +\t\t\t  const ControlList &controls) override;\n> +\tvoid prepare(IPAContext &context, const uint32_t frame,\n> +\t\t     IPAFrameContext &frameContext,\n> +\t\t     RppX1Params *params) override;\n> +\tvoid process(IPAContext &context, const uint32_t frame,\n> +\t\t     IPAFrameContext &frameContext,\n> +\t\t     const RppX1Stats *stats,\n> +\t\t     ControlList &metadata) override;\n> +\n> +private:\n> +\tvoid parseYaml(const ValueNode &tuningData);\n> +\tvoid setParameters(RppX1Params *params, IPAFrameContext &context);\n> +\n> +\tunsigned int ct_;\n> +\tInterpolator<Matrix<float, 3, 3>> ccm_;\n> +\tInterpolator<Matrix<int16_t, 3, 1>> offsets_;\n> +\n> +\tCcmAlgorithm<Q<4, 12>> ccmAlgo_;\n> +};\n> +\n> +} /* namespace ipa::rppx1::algorithms */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/rppx1/algorithms/meson.build b/src/ipa/rppx1/algorithms/meson.build\n> index 70368ad9bac9..ddebbfbecbee 100644\n> --- a/src/ipa/rppx1/algorithms/meson.build\n> +++ b/src/ipa/rppx1/algorithms/meson.build\n> @@ -2,4 +2,5 @@\n>  \n>  rppx1_ipa_algorithms = files([\n>      'awb.cpp',\n> +    'ccm.cpp'\n>  ])\n> diff --git a/src/ipa/rppx1/ipa_context.cpp b/src/ipa/rppx1/ipa_context.cpp\n> index ea7008f5685d..57a2b56774bc 100644\n> --- a/src/ipa/rppx1/ipa_context.cpp\n> +++ b/src/ipa/rppx1/ipa_context.cpp\n> @@ -51,6 +51,11 @@ namespace libcamera::ipa::rppx1 {\n>   * \\copydoc ipa::awb::ActiveState\n>   */\n>  \n> +/**\n> + * \\var IPAActiveState::ccm\n> + * \\copydoc ipa::ccm::ActiveState\n> + */\n> +\n>  /**\n>   * \\struct IPAFrameContext\n>   * \\brief Per-frame context for algorithms\n> @@ -61,6 +66,11 @@ namespace libcamera::ipa::rppx1 {\n>   * \\copydoc ipa::awb::FrameContext\n>   */\n>  \n> +/**\n> + * \\var IPAFrameContext::ccm\n> + * \\copydoc ipa::ccm::FrameContext\n> + */\n> +\n>  /**\n>   * \\struct IPAContext\n>   * \\brief Global IPA context data shared between all algorithms\n> diff --git a/src/ipa/rppx1/ipa_context.h b/src/ipa/rppx1/ipa_context.h\n> index 57197d865d3f..bba1d10da73f 100644\n> --- a/src/ipa/rppx1/ipa_context.h\n> +++ b/src/ipa/rppx1/ipa_context.h\n> @@ -21,6 +21,7 @@\n>  #include <libipa/fc_queue.h>\n>  \n>  #include \"libipa/awb.h\"\n> +#include \"libipa/ccm.h\"\n>  \n>  namespace libcamera {\n>  \n> @@ -37,10 +38,12 @@ struct IPASessionConfiguration {\n>  \n>  struct IPAActiveState {\n>  \tipa::awb::ActiveState awb;\n> +\tipa::ccm::ActiveState ccm;\n>  };\n>  \n>  struct IPAFrameContext : public FrameContext {\n>  \tipa::awb::FrameContext awb;\n> +\tipa::ccm::FrameContext ccm;\n>  };\n>  \n>  struct IPAContext {\n> \n> -- \n> 2.54.0\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 9CC38C3301\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 20 Jun 2026 09:30:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BC274656D3;\n\tSat, 20 Jun 2026 11:30:46 +0200 (CEST)","from fout-a8-smtp.messagingengine.com\n\t(fout-a8-smtp.messagingengine.com [103.168.172.151])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 15CAF623F6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 20 Jun 2026 11:30:45 +0200 (CEST)","from phl-compute-02.internal (phl-compute-02.internal\n\t[10.202.2.42])\n\tby mailfout.phl.internal (Postfix) with ESMTP id B262DEC0330;\n\tSat, 20 Jun 2026 05:30:43 -0400 (EDT)","from phl-frontend-04 ([10.202.2.163])\n\tby phl-compute-02.internal (MEProxy); Sat, 20 Jun 2026 05:30:43 -0400","by mail.messagingengine.com (Postfix) with ESMTPA; Sat,\n\t20 Jun 2026 05:30:42 -0400 (EDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=ragnatech.se header.i=@ragnatech.se\n\theader.b=\"ezqi9vXp\"; dkim=pass (2048-bit key;\n\tunprotected) header.d=messagingengine.com\n\theader.i=@messagingengine.com header.b=\"hmu3bqug\"; \n\tdkim-atps=neutral","DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/relaxed; d=ragnatech.se; h=\n\tcc:cc:content-transfer-encoding:content-type:content-type:date\n\t:date:from:from:in-reply-to:in-reply-to:message-id:mime-version\n\t:references:reply-to:subject:subject:to:to; s=fm3; t=1781947843;\n\tx=1782034243; bh=56rw2t8uBoDvsrhEhB52gSocNQm7Bk7glLzp7W2g3aA=; b=\n\tezqi9vXpAXW2zIyTGjMhnJ3jz/pIxszhzXt3SiwF7prJ3yUUz0eY1ftpGDhA22ZK\n\tH1MscHY4pzEBS5bnYeUKztrKCorlA4KTTJ++gnLNhfBBBDu5NmEiir++AoijXwf5\n\tbUpT1phFczYrSbOLxpt3tKzK/jGrlGFIyzbN+mnuRqDeITyh4kUv6Pliq9p0aV4X\n\tXLCNij72ongl1EM0iaKgdbIYD5hJJCgWdyBVRuqitXwFFls9TzKxvngzR4uBHvTP\n\tQTuGxYWRMYdR8WfRobwXhsqZUwVYd9F9niDR74vrTOdxmNgTAKkmivxuaHS4f+sh\n\tkWuNGKS114xHKtFHu3yFaw==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=\n\tmessagingengine.com; h=cc:cc:content-transfer-encoding\n\t:content-type:content-type:date:date:feedback-id:feedback-id\n\t:from:from:in-reply-to:in-reply-to:message-id:mime-version\n\t:references:reply-to:subject:subject:to:to:x-me-proxy\n\t:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t=1781947843; x=\n\t1782034243; bh=56rw2t8uBoDvsrhEhB52gSocNQm7Bk7glLzp7W2g3aA=; b=h\n\tmu3bqugEnszcMwr0dV/1UTxa1VOsS7HOwwqmSTgzE6xrL9rVnu/x1LqLZs8KlyWZ\n\t+xWhiJLkJxviLnafuyIIflMWv4QxUjN9Tjsk48Ow1GV1VeQ8ZXJkeXtvkUqdqDZY\n\tvZ/w+EYr7fU3z+zSWziCii/lF5Hlg5iyP3IifFaRvwSpIPDb7f5nPe5K4lMe9S5x\n\taMcluO2nir+u6zV23XkBjd27mF0HP9pK5QaDUf9iZD+EXZLGdJn5RCJ8aPDhOI6b\n\tEMHvQR810ZcFY7mqQRfn2MSY4BzukE3DbTxliJ4ZltCq0+Y0erN9Kx401TQkC8gC\n\tzivlyVs2X3vMFwL8+sX6Q=="],"X-ME-Sender":"<xms:wl02aozoKNdtgcRD_dCStg4dt6f9ODy3Vj7BBtiFnGKmE1nYkNrjOw>\n\t<xme:wl02aiRr_P7TQM0Y0DaBidmllMgMr02LhSdU_G4qzjEiI2A1y486uAw3Y9BJ_mHWA\n\tD6WBXQsD628JR9FicZVrOpS4qV-hi3tuahF3oPaSj58SGA-FOAE1Q>","X-ME-Received":"<xmr:wl02an_sNIuiBWgcQvKwDd5_-FHT5nMWiNKujlv5u5xcSnFczME_ljMrRUhwmA5CzunwWYj-p6NomRzRN_V0oclUdHJT>","X-ME-Proxy-Cause":"dmFkZTEtZ8bwFYIc568XLWZ8zd4I1J4QmkDu1lAPaooWOvt1QVyd6H+wzkuaxAMBA/KKsB\n\trRn35XfnZ/Uu4TG2+LhS+7+NcBxdVTVSBuVer8SuhFzyAaQthjRa2ZGFVGN285oRYOLXnm\n\twrJJO5GjwJqRpzj/3LdMMasRkYp4zAguh7RmRWGw3BzPhtXprBsmC90izH1miOFSsTqZjK\n\tmY+nqTNRFBVx+iyOyz+MfNlO6bt82skuFZMXbNzj3YnC1BDCHaZCPPjmkbn3SA0P4mHzT9\n\tQJhaowXQy6YiCiSRnxhEnlj+R3Z3vufbaBzHhH+6/lexs5qyhOsrkNqR9PlGF+DXeLHgL0\n\tcg2TDW7lHuYL4jWEc9QI319E3JPSO6Ypz+UgBGp9zvjAFlXcUtQXyaE+c/p31/pbuav44I\n\tDKJ3dLYm+uJo0mZa1aKOFrk4ivkqHnIZOcuTsQNnUwfZJHzx2qhrhl35KEdjh2MfnhM5Cv\n\tGASeuV+lrztRhrxUvq4OTkmNo4xMaM7pP1HII//E3FPyhUxz+s2cO07Sc2o3egE1JHxTq7\n\t3L8I7c6M6tK8a6aciKAsbWX+g/JOPFbZSbHAyQQPoM/7Q7W4OpcZrDvZqmNoMo3FQsPRwC\n\tJsW9EdTnb0Fb+KjiATm554dMA7qQuG5Z3++GFbTT9Yg0qSFTNmZ1oC1AF/Qw","X-ME-Proxy":"<xmx:wl02ahoV8SB6LWhZgmhrywc5d_MejwGpH6-S-BPREX4APIFzF6VBqg>\n\t<xmx:wl02asnVWYnm2EiR7mKYUWcoxn1uEDRAIROMMg1s3lqAc1XsiLXiSA>\n\t<xmx:wl02agKINjJGk5zO4j2gb82bIacDQvGcHhJmmsnBRBnBHTVXMZIojA>\n\t<xmx:wl02apxn1lhJwzTJGsXXljw7dlp9X5RyEVikC-x9uSfaWnw0Ner17A>\n\t<xmx:w102atHqNRKEuanBTepiav4B1YGxAz62ajIBf9gTeE60n-b618bWNZjF>","Feedback-ID":"i80c9496c:Fastmail","Date":"Sat, 20 Jun 2026 11:30:39 +0200","From":"Niklas =?utf-8?q?S=C3=B6derlund?=\n\t<niklas.soderlund+renesas@ragnatech.se>","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 14/14] ipa: rppx1: Add Ccm algorithm","Message-ID":"<20260620093039.GC3631325@ragnatech.se>","References":"<20260618-rppx1-ipa-v1-0-32337264cfcd@ideasonboard.com>\n\t<20260618-rppx1-ipa-v1-14-32337264cfcd@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20260618-rppx1-ipa-v1-14-32337264cfcd@ideasonboard.com>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]