From patchwork Wed Jul 8 15:51:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 27344 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 7BB34C333B for ; Wed, 8 Jul 2026 15:52:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 22E2C6611C; Wed, 8 Jul 2026 17:52:00 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="J4xgvpuM"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1489F660E5 for ; Wed, 8 Jul 2026 17:51:24 +0200 (CEST) Received: from [192.168.1.182] (93-46-82-201.ip106.fastwebnet.it [93.46.82.201]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 244D640AD; Wed, 8 Jul 2026 17:50:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783525834; bh=gBpwl50taYAIJtQ3lC8VXTG531PwJCLIUQHX6s44Emo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=J4xgvpuMiFFfzI9p+VEipFNDf88Yu42IDB6sn9Knam623N+Wcid2+PyAGvfustcNo 8fqPdrQkIvuJZAGSViRIvi9Gi/+Fv5epzp11wSITXn46bpqUCDai7+zykpzBfEnPGe 6nbrKR+deGva/evh5ZsjMOA7Ylbf/3bDDrfV0q5Q= From: Jacopo Mondi Date: Wed, 08 Jul 2026 17:51:18 +0200 Subject: [PATCH v5 36/36] ipa: mali-c55: Port to use LscAlgorithm MIME-Version: 1.0 Message-Id: <20260708-libipa-algorithms-v5-36-0759d0359f52@ideasonboard.com> References: <20260708-libipa-algorithms-v5-0-0759d0359f52@ideasonboard.com> In-Reply-To: <20260708-libipa-algorithms-v5-0-0759d0359f52@ideasonboard.com> To: libcamera-devel@lists.libcamera.org, Stefan Klug Cc: Jacopo Mondi , Kieran Bingham , Daniel Scally X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=10843; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=gBpwl50taYAIJtQ3lC8VXTG531PwJCLIUQHX6s44Emo=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqTnHuZjwBjze/PMt2K9ClMCoc3ATVePPr8WK7e Lz9kyFSbBaJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCak5x7gAKCRByNAaPFqFW PN4jD/9oOBmaeifR+1Q/Pjpgvn2sagY7xENT8NoocBQa2qt0b0+AWkyLfMClv1n1K2RQ74ifCUn W30GPMfELdAXBLQJfc+eizdG33viaE4Rn7qWBdWOD/qviJBxlDIB+l64ufGfOIlORe5wmV3plLJ XsSF+qNgorxXdRkClkWPzGNrvc0QCfTrtOzoguwAelNAyz/XeAQy7yWVNk9BZFL7kIOj3HvrVVw 4+ojFaz4VLUnt7K6NZj62Wj2FAVi1+Xej2VT20lsUBP6mFfsxwdJqJM7iY5/zkNZebZrXpftRfd cxgRF/VZ02wMl6dYpo6qHxBI1ix+JywSRzZpRZkoCpF+nD7tVmfa7zuWlHhm4yFb/5+BCALV2if kjAodjCAgOnT9ZtmjHqVZLaWSApOlm/V7gjWyr9zLAD6CW6UB2ROqg4hAug9gx7QTHKiJUZXPxt euW11CYUwQv+fORSM84E12ZWjwGw0Qternl0FXlxgXf5N8vb4P9C3S88WP5vI650Jbz5oe5HH0Z AoQkiHG07HXtsTqaRfkGKHYo+VCX/6VB6mPlqghXXTnc2VV4hxcYFtMx1xcm9az4Npsh7u7iWPP rtLwTC0oSyz4xH+xBhw8TZK/zyn7UV/CbfiVc7DhfOkftLHtc9FL3uoSsRgsrcfYD19NMUCqpoJ 2gDukHF5yYLPaEA== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Port the Mali-C55 LSC algorithm to use libIPA LscAlgorithm. Signed-off-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Daniel Scally --- src/ipa/mali-c55/algorithms/lsc.cpp | 156 ++++++++++++++++++++++-------------- src/ipa/mali-c55/algorithms/lsc.h | 32 ++++++-- src/ipa/mali-c55/ipa_context.h | 3 + 3 files changed, 122 insertions(+), 69 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp index 097239dcef3e..36b422939e43 100644 --- a/src/ipa/mali-c55/algorithms/lsc.cpp +++ b/src/ipa/mali-c55/algorithms/lsc.cpp @@ -9,38 +9,74 @@ #include -#include "libcamera/internal/value_node.h" - namespace libcamera { namespace ipa::mali_c55::algorithms { LOG_DEFINE_CATEGORY(MaliC55Lsc) -int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) +/* Gain values in [1, 5] range */ +static constexpr unsigned int kMeshScale = 6; + +/* Mali-C55 hw supports configurable mesh sizes; we fix it to 32. */ +static constexpr unsigned int kMeshSize = 32; +static constexpr unsigned int kGridSize = kMeshSize * kMeshSize; + +/* Per-colour component page offsets in the mesh table. */ +static constexpr unsigned int kRedOffset = 0; +static constexpr unsigned int kGreenOffset = 1024; +static constexpr unsigned int kBlueOffset = 2048; + +/* + * \todo Clarify if Mali-C55 can support up to 4 colour temperatures. + * + * The uAPI only expose MALI_C55_NUM_MESH_SHADING_ELEMENTS (3072) gain elements, + * which correspond to three pages of 1024 (32x32) entries. + */ +static constexpr unsigned int kMaxColourTemperatures = 3; + +/* + * The LSC algorithm implementation only supports 32x32 grids. Create a list of + * positions from the grid size. + */ +std::vector Lsc::segmentsToPosition() const { - if (!tuningData.contains("meshScale")) { - LOG(MaliC55Lsc, Error) << "meshScale missing from tuningData"; - return -EINVAL; - } + std::vector positions(kMeshSize); + for (double i = 0.0; i < kMeshSize; ++i) + positions[i] = i / (kMeshSize - 1); - meshScale_ = tuningData["meshScale"].get(0); + return positions; +} - const ValueNode &sets = tuningData["sets"]; - if (!sets.isList()) { - LOG(MaliC55Lsc, Error) << "LSC tables missing or invalid"; - return -EINVAL; - } +int Lsc::init(IPAContext &context, const ValueNode &tuningData) +{ + gridPos_ = segmentsToPosition(); + + return lscAlgo_.init(tuningData, context.ctrlMap, { + .keys = { "r", "g", "b" }, + .numHCells = kMeshSize, + .numVCells = kMeshSize, + .sensorSize = context.sensorInfo.activeAreaSize + }); +} - size_t tableSize = 0; - for (const auto &set : sets.asList()) { - uint32_t ct = set["ct"].get(0); +int Lsc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) +{ + int ret = lscAlgo_.configure(context.activeState.lsc, configInfo.analogCrop, + gridPos_, gridPos_); + if (ret) + return ret; - if (!ct) { - LOG(MaliC55Lsc, Error) << "Invalid colour temperature"; - return -EINVAL; - } + /* Re-initialize the mesh tables and reserve space for enough entries. */ + mesh_ = std::vector(kGridSize * kMaxColourTemperatures); + colourTemperatures_.clear(); + /* + * Get the lsc tables per colour components and populate mesh_ with + * their content. + */ + const auto &components = lscAlgo_.getComponents(); + for (auto const &[ct, component] : components) { if (std::count(colourTemperatures_.begin(), colourTemperatures_.end(), ct)) { LOG(MaliC55Lsc, Error) @@ -48,44 +84,29 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) return -EINVAL; } - std::vector rTable = - set["r"].get>().value_or(utils::defopt); - std::vector gTable = - set["g"].get>().value_or(utils::defopt); - std::vector bTable = - set["b"].get>().value_or(utils::defopt); - - /* - * Some validation to do; only 16x16 and 32x32 tables of - * coefficients are acceptable, and all tables across all of the - * sets must be the same size. The first time we encounter a - * table we check that it is an acceptable size and if so make - * sure all other tables are of equal size. - */ - if (!tableSize) { - if (rTable.size() != 256 && rTable.size() != 1024) { - LOG(MaliC55Lsc, Error) - << "Invalid table size for colour temperature " << ct; - return -EINVAL; - } - tableSize = rTable.size(); - } + std::vector rTable = component.at("r"); + std::vector gTable = component.at("g"); + std::vector bTable = component.at("b"); - if (rTable.size() != tableSize || - gTable.size() != tableSize || - bTable.size() != tableSize) { + /* Only 32x32 tables of coefficients are accepted. */ + if (rTable.size() != kGridSize || gTable.size() != kGridSize || + bTable.size() != kGridSize) { LOG(MaliC55Lsc, Error) - << "Invalid or mismatched table size for colour temperature " << ct; + << "Invalid table size for colour temperature " << ct; return -EINVAL; } - if (colourTemperatures_.size() >= 3) { + if (colourTemperatures_.size() >= kMaxColourTemperatures) { LOG(MaliC55Lsc, Error) << "A maximum of 3 colour temperatures are supported"; return -EINVAL; } - for (unsigned int i = 0; i < tableSize; i++) { + /* + * Create the mesh table entries by assembling up to 3 gains per + * colour temperature in a u32 word. + */ + for (unsigned int i = 0; i < kGridSize; i++) { mesh_[kRedOffset + i] |= (rTable[i] << (colourTemperatures_.size() * 8)); mesh_[kGreenOffset + i] |= @@ -97,35 +118,36 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) colourTemperatures_.push_back(ct); } - /* - * The mesh has either 16x16 or 32x32 nodes, we tell the driver which it - * is based on the number of values in the tuning data's table. - */ - if (tableSize == 256) - meshSize_ = 15; - else - meshSize_ = 31; - return 0; } +/** + * \copydoc libcamera::ipa::Algorithm::queueRequest + */ +void Lsc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, const ControlList &controls) +{ + lscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc, + controls); +} + void Lsc::fillConfigParamsBlock(MaliC55Params *params) const { auto block = params->block(); block->mesh_show = false; - block->mesh_scale = meshScale_; + block->mesh_scale = kMeshScale; block->mesh_page_r = 0; block->mesh_page_g = 1; block->mesh_page_b = 2; - block->mesh_width = meshSize_; - block->mesh_height = meshSize_; + block->mesh_width = kMeshSize - 1; + block->mesh_height = kMeshSize - 1; std::copy(mesh_.begin(), mesh_.end(), block->mesh); } void Lsc::fillSelectionParamsBlock(MaliC55Params *params, uint8_t bank, - uint8_t alpha) const + uint8_t alpha) const { auto block = params->block(); @@ -198,6 +220,18 @@ void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, fillConfigParamsBlock(params); } +/** + * \copydoc libcamera::ipa::Algorithm::process + */ +void Lsc::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const mali_c55_stats_buffer *stats, + ControlList &metadata) +{ + lscAlgo_.process(frameContext.lsc, metadata); +} + REGISTER_IPA_ALGORITHM(Lsc, "Lsc") } /* namespace ipa::mali_c55::algorithms */ diff --git a/src/ipa/mali-c55/algorithms/lsc.h b/src/ipa/mali-c55/algorithms/lsc.h index 3d35fd72bfa8..5f752eebaef5 100644 --- a/src/ipa/mali-c55/algorithms/lsc.h +++ b/src/ipa/mali-c55/algorithms/lsc.h @@ -5,10 +5,19 @@ * Mali-C55 Lens shading correction algorithm */ -#include +#include #include +#include + +#include "libcamera/internal/value_node.h" + +#include "libipa/fixedpoint.h" +#include "libipa/lsc.h" + #include "algorithm.h" +#include "ipa_context.h" +#include "params.h" namespace libcamera { @@ -21,23 +30,30 @@ public: ~Lsc() = default; int init(IPAContext &context, const ValueNode &tuningData) override; + int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; + void queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, MaliC55Params *params) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const mali_c55_stats_buffer *stats, + ControlList &metadata) override; private: - static constexpr unsigned int kRedOffset = 0; - static constexpr unsigned int kGreenOffset = 1024; - static constexpr unsigned int kBlueOffset = 2048; - + std::vector segmentsToPosition() const; void fillConfigParamsBlock(MaliC55Params *params) const; void fillSelectionParamsBlock(MaliC55Params *params, uint8_t bank, uint8_t alpha) const; std::tuple findBankAndAlpha(uint32_t ct) const; - std::vector mesh_ = std::vector(3072); std::vector colourTemperatures_; - uint32_t meshScale_; - uint32_t meshSize_; + std::vector mesh_; + + std::vector gridPos_; + + LscAlgorithm> lscAlgo_; }; } /* namespace ipa::mali_c55::algorithms */ diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index deb3f39e0857..7017dfc87c2d 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -19,6 +19,7 @@ #include "libipa/awb.h" #include "libipa/ccm.h" #include "libipa/fixedpoint.h" +#include "libipa/lsc.h" namespace libcamera { @@ -60,6 +61,7 @@ struct IPAActiveState { ipa::awb::ActiveState awb; ipa::ccm::ActiveState ccm; + ipa::lsc::ActiveState lsc; }; struct IPAFrameContext : public FrameContext { @@ -71,6 +73,7 @@ struct IPAFrameContext : public FrameContext { ipa::awb::FrameContext awb; ipa::ccm::FrameContext ccm; + ipa::lsc::FrameContext lsc; }; struct IPAContext {