Patch Detail
Show a patch.
GET /api/patches/26887/?format=api
{ "id": 26887, "url": "https://patchwork.libcamera.org/api/patches/26887/?format=api", "web_url": "https://patchwork.libcamera.org/patch/26887/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "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-11-e949c937422e@ideasonboard.com>", "date": "2026-06-15T14:05:36", "name": "[11/11] ipa: mali-c55: Port to use LscAlgorithm", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "5ec5c778d82e3138e29be2ad497e8ad11f5c0360", "submitter": { "id": 143, "url": "https://patchwork.libcamera.org/api/people/143/?format=api", "name": "Jacopo Mondi", "email": "jacopo.mondi@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/26887/mbox/", "series": [ { "id": 5992, "url": "https://patchwork.libcamera.org/api/series/5992/?format=api", "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/26887/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/26887/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 271FAC324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 15 Jun 2026 14:06:07 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1548D623ED;\n\tMon, 15 Jun 2026 16:06:04 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EA520623E3\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 2DCC51E2B;\n\tMon, 15 Jun 2026 16:05:17 +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=\"P4Zx/bEx\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1781532317;\n\tbh=zg4cmPp5HRWt/gyBfY1mXsplO4CkJuGZjtP200olsWE=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=P4Zx/bExvvXFPnfYemcS8CglYiQ6QsGB+jAwGIxA06mUTKuYxREXg3KTHqN7/42a+\n\tsZ+rM9jXWUF270GLxq42QGTUiwG+J6sVkDn22zYpHfc90rXKO0JvvpUwomWg7simK3\n\t5uukTYaOiBtOm13ZQVPJmodeG27fEZPfDqIHgOo0=", "From": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>", "Date": "Mon, 15 Jun 2026 16:05:36 +0200", "Subject": "[PATCH 11/11] ipa: mali-c55: Port to use LscAlgorithm", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20260615-libipa-algorithms-v1-11-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=11475;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=zg4cmPp5HRWt/gyBfY1mXsplO4CkJuGZjtP200olsWE=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqMAa6MHKpTNv5+E66H6HJ8AwDf0WMxTRLo6ao9\n\t+YrPmsU+16JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCajAGugAKCRByNAaPFqFW\n\tPAxxD/4onSo6uXwWiIyubkpsLolF1UhGfqMnTSyrTRsoRz+gJLnfp72vE1j4Hzx8MDvQCuJYnUa\n\t3gtoJBmcJ76pHkWXQHKxwVHKPQ9pj4NRf8Xbr9DASDMD0kcaxyg5ga48ZGEZlvcWhm2fYKeMUBK\n\tKSvob7dTKzTCmBaIJVRlXWb9zcPmIZdudO/RFeVF6DcNLM4EVhUkuuoknjfUfdyusb0H6DjwHdj\n\tnVZkzkvm0DvYmgN2sqe3EGbx1PhHXgIvxJ3rZD8+cKpAbbBAUwrs1HiuD+1GEgMG2sMHZlMVyjQ\n\tmThh5H871++5PKqYAOzChwP0AXv0/ibI9ed0LB0CyO6+LBFa067Dp0qu5FV56mI17VvRDo3XigB\n\ttnM5nx45Se5vWnjIb+j4fyEDcEN2go9bPEQzlrm7Iy5NQDkkvxAbAFi71/+lXd0VTcmWDgQczEo\n\tQA8kgmOv3oZLGJIc9B6USAtDljerR1eE0D+VOMn9mI//NmikBwQermTCAd7TCJohYbFhgd/us/3\n\tzTrgwBWXFEFG7mCUYL+qSgQM6OKaT3h1NkGy/3hmMJWsdVr/nTTgbx5KdBzcL4NVurYsKEg31KI\n\t7598dkdyhDvrlo6hPXhB8BTo5nBTAT5i1ZLKYp1frq7RaGV1wPwlJsINt5B43zhBvz3HYVPJqEf\n\tZrxpIsF879P/wFA==", "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": "Port the Mali-C55 LSC algorithm to use libIPA LscAlgorithm.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/mali-c55/algorithms/lsc.cpp | 163 ++++++++++++++++++++++--------------\n src/ipa/mali-c55/algorithms/lsc.h | 38 ++++++---\n src/ipa/mali-c55/ipa_context.h | 5 ++\n 3 files changed, 130 insertions(+), 76 deletions(-)", "diff": "diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp\nindex 36e50163aecc..624b393b80e5 100644\n--- a/src/ipa/mali-c55/algorithms/lsc.cpp\n+++ b/src/ipa/mali-c55/algorithms/lsc.cpp\n@@ -9,39 +9,74 @@\n \n #include <libcamera/base/utils.h>\n \n-#include \"libcamera/internal/value_node.h\"\n-\n namespace libcamera {\n \n namespace ipa::mali_c55::algorithms {\n \n LOG_DEFINE_CATEGORY(MaliC55Lsc)\n \n-int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)\n+/* Gain values in [1, 5] range */\n+static constexpr unsigned int kMeshScale = 6;\n+\n+/* Mali-C55 hw supports configurable mesh sizes; we fix it to 32. */\n+static constexpr unsigned int kMeshSize = 32;\n+static constexpr unsigned int kGridSize = kMeshSize * kMeshSize;\n+\n+/* Per-colour component page offsets in the mesh table. */\n+static constexpr unsigned int kRedOffset = 0;\n+static constexpr unsigned int kGreenOffset = 1024;\n+static constexpr unsigned int kBlueOffset = 2048;\n+\n+/*\n+ * \\todo Mali-C55 supports up to 4 colour temperatures.\n+ *\n+ * Unfortunately the uAPI only expose MALI_C55_NUM_MESH_SHADING_ELEMENTS (3072)\n+ * gain elements, which correspond to three pages of 1024 (32x32) entries.\n+ */\n+static constexpr unsigned int kMaxColourTemperature = 3;\n+\n+/*\n+ * The LSC algorithm implementation only supports 32x32 grids. Create a list of\n+ * positions from the grid size.\n+ */\n+std::vector<double> Lsc::segmentsToPosition() const\n {\n-\tif (!tuningData.contains(\"meshScale\")) {\n-\t\tLOG(MaliC55Lsc, Error) << \"meshScale missing from tuningData\";\n-\t\treturn -EINVAL;\n-\t}\n+\tstd::vector<double> positions(kMeshSize);\n+\tfor (double i = 0.0; i < kMeshSize; ++i)\n+\t\tpositions[i] = i / (kMeshSize - 1);\n \n-\tmeshScale_ = tuningData[\"meshScale\"].get<uint32_t>(0);\n+\treturn positions;\n+}\n \n-\tconst ValueNode &yamlSets = tuningData[\"sets\"];\n-\tif (!yamlSets.isList()) {\n-\t\tLOG(MaliC55Lsc, Error) << \"LSC tables missing or invalid\";\n-\t\treturn -EINVAL;\n-\t}\n+int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)\n+{\n+\tgridPos_ = segmentsToPosition();\n+\n+\treturn lscAlgo_.init(tuningData, context.ctrlMap, {\n+\t\t\t .keys = { \"r\", \"g\", \"b\" },\n+\t\t\t .numHCells = kMeshSize,\n+\t\t\t .numVCells = kMeshSize,\n+\t\t\t .sensorSize = context.sensorInfo.activeAreaSize\n+\t\t\t});\n+}\n \n-\tsize_t tableSize = 0;\n-\tconst auto &sets = yamlSets.asList();\n-\tfor (const auto &yamlSet : sets) {\n-\t\tuint32_t ct = yamlSet[\"ct\"].get<uint32_t>(0);\n+int Lsc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)\n+{\n+\tint ret = lscAlgo_.configure(context.activeState.lsc, configInfo.analogCrop,\n+\t\t\t\t gridPos_, gridPos_);\n+\tif (ret)\n+\t\treturn ret;\n \n-\t\tif (!ct) {\n-\t\t\tLOG(MaliC55Lsc, Error) << \"Invalid colour temperature\";\n-\t\t\treturn -EINVAL;\n-\t\t}\n+\t/* Re-initialize the mesh tables and reserve space for enough entries. */\n+\tmesh_ = std::vector<uint32_t>(kGridSize * kMaxColourTemperature);\n+\tcolourTemperatures_.clear();\n \n+\t/*\n+\t * Get the lsc tables per colour components and populate mesh_ with\n+\t * their content.\n+\t */\n+\tconst auto &components = lscAlgo_.getComponents();\n+\tfor (auto const &[ct, component] : components) {\n \t\tif (std::count(colourTemperatures_.begin(),\n \t\t\t colourTemperatures_.end(), ct)) {\n \t\t\tLOG(MaliC55Lsc, Error)\n@@ -49,44 +84,29 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)\n \t\t\treturn -EINVAL;\n \t\t}\n \n-\t\tstd::vector<uint8_t> rTable =\n-\t\t\tyamlSet[\"r\"].get<std::vector<uint8_t>>().value_or(utils::defopt);\n-\t\tstd::vector<uint8_t> gTable =\n-\t\t\tyamlSet[\"g\"].get<std::vector<uint8_t>>().value_or(utils::defopt);\n-\t\tstd::vector<uint8_t> bTable =\n-\t\t\tyamlSet[\"b\"].get<std::vector<uint8_t>>().value_or(utils::defopt);\n-\n-\t\t/*\n-\t\t * Some validation to do; only 16x16 and 32x32 tables of\n-\t\t * coefficients are acceptable, and all tables across all of the\n-\t\t * sets must be the same size. The first time we encounter a\n-\t\t * table we check that it is an acceptable size and if so make\n-\t\t * sure all other tables are of equal size.\n-\t\t */\n-\t\tif (!tableSize) {\n-\t\t\tif (rTable.size() != 256 && rTable.size() != 1024) {\n-\t\t\t\tLOG(MaliC55Lsc, Error)\n-\t\t\t\t\t<< \"Invalid table size for colour temperature \" << ct;\n-\t\t\t\treturn -EINVAL;\n-\t\t\t}\n-\t\t\ttableSize = rTable.size();\n-\t\t}\n+\t\tstd::vector<uint8_t> rTable = component.at(\"r\");\n+\t\tstd::vector<uint8_t> gTable = component.at(\"g\");\n+\t\tstd::vector<uint8_t> bTable = component.at(\"b\");\n \n-\t\tif (rTable.size() != tableSize ||\n-\t\t gTable.size() != tableSize ||\n-\t\t bTable.size() != tableSize) {\n+\t\t/* Only 32x32 tables of coefficients are accepted. */\n+\t\tif (rTable.size() != kGridSize || gTable.size() != kGridSize ||\n+\t\t bTable.size() != kGridSize) {\n \t\t\tLOG(MaliC55Lsc, Error)\n-\t\t\t\t<< \"Invalid or mismatched table size for colour temperature \" << ct;\n+\t\t\t\t<< \"Invalid table size for colour temperature \" << ct;\n \t\t\treturn -EINVAL;\n \t\t}\n \n-\t\tif (colourTemperatures_.size() >= 3) {\n+\t\tif (colourTemperatures_.size() >= kMaxColourTemperature) {\n \t\t\tLOG(MaliC55Lsc, Error)\n \t\t\t\t<< \"A maximum of 3 colour temperatures are supported\";\n \t\t\treturn -EINVAL;\n \t\t}\n \n-\t\tfor (unsigned int i = 0; i < tableSize; i++) {\n+\t\t/*\n+\t\t * Create the mesh table entries by assembling up to 3 gains per\n+\t\t * colour temperature in a u32 word.\n+\t\t */\n+\t\tfor (unsigned int i = 0; i < kGridSize; i++) {\n \t\t\tmesh_[kRedOffset + i] |=\n \t\t\t\t(rTable[i] << (colourTemperatures_.size() * 8));\n \t\t\tmesh_[kGreenOffset + i] |=\n@@ -98,34 +118,35 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)\n \t\tcolourTemperatures_.push_back(ct);\n \t}\n \n-\t/*\n-\t * The mesh has either 16x16 or 32x32 nodes, we tell the driver which it\n-\t * is based on the number of values in the tuning data's table.\n-\t */\n-\tif (tableSize == 256)\n-\t\tmeshSize_ = 15;\n-\telse\n-\t\tmeshSize_ = 31;\n-\n \treturn 0;\n }\n \n-void Lsc::fillConfigParamsBlock(MaliC55Params *params) const\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::queueRequest\n+ */\n+void Lsc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n+\t\t IPAFrameContext &frameContext, const ControlList &controls)\n+{\n+\tlscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc,\n+\t\t\t controls);\n+}\n+\n+void Lsc::programMeshTables(MaliC55Params *params) const\n {\n \tauto block = params->block<MaliC55Blocks::MeshShadingConfig>();\n \n \tblock->mesh_show = false;\n-\tblock->mesh_scale = meshScale_;\n+\tblock->mesh_scale = kMeshScale;\n \tblock->mesh_page_r = 0;\n \tblock->mesh_page_g = 1;\n \tblock->mesh_page_b = 2;\n-\tblock->mesh_width = meshSize_;\n-\tblock->mesh_height = meshSize_;\n+\tblock->mesh_width = kMeshSize - 1;\n+\tblock->mesh_height = kMeshSize - 1;\n \n \tstd::copy(mesh_.begin(), mesh_.end(), block->mesh);\n }\n \n-void Lsc::fillSelectionParamsBlock(MaliC55Params *params, uint8_t bank,\n+void Lsc::configureMeshBlending(MaliC55Params *params, uint8_t bank,\n \t\t\t\t uint8_t alpha) const\n {\n \tauto block = params->block<MaliC55Blocks::MeshShadingSel>();\n@@ -187,7 +208,7 @@ void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \t\tstd::tie(bank, alpha) = findBankAndAlpha(temperatureK);\n \t}\n \n-\tfillSelectionParamsBlock(params, bank, alpha);\n+\tconfigureMeshBlending(params, bank, alpha);\n \n \tif (frame > 0)\n \t\treturn;\n@@ -196,7 +217,19 @@ void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \t * If this is the first frame, we need to load the parsed coefficient\n \t * tables from tuning data to the ISP.\n \t */\n-\tfillConfigParamsBlock(params);\n+\tprogramMeshTables(params);\n+}\n+\n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::process\n+ */\n+void Lsc::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+\tlscAlgo_.process(frameContext.lsc, metadata);\n }\n \n REGISTER_IPA_ALGORITHM(Lsc, \"Lsc\")\ndiff --git a/src/ipa/mali-c55/algorithms/lsc.h b/src/ipa/mali-c55/algorithms/lsc.h\nindex 3d35fd72bfa8..79ad42094c42 100644\n--- a/src/ipa/mali-c55/algorithms/lsc.h\n+++ b/src/ipa/mali-c55/algorithms/lsc.h\n@@ -5,10 +5,19 @@\n * Mali-C55 Lens shading correction algorithm\n */\n \n-#include <map>\n+#include <vector>\n #include <tuple>\n \n+#include <linux/media/arm/mali-c55-config.h>\n+\n+#include \"libcamera/internal/value_node.h\"\n+\n+#include \"libipa/fixedpoint.h\"\n+#include \"libipa/lsc.h\"\n+\n #include \"algorithm.h\"\n+#include \"ipa_context.h\"\n+#include \"params.h\"\n \n namespace libcamera {\n \n@@ -21,23 +30,30 @@ public:\n \t~Lsc() = default;\n \n \tint init(IPAContext &context, const ValueNode &tuningData) override;\n+\tint configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;\n+\tvoid queueRequest(IPAContext &context, 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 private:\n-\tstatic constexpr unsigned int kRedOffset = 0;\n-\tstatic constexpr unsigned int kGreenOffset = 1024;\n-\tstatic constexpr unsigned int kBlueOffset = 2048;\n-\n-\tvoid fillConfigParamsBlock(MaliC55Params *params) const;\n-\tvoid fillSelectionParamsBlock(MaliC55Params *params,\n-\t\t\t\t uint8_t bank, uint8_t alpha) const;\n+\tstd::vector<double> segmentsToPosition() const;\n+\tvoid programMeshTables(MaliC55Params *params) const;\n+\tvoid configureMeshBlending(MaliC55Params *params,\n+\t\t\t\t uint8_t bank, uint8_t alpha) const;\n \tstd::tuple<uint8_t, uint8_t> findBankAndAlpha(uint32_t ct) const;\n \n-\tstd::vector<uint32_t> mesh_ = std::vector<uint32_t>(3072);\n \tstd::vector<uint32_t> colourTemperatures_;\n-\tuint32_t meshScale_;\n-\tuint32_t meshSize_;\n+\tstd::vector<uint32_t> mesh_;\n+\n+\tstd::vector<double> gridPos_;\n+\n+\tLscAlgorithm<uint8_t, UQ<2, 6>> lscAlgo_;\n };\n \n } /* namespace ipa::mali_c55::algorithms */\ndiff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h\nindex 2f2092677fbf..749309115d84 100644\n--- a/src/ipa/mali-c55/ipa_context.h\n+++ b/src/ipa/mali-c55/ipa_context.h\n@@ -20,6 +20,7 @@\n #include \"libipa/awb.h\"\n #include \"libipa/ccm.h\"\n #include \"libipa/fixedpoint.h\"\n+#include \"libipa/lsc.h\"\n \n namespace libcamera {\n \n@@ -64,6 +65,8 @@ struct IPAActiveState {\n \tipa::awb::ActiveState awb;\n \n \tipa::ccm::ActiveState ccm;\n+\n+\tipa::lsc::ActiveState lsc;\n };\n \n struct IPAFrameContext : public FrameContext {\n@@ -76,6 +79,8 @@ struct IPAFrameContext : public FrameContext {\n \tipa::awb::FrameContext awb;\n \n \tipa::ccm::FrameContext ccm;\n+\n+\tipa::lsc::FrameContext lsc;\n };\n \n struct IPAContext {\n", "prefixes": [ "11/11" ] }