{"id":26885,"url":"https://patchwork.libcamera.org/api/patches/26885/?format=json","web_url":"https://patchwork.libcamera.org/patch/26885/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260615-libipa-algorithms-v1-8-e949c937422e@ideasonboard.com>","date":"2026-06-15T14:05:33","name":"[08/11] ipa: mali-c55: Implement Ccm algorithm","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"14bbf36018a167e3d8bea8b6d80b5f171801be0c","submitter":{"id":143,"url":"https://patchwork.libcamera.org/api/people/143/?format=json","name":"Jacopo Mondi","email":"jacopo.mondi@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/26885/mbox/","series":[{"id":5992,"url":"https://patchwork.libcamera.org/api/series/5992/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5992","date":"2026-06-15T14:05:25","name":"ipa: libipa: Introduce libipa algorithms","version":1,"mbox":"https://patchwork.libcamera.org/series/5992/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/26885/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/26885/checks/","tags":{},"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 ABD92C32D4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 15 Jun 2026 14:06:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5C133623F1;\n\tMon, 15 Jun 2026 16:06:01 +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 187EF623EB\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 15 Jun 2026 16:05:49 +0200 (CEST)","from [192.168.1.104] (net-93-65-100-155.cust.vodafonedsl.it\n\t[93.65.100.155])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4E93B1E48;\n\tMon, 15 Jun 2026 16:05:16 +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=\"uRIXCLuD\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1781532316;\n\tbh=6bqc2/XcD2qmj+9SAp2g4fk3X/yoP9cnFEchCTiEYUo=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=uRIXCLuD5TeV1+Ht7PoY/b6NpW9xAOTbUpoCIl7R2nzDbt3rRrxs6dLoHggT5ai5f\n\tC/MifBgHBn6zBQ4aQxCG3INlgBXEHNPsl0fOSYwBLeMZss8D2Ai8RoAu98nXwtl9SJ\n\trYEKziLSlnRTvp7MbRqdR+Z6hiE2RyaRq7zpQcjs=","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Date":"Mon, 15 Jun 2026 16:05:33 +0200","Subject":"[PATCH 08/11] ipa: mali-c55: Implement Ccm algorithm","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"<20260615-libipa-algorithms-v1-8-e949c937422e@ideasonboard.com>","References":"<20260615-libipa-algorithms-v1-0-e949c937422e@ideasonboard.com>","In-Reply-To":"<20260615-libipa-algorithms-v1-0-e949c937422e@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","X-Mailer":"b4 0.14.3","X-Developer-Signature":"v=1; a=openpgp-sha256; l=9110;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=6bqc2/XcD2qmj+9SAp2g4fk3X/yoP9cnFEchCTiEYUo=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqMAa51zvayb9KaJ77YW3k6C3D6StTZBnxiWg7l\n\t17aHHcep7yJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCajAGuQAKCRByNAaPFqFW\n\tPELeD/9k3vWVtL6j3UBKlORsjdZWL9hU+WqKFEHuFASyrjx6dvW0Du8V5EjUWPF+oNMhcKxxUbL\n\tFRIYvMRkQUXxjKNjTDKUXg6Vd1XHJMLy3l4R6dwtarcN5wFn1/6uWrrZehJeFTQr8Fh6FLd3lhR\n\toKgBOo4seibw4itU6EFs+PgWmDEuL/kfjCGLPsWITvwZWHy4Bzj9LMqv2rf8pKIcGiozovqdNkF\n\ts3rdW5GBFupI+bESgJ9RQhqJdD63a9TeLb/Fn07JexqZC7Nps1lLa2RhnlnI4RtzqsVUgSCnB6E\n\tEDzrxIZnyFRjV/+Yuqws/WVLcPp1FBm0Azy1eX2WtLTqNKGSZONrJf+ehJOOQf0ueC/DTnYzQqX\n\tce61uGAbRPoIRmAhoyoSDL4F+L5tnPoJK7YjwMUvqzRg52MlbPXSmy2OmgSp2OyNEYODdaTiCEM\n\tLEKxaigHVXeBdhotfxBSYq5gwYshMevu3v4FjaMrO2rf95lDwL/kHxbt+hjWMkZiNebJStu1O/8\n\t/KF6ruVFSWxANhwVpiPKBQgoCblvvCwgcQDdfYqRHPspJKzd954zur69Gvt/1A2n+l9qJQYRjfT\n\tThqdrYF2SQyDHaQsDm9yIIrSPAGr6HngPjHpB55jQV5E9ruSOlF+fRQxiM2I8BD7EWdz7WOT+UY\n\tdE29+68tj01lh7g==","X-Developer-Key":"i=jacopo.mondi@ideasonboard.com; a=openpgp;\n\tfpr=72392EDC88144A65C701EA9BA5826A2587AD026B","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>"},"content":"Implement Ccm algorithm for Mali-C55 base on the libipa CcmAlgorithm class.\n\nMali has configurable colour gains as part of the CCM hardware block.\nFix them to 1.0 for the time being as white balance is performed by the\ndedicated Awb algorithm in the Bayer colour space.\n\nThe existing Mali Ccm algorithm implementation is a bit more strict on\nwhen to re-interpolate the CCM coefficients. Keep the algorithm a little\nmore strict and only interpolate on 20% color temperature changes.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/mali-c55/algorithms/ccm.cpp     | 175 ++++++++++++++++++++++++++++++++\n src/ipa/mali-c55/algorithms/ccm.h       |  66 ++++++++++++\n src/ipa/mali-c55/algorithms/meson.build |   1 +\n src/ipa/mali-c55/ipa_context.h          |   5 +\n 4 files changed, 247 insertions(+)","diff":"diff --git a/src/ipa/mali-c55/algorithms/ccm.cpp b/src/ipa/mali-c55/algorithms/ccm.cpp\nnew file mode 100644\nindex 000000000000..008ab2d1e31e\n--- /dev/null\n+++ b/src/ipa/mali-c55/algorithms/ccm.cpp\n@@ -0,0 +1,175 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas On Board\n+ *\n+ * Mali C55 Color Correction Matrix control algorithm\n+ */\n+\n+#include \"ccm.h\"\n+\n+#include <libcamera/base/log.h>\n+\n+/**\n+ * \\file ccm.h\n+ * \\brief Mali-C55 CCM implementation\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa::mali_c55::algorithms {\n+\n+LOG_DEFINE_CATEGORY(MaliC55Ccm)\n+\n+/**\n+ * \\class Ccm\n+ * \\brief Mali-C55 color correction matrix algorithm\n+ */\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::init\n+ */\n+int Ccm::init(IPAContext &context, const ValueNode &tuningData)\n+{\n+\tauto &cmap = context.ctrlMap;\n+\tint ret = ccmAlgo_.init(tuningData, cmap);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/*\n+\t * Mali-C55 allows to perform WB in the RGB color space as part of the\n+\t * CCM. Fix the gains at 1.0 as we perform White Balance in the Bayer\n+\t * domain.\n+\t */\n+\tgain_ = 1.0;\n+\n+\treturn 0;\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+\tlastCt_ = context.activeState.awb.automatic.temperatureK;\n+\treturn ccmAlgo_.configure(context.activeState.ccm, lastCt_);\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(MaliC55Params *params, const IPAFrameContext &frameContext)\n+{\n+\tauto config = params->block<MaliC55Blocks::Ccm>();\n+\n+\tfor (unsigned int i = 0; i < 3; i++)\n+\t\tconfig->gains[i] = UQ<4, 8>(gain_).quantized();\n+\n+\tfor (unsigned int i = 0; i < 3; i++)\n+\t\tconfig->offs[i] = frameContext.ccm.offsets[i][0];\n+\n+\tconst Matrix<float, 3, 3> &ccm = frameContext.ccm.ccm;\n+\tfor (unsigned int i = 0; i < 3; i++) {\n+\t\tfor (unsigned int j = 0; j < 3; j++) {\n+\t\t\tuint16_t val = Q<5, 8>(ccm[i][j]).quantized();\n+\n+\t\t\tif (val && ccm[i][j] < 0) {\n+\t\t\t\t/*\n+\t\t\t\t * CCM coefficients are expected to be in\n+\t\t\t\t * sign/magnitude format.\n+\t\t\t\t *\n+\t\t\t\t * As we're here handling the case where\n+\t\t\t\t * ccm[i][j] is negative, its quantized\n+\t\t\t\t * representation is stored in 'val' as\n+\t\t\t\t * 2's complement.\n+\t\t\t\t *\n+\t\t\t\t * We need to invert the 2's complement by\n+\t\t\t\t * re-complementing it to 1 and adding 1 back.\n+\t\t\t\t *\n+\t\t\t\t * Let's make a practical example on how to\n+\t\t\t\t * reverse the 2's complement of -7 with 4 bits\n+\t\t\t\t * and BIT(5) for sign.\n+\t\t\t\t *\n+\t\t\t\t * 2's complement (ignore sign bit)\n+\t\t\t\t *  - 1's complement of abs(-7)\n+\t\t\t\t *    2^4 - 1 - 7 = 8 -> \t1000\n+\t\t\t\t *  - Add one: 8 + 1 = 9 -> \t1001\n+\t\t\t\t *\n+\t\t\t\t * -7 is then [1 1001] in memory in 2's complement\n+\t\t\t\t *\n+\t\t\t\t * Reverse 2's complement (ignore sign bit)\n+\t\t\t\t *  - 1's complement of the 2's complement representation\n+\t\t\t\t *    2^4 - 1 - 9 = 6 ->\t0110\n+\t\t\t\t *  - add one: 6 + 1 = 7 ->\t0111\n+\t\t\t\t *\n+\t\t\t\t *  -7 is then [1 0111] in memory in Sign/Magnitude\n+\t\t\t\t */\n+\t\t\t\tval = ((~(val & ~(1 << 12)) & 0x1ff) + 1) | (1 << 12);\n+\t\t\t}\n+\t\t\tconfig->coeffs[i][j] = val;\n+\t\t}\n+\t}\n+\n+\tconfig.setEnabled(true);\n+\n+\tLOG(MaliC55Ccm, Debug) << \"Setting ccm: \" << ccm << \" offsets: \"\n+\t\t\t       << frameContext.ccm.offsets\n+\t\t\t       << \" with fixed gain \" << gain_;\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, MaliC55Params *params)\n+{\n+\tif (!frameContext.awb.autoEnabled) {\n+\t\tsetParameters(params, frameContext);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * The Mali-C55 Ccm implementation is slightly stricter than the\n+\t * CcmAlgorithm class and only re-interpolates if the colour temperature\n+\t * changes of a certain amount.\n+\t */\n+\tfloat ct = frameContext.awb.temperatureK * 1.0f;\n+\tif (frame > 0 && (ct < lastCt_ * 1.2 && ct > lastCt_ * 0.8)) {\n+\t\tframeContext.ccm.ccm = context.activeState.ccm.automatic.ccm;\n+\t\tframeContext.ccm.offsets = context.activeState.ccm.automatic.offsets;\n+\t\tlastCt_ = ct;\n+\n+\t\treturn;\n+\t}\n+\n+\tccmAlgo_.prepare(context.activeState.ccm, frameContext.ccm, frame, ct);\n+\tsetParameters(params, frameContext);\n+\tlastCt_ = ct;\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 mali_c55_stats_buffer *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::mali_c55::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/mali-c55/algorithms/ccm.h b/src/ipa/mali-c55/algorithms/ccm.h\nnew file mode 100644\nindex 000000000000..73649204a7ee\n--- /dev/null\n+++ b/src/ipa/mali-c55/algorithms/ccm.h\n@@ -0,0 +1,66 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas On Board\n+ *\n+ * Mali C55 Color Correction Matrix control algorithm\n+ */\n+\n+#pragma once\n+\n+#include <linux/media/arm/mali-c55-config.h>\n+\n+#include <libcamera/controls.h>\n+\n+#include \"libcamera/internal/value_node.h\"\n+\n+#include \"libipa/ccm.h\"\n+#include \"libipa/fixedpoint.h\"\n+\n+#include \"algorithm.h\"\n+#include \"ipa_context.h\"\n+#include \"params.h\"\n+\n+namespace libcamera {\n+\n+namespace ipa::mali_c55::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     MaliC55Params *params) override;\n+\tvoid process(IPAContext &context, const uint32_t frame,\n+\t\t     IPAFrameContext &frameContext,\n+\t\t     const mali_c55_stats_buffer *stats,\n+\t\t     ControlList &metadata) override;\n+\n+private:\n+\tvoid setParameters(MaliC55Params *params, const IPAFrameContext &context);\n+\n+\t/*\n+\t * The CCM coefficient registers are said to be in Q<4,8> but this\n+\t * doesn't include the sign bit as the register is 13 bits wide\n+\t * (Q-format TI variant).\n+\t *\n+\t * As the Quantized class uses the ARM variant of the Q-format notation,\n+\t * make it <5, 8> to include the sign bit.\n+\t */\n+\tCcmAlgorithm<Q<5, 8>> ccmAlgo_;\n+\tfloat gain_;\n+\tfloat lastCt_;\n+};\n+\n+} /* namespace ipa::mali_c55::algorithms */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/mali-c55/algorithms/meson.build b/src/ipa/mali-c55/algorithms/meson.build\nindex 1665da071634..24a27ce66562 100644\n--- a/src/ipa/mali-c55/algorithms/meson.build\n+++ b/src/ipa/mali-c55/algorithms/meson.build\n@@ -4,5 +4,6 @@ mali_c55_ipa_algorithms = files([\n     'agc.cpp',\n     'awb.cpp',\n     'blc.cpp',\n+    'ccm.cpp',\n     'lsc.cpp',\n ])\ndiff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h\nindex 3d884ea17eb8..2d3e91d56baa 100644\n--- a/src/ipa/mali-c55/ipa_context.h\n+++ b/src/ipa/mali-c55/ipa_context.h\n@@ -16,6 +16,7 @@\n #include <libipa/fc_queue.h>\n \n #include \"libipa/awb.h\"\n+#include \"libipa/ccm.h\"\n #include \"libipa/fixedpoint.h\"\n \n namespace libcamera {\n@@ -59,6 +60,8 @@ struct IPAActiveState {\n \t} agc;\n \n \tipa::awb::ActiveState awb;\n+\n+\tipa::ccm::ActiveState ccm;\n };\n \n struct IPAFrameContext : public FrameContext {\n@@ -69,6 +72,8 @@ struct IPAFrameContext : public FrameContext {\n \t} agc;\n \n \tipa::awb::FrameContext awb;\n+\n+\tipa::ccm::FrameContext ccm;\n };\n \n struct IPAContext {\n","prefixes":["08/11"]}