From patchwork Mon Jun 15 14:05:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 26887 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 271FAC324C for ; Mon, 15 Jun 2026 14:06:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1548D623ED; Mon, 15 Jun 2026 16:06:04 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="P4Zx/bEx"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EA520623E3 for ; Mon, 15 Jun 2026 16:05:49 +0200 (CEST) Received: from [192.168.1.104] (net-93-65-100-155.cust.vodafonedsl.it [93.65.100.155]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2DCC51E2B; Mon, 15 Jun 2026 16:05:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781532317; bh=zg4cmPp5HRWt/gyBfY1mXsplO4CkJuGZjtP200olsWE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=P4Zx/bExvvXFPnfYemcS8CglYiQ6QsGB+jAwGIxA06mUTKuYxREXg3KTHqN7/42a+ sZ+rM9jXWUF270GLxq42QGTUiwG+J6sVkDn22zYpHfc90rXKO0JvvpUwomWg7simK3 5uukTYaOiBtOm13ZQVPJmodeG27fEZPfDqIHgOo0= From: Jacopo Mondi Date: Mon, 15 Jun 2026 16:05:36 +0200 Subject: [PATCH 11/11] ipa: mali-c55: Port to use LscAlgorithm MIME-Version: 1.0 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 X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=11475; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=zg4cmPp5HRWt/gyBfY1mXsplO4CkJuGZjtP200olsWE=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqMAa6MHKpTNv5+E66H6HJ8AwDf0WMxTRLo6ao9 +YrPmsU+16JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCajAGugAKCRByNAaPFqFW PAxxD/4onSo6uXwWiIyubkpsLolF1UhGfqMnTSyrTRsoRz+gJLnfp72vE1j4Hzx8MDvQCuJYnUa 3gtoJBmcJ76pHkWXQHKxwVHKPQ9pj4NRf8Xbr9DASDMD0kcaxyg5ga48ZGEZlvcWhm2fYKeMUBK KSvob7dTKzTCmBaIJVRlXWb9zcPmIZdudO/RFeVF6DcNLM4EVhUkuuoknjfUfdyusb0H6DjwHdj nVZkzkvm0DvYmgN2sqe3EGbx1PhHXgIvxJ3rZD8+cKpAbbBAUwrs1HiuD+1GEgMG2sMHZlMVyjQ mThh5H871++5PKqYAOzChwP0AXv0/ibI9ed0LB0CyO6+LBFa067Dp0qu5FV56mI17VvRDo3XigB tnM5nx45Se5vWnjIb+j4fyEDcEN2go9bPEQzlrm7Iy5NQDkkvxAbAFi71/+lXd0VTcmWDgQczEo QA8kgmOv3oZLGJIc9B6USAtDljerR1eE0D+VOMn9mI//NmikBwQermTCAd7TCJohYbFhgd/us/3 zTrgwBWXFEFG7mCUYL+qSgQM6OKaT3h1NkGy/3hmMJWsdVr/nTTgbx5KdBzcL4NVurYsKEg31KI 7598dkdyhDvrlo6hPXhB8BTo5nBTAT5i1ZLKYp1frq7RaGV1wPwlJsINt5B43zhBvz3HYVPJqEf ZrxpIsF879P/wFA== 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 --- src/ipa/mali-c55/algorithms/lsc.cpp | 163 ++++++++++++++++++++++-------------- src/ipa/mali-c55/algorithms/lsc.h | 38 ++++++--- src/ipa/mali-c55/ipa_context.h | 5 ++ 3 files changed, 130 insertions(+), 76 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp index 36e50163aecc..624b393b80e5 100644 --- a/src/ipa/mali-c55/algorithms/lsc.cpp +++ b/src/ipa/mali-c55/algorithms/lsc.cpp @@ -9,39 +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 Mali-C55 supports up to 4 colour temperatures. + * + * Unfortunately 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 kMaxColourTemperature = 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 &yamlSets = tuningData["sets"]; - if (!yamlSets.isList()) { - LOG(MaliC55Lsc, Error) << "LSC tables missing or invalid"; - return -EINVAL; - } +int Lsc::init([[maybe_unused]] 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; - const auto &sets = yamlSets.asList(); - for (const auto &yamlSet : sets) { - uint32_t ct = yamlSet["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 * kMaxColourTemperature); + 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) @@ -49,44 +84,29 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) return -EINVAL; } - std::vector rTable = - yamlSet["r"].get>().value_or(utils::defopt); - std::vector gTable = - yamlSet["g"].get>().value_or(utils::defopt); - std::vector bTable = - yamlSet["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() >= kMaxColourTemperature) { 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] |= @@ -98,34 +118,35 @@ 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; } -void Lsc::fillConfigParamsBlock(MaliC55Params *params) const +/** + * \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::programMeshTables(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, +void Lsc::configureMeshBlending(MaliC55Params *params, uint8_t bank, uint8_t alpha) const { auto block = params->block(); @@ -187,7 +208,7 @@ void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, std::tie(bank, alpha) = findBankAndAlpha(temperatureK); } - fillSelectionParamsBlock(params, bank, alpha); + configureMeshBlending(params, bank, alpha); if (frame > 0) return; @@ -196,7 +217,19 @@ void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, * If this is the first frame, we need to load the parsed coefficient * tables from tuning data to the ISP. */ - fillConfigParamsBlock(params); + programMeshTables(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") diff --git a/src/ipa/mali-c55/algorithms/lsc.h b/src/ipa/mali-c55/algorithms/lsc.h index 3d35fd72bfa8..79ad42094c42 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; - - void fillConfigParamsBlock(MaliC55Params *params) const; - void fillSelectionParamsBlock(MaliC55Params *params, - uint8_t bank, uint8_t alpha) const; + std::vector segmentsToPosition() const; + void programMeshTables(MaliC55Params *params) const; + void configureMeshBlending(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 2f2092677fbf..749309115d84 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -20,6 +20,7 @@ #include "libipa/awb.h" #include "libipa/ccm.h" #include "libipa/fixedpoint.h" +#include "libipa/lsc.h" namespace libcamera { @@ -64,6 +65,8 @@ struct IPAActiveState { ipa::awb::ActiveState awb; ipa::ccm::ActiveState ccm; + + ipa::lsc::ActiveState lsc; }; struct IPAFrameContext : public FrameContext { @@ -76,6 +79,8 @@ struct IPAFrameContext : public FrameContext { ipa::awb::FrameContext awb; ipa::ccm::FrameContext ccm; + + ipa::lsc::FrameContext lsc; }; struct IPAContext {