From patchwork Thu Feb 19 15:05:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26211 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 8F27CC32F3 for ; Thu, 19 Feb 2026 15:05:28 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3CE196229B; Thu, 19 Feb 2026 16:05:26 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ZE4HGxcM"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4CD1862270 for ; Thu, 19 Feb 2026 16:05:12 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 27E6727F7; Thu, 19 Feb 2026 16:04:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513459; bh=8mR7Mod1Kgrw0Q8pG6KfQsCxjvvIGVE6ohbpnqr2JS4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ZE4HGxcM5n+M78FT3H0Mx7+E+3dvgapTyizu202gJ1PMe1dmLMTYKSWbnvyWKDmw4 a97R5uy/HYaFFvRbnUb1VcTu9V2dkdtttKJVc1Gm1hdcJYcAxIL6VVo63v8WrurKmP j04lKvxAd7DdFtGEd+4bSGqtQ1+NllLmxc4UujuU= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:14 +0000 Subject: [PATCH v8 13/15] ipa: mali-c55: agc: Quantise the ISP Digital Gain MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-13-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , Stefan Klug X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=4981; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=8mR7Mod1Kgrw0Q8pG6KfQsCxjvvIGVE6ohbpnqr2JS4=; b=6xOklpOspfW3Nkx7kpqKymQ9Ig+/8Pim+p6Yyl/kYpXiL4hQajCO0pm+lh1zj44IvAITWfFED 7oOy9qUcTIQCGceJNm+L1lf9yMOYJLUYhR2lKnyrUKBSt9jUTPI0g9D X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" The Mali-C55 ISP has a digital gain block which allows setting gain in Q5.8 format, a range of 0.0 to (very nearly) 32.0. Convert usage to the new UQ<5, 8> FixedPoint Quantised type which will support the conversion, clamping and quantisation so that the metadata and debug prints can now report the effective gain applied instead of the potentially inaccurate float. Reviewed-by: Isaac Scott Reviewed-by: Stefan Klug Signed-off-by: Kieran Bingham --- v8: - Keep the gain clamps --- src/ipa/mali-c55/algorithms/agc.cpp | 25 ++++++++++++------------- src/ipa/mali-c55/ipa_context.h | 6 +++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 014fd12452ac..34d676677efa 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -38,8 +38,8 @@ static constexpr unsigned int kNumHistogramBins = 256; * format, a range of 0.0 to (very nearly) 32.0. We clamp from 1.0 to the actual * max value which is 8191 * 2^-8. */ -static constexpr double kMinDigitalGain = 1.0; -static constexpr double kMaxDigitalGain = 31.99609375; +static constexpr float kMinDigitalGain = 1.0; +static constexpr float kMaxDigitalGain = UQ<5, 8>::TraitsType::max; uint32_t AgcStatistics::decodeBinValue(uint16_t binVal) { @@ -139,10 +139,7 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true); context.ctrlMap[&controls::DigitalGain] = ControlInfo( - static_cast(kMinDigitalGain), - static_cast(kMaxDigitalGain), - static_cast(kMinDigitalGain) - ); + kMinDigitalGain, kMaxDigitalGain, kMinDigitalGain); context.ctrlMap.merge(controls()); return 0; @@ -245,7 +242,7 @@ void Agc::fillGainParamBlock(IPAContext &context, IPAFrameContext &frameContext, MaliC55Params *params) { IPAActiveState &activeState = context.activeState; - double gain; + UQ<5, 8> gain; if (activeState.agc.autoEnabled) gain = activeState.agc.automatic.ispGain; @@ -253,7 +250,7 @@ void Agc::fillGainParamBlock(IPAContext &context, IPAFrameContext &frameContext, gain = activeState.agc.manual.ispGain; auto block = params->block(); - block->gain = floatingToFixedPoint<5, 8, uint16_t, double>(gain); + block->gain = gain.quantized(); frameContext.agc.ispGain = gain; } @@ -358,7 +355,7 @@ void Agc::process(IPAContext &context, */ uint32_t exposure = frameContext.agc.exposure; double analogueGain = frameContext.agc.sensorGain; - double digitalGain = frameContext.agc.ispGain; + double digitalGain = frameContext.agc.ispGain.value(); double totalGain = analogueGain * digitalGain; utils::Duration currentShutter = exposure * configuration.sensor.lineDuration; utils::Duration effectiveExposureValue = currentShutter * totalGain; @@ -370,19 +367,21 @@ void Agc::process(IPAContext &context, activeState.agc.exposureMode, statistics_.yHist, effectiveExposureValue); - dGain = std::clamp(dGain, kMinDigitalGain, kMaxDigitalGain); + UQ<5, 8> dGainQ = std::clamp(static_cast(dGain), + kMinDigitalGain, + kMaxDigitalGain); LOG(MaliC55Agc, Debug) << "Divided up shutter, analogue gain and digital gain are " - << shutterTime << ", " << aGain << " and " << dGain; + << shutterTime << ", " << aGain << " and " << dGainQ; activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration; activeState.agc.automatic.sensorGain = aGain; - activeState.agc.automatic.ispGain = dGain; + activeState.agc.automatic.ispGain = dGainQ; metadata.set(controls::ExposureTime, currentShutter.get()); metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain); - metadata.set(controls::DigitalGain, frameContext.agc.ispGain); + metadata.set(controls::DigitalGain, frameContext.agc.ispGain.value()); metadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK); } diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index 08f78e4f74ce..ac4b83773803 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -41,12 +41,12 @@ struct IPAActiveState { struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } automatic; struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } manual; bool autoEnabled; uint32_t constraintMode; @@ -64,7 +64,7 @@ struct IPAFrameContext : public FrameContext { struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } agc; struct {