From patchwork Wed Sep 2 19:15:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Neiva de Carvalho X-Patchwork-Id: 28177 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 E7FC5C334A for ; Wed, 2 Sep 2026 20:04:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8E794685CC; Wed, 2 Sep 2026 22:04:33 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="SFy9wrzR"; dkim-atps=neutral Received: from mail-vs1-xe2e.google.com (mail-vs1-xe2e.google.com [IPv6:2607:f8b0:4864:20::e2e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E7B96685CC for ; Wed, 2 Sep 2026 21:16:10 +0200 (CEST) Received: by mail-vs1-xe2e.google.com with SMTP id ada2fe7eead31-7564b7a9490so173588137.1 for ; Wed, 02 Sep 2026 12:16:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788376570; x=1788981370; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=/ZWh8i8tdb81/tpHA7u6h+2efRXqZdoe8eToPoqLh4w=; b=SFy9wrzRtVviv3plwU6i12dE57/WZ/aubr8kJxObZ14pdwBtsa6Dv7RjfbxpWmlUI9 CrBMNcdAVvIDHTrMZbfwg/evWukJPoju8gI2rsroUhOqhfodT7lSoEEphroc3YI02nvE jxhu0eHi0Thsw7e8u2of5KbFbu797NTFXtvxu+34EcJCFlpckLUhNILdN18H5f2t3M5l kKaUkGyx3v2hy140whbfK0UM0X598ohwDHlihGHnBMAzO6BlcrZ84iIctrM5oCWk4O/G vDOxPoVipQ7hpMCxh9TySKHY1QkHw7E9Wu82B0QjD/hiceoDsrwWI8KW2Z4QdiN2X4jj UzCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788376570; x=1788981370; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=/ZWh8i8tdb81/tpHA7u6h+2efRXqZdoe8eToPoqLh4w=; b=IbtXGYby/AiXTkS1QTo9YrrPvFRJXwqFNC5Og4BVu1VuUQPsalcW7RivZMrG4jt4XK uyQJJWxynNp+wL+HS4mFXnpiu88zbsU1xiC2Vz54sYxqoL4X0XDh2pIC0YWAUjy2iYxq AeClllFPgolv7NIPGryx1Z+lNWjT9zhyCVGynZ1N1KKdRZsiBh0dLnKtPyXlOjsPjSFG Wh5Fx3rl5TT/8SFuAwi2i6HjaB7aecDbQ4zUB3qAIhEWc8PxbuzG9ci6HGJV3GZ0HoeX ZmOUuhzMPc5I2INIY39OzfRrXTutNO51IBscGSB3pj7mkVfpAGOoJYnEFVZc11Mmj5vQ 0NfA== X-Gm-Message-State: AFuF++nkzbB/DJtfR5HXF4v5hBOg4dzQdB4np/Ew19yDyx/122Qgtefg aE5EdXPjoe+v3m56jl+EkkgxgJZXwo8SBgpZ+7XHqWYLoFEqX4h+gHueXVv/s4qYs9E= X-Gm-Gg: AYBFou1lK4vb8a3WkigkKu7snzFltci5jgjOJ62A4n910wY0TQjYPRqx5rPcq5bb1Dh 5Xy8MWyD/Sm1wmOaHI7dS51vr1ONgelrmQjs5irwgD6DX6SrOH85U7d+rK+7DZBZdE1nRgESz6b N1deuFECq9TvpmLDcb+yHb2g/O+40LUlMPT9MloFlvbdtjUPuThtt9Ur1FN8hshT7Vbt33M6L2Y owA6ke+gvPetiGxNKqOX1SpqfDeN2RoBemYOWSX4wd0tigBW4xhISWD6W6WYg0IHUPhyrLWLLtE io4ha9elFDG6mKDUH/IHxuv0T5dTbMI2oCo2JLEG9m/polRii0bqqvaZK/uKRsnq/B53Sg1msWy ev3JDlC8aen65Z1ZS35H8lSVNVULLBJ4zKYqjxrBDjB/FHc0jDpxn1KlUMC5O/y81aBal0Ewmkt QD1DeNz6RGf68OrZOdIs4JWeqPcB4wcG2KNZYhEEJxop/EjmlYVKZKgQZ2qos= X-Received: by 2002:a05:6102:5615:b0:786:56ab:910b with SMTP id ada2fe7eead31-78a2ee66256mr387946137.3.1788376569719; Wed, 02 Sep 2026 12:16:09 -0700 (PDT) Received: from laptop ([45.184.68.165]) by smtp.gmail.com with ESMTPSA id ada2fe7eead31-78a1a1feac0sm3003754137.2.2026.09.02.12.16.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2026 12:16:09 -0700 (PDT) From: Martin Neiva de Carvalho To: libcamera-devel@lists.libcamera.org Cc: Martin Neiva de Carvalho Subject: [PATCH 4/5] ipa: softisp: Derive the minimum gain step from the sensor Date: Wed, 2 Sep 2026 16:15:53 -0300 Message-ID: <20260902191554.84922-5-martincarvalho@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260902191554.84922-1-martincarvalho@gmail.com> References: <20260902191554.84922-1-martincarvalho@gmail.com> MIME-Version: 1.0 X-Mailman-Approved-At: Wed, 02 Sep 2026 22:04:26 +0200 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 AGC uses againMinStep to tell when a multiplicative correction is too small to reach the next gain code, so that it steps by one code rather than asking for a change that quantises away and stalls the loop. The value is currently taken as one hundredth of the gain range, which ties it to how much gain the sensor offers rather than to how finely that gain can be set. On a sensor with a wide range the two are far apart. With a range of 1 to 248 the step becomes 2.47, so the smallest correction the AGC can make is around 12% at a typical indoor gain - much larger than the correction it is trying to make. Every step then crosses the target and the loop oscillates about it indefinitely, which is visible as the picture pulsing. Take the step from the sensor helper instead, as the gain difference between the two lowest codes. On an OV02C10, whose gain is linear in 1/16 steps, this changes it from 2.47 to 0.0625. Measured over a static scene, with the two settings interleaved so that changing light could not favour either, the frame to frame brightness ripple around the trend drops from 2.80 to 0.38 DN RMS, and its peak to peak excursion from 9.5 to 2.3 DN. Signed-off-by: Martin Neiva de Carvalho --- src/ipa/softisp/softisp.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp index aec04c528..5a20475b1 100644 --- a/src/ipa/softisp/softisp.cpp +++ b/src/ipa/softisp/softisp.cpp @@ -228,10 +228,26 @@ int IPASoftIsp::configure(const IPAConfigInfo &configInfo) context_.configuration.agc.againMin = camHelper_->gain(againMin); context_.configuration.agc.againMax = camHelper_->gain(againMax); context_.configuration.agc.again10 = std::max(context_.configuration.agc.againMin, 1.0); + /* + * The smallest gain change the sensor can actually make. + * + * The AGC needs this to know when a multiplicative correction + * is too small to reach the next gain code, so that it steps + * by one code instead of asking for a change that quantises + * away and stalls the loop. + * + * Taking a fixed fraction of the total range instead ties the + * step to how much gain the sensor offers, which is unrelated + * to how finely it can be set. On a sensor whose range runs + * from 1 to 248 that fraction is 2.47, so the smallest + * correction the AGC can make is 12% at a typical indoor gain, + * far larger than the correction it wants. The loop then + * cannot settle: every step crosses the target and the + * exposure oscillates about it indefinitely. + */ context_.configuration.agc.againMinStep = - (context_.configuration.agc.againMax - - context_.configuration.agc.againMin) / - 100.0; + camHelper_->gain(againMin + 1) - + camHelper_->gain(againMin); if (camHelper_->blackLevel().has_value()) { /* * The black level from camHelper_ is a 16 bit value, software ISP