From patchwork Fri Nov 14 14:16:56 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25053 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 8C18FC3241 for ; Fri, 14 Nov 2025 14:17:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B937F60A81; Fri, 14 Nov 2025 15:17:20 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="QfADUwe8"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E64E2609D8 for ; Fri, 14 Nov 2025 15:17:17 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D9ED2BD2; Fri, 14 Nov 2025 15:15:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129717; bh=5iyhsG+PxIIOdWozo5WrA+PgYKaevQSGKPX52cc/AYk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=QfADUwe8FjgK7Ikzy3oFimICstcGSFFUnHKJ9pBpPvA0OVne5eOeY5g9n1E9j8Q3l GBAxFvhRUeG/t5VBBxOJ3nG+eVnnwBpAUmP80xuuNB6I1fS2C1yBJjUZFn50woWYXF RiWy6EaQHlS2vVI83z52oVBii1xf1J/e2yVHHJI4= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:16:56 +0100 Subject: [PATCH v3 01/19] ipa: mali-c55: Move CameraHelper to context MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-1-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi , Kieran Bingham , Laurent Pinchart X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=5760; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=XXjCIJilmpPNFOSZreYmdd/oc9KrrhoPAYsrfWG9wdc=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznqN9hK0npMkS3w9KCmAeFRf/nRfZol0jaOq GJLKH50h9mJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56gAKCRByNAaPFqFW PMiCD/9boUoPhbn291oFcnPJMZDjcLrhlg2LhSSNriSvlPL7UlBVu5bciYc0O1UXs9bWGebdXbI VEi1im5CV1CWRziC8T4i2j+JjJGPhg5DckImBWO/IqcXg+z6bgVPR2SRIKFiHoAAUZMnwta0zdP y5F3PH7GlqxXPifbGHyp11zQpZdQktCg2E+CpCyOHTNMm4N40J+6vxncyRcXz4winjYfr39fIhE 3tx3AIhbZJaHvRVIISjBQ55oD6TcKfhJ5teJfnT5+xYw8I6v8rcPpxJPvAErw/6f7UXW6iiRmvX MPXLNagKRDARHpp2ICM359TtI7+Wye9MhQWqbb8j/uukkkJmWb23I8ZW3WDMwcVvzxBVrPHTAgh VXRvRK8ci5tSW0YpYdeniUnstOBpM76rrELB2BG/Y7Al3HNCpHuHu5qZAND0yzDni8n9oxEmxq3 nKGzVZ7SvFu70z1Y2Lycyon8Ul+uCo7O1WdVn/jL9RgMG1QCz342KIthGyVj6MiSXRbL1QgJNIW WzHG5k/E0nRqfbt/HlHjKSfDJlnaxvTDn++4fmUoqlSe9Bh7vUi1AWkFCXL5Bqf8WSgHS6fGUNi Bgl2gpLIPem3pOlb1Opv3bG3JgggXuCUhuGMtbkPtDP59fiH3eaXM0t/dtzpiz/tODDlkc9vwFU PjTis1m3OC5C5zw== 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" From: Kieran Bingham Move the CameraHelper sensor to the Context so that it can be used by the algorithms directly. While at it, document the undocumented ctrlMap member of IPAContext. Signed-off-by: Kieran Bingham Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/ipa/mali-c55/ipa_context.cpp | 6 ++++++ src/ipa/mali-c55/ipa_context.h | 4 ++++ src/ipa/mali-c55/mali-c55.cpp | 27 ++++++++++++--------------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/ipa/mali-c55/ipa_context.cpp b/src/ipa/mali-c55/ipa_context.cpp index 1b203e2b260592cda945855d54f1aceefaaece91..a1e4c39f38704d3a4b5948ab789ffb9773f216c9 100644 --- a/src/ipa/mali-c55/ipa_context.cpp +++ b/src/ipa/mali-c55/ipa_context.cpp @@ -96,6 +96,12 @@ namespace libcamera::ipa::mali_c55 { * * \var IPAContext::frameContexts * \brief Ring buffer of per-frame contexts + * + * \var IPAContext::ctrlMap + * \brief A ControlInfoMap::Map of controls populated by the algorithms + * + * \var IPAContext::camHelper + * \brief The camera sensor helper */ } /* namespace libcamera::ipa::mali_c55 */ diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index 13885eb83b5c4d7c7af6c4f8d5673890834fed58..bfa805c7b93f313dda2497365e83542bbc39e291 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -12,6 +12,7 @@ #include "libcamera/internal/bayer_format.h" +#include #include namespace libcamera { @@ -83,6 +84,9 @@ struct IPAContext { FCQueue frameContexts; ControlInfoMap::Map ctrlMap; + + /* Interface to the Camera Helper */ + std::unique_ptr camHelper; }; } /* namespace ipa::mali_c55 */ diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp index 7d45e7310aecdae0e47655e6d2e8830e776d74cd..0751513dc584ca84dd212bf8c1469dd4b40c053d 100644 --- a/src/ipa/mali-c55/mali-c55.cpp +++ b/src/ipa/mali-c55/mali-c55.cpp @@ -74,9 +74,6 @@ private: ControlInfoMap sensorControls_; - /* Interface to the Camera Helper */ - std::unique_ptr camHelper_; - /* Local parameter storage */ struct IPAContext context_; }; @@ -98,8 +95,8 @@ std::string IPAMaliC55::logPrefix() const int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig, ControlInfoMap *ipaControls) { - camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel); - if (!camHelper_) { + context_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel); + if (!context_.camHelper) { LOG(IPAMaliC55, Error) << "Failed to create camera sensor helper for " << settings.sensorModel; @@ -142,10 +139,10 @@ void IPAMaliC55::setControls() if (activeState.agc.autoEnabled) { exposure = activeState.agc.automatic.exposure; - gain = camHelper_->gainCode(activeState.agc.automatic.sensorGain); + gain = context_.camHelper->gainCode(activeState.agc.automatic.sensorGain); } else { exposure = activeState.agc.manual.exposure; - gain = camHelper_->gainCode(activeState.agc.manual.sensorGain); + gain = context_.camHelper->gainCode(activeState.agc.manual.sensorGain); } ControlList ctrls(sensorControls_); @@ -191,17 +188,17 @@ void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info, context_.configuration.agc.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration; context_.configuration.agc.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration; context_.configuration.agc.defaultExposure = defExposure; - context_.configuration.agc.minAnalogueGain = camHelper_->gain(minGain); - context_.configuration.agc.maxAnalogueGain = camHelper_->gain(maxGain); + context_.configuration.agc.minAnalogueGain = context_.camHelper->gain(minGain); + context_.configuration.agc.maxAnalogueGain = context_.camHelper->gain(maxGain); - if (camHelper_->blackLevel().has_value()) { + if (context_.camHelper->blackLevel().has_value()) { /* * The black level from CameraSensorHelper is a 16-bit value. * The Mali-C55 ISP expects 20-bit settings, so we shift it to * the appropriate width */ context_.configuration.sensor.blackLevel = - camHelper_->blackLevel().value() << 4; + context_.camHelper->blackLevel().value() << 4; } } @@ -252,9 +249,9 @@ void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo, /* Compute the analogue gain limits. */ const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; - float minGain = camHelper_->gain(v4l2Gain.min().get()); - float maxGain = camHelper_->gain(v4l2Gain.max().get()); - float defGain = camHelper_->gain(v4l2Gain.def().get()); + float minGain = context_.camHelper->gain(v4l2Gain.min().get()); + float maxGain = context_.camHelper->gain(v4l2Gain.max().get()); + float defGain = context_.camHelper->gain(v4l2Gain.def().get()); ctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain); /* @@ -362,7 +359,7 @@ void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId, frameContext.agc.exposure = sensorControls.get(V4L2_CID_EXPOSURE).get(); frameContext.agc.sensorGain = - camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get()); + context_.camHelper->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get()); ControlList metadata(controls::controls); From patchwork Fri Nov 14 14:16:57 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25054 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 3857CC3241 for ; Fri, 14 Nov 2025 14:17:28 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7D3CD60AAF; Fri, 14 Nov 2025 15:17:22 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="QjXjkeN8"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 86A12609D8 for ; Fri, 14 Nov 2025 15:17:18 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 59EDCC77; Fri, 14 Nov 2025 15:15:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129717; bh=9ZfASp4CccITLhl3MKlou4OHekrvmW0G8T01Ii0WxtE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=QjXjkeN8ozYMeBBPFby1eSqpy53pPLsOFlZ/S3hfvpoEY9+cc7NevegwW7Qt42Sx4 ecNA927iu4aDISqMXLr/fjhrhCRhQVYqui65o4+xI8tFoDoFwCzM1SJIpOhKFa1vGk guHH5hT/aKrTfLISg59tpX6Tf3eaA09vbqye/huA= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:16:57 +0100 Subject: [PATCH v3 02/19] ipa: ipu3: Move CameraHelper to context MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-2-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi , Laurent Pinchart X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=4094; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=9ZfASp4CccITLhl3MKlou4OHekrvmW0G8T01Ii0WxtE=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznq6H1jeOLq2k2cwa5eZw1pKDSo8TRlEaIT8 xXKLnSPqo2JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56gAKCRByNAaPFqFW PIdxD/9N5LrlL3eX+O4/ZrQGpUzovrHBjqcyP+7ynSu4ubHLRGtusoSsunqgaBq2TFvOner7CM9 K9FAS+KQFL8gxjc9O41+XpmkNPZI4vrRiWS9swkJHLlsrUZmkktbi6An/b9NsbEkVdN8SBzKchw dijSjqPpVzbTbXK/HPHTf3vEmeagyGPi613dxUQaMKhCgylnaszS57r0yDKLjm6wRdUBQMuSdFs dqO6iCFUJFnVtZShiFMGTPZ4r1xuQFfrxHasJcyX6J1jCL0O+kPpbO9Hh4i6B8kzcUbeXSLBYh4 2n/79UGE+bdtLeNE8hxwy3g9t76aDx+kOAlxUTaSnBAeFqVEmmINcGjG3Ju+QNqeK66F6H1/yXO qqT3R3365yZY8kzfXuMzX4k7GkCSqGJwTY2pMWGELkU6QZUlxq49r7LGaE/vWYARVHHjUiKFqzE W3+PofZF2MCprdCmnsvAftN0LHJQMsdV/eJsEFevS7/GUJAVhiQnDfVoXuL7Y+OFhqx04+8xEeM mYS77b0CPq4R4ku4kWKGWeW9493K179hRqwMnB/i4czcoIGK6XrxcdN8RSeWlIPIcrkdq33VxOZ F+pynP0Um81UvkpX4uI9EshTu9pbtQagqZ81U7SSD1k3AXBp4tM/KsONXWhAQ8l8kNMItzGiZX9 K+rQcG18UWUbqcw== 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" Move the CameraHelper sensor to the Context so that it can be used by the algorithms directly. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/ipa/ipu3/ipa_context.cpp | 3 +++ src/ipa/ipu3/ipa_context.h | 3 +++ src/ipa/ipu3/ipu3.cpp | 15 ++++++--------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index 3b22f7917650d9e400d5368c2f890d6b2dc846a0..5d0ebaaec454fca823851228614a3eb229f0132b 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -54,6 +54,9 @@ namespace libcamera::ipa::ipu3 { * * \var IPAContext::ctrlMap * \brief A ControlInfoMap::Map of controls populated by the algorithms + * + * \var IPAContext::camHelper + * \brief The camera sensor helper */ /** diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index 97fcf06cd4ac9ac6d64c4933fcea80ace0e572df..4544d663642fb6fde1cf69a32f1c0fbb2fbd98f6 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -15,6 +15,7 @@ #include #include +#include #include namespace libcamera { @@ -95,6 +96,8 @@ struct IPAContext { FCQueue frameContexts; ControlInfoMap::Map ctrlMap; + + std::unique_ptr camHelper; }; } /* namespace ipa::ipu3 */ diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 1cae08bf255f6f53a0ebee02ba3aadda5a650ae3..36d8f0da430a3d4f94e2a3f760850519742ea992 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -179,9 +179,6 @@ private: IPACameraSensorInfo sensorInfo_; - /* Interface to the Camera Helper */ - std::unique_ptr camHelper_; - /* Local parameter storage */ struct IPAContext context_; }; @@ -222,8 +219,8 @@ void IPAIPU3::updateSessionConfiguration(const ControlInfoMap &sensorControls) */ context_.configuration.agc.minExposureTime = minExposure * context_.configuration.sensor.lineDuration; context_.configuration.agc.maxExposureTime = maxExposure * context_.configuration.sensor.lineDuration; - context_.configuration.agc.minAnalogueGain = camHelper_->gain(minGain); - context_.configuration.agc.maxAnalogueGain = camHelper_->gain(maxGain); + context_.configuration.agc.minAnalogueGain = context_.camHelper->gain(minGain); + context_.configuration.agc.maxAnalogueGain = context_.camHelper->gain(maxGain); } /** @@ -301,8 +298,8 @@ int IPAIPU3::init(const IPASettings &settings, const ControlInfoMap &sensorControls, ControlInfoMap *ipaControls) { - camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel); - if (camHelper_ == nullptr) { + context_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel); + if (context_.camHelper == nullptr) { LOG(IPAIPU3, Error) << "Failed to create camera sensor helper for " << settings.sensorModel; @@ -597,7 +594,7 @@ void IPAIPU3::processStats(const uint32_t frame, IPAFrameContext &frameContext = context_.frameContexts.get(frame); frameContext.sensor.exposure = sensorControls.get(V4L2_CID_EXPOSURE).get(); - frameContext.sensor.gain = camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get()); + frameContext.sensor.gain = context_.camHelper->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get()); ControlList metadata(controls::controls); @@ -643,7 +640,7 @@ void IPAIPU3::queueRequest(const uint32_t frame, const ControlList &controls) void IPAIPU3::setControls(unsigned int frame) { int32_t exposure = context_.activeState.agc.exposure; - int32_t gain = camHelper_->gainCode(context_.activeState.agc.gain); + int32_t gain = context_.camHelper->gainCode(context_.activeState.agc.gain); ControlList ctrls(sensorCtrls_); ctrls.set(V4L2_CID_EXPOSURE, exposure); From patchwork Fri Nov 14 14:16:58 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25055 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 CAF5FC32DB for ; Fri, 14 Nov 2025 14:17:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 85BE760AA6; Fri, 14 Nov 2025 15:17:23 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="d2myymeX"; 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 C50FA60A80 for ; Fri, 14 Nov 2025 15:17:18 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C54E31198; Fri, 14 Nov 2025 15:15:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129718; bh=1+dy+sNJ94ko/yXx29Va+mfbcWlDmcuQloJYWwxM//M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=d2myymeXep8/2y2fR2jlI2hLhyQ/HcMrpfDax1GkCWfmsN0CxzFhOAGaTkekTJE33 4tXCtuGmUJWMYyfq2QQJo2uAIGXjNsKcIdxxPYpBELhsGox1I8FAIECvztHwLiFRTk mvov00AAG/MWbefXHJDPyR45LNZaC3hkA8ses+4A= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:16:58 +0100 Subject: [PATCH v3 03/19] ipa: rkisp1: Store FrameDurationLimits as sensor config MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-3-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=8586; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=1+dy+sNJ94ko/yXx29Va+mfbcWlDmcuQloJYWwxM//M=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznq7HzfDhzRSPmnyXWhMyCLvFHbipRhHBJPH qTvkafcBXKJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56gAKCRByNAaPFqFW PKyhD/wPsunu7V3Hujkk1h3XURM17Qp2uuJ+jczTU+wejkCvxPem0oxY9SdrqaUCu+2rcHQQE5C t+B2wqEu6a3jBOXW1MQCuwqHqos7ZO0HRbmLabyUXssBw4Qitgf6aABwsc33j0CnPKmEnmlcXxI iF0OMUJiljZUxNpXy1jAEiuT3o3vLYA80qvk/RmRP82O+GdthczhJImZeL7UYfk7pzTrpNwIYfs Z3J59TrZncWBiiUjGFOSlxuDT0AbCsfXJIYbXZIkvs3GNwpUVw1yELudJl/sxXIyo2NeOJn1nDl 0PD/ipek+Z3XYFoQ93tMWamXAY/nFGSQpv2wGft85NHMt7kXkXZqGi4huL/mduaMuPmvQNsCBsk ON+HebxPmB6zKJihmeViyC4mleRVUs/O3qR3SQZztxwPKEbuwh92/L94qlQs9lRecjzKI26iRYV y3Esy0301XdOjdzT4OZQ/YxAt/dVF2DGfgczItcxwc/fR4wmwcI0q0cv/5coQ9F2JvkHJ6gPv5T Kb+8xawgWaXUc4jJOAKSsG88saYohoWdhjAxrLLSMxS4d02S3OrCjbVkzERWyy2gPwR1iEzR8uG YNm7VD2p5I5YZMP7yIIazs9z3XLLoNmreY4VXvoeHi8S6zPCEguYsodvRipbUn8/W/h0VeIjczz 5y6APzDI1KfdQaQ== 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" The RkISP1 IPA context stores a control list ctrlMap which is used by the AGC algorithm to initialize controls that are then added to the list of the ones registered by the IPA module. The FrameDurationLimits control is a bit of an outlier, as it's the only control that is registered by the IPA in the context ctrlMap and is then used by the AGC algorithm. As the purpose of the context ctrlMap is to be populated by algorithms for the IPA, do not abuse it by registering a control there in the main IPA module and only store the control limits in the active context. Removing FrameDurationLimits from the ctrlMap requires to store it as a sensor configuration parameter in the IPA context and use it for clamping the FrameDurationLimits control and to initialize the AGC active state. Signed-off-by: Jacopo Mondi --- src/ipa/rkisp1/algorithms/agc.cpp | 28 ++++++++++++------------ src/ipa/rkisp1/ipa_context.h | 2 ++ src/ipa/rkisp1/rkisp1.cpp | 46 ++++++++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index f5a3c917cb6909f6ef918e5ee8e46cf97ba55010..aa1a90daf3ca7d0041c56000c12fc4d1ab5700eb 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -190,10 +190,10 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.activeState.agc.meteringMode = static_cast(meteringModes_.begin()->first); - /* Limit the frame duration to match current initialisation */ - ControlInfo &frameDurationLimits = context.ctrlMap[&controls::FrameDurationLimits]; - context.activeState.agc.minFrameDuration = std::chrono::microseconds(frameDurationLimits.min().get()); - context.activeState.agc.maxFrameDuration = std::chrono::microseconds(frameDurationLimits.max().get()); + context.activeState.agc.minFrameDuration = + context.configuration.sensor.minFrameDuration; + context.activeState.agc.maxFrameDuration = + context.configuration.sensor.maxFrameDuration; context.configuration.agc.measureWindow.h_offs = 0; context.configuration.agc.measureWindow.v_offs = 0; @@ -320,16 +320,16 @@ void Agc::queueRequest(IPAContext &context, const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits); if (frameDurationLimits) { - /* Limit the control value to the limits in ControlInfo */ - ControlInfo &limits = context.ctrlMap[&controls::FrameDurationLimits]; - int64_t minFrameDuration = - std::clamp((*frameDurationLimits).front(), - limits.min().get(), - limits.max().get()); - int64_t maxFrameDuration = - std::clamp((*frameDurationLimits).back(), - limits.min().get(), - limits.max().get()); + /* Limit the control value to the sensor constraints. */ + int64_t sensorMinFrameDuration = + context.configuration.sensor.minFrameDuration.get(); + int64_t sensorMaxFrameDuration = + context.configuration.sensor.maxFrameDuration.get(); + + int64_t minFrameDuration = std::clamp((*frameDurationLimits).front(), + sensorMinFrameDuration, sensorMaxFrameDuration); + int64_t maxFrameDuration = std::clamp((*frameDurationLimits).back(), + sensorMinFrameDuration, sensorMaxFrameDuration); agc.minFrameDuration = std::chrono::microseconds(minFrameDuration); agc.maxFrameDuration = std::chrono::microseconds(maxFrameDuration); diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index f85a130d9c23dba7987f388e395239e4b141d776..5fe727bd0b508617d993d226ae785056a3771ce0 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -62,6 +62,8 @@ struct IPASessionConfiguration { struct { utils::Duration minExposureTime; utils::Duration maxExposureTime; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; double minAnalogueGain; double maxAnalogueGain; diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index fa22bfc349043002345d275b11a60ac983e329d7..f25e477f0fb77241bd1ccddb7778205e58bdc8a9 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -227,6 +227,7 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, const std::map &streamConfig, ControlInfoMap *ipaControls) { + const IPACameraSensorInfo &info = ipaConfig.sensorInfo; sensorControls_ = ipaConfig.sensorControls; const auto itExp = sensorControls_.find(V4L2_CID_EXPOSURE); @@ -237,6 +238,12 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, int32_t minGain = itGain->second.min().get(); int32_t maxGain = itGain->second.max().get(); + const auto itVBlank = sensorControls_.find(V4L2_CID_VBLANK); + std::array frameHeights{ + itVBlank->second.min().get() + info.outputSize.height, + itVBlank->second.max().get() + info.outputSize.height, + }; + LOG(IPARkISP1, Debug) << "Exposure: [" << minExposure << ", " << maxExposure << "], gain: [" << minGain << ", " << maxGain << "]"; @@ -248,11 +255,10 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, context_.configuration.paramFormat = ipaConfig.paramFormat; - const IPACameraSensorInfo &info = ipaConfig.sensorInfo; - const ControlInfo vBlank = sensorControls_.find(V4L2_CID_VBLANK)->second; - context_.configuration.sensor.defVBlank = vBlank.def().get(); + utils::Duration lineDuration = info.minLineLength * 1.0s / info.pixelRate; + context_.configuration.sensor.defVBlank = itVBlank->second.def().get(); context_.configuration.sensor.size = info.outputSize; - context_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate; + context_.configuration.sensor.lineDuration = lineDuration; /* Update the camera controls using the new sensor settings. */ updateControls(info, sensorControls_, ipaControls); @@ -261,17 +267,13 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, * When the AGC computes the new exposure values for a frame, it needs * to know the limits for exposure time and analogue gain. As it depends * on the sensor, update it with the controls. - * - * \todo take VBLANK into account for maximum exposure time */ - context_.configuration.sensor.minExposureTime = - minExposure * context_.configuration.sensor.lineDuration; - context_.configuration.sensor.maxExposureTime = - maxExposure * context_.configuration.sensor.lineDuration; - context_.configuration.sensor.minAnalogueGain = - context_.camHelper->gain(minGain); - context_.configuration.sensor.maxAnalogueGain = - context_.camHelper->gain(maxGain); + context_.configuration.sensor.minExposureTime = minExposure * lineDuration; + context_.configuration.sensor.maxExposureTime = maxExposure * lineDuration; + context_.configuration.sensor.minFrameDuration = frameHeights[0] * lineDuration; + context_.configuration.sensor.maxFrameDuration = frameHeights[1] * lineDuration; + context_.configuration.sensor.minAnalogueGain = context_.camHelper->gain(minGain); + context_.configuration.sensor.maxAnalogueGain = context_.camHelper->gain(maxGain); context_.configuration.raw = std::any_of(streamConfig.begin(), streamConfig.end(), [](auto &cfg) -> bool { @@ -436,12 +438,20 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo, uint64_t frameSize = lineLength * frameHeights[i]; frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U); } - - /* \todo Move this (and other agc-related controls) to agc */ - context_.ctrlMap[&controls::FrameDurationLimits] = + ctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0], frameDurations[1], frameDurations[2]); - ctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end()); + /* + * Store the min/max frame duration in the active context to initialize + * the AGC algorithm. + * + * \todo Move this (and other agc-related controls) to agc + */ + context_.activeState.agc.minFrameDuration = std::chrono::microseconds(frameDurations[0]); + context_.activeState.agc.maxFrameDuration = std::chrono::microseconds(frameDurations[1]); + + ctrlMap.merge(context_.ctrlMap); + *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls); } From patchwork Fri Nov 14 14:16:59 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25056 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 CDDD8C3241 for ; Fri, 14 Nov 2025 14:17:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 38287609DE; Fri, 14 Nov 2025 15:17:24 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="SgaPUwr9"; 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 3558C60AA1 for ; Fri, 14 Nov 2025 15:17:19 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 386861440; Fri, 14 Nov 2025 15:15:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129718; bh=WoJb4kUq4QiCHvYm/XK3vK7Tsp/phnx111acAD/Xlk0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=SgaPUwr9TEQc6sFFN240GiZpfrDbK6sYc0Iky8wVeZOCbOa5pbLQ4O/EBQQx++vEa FPol98hhMnrBuLVmNUzAn8soq/joC96P4E9A/ovQf/3PJez/oOqyBDBWAsEf+oedQR zQmgu/FQ2GjKtvWf/tAeGY37FXdWS9zVA/Z9bDY0= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:16:59 +0100 Subject: [PATCH v3 04/19] ipa: mali: Move exposure limits to sensor config MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-4-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=4541; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=WoJb4kUq4QiCHvYm/XK3vK7Tsp/phnx111acAD/Xlk0=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznq4AMiYpe5zsAfK19P/qWt1crR53e12Bgor TAKivVaak2JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56gAKCRByNAaPFqFW PPzzEACrAzGUnYjH4XKFC7aKxtVRJHOBDyn1YdLgXxCkIeEkTtbQ5FwfxcF6v4vxpjkmLkv2YLn oFZuFgNsqFht6W7kqFvBNxl0FTeBV61dV4Bgof4Dl07v0tq9P4K4OO+b/NaScJuzxdRpTMNGzVq M35ZnSDCgTim0fP7I6Oqne2o+8+DeS+94DvNlglt+PDNr6IYhKyyxd3OdNP5LzwpCeeI4XbBo77 mRjQMf0wJUYVF5YkXyqW+fyR8NtPJCizbbJYf0JqvZVat7BrBVtlpjXwinNXzC9SS/dJGsRTLvo whxzUudTnDqTKktJyZCFocBPA4dHSdf4CEd1rOVSqYDMvHY0l5cAmUCkOFJnzXw0gWk7pSxjHlB 06mi4m8lbHOle2ufZ8zwf/plqCX/4V8PqysvwSCYIol2sS89NN7YBpS9qZizJatfAdNvFgbqE79 NUse3dijYAvBtdMwj5wx1lIT8koVYy7+BDMneD47daXGOow2k/b/PUoMupB/eMQMXpRyXMIG45o DVsc0Nk3aW5jOT3YReWPCthQwPWosU/Hv77Os7WkU8oEJ6iYo1WgGc9ZT8IOXpxgOOTCx1kT16w 4B25t6GzfJCV9ZBsg06IJQTLONb3QDz2ZWT4UbxnoCkWIYVL/4JXKQFAwQH8zfFD7N0PELKP1rc BbLUdiwsDGAWcmw== 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" Move the exposure limits (shutter time and gains) in the IPAContext sensor configuration and not in the 'agc' member. This aligns the Mali IPA with the RkISP1 and will help unifying the handling of sensor configuration data through a common type. Signed-off-by: Jacopo Mondi --- src/ipa/mali-c55/algorithms/agc.cpp | 12 ++++++------ src/ipa/mali-c55/ipa_context.h | 8 ++++---- src/ipa/mali-c55/mali-c55.cpp | 9 +++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index f60fddac3f04fd6f09dc782e929ff1593758c29b..4fa00769d201d906be357809f5af02c71201a4f1 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -160,20 +160,20 @@ int Agc::configure(IPAContext &context, * minimum analogue gain. AEGC is _active_ by default. */ context.activeState.agc.autoEnabled = true; - context.activeState.agc.automatic.sensorGain = context.configuration.agc.minAnalogueGain; + context.activeState.agc.automatic.sensorGain = context.configuration.sensor.minAnalogueGain; context.activeState.agc.automatic.exposure = context.configuration.agc.defaultExposure; context.activeState.agc.automatic.ispGain = kMinDigitalGain; - context.activeState.agc.manual.sensorGain = context.configuration.agc.minAnalogueGain; + context.activeState.agc.manual.sensorGain = context.configuration.sensor.minAnalogueGain; context.activeState.agc.manual.exposure = context.configuration.agc.defaultExposure; context.activeState.agc.manual.ispGain = kMinDigitalGain; context.activeState.agc.constraintMode = constraintModes().begin()->first; context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first; /* \todo Run this again when FrameDurationLimits is passed in */ - setLimits(context.configuration.agc.minShutterSpeed, - context.configuration.agc.maxShutterSpeed, - context.configuration.agc.minAnalogueGain, - context.configuration.agc.maxAnalogueGain, + setLimits(context.configuration.sensor.minShutterSpeed, + context.configuration.sensor.maxShutterSpeed, + context.configuration.sensor.minAnalogueGain, + context.configuration.sensor.maxAnalogueGain, {}); resetFrameCount(); diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index bfa805c7b93f313dda2497365e83542bbc39e291..fe75590ec93302e61a31e4832f5c497aab80cf4d 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -21,17 +21,17 @@ namespace ipa::mali_c55 { struct IPASessionConfiguration { struct { - utils::Duration minShutterSpeed; - utils::Duration maxShutterSpeed; uint32_t defaultExposure; - double minAnalogueGain; - double maxAnalogueGain; } agc; struct { BayerFormat::Order bayerOrder; utils::Duration lineDuration; uint32_t blackLevel; + utils::Duration minShutterSpeed; + utils::Duration maxShutterSpeed; + double minAnalogueGain; + double maxAnalogueGain; } sensor; }; diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp index 0751513dc584ca84dd212bf8c1469dd4b40c053d..9375facf7ab559853986f66634d4e36b896361c8 100644 --- a/src/ipa/mali-c55/mali-c55.cpp +++ b/src/ipa/mali-c55/mali-c55.cpp @@ -185,11 +185,12 @@ void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info, * \todo take VBLANK into account for maximum shutter speed */ context_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate; - context_.configuration.agc.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration; - context_.configuration.agc.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration; + context_.configuration.sensor.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration; + context_.configuration.sensor.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration; + context_.configuration.sensor.minAnalogueGain = context_.camHelper->gain(minGain); + context_.configuration.sensor.maxAnalogueGain = context_.camHelper->gain(maxGain); + context_.configuration.agc.defaultExposure = defExposure; - context_.configuration.agc.minAnalogueGain = context_.camHelper->gain(minGain); - context_.configuration.agc.maxAnalogueGain = context_.camHelper->gain(maxGain); if (context_.camHelper->blackLevel().has_value()) { /* From patchwork Fri Nov 14 14:17:00 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25057 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 49628C32DB for ; Fri, 14 Nov 2025 14:17:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6145360AC4; Fri, 14 Nov 2025 15:17:26 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="q5Id4laL"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9A17F60A8B for ; Fri, 14 Nov 2025 15:17:19 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9B03415D2; Fri, 14 Nov 2025 15:15:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129718; bh=UEBgBcx8IvVKaJmWZ6RWcsCM2RsMZRe0Q+bBBarwuno=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=q5Id4laLfh/aHHPM7Spl3cLlibC63GHpu9JzdxxSi/XlPQ2ZlQhNhPUwMBBHPqg8U WmggTAM42TGzsUbhCwyfsT1XlbbkAH9De93b+KDw/NxfxsEwL8pAVOrveDpXFH9v3l lnzPtiWdMcIL+oHjC/n7H91zAiIvobEJk/0OhTPk= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:00 +0100 Subject: [PATCH v3 05/19] ipa: ipu3: Move exposure limits to sensor config MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-5-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=5067; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=UEBgBcx8IvVKaJmWZ6RWcsCM2RsMZRe0Q+bBBarwuno=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznqe6V0YFI3EeYqDh6/dP6GMzgbuo5aFY2NQ ilELLUf42WJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56gAKCRByNAaPFqFW PLYfD/9ygxAjK6MYWjYhPChlkVYVXXTsQ8pafS3ayRy2uzQ+jK/26twFonMaQ/ORBJiy5shhOtx UoXgbDsvEoei/Pfx5cjqE5d5pUfwqB5L29dZzQLcsXMdXwy31WMWO53MPQPfT6p2qh7BaqpS0WJ /z4E4ST2i0LEyPKeZf2yXf+oKuIDIvOV4KhuaVJtLFU1vEhPORug2X/kiDKcWYrnB9K5j45kmGj NzdIA5DpnMyxcoT6wDWeYkni3HV/JMI0TmB4GWTBD7Br0E5KnlsKCcT/E2VrJPtOntfL/RqSjuK joZqQb6MexiAzKOSUon4Z1rhWFJA5mE7pg2eBZaevgYln1vZgwR4fbw7rvXRl23vBSPhHT6XGT2 6aT6tpIDQJLGfkVkJnlpFX3svAdVafJyFM5jNtStRH9Zl28ZcOH7d7eIeNU/LMvIMrn0Ck3wOu1 KruH0I9YG+2asmJU+QpYYfFt4cu39AgEn6wf+5i+9osq4Xvp24cWij+SzkMMPTSERH4CNCZ0iHM UdVKHUUmcBLBbch5kdNteyoX6qkz2QFul+ORDA/vObGH7rYOubI0GpG2XZYG3QVefaPXZa8SH/U 3rs5a+xtz+CooABFddk4dKMhAmdj8aspZZMpukCIFNRUW5ToRgyscMJQPHX2JwGaEz1QZn6x5W4 UCRK7ysQccAFcpw== 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" Move the exposure limits (shutter time and gains) in the IPAContext sensor configuration and not in the 'agc' member. This aligns the IPU3 IPA with the RkISP1 and Mali ones and will help unifying the handling of sensor configuration data through a common type. Signed-off-by: Jacopo Mondi --- src/ipa/ipu3/algorithms/agc.cpp | 8 ++++---- src/ipa/ipu3/ipa_context.cpp | 27 +++++++++++---------------- src/ipa/ipu3/ipa_context.h | 9 +++------ src/ipa/ipu3/ipu3.cpp | 8 ++++---- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index b0d89541da8550bd50472779da7fa8e33b96e2f0..4574f3a1a9cd3f40b1b1402238809ee1a777946d 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -101,12 +101,12 @@ int Agc::configure(IPAContext &context, stride_ = configuration.grid.stride; bdsGrid_ = configuration.grid.bdsGrid; - minExposureTime_ = configuration.agc.minExposureTime; - maxExposureTime_ = std::min(configuration.agc.maxExposureTime, + minExposureTime_ = configuration.sensor.minExposureTime; + maxExposureTime_ = std::min(configuration.sensor.maxExposureTime, kMaxExposureTime); - minAnalogueGain_ = std::max(configuration.agc.minAnalogueGain, kMinAnalogueGain); - maxAnalogueGain_ = configuration.agc.maxAnalogueGain; + minAnalogueGain_ = std::max(configuration.sensor.minAnalogueGain, kMinAnalogueGain); + maxAnalogueGain_ = configuration.sensor.maxAnalogueGain; /* Configure the default exposure and gain. */ activeState.agc.gain = minAnalogueGain_; diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index 5d0ebaaec454fca823851228614a3eb229f0132b..47ae750f0ddb859e1a08e75e255b8decacd730a1 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -96,8 +96,17 @@ namespace libcamera::ipa::ipu3 { */ /** - * \var IPASessionConfiguration::agc - * \brief AGC parameters configuration of the IPA + * \var IPASessionConfiguration::sensor + * \brief Sensor-specific configuration of the IPA + * + * \var IPASessionConfiguration::sensor.lineDuration + * \brief Line duration in microseconds + * + * \var IPASessionConfiguration::sensor.defVBlank + * \brief The default vblank value of the sensor + * + * \var IPASessionConfiguration::sensor.size + * \brief Sensor output resolution * * \var IPASessionConfiguration::agc.minExposureTime * \brief Minimum exposure time supported with the configured sensor @@ -112,20 +121,6 @@ namespace libcamera::ipa::ipu3 { * \brief Maximum analogue gain supported with the configured sensor */ -/** - * \var IPASessionConfiguration::sensor - * \brief Sensor-specific configuration of the IPA - * - * \var IPASessionConfiguration::sensor.lineDuration - * \brief Line duration in microseconds - * - * \var IPASessionConfiguration::sensor.defVBlank - * \brief The default vblank value of the sensor - * - * \var IPASessionConfiguration::sensor.size - * \brief Sensor output resolution - */ - /** * \var IPAActiveState::agc * \brief Context for the Automatic Gain Control algorithm diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index 4544d663642fb6fde1cf69a32f1c0fbb2fbd98f6..baf84b8cbc9a1ff9411c0a994e35317b613580dd 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -34,16 +34,13 @@ struct IPASessionConfiguration { } af; struct { + int32_t defVBlank; + utils::Duration lineDuration; + Size size; utils::Duration minExposureTime; utils::Duration maxExposureTime; double minAnalogueGain; double maxAnalogueGain; - } agc; - - struct { - int32_t defVBlank; - utils::Duration lineDuration; - Size size; } sensor; }; diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 36d8f0da430a3d4f94e2a3f760850519742ea992..60f22727a0f75e374b53fb6d3185946b70c25582 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -217,10 +217,10 @@ void IPAIPU3::updateSessionConfiguration(const ControlInfoMap &sensorControls) * * \todo take VBLANK into account for maximum exposure time */ - context_.configuration.agc.minExposureTime = minExposure * context_.configuration.sensor.lineDuration; - context_.configuration.agc.maxExposureTime = maxExposure * context_.configuration.sensor.lineDuration; - context_.configuration.agc.minAnalogueGain = context_.camHelper->gain(minGain); - context_.configuration.agc.maxAnalogueGain = context_.camHelper->gain(maxGain); + context_.configuration.sensor.minExposureTime = minExposure * context_.configuration.sensor.lineDuration; + context_.configuration.sensor.maxExposureTime = maxExposure * context_.configuration.sensor.lineDuration; + context_.configuration.sensor.minAnalogueGain = context_.camHelper->gain(minGain); + context_.configuration.sensor.maxAnalogueGain = context_.camHelper->gain(maxGain); } /** From patchwork Fri Nov 14 14:17:01 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25058 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 9FDADC32DC for ; Fri, 14 Nov 2025 14:17:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7EC2B60A8B; Fri, 14 Nov 2025 15:17:27 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="oN/3OJEz"; 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 0A8DF60AA3 for ; Fri, 14 Nov 2025 15:17:20 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 062E8161D; Fri, 14 Nov 2025 15:15:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129719; bh=3Lgy+ZqImtVlL+vD32qv8xZc2C9m6ZCFyD9/jgAzbFo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=oN/3OJEzAY7nRSTJFz96M2A0IBF3p1Vw35UeZqeYCMVAVhRsYnBzN8vsfRKTZDea4 AdHst8FdUhl2APsxAMsI+jdQBT9SxI3wAYccYJ0i03M3qbmZnXZhvuGcfV78ovshxY XZH4/W57ADBrvPWRVoZ1mZglusrBGcPQnT68h64o= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:01 +0100 Subject: [PATCH v3 06/19] ipa: camera_sensor_helper: Introduce maxShutterTime() MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-6-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=11405; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=3Lgy+ZqImtVlL+vD32qv8xZc2C9m6ZCFyD9/jgAzbFo=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznr1gBCW2hvkJyErZOcaAmvqiptxs/TmP2v/ atYOs4tuo+JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56wAKCRByNAaPFqFW PDpMEACAzZTuAx/E1pD9PHn+OlZfUWKILqyQwPatb8ZZmsfmtqQsnhQuChImEmqtXR42JMaHKNA f6Z+jamJVPcyPDuBMPtXt2PeLb+kv42dD+WKJNzg2h2KGfURuO/1TVFNQdMf8cFJInuOQjovTfB 3c6TYpqUaa3ESz9szaAKJIeJKm32f6fzDY9kq6tJw3Z/caL5b2WLvMlg8fIMJ+hKK7phHmsj9j5 cNBYFJURqf1LFwkMrrzVcJjpPLaLHtQyoOA7Rv1b21FD83cD7trtOTVYErru9fsQLKDCU8cp9iI b9KIOncFrTKUjG5lBPbgf0Q09hU6mEV+PAT325HO0aOYSrWXrvbjxmYSjQAnk8qGeXRMXndDPoA t4AuHu2uJ5zzmkxt0Yt/QqAArmXo0f/RXcZwK/EjimFDmRjiARstC9ydql0yNX/wIxf56Tp3pmg DhgVu4pdRXZ+Wr//43DqE2/EVJT+wCCDooDcr5uKLoBcAKag2EABFclGSFX/fues/67PrWZjL51 DkPeFTERr2uESW378Nz3v2sxfWQZhwvKgSrDNIm/40tjyZmppWLA5rmadlopH2umYw3jYRspt97 4514Crvg3icXGZe3DEOSwtQg1lKM7f2EnkvEzjNK8khVKmCUzhboy1jXGUzq+FcN0W9nEb7E6oV e/VWp5o72c4UDLQ== 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" There is a margin between the maximum achievable shutter time and the frame length. The value is sensor-dependent and should be used to adjust the maximum achievable exposure time whenever the frame length changes. Introduce in the CameraSensorHelper the notion of exposureMargin_ as the minimum difference in number of lines between the current frame length and the maximum achievable shutter time and the maxShutterTime() function that returns the maximum achievable shutter time give the current maximum frame length. This feature is already implemented in the RPi CamHelper class hierarchy with the name of frameIntegrationDiff. Populate the CameraSensorHelper instances with an exposureMargin_. The value of the exposure margin comes from the mainline driver version of each sensor, and it has been compared with the frameIntegrationDiff_ value in the rpi camera helpers. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/camera_sensor_helper.cpp | 80 +++++++++++++++++++++++++++++++++ src/ipa/libipa/camera_sensor_helper.h | 4 ++ 2 files changed, 84 insertions(+) diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp index e3e3e535541384ba862ba2045114a69db7efff34..3c3e24c1cdefa4bca059352482bb29654a37b42f 100644 --- a/src/ipa/libipa/camera_sensor_helper.cpp +++ b/src/ipa/libipa/camera_sensor_helper.cpp @@ -154,6 +154,36 @@ double CameraSensorHelper::quantizeGain(double _gain, double *quantizationGain) return g; } +/** + * \brief Compute the maximum shutter time given the maximum frame duration + * \param[in] maxFrameDuration The maximum frame duration + * \param[in] lineDuration The current sensor line duration + * + * This function returns the maximum achievable shutter time by subtracting to + * \a maxFrameDuration the difference between the frame length and the maximum + * achievable integration time. + * + * \todo The line duration should be a property of the CameraSensorHelper class + * instead of being provided by the IPA. + * + * \return The maximum achievable shutter time for the current sensor + * configuration + */ +utils::Duration CameraSensorHelper::maxShutterTime(utils::Duration maxFrameDuration, + utils::Duration lineDuration) const +{ + /* Use a static to rate-limit the error message. */ + static uint32_t exposureMargin = exposureMargin_.has_value() + ? exposureMargin_.value() : 0; + if (!exposureMargin_.has_value() && !exposureMargin) { + LOG(CameraSensorHelper, Warning) + << "Exposure margin not known. Default to 4"; + exposureMargin = 4; + } + + return maxFrameDuration - exposureMargin * lineDuration; +} + /** * \struct CameraSensorHelper::AnalogueGainLinear * \brief Analogue gain constants for the linear gain model @@ -229,6 +259,13 @@ double CameraSensorHelper::quantizeGain(double _gain, double *quantizationGain) * sensor specific. Use this variable to store the values at init time. */ +/** + * \var CameraSensorHelper::exposureMargin_ + * \brief The smallest margin between the integration time and the frame lenght + * in lines + * \sa CameraSensorHelper::maxShutterTime() + */ + /** * \class CameraSensorHelperFactoryBase * \brief Base class for camera sensor helper factories @@ -385,6 +422,7 @@ public: { /* Power-on default value: 168 at 12bits. */ blackLevel_ = 2688; + exposureMargin_ = 4; } uint32_t gainCode(double gain) const override @@ -474,6 +512,11 @@ REGISTER_CAMERA_SENSOR_HELPER("ar0144", CameraSensorHelperAr0144) class CameraSensorHelperAr0521 : public CameraSensorHelper { public: + CameraSensorHelperAr0521() + { + exposureMargin_ = 4; + } + uint32_t gainCode(double gain) const override { gain = std::clamp(gain, 1.0, 15.5); @@ -504,6 +547,7 @@ public: /* From datasheet: 64 at 10bits. */ blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 100, 0, 0, 1024 }; + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("gc05a2", CameraSensorHelperGc05a2) @@ -516,6 +560,7 @@ public: /* From datasheet: 64 at 10bits. */ blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 100, 0, 0, 1024 }; + exposureMargin_ = 16; } }; REGISTER_CAMERA_SENSOR_HELPER("gc08a3", CameraSensorHelperGc08a3) @@ -526,6 +571,7 @@ public: CameraSensorHelperHm1246() { gain_ = AnalogueGainLinear{ 1, 16, 0, 16 }; + exposureMargin_ = 2; } }; REGISTER_CAMERA_SENSOR_HELPER("hm1246", CameraSensorHelperHm1246) @@ -538,6 +584,7 @@ public: /* From datasheet: 64 at 10bits. */ blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 0, 512, -1, 512 }; + exposureMargin_ = 10; } }; REGISTER_CAMERA_SENSOR_HELPER("imx214", CameraSensorHelperImx214) @@ -550,6 +597,7 @@ public: /* From datasheet: 64 at 10bits. */ blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 0, 256, -1, 256 }; + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("imx219", CameraSensorHelperImx219) @@ -562,6 +610,7 @@ public: /* From datasheet: 0x40 at 10bits. */ blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 0, 512, -1, 512 }; + exposureMargin_ = 10; } }; REGISTER_CAMERA_SENSOR_HELPER("imx258", CameraSensorHelperImx258) @@ -574,6 +623,7 @@ public: /* From datasheet: 0x32 at 10bits. */ blackLevel_ = 3200; gain_ = AnalogueGainLinear{ 0, 2048, -1, 2048 }; + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("imx283", CameraSensorHelperImx283) @@ -586,6 +636,7 @@ public: /* From datasheet: 0xf0 at 12bits. */ blackLevel_ = 3840; gain_ = AnalogueGainExp{ 1.0, expGainDb(0.3) }; + exposureMargin_ = 2; } }; REGISTER_CAMERA_SENSOR_HELPER("imx290", CameraSensorHelperImx290) @@ -596,6 +647,11 @@ public: CameraSensorHelperImx296() { gain_ = AnalogueGainExp{ 1.0, expGainDb(0.1) }; + /* + * The driver doesn't apply any margin. Use the value + * in RPi's CamHelper. + */ + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("imx296", CameraSensorHelperImx296) @@ -613,6 +669,7 @@ public: /* From datasheet: 0x32 at 10bits. */ blackLevel_ = 3200; gain_ = AnalogueGainExp{ 1.0, expGainDb(0.3) }; + exposureMargin_ = 9; } }; REGISTER_CAMERA_SENSOR_HELPER("imx335", CameraSensorHelperImx335) @@ -623,6 +680,7 @@ public: CameraSensorHelperImx415() { gain_ = AnalogueGainExp{ 1.0, expGainDb(0.3) }; + exposureMargin_ = 8; } }; REGISTER_CAMERA_SENSOR_HELPER("imx415", CameraSensorHelperImx415) @@ -638,6 +696,7 @@ public: CameraSensorHelperImx477() { gain_ = AnalogueGainLinear{ 0, 1024, -1, 1024 }; + exposureMargin_ = 22; } }; REGISTER_CAMERA_SENSOR_HELPER("imx477", CameraSensorHelperImx477) @@ -663,6 +722,7 @@ public: * This has been validated with some empirical testing only. */ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("ov2685", CameraSensorHelperOv2685) @@ -673,6 +733,7 @@ public: CameraSensorHelperOv2740() { gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 8; } }; REGISTER_CAMERA_SENSOR_HELPER("ov2740", CameraSensorHelperOv2740) @@ -685,6 +746,7 @@ public: /* From datasheet: 0x40 at 12bits. */ blackLevel_ = 1024; gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("ov4689", CameraSensorHelperOv4689) @@ -697,6 +759,14 @@ public: /* From datasheet: 0x10 at 10bits. */ blackLevel_ = 1024; gain_ = AnalogueGainLinear{ 1, 0, 0, 16 }; + /* + * Very convoluted in the driver that however applies a margin + * of 4 lines when setting vts. + * + * cap_vts = cap_shutter + 4; + * ret = ov5640_set_vts(sensor, cap_vts); + */ + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("ov5640", CameraSensorHelperOv5640) @@ -707,6 +777,7 @@ public: CameraSensorHelperOv5647() { gain_ = AnalogueGainLinear{ 1, 0, 0, 16 }; + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("ov5647", CameraSensorHelperOv5647) @@ -717,6 +788,7 @@ public: CameraSensorHelperOv5670() { gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 8; } }; REGISTER_CAMERA_SENSOR_HELPER("ov5670", CameraSensorHelperOv5670) @@ -729,6 +801,7 @@ public: /* From Linux kernel driver: 0x40 at 10bits. */ blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("ov5675", CameraSensorHelperOv5675) @@ -739,6 +812,7 @@ public: CameraSensorHelperOv5693() { gain_ = AnalogueGainLinear{ 1, 0, 0, 16 }; + exposureMargin_ = 8; } }; REGISTER_CAMERA_SENSOR_HELPER("ov5693", CameraSensorHelperOv5693) @@ -749,6 +823,7 @@ public: CameraSensorHelperOv64a40() { gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 32; } }; REGISTER_CAMERA_SENSOR_HELPER("ov64a40", CameraSensorHelperOv64a40) @@ -765,6 +840,7 @@ public: * See: https://patchwork.linuxtv.org/project/linux-media/patch/20221106171129.166892-2-nicholas@rothemail.net/#142267 */ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 4; } }; REGISTER_CAMERA_SENSOR_HELPER("ov8858", CameraSensorHelperOv8858) @@ -775,6 +851,7 @@ public: CameraSensorHelperOv8865() { gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 8; } }; REGISTER_CAMERA_SENSOR_HELPER("ov8865", CameraSensorHelperOv8865) @@ -785,6 +862,7 @@ public: CameraSensorHelperOv13858() { gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; + exposureMargin_ = 8; } }; REGISTER_CAMERA_SENSOR_HELPER("ov13858", CameraSensorHelperOv13858) @@ -797,6 +875,7 @@ public: /* From datasheet: 0x40 at 10bits. */ blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 0, 32, -1, 32 }; + exposureMargin_ = 64; } }; REGISTER_CAMERA_SENSOR_HELPER("vd55g1", CameraSensorHelperVd55g1) @@ -809,6 +888,7 @@ public: /* From datasheet: 0x40 at 10bits. */ blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 0, 32, -1, 32 }; + exposureMargin_ = 75; } }; REGISTER_CAMERA_SENSOR_HELPER("vd56g3", CameraSensorHelperVd56g3) diff --git a/src/ipa/libipa/camera_sensor_helper.h b/src/ipa/libipa/camera_sensor_helper.h index bd3d0beec77f006b68fecf45eee850c5283fefa5..a1cf4bc334ad3b9a51d26b345bd5f0630c7ae87c 100644 --- a/src/ipa/libipa/camera_sensor_helper.h +++ b/src/ipa/libipa/camera_sensor_helper.h @@ -15,6 +15,7 @@ #include #include +#include namespace libcamera { @@ -30,6 +31,8 @@ public: virtual uint32_t gainCode(double gain) const; virtual double gain(uint32_t gainCode) const; double quantizeGain(double gain, double *quantizationGain) const; + utils::Duration maxShutterTime(utils::Duration maxFrameDuration, + utils::Duration lineDuration) const; protected: struct AnalogueGainLinear { @@ -46,6 +49,7 @@ protected: std::optional blackLevel_; std::variant gain_; + std::optional exposureMargin_; private: LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraSensorHelper) From patchwork Fri Nov 14 14:17:02 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25059 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 C268CC3241 for ; Fri, 14 Nov 2025 14:17:35 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 519F760B0C; Fri, 14 Nov 2025 15:17:29 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fGbxdtTd"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 626B0606E6 for ; Fri, 14 Nov 2025 15:17:20 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6BA4516A8; Fri, 14 Nov 2025 15:15:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129719; bh=fNhOjrRZ0+5i+LCbzY00gFZZx32gNfi6ytAYY93//5Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fGbxdtTdWkFUW8Dq5eq7KVwzyJVm9mSDofb9U0WKTrqmt/qGI8SUF3VCOyWwucMd3 bV5PS7WFQArBkM3kCH3Uy16cdj7ysLm2Oat7ZfBkZ2q/GfQS2rR1ia71vn5l9UcdGS Py5u4htiDt28laG9MzdOXlXhbr0uRtKwvkMBRjZI= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:02 +0100 Subject: [PATCH v3 07/19] ipa: camera_sensor_helper: Introduce minFrameDuration() MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-7-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=3332; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=fNhOjrRZ0+5i+LCbzY00gFZZx32gNfi6ytAYY93//5Y=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznriHKLuoteRA/oX9HiveLoN0B4nbqTNYnig POhdRNQ/taJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56wAKCRByNAaPFqFW POm8D/97fthPNErIDv8KE2BD2C0Lkq0IocUJEkRT1l91qInraR6/APwpLDFuFD6uhqVAJhOod2O 7Zl+r6241S/fQc+0xoaCUg0PcmLMGZl50XyvtslB53HTA6MCpcFEqaFcMaAqeb7myE17aT6y29c DjILXJIIXMeh3RctDqQlzUMi15HuyPojJoFSAzEc5hRn9jxiot4VHydUylVVyJMkLZMU1qyakPv cqBuETVjHZZegb3Osg1e7IiEom5bL7KEr42OG1kWeAZnCMXTeH7+qgCL+Oul9yQPneSGmGQWnXD YY25iTw7iE9ENTsSCLlZFvAQWeQ4oPqqZdtpdIfToNTylw2ykjYFkhBgfGWeQR6o3bTAbCKH3kv K4QihJisaxMqtS4dk8BiqChjMnBVuzvQhwpj8WF7rv6scLDfZhWBGHR2y01C+4N9uuPsOuVwbAp FyvIZQ4Z2EcGdkHZGtrYY0WDunk6F1+Um51WSXg84hgBAzkJ1augTJXKT19BXFv/RYbsiukPe2/ BGzP/lY5FSoQ1FIu8XyIOkujjm5HsG9YsaDsTzLYe4cFs3ZOCTKMuWMzbonVjk3Y4T1ih9RvfOb n/qHZ8LLZ0tyTj7CzPyVnStCb2aTkILahsHx9lNDBmpLwN2V+wEgk+xiVb+8SJourCqv3YCR2NF gBAhC8g3psnwjJw== 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" After having introduced CameraSensorHelper::maxShutterTime() which calculates the maximum shutter time which can be achieved given a frame duration, this patch introduces the opposite operation, that given a desired shutter time calculates what is the minimum frame duration required to achieve it. The intended users of this function are IPA modules that after having calculated a new exposure time need to regulate the frame duration to achieve it. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/camera_sensor_helper.cpp | 33 +++++++++++++++++++++++++++++++++ src/ipa/libipa/camera_sensor_helper.h | 2 ++ 2 files changed, 35 insertions(+) diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp index 3c3e24c1cdefa4bca059352482bb29654a37b42f..a38f1eb40446b98a0db4642f086742674a13854f 100644 --- a/src/ipa/libipa/camera_sensor_helper.cpp +++ b/src/ipa/libipa/camera_sensor_helper.cpp @@ -184,6 +184,39 @@ utils::Duration CameraSensorHelper::maxShutterTime(utils::Duration maxFrameDurat return maxFrameDuration - exposureMargin * lineDuration; } +/** + * \brief Compute the minimum frame duration required for a desired exposure + * \param[in] shutterTime The shutter time + * \param[in] lineDuration The current sensor line duration + * + * This function returns the minimum frame duration required to achieve the + * desired \a shutterTime. The frame duration is calculated by adding to + * \a shutterTime the difference between the frame length and the maximum + * achievable integration time. + * + * The intended users of this function are IPA modules that want to calculate + * the minium required frame duration give a newly calculated shutter time. + * + * \todo The line duration should be a property of the CameraSensorHelper class + * instead of being provided by the IPA. + * + * \return The minimum frame duration required to achieve the desired shutter + * time + */ +utils::Duration CameraSensorHelper::minFrameDuration(utils::Duration shutterTime, + utils::Duration lineDuration) const +{ + /* Use a static to rate-limit the error message. */ + static uint32_t exposureMargin = exposureMargin_.has_value() + ? exposureMargin_.value() : 0; + if (!exposureMargin_.has_value() && !exposureMargin) { + LOG(CameraSensorHelper, Warning) + << "Exposure margin not known. Default to 4"; + exposureMargin = 4; + } + + return shutterTime + exposureMargin * lineDuration; +} /** * \struct CameraSensorHelper::AnalogueGainLinear * \brief Analogue gain constants for the linear gain model diff --git a/src/ipa/libipa/camera_sensor_helper.h b/src/ipa/libipa/camera_sensor_helper.h index a1cf4bc334ad3b9a51d26b345bd5f0630c7ae87c..e9b53f183e8e6d305b50b5f3b46dc0199efee018 100644 --- a/src/ipa/libipa/camera_sensor_helper.h +++ b/src/ipa/libipa/camera_sensor_helper.h @@ -33,6 +33,8 @@ public: double quantizeGain(double gain, double *quantizationGain) const; utils::Duration maxShutterTime(utils::Duration maxFrameDuration, utils::Duration lineDuration) const; + utils::Duration minFrameDuration(utils::Duration exposureTime, + utils::Duration lineDuration) const; protected: struct AnalogueGainLinear { From patchwork Fri Nov 14 14:17:03 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25060 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 F1D9CC3317 for ; Fri, 14 Nov 2025 14:17:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 41A3560AB6; Fri, 14 Nov 2025 15:17:32 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="uUF9nate"; 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 C7B7060A85 for ; Fri, 14 Nov 2025 15:17:20 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CB79419AD; Fri, 14 Nov 2025 15:15:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129720; bh=MkyAuTFUvynNr2wv/aJybpKRRVV0Ti2Z7sUQF8pUMoo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=uUF9nateHiXmp/cF/cekjyfJnvMjkFDBhJpnRYQ+kJbjtOsndPrDasoSVgqKfua7f NOEa2zOXaQhp7B95ZVECeFdpWirWTzzpCwBVpqM4gXPbLxYBhSy4kv6G/lDd1xfNCk AODfCmr8PEmtY1j/ragFTtFNbD/ZlShSFbYmagXY= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:03 +0100 Subject: [PATCH v3 08/19] ipa: libipa: agc: Remove unused accessors MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-8-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=2342; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=MkyAuTFUvynNr2wv/aJybpKRRVV0Ti2Z7sUQF8pUMoo=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznrF/GZbFsNXTHVwL9B+ZI0WH5EgqHN+Y7QM yMcjGea5JyJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56wAKCRByNAaPFqFW PMIxEACsUTXA/jehYw0+xrcB1hQnKDfLzFcYxlgq48yWiXJdJuagESRmmlQjPzD0fnbf+iJjdWf VZJoeNbX6MOqFfbdRCiei22VDdjM5ubkhFSIw+Wkr8+tCxuwmfyve2peSg6mrhj2pP9/1u9eyvs IXod7wzVSQz+GyBtoJ7nl8HZhLlUTU4/eFpSz3WVFWbWCrxMAGgtet2WN3cWWEM/MOY653Rhh7k o4yEJOZnAwF9mio+g/bfIvkJmwMhzqb7cl8T+0TH9YQFjmnehb+zgtnYv1it3cqiYLQRgNyiSoU pJNM9pIDeCVJR05iMYQbm/d9ru6+IpJZDarCLyCVo6MrqOIAB/+zGZEq459xHbHzknR77YfceTM 7PC2zH2ZQ6y3jMpRYpvT3/GrUo1iQFJKh9c3zCZdjQjUYr9kK5GmfnDb5dYPj16ZKctDHVqzOd2 +/y+Nm1jTLIGhOMnoPVPDoa57riSV9XH0YgRt5yUplig3uxvfxP/RPxu/ZnkLCpYhkA4xgOFaX0 phriiUPTwrO65bwdcO0a7a6OI9jcEqijjt9OYPOLOjDcKqx4iJamCYxTjhhZcuE3cfbcUXIblvU k34Xwg+DZEO89iS3SSQ52ky0yBTa4VFplxHXrxXO8cSDUmRXYCTUNhefNC3xF00khljeW3EDhTw H6rHupaS148sJFw== 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" Remove unused accessors. They're not only unused but it is not even clear what the purpose of accessing the fields is. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/exposure_mode_helper.cpp | 25 ------------------------- src/ipa/libipa/exposure_mode_helper.h | 5 ----- 2 files changed, 30 deletions(-) diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp index 29e316d9d09181cd65cb08f26babb1a680bee62a..962ce1b1b7503e0f86f3857b484b249cb6383fde 100644 --- a/src/ipa/libipa/exposure_mode_helper.cpp +++ b/src/ipa/libipa/exposure_mode_helper.cpp @@ -266,31 +266,6 @@ ExposureModeHelper::splitExposure(utils::Duration exposure) const exposure / (exposureTime * gain * quantGain) }; } -/** - * \fn ExposureModeHelper::minExposureTime() - * \brief Retrieve the configured minimum exposure time limit set through - * setLimits() - * \return The minExposureTime_ value - */ - -/** - * \fn ExposureModeHelper::maxExposureTime() - * \brief Retrieve the configured maximum exposure time set through setLimits() - * \return The maxExposureTime_ value - */ - -/** - * \fn ExposureModeHelper::minGain() - * \brief Retrieve the configured minimum gain set through setLimits() - * \return The minGain_ value - */ - -/** - * \fn ExposureModeHelper::maxGain() - * \brief Retrieve the configured maximum gain set through setLimits() - * \return The maxGain_ value - */ - } /* namespace ipa */ } /* namespace libcamera */ diff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h index 968192ddc5af768ae0de58aca6c7230c7b3bd507..f8b7a4aa4800b59459f8fc80f502b83647547f51 100644 --- a/src/ipa/libipa/exposure_mode_helper.h +++ b/src/ipa/libipa/exposure_mode_helper.h @@ -33,11 +33,6 @@ public: std::tuple splitExposure(utils::Duration exposure) const; - utils::Duration minExposureTime() const { return minExposureTime_; } - utils::Duration maxExposureTime() const { return maxExposureTime_; } - double minGain() const { return minGain_; } - double maxGain() const { return maxGain_; } - private: utils::Duration clampExposureTime(utils::Duration exposureTime, double *quantizationGain = nullptr) const; From patchwork Fri Nov 14 14:17:04 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25061 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 D08D6C3331 for ; Fri, 14 Nov 2025 14:17:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B7CBE60AAD; Fri, 14 Nov 2025 15:17:33 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Fi58AOCY"; 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 2AFD060A9E for ; Fri, 14 Nov 2025 15:17:21 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3507F19B5; Fri, 14 Nov 2025 15:15:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129720; bh=J27XAYPsp2fkW+AjzsTSjz1qjYXrb/+aWHefj5WsL7E=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Fi58AOCY5OWmxMhtkuNmGCnOk29OZPctT27cTTwUgOT6dilkKhTJR6b5wAKMn5QFG uhkZbIu0MO0MRiJdR3cyLTqViWEgziTFPMG8keh/4KW296eb931Lehp33CBUJ6XsZk fL+CyQPAFgqtlWois92KYYBuzBfVrnupmjj3k0Ts= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:04 +0100 Subject: [PATCH v3 09/19] ipa: libipa: agc: Store sensor configuration parameters MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-9-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=5101; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=J27XAYPsp2fkW+AjzsTSjz1qjYXrb/+aWHefj5WsL7E=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznrKvGWzVN4EF74fbgRb7bXF5ZhnJqP17Ky+ QOPDWFQeZSJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56wAKCRByNAaPFqFW PILWD/9ReQ/O/l52x+tFi9mNA3xwvKBe53UXmJXjJ/tBPlrOOH/ch+Nsxuyqn+5+VOi//YSrVl4 TGTI11aZpQraTOM3STFibT9Q+73K/Z0UvNET2I/srjxR/WDkjRoEZbcklzz7jZoycaRFGVBQ2zB d4ec+aDpCL288lZ4Pn5uZ/vNVcIirlbEPKEPs01XacFPWbjj2zeKa9zlYP3S6AnFmMD5oBQyLVx l8WWEpygxx+LYXeidKkqvxxvGUMJoZ/GRcqq6AzeCpKEjfiTkabEqLRPmUHco0CEtXD3mw+I/ig w0DJeI29YtyoKGXJheNrej71J9Tqi12M/F85KuSAk9XeqbZ+JTjLaxubcOJu0c4xLBdr55wBhVG EkkGRwbVzhZHc1d2WVch2kR6XUddy5KxO2S5FkpDOR1/OEqDv/dkRL83h8E8Y4Hx5/kNhrVe/W2 LP1HxFp5tUgjAB7cBMmTqoM4hz9tmt27vB1BfgBIqWVyb22/LuceuolNk4zSVgilJ+H71NhctnI DzGEOv3hSEGPNztgQgxWDLVZqddat5hsPVF5mwYOjXmV2v6tnaTZDRQ4Elvmj7CGjbIIrOrwBdI rbWT1E/8TOLmjouV5KeDdoGgoq01KS5zMsFJiAQKVxD3wEibiO1p98LvQklyN5CrhiwskZUVthp n5zewnYCFUC2KDA== 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" To prepare for changing the configure() and setLimits() functions of the ExposureModeHelper class, introduce the sensor configuration parameters that will remain valid for a streaming session, and separate them from the run-time configuration parameters used by the exposure helper to split shutter time and gains. Only introduce the type in this patch but do not make use of it yet. This change also prepares to centralize the sensor configuration paramters in the CameraHelper. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/exposure_mode_helper.cpp | 43 ++++++++++++++++++++++++++++----- src/ipa/libipa/exposure_mode_helper.h | 18 ++++++++++++-- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp index 962ce1b1b7503e0f86f3857b484b249cb6383fde..c3ed1601939bd28435bbbc540d9b8c9d92b81912 100644 --- a/src/ipa/libipa/exposure_mode_helper.cpp +++ b/src/ipa/libipa/exposure_mode_helper.cpp @@ -21,8 +21,6 @@ namespace libcamera { -using namespace std::literals::chrono_literals; - LOG_DEFINE_CATEGORY(ExposureModeHelper) namespace ipa { @@ -59,6 +57,40 @@ namespace ipa { * used. */ +/** + * \struct ExposureModeHelper::SensorConfiguration + * \brief The sensor configuration parameters + * + * This struct represents the sensor configuration paramters. Sensor + * configuration parameters are set at configure() time and remain valid for + * the duration of the streaming session. + * + * \todo Remove it once all the information are available from the + * CameraSensorHelper. + * + * \var SensorConfiguration::lineDuration_ + * \brief The sensor line duration + * + * \var SensorConfiguration::minExposureTime_ + * \brief The sensor min exposure time in microseconds + * + * \var SensorConfiguration::maxExposureTime_ + * \brief The sensor max exposure time in microseconds + * \todo Remove the max exposure time and calculcate it from the frame duration + * + * \var SensorConfiguration::minFrameDuration_ + * \brief The sensor min frame duration in microseconds + * + * \var SensorConfiguration::maxFrameDuration_ + * \brief The sensor max frame duration in microseconds + * + * \var SensorConfiguration::minGain_ + * \brief The sensor minimum analogue gain value + * + * \var SensorConfiguration::maxGain_ + * \brief The sensor maximum analogue gain value + */ + /** * \brief Construct an ExposureModeHelper instance * \param[in] stages The vector of paired exposure time and gain limits @@ -70,8 +102,6 @@ namespace ipa { * the runtime limits set through setLimits() instead. */ ExposureModeHelper::ExposureModeHelper(const Span> stages) - : lineDuration_(1us), minExposureTime_(0us), maxExposureTime_(0us), - minGain_(0), maxGain_(0), sensorHelper_(nullptr) { for (const auto &[s, g] : stages) { exposureTimes_.push_back(s); @@ -97,7 +127,7 @@ ExposureModeHelper::ExposureModeHelper(const Span(clamped / lineDuration_) * lineDuration_; + exp = static_cast(clamped / sensor_.lineDuration_) + * sensor_.lineDuration_; if (quantizationGain) *quantizationGain = clamped / exp; diff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h index f8b7a4aa4800b59459f8fc80f502b83647547f51..4971cfbf5b2be9ef0e3e95a64b815902833e93a4 100644 --- a/src/ipa/libipa/exposure_mode_helper.h +++ b/src/ipa/libipa/exposure_mode_helper.h @@ -20,9 +20,21 @@ namespace libcamera { namespace ipa { +using namespace std::literals::chrono_literals; + class ExposureModeHelper { public: + struct SensorConfiguration { + utils::Duration lineDuration_; + utils::Duration minExposureTime_; + utils::Duration maxExposureTime_; + utils::Duration minFrameDuration_; + utils::Duration maxFrameDuration_; + double minGain_; + double maxGain_; + }; + ExposureModeHelper(const Span> stages); ~ExposureModeHelper() = default; @@ -41,12 +53,14 @@ private: std::vector exposureTimes_; std::vector gains_; - utils::Duration lineDuration_; + SensorConfiguration sensor_; + const CameraSensorHelper *sensorHelper_ = nullptr; + + /* Runtime parameters, used to split exposure. */ utils::Duration minExposureTime_; utils::Duration maxExposureTime_; double minGain_; double maxGain_; - const CameraSensorHelper *sensorHelper_; }; } /* namespace ipa */ From patchwork Fri Nov 14 14:17:05 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25062 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 88086C32DB for ; Fri, 14 Nov 2025 14:17:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AC43A60AB7; Fri, 14 Nov 2025 15:17:34 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="p4o2mRgI"; 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 9620160A86 for ; Fri, 14 Nov 2025 15:17:21 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 92FD61A8F; Fri, 14 Nov 2025 15:15:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129720; bh=Vnj6OMjyGCSUwrfWqA/064JQnBS2550S1SUFuChCM9g=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=p4o2mRgITWDJ8Ifui3RgJk2zYSWoT6XgLJK2UqKSEj68dsIlLqjlyRBAKXb0vJdMr UvQuoN91ISwJynS/IOQ3oWHkbbvYVhHklxGZdrvvN66IewxUmL3WIQfAupYDN5cAOD b92Z9ygPjNBEjgKCojukKGM/Hdcd6BaXGCWd18tE= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:05 +0100 Subject: [PATCH v3 10/19] ipa: libipa: agc: Make Agc::configure() set limits MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-10-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=12196; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=Vnj6OMjyGCSUwrfWqA/064JQnBS2550S1SUFuChCM9g=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznrmTYC2+3ozsxraW2Av74ljty4sX4G67t/4 NGUhrkYhdaJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56wAKCRByNAaPFqFW PAfxEACVg7z73MD8vMTnTkWJNu+9kHapriY5LLI12IgBO1gWsJFB80qqriBNZ+6CjWXOupB3i7x Mpi/hxqOVsnTjV3rokZpibHje1NfnvPQjr9dy2HX6U0LNVyDVG1jAX8yaTlfYrn2zjnEPpxfkm1 2FFxdidtRMd7+CqO0HfcEZpBTpCAR276mK9QCy5b0j+QtTgpGgXUvfri3+/Q8HCwXIEjGCVhH5v dnV1sdPfzWQ06OTo63bt4i2Okatb5QzMUMLxIUipNHMsHIEy0PCe9PnJDu/Vg/duEfaz/S9Q+w6 5eZRSVcjUI4j/F4ki2G8930/way1Qfkxizi7u8O9dzSob1+faxxLn7Lo1IKIoiOLGlHbNPJiBqq K+wHy2GGgDPsZpLPeurKP/83twlCEiRvaXYHxqy0kvHEPsPRlrQGiT2C2LHR44ygTOZ0j19+0ck bmuSu+eo5Si6XR7F6S5G6H5khYelAZNnzq2YpcF65/vNHv0e6oltEo4O1mo4wmdxd/D/vc/7Dmb tWEAaK6880Xw2g+QixlBkcixQOv0uATK9VscgokvKW080IFi1PQFNhFQN3EbIbNi8r6wznS8t2B jwYjPH5sZDQ1l8ZurssJLECz6mfDnDPDdUKjpcadZ69G+sE5aOO7SViBjLwLjpc8UrDGbSGfk0J L0kM3fL0CEuStjQ== 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" The AgcMeanLuminance algorithm interface has a 'configure()' and 'setLimits()' API. configure() doesn't actually do much if not initialzing a few variables, and the Mali and IPU3 IPAs do not even call it. Configuring the AGC requires calling setLimits() at configure() time and at run time. In order to prepare to differentiate between configure-time settings of the Agc and run-time handling of dynamic parameters such as the frame duration limits, make the configure() operation initialize the Agc limits and provide an AgcMeanLuminance::SensorConfiguration type for IPAs to be populated with the sensor's default values. The new type mimics the ExposureModeHelper::SensorConfiguration on introduced in the previous patches and both will be removed once all the information there contained will be made available from the CameraSensorHelper. Update all IPAs Agc implementation deriving from AgcMeanLuminance to populate a AgcMeanLuminance::SensorConfiguration and use it in configure(). Signed-off-by: Jacopo Mondi --- src/ipa/ipu3/algorithms/agc.cpp | 14 +++++++-- src/ipa/libipa/agc_mean_luminance.cpp | 56 ++++++++++++++++++++++++++++++--- src/ipa/libipa/agc_mean_luminance.h | 11 ++++++- src/ipa/libipa/exposure_mode_helper.cpp | 20 +++++++----- src/ipa/libipa/exposure_mode_helper.h | 3 +- src/ipa/mali-c55/algorithms/agc.cpp | 16 ++++++---- src/ipa/rkisp1/algorithms/agc.cpp | 15 ++++----- 7 files changed, 105 insertions(+), 30 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index 4574f3a1a9cd3f40b1b1402238809ee1a777946d..5c72806dede5f55459bde69ab8cfaebc495c7560 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -115,9 +115,17 @@ int Agc::configure(IPAContext &context, context.activeState.agc.constraintMode = constraintModes().begin()->first; context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first; - /* \todo Run this again when FrameDurationLimits is passed in */ - setLimits(minExposureTime_, maxExposureTime_, minAnalogueGain_, - maxAnalogueGain_, {}); + AgcMeanLuminance::SensorConfiguration sensorConfig; + sensorConfig.lineDuration = context.configuration.sensor.lineDuration; + sensorConfig.minExposureTime = minExposureTime_; + sensorConfig.maxExposureTime = maxExposureTime_; + sensorConfig.minAnalogueGain = minAnalogueGain_; + sensorConfig.maxAnalogueGain = maxAnalogueGain_; + + AgcMeanLuminance::configure(sensorConfig, context.camHelper.get()); + + /* \todo Update AGC limits when FrameDurationLimits is passed in */ + resetFrameCount(); return 0; diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 64f36bd75dd213671b5a2e6810245096ed001f21..512e153791f5b98da01efad6675192a5358e7698 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -95,6 +95,35 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95; * \brief The luminance target for the constraint */ +/** + * \struct AgcMeanLuminance::SensorConfiguration + * \brief The sensor configuration parameters + * + * This structure collects the sensor configuration parameters which need + * to be provided to the AGC algorithm at configure() time. + * + * Each time configure() is called the sensor configuration need to be updated + * with new parameters. + * + * \todo Remove it once all the information are available from the + * CameraSensorHelper. + * + * \var AgcMeanLuminance::SensorConfiguration::lineDuration + * \brief The line duration in microseconds + * + * \var AgcMeanLuminance::SensorConfiguration::minExposureTime + * \brief The sensor minimum exposure time in microseconds + * + * \var AgcMeanLuminance::SensorConfiguration::maxExposureTime + * \brief The sensor maximum exposure time in microseconds + * + * \var AgcMeanLuminance::SensorConfiguration::minAnalogueGain + * \brief The sensor minimum analogue gain absolute value + * + * \var AgcMeanLuminance::SensorConfiguration::maxAnalogueGain + * \brief The sensor maximum analogue gain absolute value + */ + /** * \class AgcMeanLuminance * \brief A mean-based auto-exposure algorithm @@ -314,17 +343,34 @@ int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData) /** * \brief Configure the exposure mode helpers - * \param[in] lineDuration The sensor line length + * \param[in] config The sensor configuration * \param[in] sensorHelper The sensor helper * - * This function configures the exposure mode helpers so they can correctly + * This function configures the exposure mode helpers by providing them the + * sensor configuration parameters and the sensor helper, so they can correctly * take quantization effects into account. */ -void AgcMeanLuminance::configure(utils::Duration lineDuration, +void AgcMeanLuminance::configure(const SensorConfiguration &config, const CameraSensorHelper *sensorHelper) { - for (auto &[id, helper] : exposureModeHelpers_) - helper->configure(lineDuration, sensorHelper); + for (auto &[id, helper] : exposureModeHelpers_) { + /* + * Translate from the SensorConfiguration to the + * ExposureModeHelper::SensorConfiguration. + * + * These are just place holders before all the information are + * available from the sensor helper. + */ + + ExposureModeHelper::SensorConfiguration sensorConfig; + sensorConfig.lineDuration_ = config.lineDuration; + sensorConfig.minExposureTime_ = config.minExposureTime; + sensorConfig.maxExposureTime_ = config.maxExposureTime; + sensorConfig.minGain_ = config.minAnalogueGain; + sensorConfig.maxGain_ = config.maxAnalogueGain; + + helper->configure(sensorConfig, sensorHelper); + } } /** diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index e5f164c3186b6b99cb0df5dd8dccf9026c22af20..42ead74b0cdc197bc2b27aee16918e2b42ea3d08 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -42,7 +42,16 @@ public: double yTarget; }; - void configure(utils::Duration lineDuration, const CameraSensorHelper *sensorHelper); + struct SensorConfiguration { + utils::Duration lineDuration; + utils::Duration minExposureTime; + utils::Duration maxExposureTime; + double minAnalogueGain; + double maxAnalogueGain; + }; + + void configure(const SensorConfiguration &config, + const CameraSensorHelper *sensorHelper); int parseTuningData(const YamlObject &tuningData); void setExposureCompensation(double gain) diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp index c3ed1601939bd28435bbbc540d9b8c9d92b81912..f771b10a28eead2976c0000cf099ba5cfbe78e0f 100644 --- a/src/ipa/libipa/exposure_mode_helper.cpp +++ b/src/ipa/libipa/exposure_mode_helper.cpp @@ -111,24 +111,30 @@ ExposureModeHelper::ExposureModeHelper(const Span> stages); ~ExposureModeHelper() = default; - void configure(utils::Duration lineLength, const CameraSensorHelper *sensorHelper); + void configure(const SensorConfiguration &sensorConfig, + const CameraSensorHelper *sensorHelper); void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, double minGain, double maxGain); diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 4fa00769d201d906be357809f5af02c71201a4f1..d6a1ff5aaca136c387feb8c948053fc83bb664ee 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -169,12 +169,16 @@ int Agc::configure(IPAContext &context, context.activeState.agc.constraintMode = constraintModes().begin()->first; context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first; - /* \todo Run this again when FrameDurationLimits is passed in */ - setLimits(context.configuration.sensor.minShutterSpeed, - context.configuration.sensor.maxShutterSpeed, - context.configuration.sensor.minAnalogueGain, - context.configuration.sensor.maxAnalogueGain, - {}); + AgcMeanLuminance::SensorConfiguration sensorConfig; + sensorConfig.lineDuration = context.configuration.sensor.lineDuration; + sensorConfig.minExposureTime = context.configuration.sensor.minShutterSpeed; + sensorConfig.maxExposureTime = context.configuration.sensor.maxShutterSpeed; + sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain; + sensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; + + AgcMeanLuminance::configure(sensorConfig, context.camHelper.get()); + + /* \todo Update AGC limits when FrameDurationLimits is passed in */ resetFrameCount(); diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index aa1a90daf3ca7d0041c56000c12fc4d1ab5700eb..b0c8966eea63901640bbe16af2a5d8a303c63ece 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -200,13 +200,14 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.configuration.agc.measureWindow.h_size = configInfo.outputSize.width; context.configuration.agc.measureWindow.v_size = configInfo.outputSize.height; - AgcMeanLuminance::configure(context.configuration.sensor.lineDuration, - context.camHelper.get()); - - setLimits(context.configuration.sensor.minExposureTime, - context.configuration.sensor.maxExposureTime, - context.configuration.sensor.minAnalogueGain, - context.configuration.sensor.maxAnalogueGain, {}); + AgcMeanLuminance::SensorConfiguration sensorConfig; + sensorConfig.lineDuration = context.configuration.sensor.lineDuration; + sensorConfig.minExposureTime = context.configuration.sensor.minExposureTime; + sensorConfig.maxExposureTime = context.configuration.sensor.maxExposureTime; + sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain; + sensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; + + AgcMeanLuminance::configure(sensorConfig, context.camHelper.get()); context.activeState.agc.automatic.yTarget = effectiveYTarget(); From patchwork Fri Nov 14 14:17:06 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25063 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 603B5C3332 for ; Fri, 14 Nov 2025 14:17:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0E7EA60B2C; Fri, 14 Nov 2025 15:17:36 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="oI+6knlW"; 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 0657560A8A for ; Fri, 14 Nov 2025 15:17:22 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 037481AED; Fri, 14 Nov 2025 15:15:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129721; bh=Un6DimgZnEXwkGeYxO0GXkQ1C5J3iMRmDxlXCIsp1N0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=oI+6knlWsDduoZzse89BE4tBNYlp6MraZJMfl4QgD/rOFwmCq+CtNfwoXU4wNRMBP WXltMyLQbyOkRGKot7yssQra8g6qJ4BcpDltCFL8JZ5ZxCcbdN8iF3JAbiLw5KZ2Kc E3gKDvVOr5wXRJ8nhEPQcAUYeeUpeJ+O6Pfj2vxk= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:06 +0100 Subject: [PATCH v3 11/19] ipa: libipa: agc: Pass the frame duration to configure() MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-11-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=12478; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=Un6DimgZnEXwkGeYxO0GXkQ1C5J3iMRmDxlXCIsp1N0=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznrDM0hmmIBWJFe9NzTMVwtdgmzgH09yZwgt 6tVvNuBbq+JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56wAKCRByNAaPFqFW PIG3EADFAXPcp0pcNFs9jJK0E+vLYv904IJ6AhGVtHkJZHQhm7GU7knuovGLakulk23NXtdQbrJ C+t72oQ+vosfS61EeaF54GytaXkL7UnvMrbDC/t635fjNuFSXO4V/wechWGFjsw5IrP1CAdmjjS NlRRXAk19KsrR1FbG+zCdIORpNgboflpg43m3Vh3MaXDr10EFDjpiEUrvLtBxngiLJldJ4vNjKh u7+9cV0hWS/wpdWHl1dWbVIQRKTPbLlgDJZxUPv6yKwhygeibu1a2UHSVxMYsEnUq1jv/qAGH27 rJnlq+1yoJ1zpWsbi3yG7AgT3td67kXp3kIsq8YTqJRvGVsErckZTFbr2raSp7pmU41+sSF9z2T m8HvgzgBV8SXdA75+roukDeOJPOCx7+rw9WgxohO9FdpSLHvh46vgZ1BzBYCN1HInKlkA/Gqx/U s2F2pKMvW3LdCxu+llxfJeX8+b97xX01yhPWl0KyGhF9dc6ygHORrSylaRU077I/X9Nvx2x1+7u YYuUtern67qWtS7+mQQ5D7PMsHRvANVOK9X4P5Mp4dqtpINs7Zj8KGWGoO5sOqA6bMlBJaAaeA1 oVkY+EgnV5B+6oTbvJV5qYNhe6maxx63zk92MOdwiUzjZweX0MpNpJiAG2b3O9fQVvw5S4JZQpl bcD9tGYz6/9qPKA== 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" Pass the min/max sensor frame duration to the AGC configure() operation. This prepares for limiting the exposure time to the frame duration. In order to be able to provide to the AGC the frame duration in the IPU3 and Mali-C55 IPAs it is necessary to store the FrameDurationLimits in the context sensor configuration, like it was already done for the RkISP1 IPA. Signed-off-by: Jacopo Mondi --- src/ipa/ipu3/algorithms/agc.cpp | 2 ++ src/ipa/ipu3/ipa_context.h | 4 ++++ src/ipa/ipu3/ipu3.cpp | 18 +++++++++++++++--- src/ipa/libipa/agc_mean_luminance.cpp | 8 ++++++++ src/ipa/libipa/agc_mean_luminance.h | 2 ++ src/ipa/mali-c55/algorithms/agc.cpp | 6 ++++++ src/ipa/mali-c55/ipa_context.h | 4 ++++ src/ipa/mali-c55/mali-c55.cpp | 17 ++++++++++++----- src/ipa/rkisp1/algorithms/agc.cpp | 2 ++ 9 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index 5c72806dede5f55459bde69ab8cfaebc495c7560..002ee574c02b79c25834a9d87a5881a9de52e39e 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -118,6 +118,8 @@ int Agc::configure(IPAContext &context, AgcMeanLuminance::SensorConfiguration sensorConfig; sensorConfig.lineDuration = context.configuration.sensor.lineDuration; sensorConfig.minExposureTime = minExposureTime_; + sensorConfig.minFrameDuration = context.configuration.sensor.minFrameDuration; + sensorConfig.maxFrameDuration = context.configuration.sensor.maxFrameDuration; sensorConfig.maxExposureTime = maxExposureTime_; sensorConfig.minAnalogueGain = minAnalogueGain_; sensorConfig.maxAnalogueGain = maxAnalogueGain_; diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index baf84b8cbc9a1ff9411c0a994e35317b613580dd..33f3fe3ae1b503d47887873046def0835cd53894 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -39,6 +39,8 @@ struct IPASessionConfiguration { Size size; utils::Duration minExposureTime; utils::Duration maxExposureTime; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; double minAnalogueGain; double maxAnalogueGain; } sensor; @@ -56,6 +58,8 @@ struct IPAActiveState { double gain; uint32_t constraintMode; uint32_t exposureMode; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; } agc; struct { diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 60f22727a0f75e374b53fb6d3185946b70c25582..0c8651c5235f9e4e9944eb88595aeef41f016310 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -167,7 +167,8 @@ private: void updateControls(const IPACameraSensorInfo &sensorInfo, const ControlInfoMap &sensorControls, ControlInfoMap *ipaControls); - void updateSessionConfiguration(const ControlInfoMap &sensorControls); + void updateSessionConfiguration(const IPACameraSensorInfo &sensorInfo, + const ControlInfoMap &sensorControls); void setControls(unsigned int frame); void calculateBdsGrid(const Size &bdsOutputSize); @@ -197,7 +198,8 @@ std::string IPAIPU3::logPrefix() const * \brief Compute IPASessionConfiguration using the sensor information and the * sensor V4L2 controls */ -void IPAIPU3::updateSessionConfiguration(const ControlInfoMap &sensorControls) +void IPAIPU3::updateSessionConfiguration(const IPACameraSensorInfo &sensorInfo, + const ControlInfoMap &sensorControls) { const ControlInfo vBlank = sensorControls.find(V4L2_CID_VBLANK)->second; context_.configuration.sensor.defVBlank = vBlank.def().get(); @@ -210,6 +212,12 @@ void IPAIPU3::updateSessionConfiguration(const ControlInfoMap &sensorControls) int32_t minGain = v4l2Gain.min().get(); int32_t maxGain = v4l2Gain.max().get(); + const ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second; + std::array frameHeights{ + v4l2VBlank.min().get() + sensorInfo.outputSize.height, + v4l2VBlank.max().get() + sensorInfo.outputSize.height, + }; + /* * When the AGC computes the new exposure values for a frame, it needs * to know the limits for exposure time and analogue gain. @@ -219,6 +227,10 @@ void IPAIPU3::updateSessionConfiguration(const ControlInfoMap &sensorControls) */ context_.configuration.sensor.minExposureTime = minExposure * context_.configuration.sensor.lineDuration; context_.configuration.sensor.maxExposureTime = maxExposure * context_.configuration.sensor.lineDuration; + context_.configuration.sensor.minFrameDuration = frameHeights[0] * + context_.configuration.sensor.lineDuration; + context_.configuration.sensor.maxFrameDuration = frameHeights[1] * + context_.configuration.sensor.lineDuration; context_.configuration.sensor.minAnalogueGain = context_.camHelper->gain(minGain); context_.configuration.sensor.maxAnalogueGain = context_.camHelper->gain(maxGain); } @@ -488,7 +500,7 @@ int IPAIPU3::configure(const IPAConfigInfo &configInfo, updateControls(sensorInfo_, sensorCtrls_, ipaControls); /* Update the IPASessionConfiguration using the sensor settings. */ - updateSessionConfiguration(sensorCtrls_); + updateSessionConfiguration(sensorInfo_, sensorCtrls_); for (auto const &algo : algorithms()) { int ret = algo->configure(context_, configInfo); diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 512e153791f5b98da01efad6675192a5358e7698..602050729fd08649e2db8d07623a8a39e7a53a77 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -117,6 +117,12 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95; * \var AgcMeanLuminance::SensorConfiguration::maxExposureTime * \brief The sensor maximum exposure time in microseconds * + * \var AgcMeanLuminance::SensorConfiguration::minFrameDuration + * \brief The sensor minimum frame duration in microseconds + * + * \var AgcMeanLuminance::SensorConfiguration::maxFrameDuration + * \brief The sensor maximum frame duration in microseconds + * * \var AgcMeanLuminance::SensorConfiguration::minAnalogueGain * \brief The sensor minimum analogue gain absolute value * @@ -366,6 +372,8 @@ void AgcMeanLuminance::configure(const SensorConfiguration &config, sensorConfig.lineDuration_ = config.lineDuration; sensorConfig.minExposureTime_ = config.minExposureTime; sensorConfig.maxExposureTime_ = config.maxExposureTime; + sensorConfig.minFrameDuration_ = config.minFrameDuration; + sensorConfig.maxFrameDuration_ = config.maxFrameDuration; sensorConfig.minGain_ = config.minAnalogueGain; sensorConfig.maxGain_ = config.maxAnalogueGain; diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 42ead74b0cdc197bc2b27aee16918e2b42ea3d08..c3c8ce1cdda9229eca400b730f2ee896e9a0af93 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -46,6 +46,8 @@ public: utils::Duration lineDuration; utils::Duration minExposureTime; utils::Duration maxExposureTime; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; double minAnalogueGain; double maxAnalogueGain; }; diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index d6a1ff5aaca136c387feb8c948053fc83bb664ee..731b29ced1030ecb3f44b83ad28a0691dd5d2f0d 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -168,11 +168,17 @@ int Agc::configure(IPAContext &context, context.activeState.agc.manual.ispGain = kMinDigitalGain; context.activeState.agc.constraintMode = constraintModes().begin()->first; context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first; + context.activeState.agc.minFrameDuration = + context.configuration.sensor.minFrameDuration; + context.activeState.agc.maxFrameDuration = + context.configuration.sensor.maxFrameDuration; AgcMeanLuminance::SensorConfiguration sensorConfig; sensorConfig.lineDuration = context.configuration.sensor.lineDuration; sensorConfig.minExposureTime = context.configuration.sensor.minShutterSpeed; sensorConfig.maxExposureTime = context.configuration.sensor.maxShutterSpeed; + sensorConfig.minFrameDuration = context.configuration.sensor.minFrameDuration; + sensorConfig.maxFrameDuration = context.configuration.sensor.maxFrameDuration; sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain; sensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index fe75590ec93302e61a31e4832f5c497aab80cf4d..3b64cb7571729d4af162def5b2316331b3561af1 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -30,6 +30,8 @@ struct IPASessionConfiguration { uint32_t blackLevel; utils::Duration minShutterSpeed; utils::Duration maxShutterSpeed; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; double minAnalogueGain; double maxAnalogueGain; } sensor; @@ -51,6 +53,8 @@ struct IPAActiveState { uint32_t constraintMode; uint32_t exposureMode; uint32_t temperatureK; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; } agc; struct { diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp index 9375facf7ab559853986f66634d4e36b896361c8..491ae71a06dbede967bfbe1bcdcab25d177ad691 100644 --- a/src/ipa/mali-c55/mali-c55.cpp +++ b/src/ipa/mali-c55/mali-c55.cpp @@ -177,16 +177,23 @@ void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info, int32_t minGain = v4l2Gain.min().get(); int32_t maxGain = v4l2Gain.max().get(); + const ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second; + std::array frameHeights{ + v4l2VBlank.min().get() + info.outputSize.height, + v4l2VBlank.max().get() + info.outputSize.height, + }; + /* * When the AGC computes the new exposure values for a frame, it needs * to know the limits for shutter speed and analogue gain. * As it depends on the sensor, update it with the controls. - * - * \todo take VBLANK into account for maximum shutter speed */ - context_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate; - context_.configuration.sensor.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration; - context_.configuration.sensor.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration; + utils::Duration lineDuration = info.minLineLength * 1.0s / info.pixelRate; + context_.configuration.sensor.lineDuration = lineDuration; + context_.configuration.sensor.minShutterSpeed = minExposure * lineDuration; + context_.configuration.sensor.maxShutterSpeed = maxExposure * lineDuration; + context_.configuration.sensor.minFrameDuration = frameHeights[0] * lineDuration; + context_.configuration.sensor.maxFrameDuration = frameHeights[1] * lineDuration; context_.configuration.sensor.minAnalogueGain = context_.camHelper->gain(minGain); context_.configuration.sensor.maxAnalogueGain = context_.camHelper->gain(maxGain); diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index b0c8966eea63901640bbe16af2a5d8a303c63ece..de2980485f2d3a8973f065981f8cdd520e3dfc47 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -204,6 +204,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) sensorConfig.lineDuration = context.configuration.sensor.lineDuration; sensorConfig.minExposureTime = context.configuration.sensor.minExposureTime; sensorConfig.maxExposureTime = context.configuration.sensor.maxExposureTime; + sensorConfig.minFrameDuration = context.configuration.sensor.minFrameDuration; + sensorConfig.maxFrameDuration = context.configuration.sensor.maxFrameDuration; sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain; sensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; From patchwork Fri Nov 14 14:17:07 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25064 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 309A0C32DC for ; Fri, 14 Nov 2025 14:17:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8CE8C60AB8; Fri, 14 Nov 2025 15:17:37 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NQjgSpPb"; 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 63E7F60AAD for ; Fri, 14 Nov 2025 15:17:22 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 65CB01D1C; Fri, 14 Nov 2025 15:15:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129721; bh=lQmnDDamLSikk20qIZdtHOhjYACIlSPUtQ790iq2k5Q=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=NQjgSpPb2X5Lx5LeIRm6kIQYOZEV2GnkWLHTIqZD4Dl2xAe/B8BHAIytT6fzzgbR3 eQcBiRBtKTew6OFxzLTuqqYHa6+N3Ffcc6xbzCUy39WbDe1Re539qInnIbWdXw/dNE jua0F/QruUyJ5Zfzo6Sz53NKjk4FUG3wcUS7IX4M= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:07 +0100 Subject: [PATCH v3 12/19] ipa: libipa: agc: Compute max shutter time with frame duration MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-12-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1973; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=lQmnDDamLSikk20qIZdtHOhjYACIlSPUtQ790iq2k5Q=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznrRVYLC7tg859E8weKoLqE5vsMtFGC1IYrv bqDtqVvYamJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56wAKCRByNAaPFqFW PK2aD/9MP47aDnjjwEUilItmYTvs3kvGKeaegtqoyzKDgcj27PBvVCVcd4MNE1OB3eaS0ERJzFw E0UyyW3Pwzei2+x05EE8pOF+//d5Jx9nbL4/j9hji6HHsVkxrOrWSbtoFxXjnXbjNAQlXHkoISm 9FMH/T6t8PSz/c1/1mkjAbMbt6+HnXFVhNRDGpW7vX5d6VBMJutYPmwDSZNnL3lvC9SQVP68vGg mdsYexQyL4CHF9rNHFXw3ZVNxYoqnJ+e8eifU58mxzpms8Bp0uP+1LdDDvyJi5MjrCfMVhDzf8L 9IOuCwLLvte2ITBj94Vq2ysqlZEpA0QQYIB3uxxdMl71Z4ixSnMDITfLbHR0cVlCQxiYjZZgqvJ GbjY3PGnHUvKCPKYXJubeK2W/DurPI2Hc0CxjjDf6WuGBL2FsefGFE4TKWQVewosQ46bk/c2qjz MKkvqPD6wQArvzX3CIoAI/eZPzlfYqs6iqXjjC5JG+3PDjugfGKz2TW1t47JCbLpSnflQUwHcd7 oKi1lxOTXhrpE+doC7Be6Gn9QcR8vvzPiadPU1fSgfJG24Iktw2vU/c4SWc/9CvuhEoLOP0DEcR NgVSQIwS/+s7qd+0TLk9vsPd1gmhhfpRUdLze/T6+zBXmB2HD+ezkujSR7CXGS0zGOk4oU64uk0 up6oDNv55URlb1Q== 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" The maximum shutter time the AGC algorithm can achieve is, by definition, limited by the maximum programmed frame duration. When configuring the AGC algorithm, use the frame duration to calculate the maximum shutter time by using the CameraSensorHelper::maxShutterTime() function, unless the IPA has asked for a fixed exposure by setting min == max. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/exposure_mode_helper.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp index f771b10a28eead2976c0000cf099ba5cfbe78e0f..45f51f9088170c983bb0de2c18714627514c5641 100644 --- a/src/ipa/libipa/exposure_mode_helper.cpp +++ b/src/ipa/libipa/exposure_mode_helper.cpp @@ -127,14 +127,30 @@ ExposureModeHelper::ExposureModeHelper(const SpanmaxShutterTime(sensorConfig.maxFrameDuration_, + sensorConfig.lineDuration_) + : minExposureTime_; } /** From patchwork Fri Nov 14 14:17:08 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25065 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 D9956C3333 for ; Fri, 14 Nov 2025 14:17:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2B3E260B2F; Fri, 14 Nov 2025 15:17:38 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="lxS4vk15"; 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 D707560AB1 for ; Fri, 14 Nov 2025 15:17:22 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C8B141E1B; Fri, 14 Nov 2025 15:15:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129722; bh=iYzyCp/zPGlYBdF4UutFJ3h+dospQqXQ5PWcTaVGjvI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=lxS4vk15lcJcKgdvgl70g50Eu/4jlet3Zhhb6uhbb73/fUK/amms7RNN1sjaZZURU gjvwHupJYfXpXvt9Lcl1J0SQMhTZYpAb1sda8DyrTSVffk7IttVn4JA9SQ5lGNSgkQ RybGZgKG7KqTNczdA31fLK8469mVWlcCUbz8TRY0= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:08 +0100 Subject: [PATCH v3 13/19] ipa: libipa: agc: Calculate exposure limits with frame duration MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-13-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=8940; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=iYzyCp/zPGlYBdF4UutFJ3h+dospQqXQ5PWcTaVGjvI=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznrCjePb8iR8uvULI4MogZSVXGV6RI1SsHOT NwC0GpX3FOJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc56wAKCRByNAaPFqFW PEb7D/4t4YPeatYPATmn+7ZrsG1q+DmIjTSkRGYa/MjNXUMmdp+c1QzM+x0OUpsMkKFaCFLT7AJ o+DJSTZTd6f4z6fxGZ8gFCKzlDfXZ0tlnwz48QF/bC0bLjVR3V/GgLAbM0TdO/fIsV6QNbFY+Gs IjbDDEQBris1EwvG9Aq1ZJJzbsZwmotpKviodH6UgOTQ4ZfHXOlkGHFaZfz9IBo3qCMMkicHxLx GPierFSKwe5+tu+e2rd6eXIZQziBu8kap2S8cApcFHG0TKfnaEwJ1oG3cp49llJx9gIWKHfjaeB mWq4fts5zm3Jn2SHur1f4q3hHSfH/tbPEvF1c+fnizNaGOac3UyCtiawnZ/V2IN2ECavSPCADUw UTiVcYAMTBl3vDHkVqYaP5bDofNU4e8fKMpLAhVRg7hWbfSXunAwvbnCKrxWo+SPTw6UMO9I48+ hO1mdlwpEcqpyRkZl0QNt63c0FJ1zYrURTOgg9hZknqIicPEImIrlVsDIa/V4NjT1ZDppYwrdwh Fmgwk44/WjvGpiH6GWRp5bixtm6eo8gSXpQ9kOH8TY4JThfLb2ywGDeNUsBoed7SFnxre/lXXfg z2spKB/ir+VRGxX7ECgrlXQtCvQJPgmjq4DyrDYxnsR8ntozgZF1M5DXdmyR4g5hcl9LQ1XRDIC E5Xc0J+Nggh9sDg== 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" Similarly to what now happens at configure() time, the AGC maximum shutter time is calculated at runtime in the setLimits() function using the CameraSensorHelper. Factor-out in the ExposureModeHelper class the maximum shutter time calculation from the configure() function so that it can be performed at setLimits() time too. Update the only IPA that handles limits update at runtime (RkISP1) to pass to the AGC the updated frame durations. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/agc_mean_luminance.cpp | 5 +++- src/ipa/libipa/agc_mean_luminance.h | 3 ++- src/ipa/libipa/exposure_mode_helper.cpp | 47 +++++++++++++++++++++------------ src/ipa/libipa/exposure_mode_helper.h | 5 +++- src/ipa/rkisp1/algorithms/agc.cpp | 8 +++--- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 602050729fd08649e2db8d07623a8a39e7a53a77..0c8e15030c377ac0797dbdc9d53694ee894cd9b8 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -461,6 +461,7 @@ int AgcMeanLuminance::parseTuningData(const YamlObject &tuningData) * \brief Set the ExposureModeHelper limits for this class * \param[in] minExposureTime Minimum exposure time to allow * \param[in] maxExposureTime Maximum ewposure time to allow + * \param[in] maxFrameDuration Maximum frame duration * \param[in] minGain Minimum gain to allow * \param[in] maxGain Maximum gain to allow * \param[in] constraints Additional constraints to apply @@ -470,11 +471,13 @@ int AgcMeanLuminance::parseTuningData(const YamlObject &tuningData) */ void AgcMeanLuminance::setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, + utils::Duration maxFrameDuration, double minGain, double maxGain, std::vector constraints) { for (auto &[id, helper] : exposureModeHelpers_) - helper->setLimits(minExposureTime, maxExposureTime, minGain, maxGain); + helper->setLimits(minExposureTime, maxExposureTime, maxFrameDuration, + minGain, maxGain); additionalConstraints_ = std::move(constraints); } diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index c3c8ce1cdda9229eca400b730f2ee896e9a0af93..535f94502dc2649a5f4ba49a7040de12f9f74179 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -62,7 +62,8 @@ public: } void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, - double minGain, double maxGain, std::vector constraints); + utils::Duration maxFrameDuration, double minGain, double maxGain, + std::vector constraints); const std::map> &constraintModes() const { diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp index 45f51f9088170c983bb0de2c18714627514c5641..fad13ff1521498244224a8a5f375738ee3fc9ff2 100644 --- a/src/ipa/libipa/exposure_mode_helper.cpp +++ b/src/ipa/libipa/exposure_mode_helper.cpp @@ -109,6 +109,27 @@ ExposureModeHelper::ExposureModeHelper(const SpanmaxShutterTime(maxFrameDuration, + sensor_.lineDuration_) + : minExposureTime; +} + /** * \brief Configure sensor details * \param[in] sensorConfig The sensor configuration @@ -136,27 +157,15 @@ void ExposureModeHelper::configure(const SensorConfiguration &sensorConfig, minGain_ = sensor_.minGain_; maxGain_ = sensor_.maxGain_; - minExposureTime_ = sensor_.minExposureTime_; - - /* - * Compute the maximum shutter time. - * - * If maxExposureTime is equal to minExposureTime then we use them - * to fix the exposure time. - * - * Otherwise, if the exposure can range between a min and max delegate - * the maximum shutter time calculation to the sensor helper. - */ - maxExposureTime_ = minExposureTime_ != sensorConfig.maxExposureTime_ - ? sensorHelper_->maxShutterTime(sensorConfig.maxFrameDuration_, - sensorConfig.lineDuration_) - : minExposureTime_; + setMaxExposure(sensorConfig.minExposureTime_, sensorConfig.maxExposureTime_, + sensorConfig.maxFrameDuration_); } /** * \brief Set the exposure time and gain limits * \param[in] minExposureTime The minimum exposure time supported * \param[in] maxExposureTime The maximum exposure time supported + * \param[in] maxFrameDuration The maximum frame duration * \param[in] minGain The minimum analogue gain supported * \param[in] maxGain The maximum analogue gain supported * @@ -168,15 +177,19 @@ void ExposureModeHelper::configure(const SensorConfiguration &sensorConfig, * If the algorithm using the helpers needs to indicate that either exposure time * or analogue gain or both should be fixed it can do so by setting both the * minima and maxima to the same value. + * + * The exposure time limits are calculated using \a maxFrameDuration as the + * upper bound, the \a maxExposureTime paramter effectivelly only serves + * to indicate that the caller wants a fixed exposure value. */ void ExposureModeHelper::setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, + utils::Duration maxFrameDuration, double minGain, double maxGain) { - minExposureTime_ = minExposureTime; - maxExposureTime_ = maxExposureTime; minGain_ = minGain; maxGain_ = maxGain; + setMaxExposure(minExposureTime, maxExposureTime, maxFrameDuration); } utils::Duration ExposureModeHelper::clampExposureTime(utils::Duration exposureTime, diff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h index e41c58767eee65dd27946336beb2bc182dd4ab58..36791c99face056835b0bb2d28b533380e8d9b95 100644 --- a/src/ipa/libipa/exposure_mode_helper.h +++ b/src/ipa/libipa/exposure_mode_helper.h @@ -41,12 +41,15 @@ public: void configure(const SensorConfiguration &sensorConfig, const CameraSensorHelper *sensorHelper); void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, - double minGain, double maxGain); + utils::Duration maxFrameDuration, double minGain, double maxGain); std::tuple splitExposure(utils::Duration exposure) const; private: + void setMaxExposure(utils::Duration minExposureTime, + utils::Duration maxExposureTime, + utils::Duration maxFrameDuration); utils::Duration clampExposureTime(utils::Duration exposureTime, double *quantizationGain = nullptr) const; double clampGain(double gain, double *quantizationGain = nullptr) const; diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index de2980485f2d3a8973f065981f8cdd520e3dfc47..db318a2c49f2fbd9b00222ec699a657eed131595 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -580,9 +580,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, if (frameContext.agc.autoExposureEnabled) { minExposureTime = context.configuration.sensor.minExposureTime; - maxExposureTime = std::clamp(frameContext.agc.maxFrameDuration, - context.configuration.sensor.minExposureTime, - context.configuration.sensor.maxExposureTime); + maxExposureTime = context.configuration.sensor.maxExposureTime; } else { minExposureTime = context.configuration.sensor.lineDuration * frameContext.agc.exposure; @@ -601,8 +599,8 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, if (context.activeState.wdr.mode != controls::WdrOff) additionalConstraints.push_back(context.activeState.wdr.constraint); - setLimits(minExposureTime, maxExposureTime, minAnalogueGain, maxAnalogueGain, - std::move(additionalConstraints)); + setLimits(minExposureTime, maxExposureTime, frameContext.agc.maxFrameDuration, + minAnalogueGain, maxAnalogueGain, std::move(additionalConstraints)); /* * The Agc algorithm needs to know the effective exposure value that was From patchwork Fri Nov 14 14:17:09 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25066 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 94173C3334 for ; Fri, 14 Nov 2025 14:17:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E30B660B33; Fri, 14 Nov 2025 15:17:39 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="g5sIHvyG"; 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 37D1F60AB3 for ; Fri, 14 Nov 2025 15:17:23 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3802D1198; Fri, 14 Nov 2025 15:15:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129722; bh=PVnj5hhNX5FryIopipaQwwhWxXo05VGoLhKTcRrE8uc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=g5sIHvyGF+nH61/QwlRWPPNA4W3B7Rjszm5HDfIhkCGuo9JMId+RA8+gfBG2cu8/O Pjr1AJGX2koaRbTvKaqc5DAGmvHII+lYSVXowBCKSzyvj7vK7rOlNThDkrGOD/sGj+ VWvs2TTPkHjM/anN9BJTuaLo2J1/gHCGRB9hD4/E= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:09 +0100 Subject: [PATCH v3 14/19] ipa: libipa: agc: Rework setLimits() interface MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-14-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=13940; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=PVnj5hhNX5FryIopipaQwwhWxXo05VGoLhKTcRrE8uc=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznsDJM/vjrRewm8vNXi1+aVvf5UPh6YhJv3A YE9/cE4jAyJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc57AAKCRByNAaPFqFW PMJeD/91/O9r9/D22O5n4dI9m8+ONe/DVMKZMS4EJ33nz9QeVJ2v4ggIW7Q3ORbJMvYAxVs+1yQ oe4QtNXapXz3Lxx0WYaQVzTej9dFZ/TpjJ5v2lil/QynsbIQ7JyB1oZwKz+G7F9aFAB3ya4mr6V hs+CAKnxgPLKTj20VOXf1WH/RAVilNkFfvofvJvauQrjV7L3vDss2dywqb7LGpYDtnI5Db94VfO 2PQqMkP/0cgSEGi/uPriYNRqU68GWEJ/4oIfhAexitwyDoSQOX4a4k271SbQB5lx7+rZrNRSjUk FLMcWEMDkZ/QcfAU6vpF/ZnlWg8sQxBvu+x5TjPTSi2wHe01Ff8oXtfxnzOanrIuF/WfY1JwI/b KuWo31TQyMOjpyD7IQuXB7XsIytvja7Vue6Mx/vybT8XlXx7Agcuf1bVm508c742XFyhz/xeJqJ zWsOwQ+Nm6QlqLgaREkrwkn3uy3mJHoHaXsoMElPdCZsqk1yy6FU74ZIls473muBt2hK75PiXHz 1vv7QEn13NGlSCZEvKwBHev7aUG2ihozF5D6lLwnWg3R0/7IzyIq69MZjidVph5Hcda8ZqCjUdx Di6qdGtmkB+J7qcavUxnULYpCHPIeXpNlaoq0uRtLZjvwOQnu+dKAva42oXYZvon8J3M9rLwS/L jBCkYU9cD/vX+tw== 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" The setLimits() interface accepts a min/max exposure and gains and detect a request for a fixed value if min == max. For the shutter time, the concept of max exposure is ill-formed as the maximum achievable exposure is determinate by the frame duration, while the min frame duration is a sensor's default parameter. For gains instead either we want a fixed value or it can range in the sensor's min and max values. As we now store the sensor's default in the exposure mode helper, rework the interface of setLimits() both in AgcMeanLuminance and ExposureModeHelper so that it receives - an optional fixed shutter time - an optional fixed gain - the current maximum frame duration If the optional fixed values are populated they are used to fix either the shutter time or the exposure. If they are not set instead the algorithm is free to range in the: - shutterTime = [minExposure, frameDuration - margin] - gain = [minGain, maxGain] Signed-off-by: Jacopo Mondi --- src/ipa/libipa/agc_mean_luminance.cpp | 30 ++++++------- src/ipa/libipa/agc_mean_luminance.h | 7 +-- src/ipa/libipa/exposure_mode_helper.cpp | 79 +++++++++++++++++---------------- src/ipa/libipa/exposure_mode_helper.h | 13 +++--- src/ipa/rkisp1/algorithms/agc.cpp | 29 ++++-------- 5 files changed, 75 insertions(+), 83 deletions(-) diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 0c8e15030c377ac0797dbdc9d53694ee894cd9b8..9fc275ea9e5b81ce107eabe1982be3c44c01479c 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -171,11 +172,10 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95; * IPA modules that want to use this class to implement their AEGC algorithm * should derive it and provide an overriding estimateLuminance() function for * this class to use. They must call parseTuningData() in init(), and must also - * call setLimits() and resetFrameCounter() in configure(). They may then use - * calculateNewEv() in process(). If the limits passed to setLimits() change for - * any reason (for example, in response to a FrameDurationLimit control being - * passed in queueRequest()) then setLimits() must be called again with the new - * values. + * call resetFrameCounter() in configure(). They may then use calculateNewEv() + * in process(). To update the algorithm limits for example, in response to a + * FrameDurationLimit control being passed in queueRequest()) then + * setExposureLimits() must be called with the new values. */ AgcMeanLuminance::AgcMeanLuminance() @@ -459,25 +459,21 @@ int AgcMeanLuminance::parseTuningData(const YamlObject &tuningData) /** * \brief Set the ExposureModeHelper limits for this class - * \param[in] minExposureTime Minimum exposure time to allow - * \param[in] maxExposureTime Maximum ewposure time to allow + * \param[in] shutterTime The (optional) fixed shutter time + * \param[in] gain The (optional) analogue gain value * \param[in] maxFrameDuration Maximum frame duration - * \param[in] minGain Minimum gain to allow - * \param[in] maxGain Maximum gain to allow * \param[in] constraints Additional constraints to apply * - * This function calls \ref ExposureModeHelper::setLimits() for each + * This function calls \ref ExposureModeHelper::setExposureLimits() for each * ExposureModeHelper that has been created for this class. */ -void AgcMeanLuminance::setLimits(utils::Duration minExposureTime, - utils::Duration maxExposureTime, - utils::Duration maxFrameDuration, - double minGain, double maxGain, - std::vector constraints) +void AgcMeanLuminance::setExposureLimits(std::optional shutterTime, + std::optional gain, + utils::Duration maxFrameDuration, + std::vector constraints) { for (auto &[id, helper] : exposureModeHelpers_) - helper->setLimits(minExposureTime, maxExposureTime, maxFrameDuration, - minGain, maxGain); + helper->setExposureLimits(shutterTime, gain, maxFrameDuration); additionalConstraints_ = std::move(constraints); } diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 535f94502dc2649a5f4ba49a7040de12f9f74179..12316ca8bbd7d8b5783a948f5e01d5f0f56bfe3a 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -61,9 +61,10 @@ public: exposureCompensation_ = gain; } - void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, - utils::Duration maxFrameDuration, double minGain, double maxGain, - std::vector constraints); + void setExposureLimits(std::optional shutterTime, + std::optional gain, + utils::Duration maxFrameDuration, + std::vector constraints); const std::map> &constraintModes() const { diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp index fad13ff1521498244224a8a5f375738ee3fc9ff2..e1e36eb1820d4080f2dc295a963a37782a484f02 100644 --- a/src/ipa/libipa/exposure_mode_helper.cpp +++ b/src/ipa/libipa/exposure_mode_helper.cpp @@ -109,25 +109,33 @@ ExposureModeHelper::ExposureModeHelper(const Span shutterTime, + utils::Duration maxFrameDuration) { - minExposureTime_ = minExposureTime; - /* - * Compute the maximum shutter time. - * - * If maxExposureTime is equal to minExposureTime then we use them - * to fix the exposure time. + * If shutterTime is populated we use it to to fix the shutter time. * - * Otherwise, if the exposure can range between a min and max delegate - * the maximum shutter time calculation to the sensor helper. + * Otherwise, if the exposure is not fixed delegate the maximum shutter + * time calculation to the sensor helper and restore the min exposure + * time to the sensor's default value. */ - maxExposureTime_ = minExposureTime != maxExposureTime - ? sensorHelper_->maxShutterTime(maxFrameDuration, - sensor_.lineDuration_) - : minExposureTime; + + maxExposureTime_ = shutterTime.has_value() + ? shutterTime.value() + : sensorHelper_->maxShutterTime(maxFrameDuration, + sensor_.lineDuration_); + minExposureTime_ = shutterTime.has_value() + ? shutterTime.value() : sensor_.minExposureTime_; +} + +void ExposureModeHelper::setGainLimits(std::optional gain) +{ + /* + * Use the fixed value as limits if populated, otherwise use the + * sensor's default ones. + */ + minGain_ = gain.has_value() ? gain.value() : sensor_.minGain_; + maxGain_ = gain.has_value() ? gain.value() : sensor_.maxGain_; } /** @@ -154,42 +162,37 @@ void ExposureModeHelper::configure(const SensorConfiguration &sensorConfig, sensorHelper_ = sensorHelper; /* Initialize run-time limits with sensor's default. */ - minGain_ = sensor_.minGain_; - maxGain_ = sensor_.maxGain_; - - setMaxExposure(sensorConfig.minExposureTime_, sensorConfig.maxExposureTime_, - sensorConfig.maxFrameDuration_); + setShutterLimits({}, sensorConfig.maxFrameDuration_); + setGainLimits({}); } /** * \brief Set the exposure time and gain limits - * \param[in] minExposureTime The minimum exposure time supported - * \param[in] maxExposureTime The maximum exposure time supported + * \param[in] shutterTime The (optional) fixed shutter time + * \param[in] gain The (optional) fixed gain * \param[in] maxFrameDuration The maximum frame duration - * \param[in] minGain The minimum analogue gain supported - * \param[in] maxGain The maximum analogue gain supported * - * This function configures the exposure time and analogue gain limits that need + * This function configures the shutter time and analogue gain limits that need * to be adhered to as the helper divides up exposure. Note that this function * *must* be called whenever those limits change and before splitExposure() is * used. * - * If the algorithm using the helpers needs to indicate that either exposure time - * or analogue gain or both should be fixed it can do so by setting both the - * minima and maxima to the same value. + * If the algorithm using the helpers needs to indicate that the shutter time + * should be fixed it should populate the optional \a shutterTime argument. + * If the shutter time is not fixed, the maximum achievable shutter time is + * calculated using \a maxFrameDuration as the upper bound while the minimum + * exposure time is reset to the sensor's default. * - * The exposure time limits are calculated using \a maxFrameDuration as the - * upper bound, the \a maxExposureTime paramter effectivelly only serves - * to indicate that the caller wants a fixed exposure value. + * If the analogue gain should be fixed the optional \a gain argument should + * be populated. If the analogue gain is not fixed its min and max values are + * reset to the sensor's default. */ -void ExposureModeHelper::setLimits(utils::Duration minExposureTime, - utils::Duration maxExposureTime, - utils::Duration maxFrameDuration, - double minGain, double maxGain) +void ExposureModeHelper::setExposureLimits(std::optional shutterTime, + std::optional gain, + utils::Duration maxFrameDuration) { - minGain_ = minGain; - maxGain_ = maxGain; - setMaxExposure(minExposureTime, maxExposureTime, maxFrameDuration); + setShutterLimits(shutterTime, maxFrameDuration); + setGainLimits(gain); } utils::Duration ExposureModeHelper::clampExposureTime(utils::Duration exposureTime, diff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h index 36791c99face056835b0bb2d28b533380e8d9b95..8831ed0751c75d60b61b608c2b0cccfaf1d59726 100644 --- a/src/ipa/libipa/exposure_mode_helper.h +++ b/src/ipa/libipa/exposure_mode_helper.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -40,16 +41,18 @@ public: void configure(const SensorConfiguration &sensorConfig, const CameraSensorHelper *sensorHelper); - void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, - utils::Duration maxFrameDuration, double minGain, double maxGain); + void setExposureLimits(std::optional shutterTime, + std::optional gain, + utils::Duration maxFrameDuration); std::tuple splitExposure(utils::Duration exposure) const; private: - void setMaxExposure(utils::Duration minExposureTime, - utils::Duration maxExposureTime, - utils::Duration maxFrameDuration); + void setShutterLimits(std::optional shutterTime, + utils::Duration maxFrameDuration); + void setGainLimits(std::optional gain); + utils::Duration clampExposureTime(utils::Duration exposureTime, double *quantizationGain = nullptr) const; double clampGain(double gain, double *quantizationGain = nullptr) const; diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index db318a2c49f2fbd9b00222ec699a657eed131595..c8210e175186a282faf586378c5a0a761612047c 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -573,34 +573,23 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, * Set the AGC limits using the fixed exposure time and/or gain in * manual mode, or the sensor limits in auto mode. */ - utils::Duration minExposureTime; - utils::Duration maxExposureTime; - double minAnalogueGain; - double maxAnalogueGain; + std::optional shutterTime; + std::optional gain; - if (frameContext.agc.autoExposureEnabled) { - minExposureTime = context.configuration.sensor.minExposureTime; - maxExposureTime = context.configuration.sensor.maxExposureTime; - } else { - minExposureTime = context.configuration.sensor.lineDuration - * frameContext.agc.exposure; - maxExposureTime = minExposureTime; + if (!frameContext.agc.autoExposureEnabled) { + shutterTime = context.configuration.sensor.lineDuration + * frameContext.agc.exposure; } - if (frameContext.agc.autoGainEnabled) { - minAnalogueGain = context.configuration.sensor.minAnalogueGain; - maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; - } else { - minAnalogueGain = frameContext.agc.gain; - maxAnalogueGain = frameContext.agc.gain; - } + if (!frameContext.agc.autoGainEnabled) + gain = frameContext.agc.gain; std::vector additionalConstraints; if (context.activeState.wdr.mode != controls::WdrOff) additionalConstraints.push_back(context.activeState.wdr.constraint); - setLimits(minExposureTime, maxExposureTime, frameContext.agc.maxFrameDuration, - minAnalogueGain, maxAnalogueGain, std::move(additionalConstraints)); + setExposureLimits(shutterTime, gain, frameContext.agc.maxFrameDuration, + std::move(additionalConstraints)); /* * The Agc algorithm needs to know the effective exposure value that was From patchwork Fri Nov 14 14:17:10 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25067 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 513AAC3335 for ; Fri, 14 Nov 2025 14:17:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E793D60A8B; Fri, 14 Nov 2025 15:17:41 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="l6eeKo2D"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 981D660AA7 for ; Fri, 14 Nov 2025 15:17:23 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 98EB0664; Fri, 14 Nov 2025 15:15:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129722; bh=0XZ1znvJpiyjfJV5NSov+PyuvJO/BC3mfohGSQTTJ0A=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=l6eeKo2D4Eeradkbh7Eai9O4drUb/yn54U5t2WlfEhlSl6MJFXFt4TWBBrAo5K1cB LaEoKV0VfZwSkG7th9X08BV6zP0iHQmrAY4hEpOJZR9Lw6EFOK3eQxuHNDg1182r3K 3Ycs6gz75cAfdfz99fmzuWZKvZ1jlQhQ/HRth1Pw= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:10 +0100 Subject: [PATCH v3 15/19] ipa: libipa: agc: Fold resetFrameCount() in configure() MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-15-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=4878; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=0XZ1znvJpiyjfJV5NSov+PyuvJO/BC3mfohGSQTTJ0A=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznsloNNoJLHrOUanq48h5ZyPmMS6mlrlOGJ5 URsFecr2fSJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc57AAKCRByNAaPFqFW PAZjEACNK56VSIjDSXNgZBgzG82x4j4CGqjTxpumCzP3jzct1ta+ZydLl61U/ax9r6xoW+4o/5A Bm3HaO7TgF/43qxylI4Bv+xVy0QNgTFnWOkdnpH/Hl9vp/FChk9s5z/7s7iUz4v5bBhArjtDHM+ WKBvcqbqwWXJRu5l66GLgzNupqg9/yrkhmH7Ue5qhKArPDoCW5x4fF27eY+KhRvRXNOd+q0naaF HQj+JnzVxkmiMyga3WXVDpUNL9xRwcChLcBSoSRctmsiJT1nOQdQfPuPH+jHolGur0776iI1Unt 1pdYRWM7sjGxZwPpHQtbloW7PNxRKwO3E5xq6g7uxQ/sI2u2fjOKiRstJ/yc9osbKBZN4Ea6nro p5E8Xp1wHCE5FGTPNgjmRvgJo1Ib8uZTfp7AaJju/SVeOldqx7qtqwYU0KA/OgLg9OrNUq4zrkc jEwVXVFuOkedO1GnRD36PbaDr9T5+4vreIJjgpXhpud4VpwKUkE2Z4J7DxRVmMnMKKaAt9vMjW5 wWg4FzpnmwlJiRfGR+r+ThXZY7OJIXiOMst7BEv/z0XksNtD9y1X1oYnqmjwXUd+CxbmGXV3/L3 qHVg55Il4R4ZkNVriZC1njRWzCd9EumsOtZuIZXcSaBS4fieRQyLrvfeUBhio5+ItdmrZgK4W/X Si+j3kLXHXjkpjg== 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" The AgcMeanLuminance::resetFrameCount() function has to be called after a call to AgcMeanLuminance::configure(). As the two calls always happen one after another, do not require each IPA implementation to do that but fold instead the call to resetFrameCount() in AgcMeanLuminance::configure(). Update the AgcMeanLuminance class documentation accordingly. Signed-off-by: Jacopo Mondi --- src/ipa/ipu3/algorithms/agc.cpp | 2 -- src/ipa/libipa/agc_mean_luminance.cpp | 19 ++++++------------- src/ipa/libipa/agc_mean_luminance.h | 9 ++++----- src/ipa/mali-c55/algorithms/agc.cpp | 2 -- src/ipa/rkisp1/algorithms/agc.cpp | 2 -- 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index 002ee574c02b79c25834a9d87a5881a9de52e39e..c9d41f93cff5b81710b76592303f1e0d10697326 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -128,8 +128,6 @@ int Agc::configure(IPAContext &context, /* \todo Update AGC limits when FrameDurationLimits is passed in */ - resetFrameCount(); - return 0; } diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 9fc275ea9e5b81ce107eabe1982be3c44c01479c..725a23ef2f6f612c6d3408701246db7415fd8327 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -171,9 +171,10 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95; * * IPA modules that want to use this class to implement their AEGC algorithm * should derive it and provide an overriding estimateLuminance() function for - * this class to use. They must call parseTuningData() in init(), and must also - * call resetFrameCounter() in configure(). They may then use calculateNewEv() - * in process(). To update the algorithm limits for example, in response to a + * this class to use. They must call parseTuningData() in init() and the use the + * sensor configuration data to call AgcMeanLuminance::configure() in their + * configure() implementation. They may then use calculateNewEv() in process(). + * To update the algorithm limits for example, in response to a * FrameDurationLimit control being passed in queueRequest()) then * setExposureLimits() must be called with the new values. */ @@ -379,6 +380,8 @@ void AgcMeanLuminance::configure(const SensorConfiguration &config, helper->configure(sensorConfig, sensorHelper); } + + resetFrameCount(); } /** @@ -692,16 +695,6 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, return exposureModeHelper->splitExposure(newExposureValue); } -/** - * \fn AgcMeanLuminance::resetFrameCount() - * \brief Reset the frame counter - * - * This function resets the internal frame counter, which exists to help the - * algorithm decide whether it should respond instantly or not. The expectation - * is for derived classes to call this function before each camera start call in - * their configure() function. - */ - } /* namespace ipa */ } /* namespace libcamera */ diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 12316ca8bbd7d8b5783a948f5e01d5f0f56bfe3a..acbefc4e5765413bc803417eae1dbd0a943bc95e 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -87,11 +87,6 @@ public: double effectiveYTarget() const; - void resetFrameCount() - { - frameCount_ = 0; - } - private: virtual double estimateLuminance(const double gain) const = 0; @@ -104,6 +99,10 @@ private: const Histogram &hist, double gain); utils::Duration filterExposure(utils::Duration exposureValue); + void resetFrameCount() + { + frameCount_ = 0; + } double exposureCompensation_; uint64_t frameCount_; diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 731b29ced1030ecb3f44b83ad28a0691dd5d2f0d..91b1438f7e5ca0498373c86fd75b91f9c5a81c3f 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -186,8 +186,6 @@ int Agc::configure(IPAContext &context, /* \todo Update AGC limits when FrameDurationLimits is passed in */ - resetFrameCount(); - return 0; } diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index c8210e175186a282faf586378c5a0a761612047c..b9a94ba03c910f73420579dd6737d8d46b26e576 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -213,8 +213,6 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.activeState.agc.automatic.yTarget = effectiveYTarget(); - resetFrameCount(); - return 0; } From patchwork Fri Nov 14 14:17:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25068 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 01B3EC3241 for ; Fri, 14 Nov 2025 14:17:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9BF6660AA7; Fri, 14 Nov 2025 15:17:42 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="GmVqZ7R0"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0571860AAB for ; Fri, 14 Nov 2025 15:17:24 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 067041E5A; Fri, 14 Nov 2025 15:15:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129723; bh=AIdwxNDRiTVtX5iE/ejoMpkXEM33NwchcLz9mIXnhXs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=GmVqZ7R0gYvKzBBYZUS+YJIL53/bBCjU3SH+bzMfd8As3ICVfTxH+1TIEe/7th60Z TDUKYMP3cc86sZjoUvOnRuUT10f69r9fAcDmbxvNgWMN3KRnypxEFJ+++gdDTEf7z+ n8/qW19kUXb4xMLGttLxdS8OYS3xjtkVs1u76JiQ= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:11 +0100 Subject: [PATCH v3 16/19] ipa: libipa: Remove maxExposureTime from sensor configuration MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-16-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=13821; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=AIdwxNDRiTVtX5iE/ejoMpkXEM33NwchcLz9mIXnhXs=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznsqlFgcBGcHZlihm+Ni4oglPlZjY0BtbH4o pPEQGW1Wn6JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc57AAKCRByNAaPFqFW PKZID/0Wy74iL7GKwpBdJAKqDuuDOpj9bFQz8g4meNo/eorIIz+qmynrrBsGz7IvX1YDzmS9qRo UnMtqbUkbPn8BrwgpoKtadKuEOrtjuUgKTyHTf88gV48lrRAWl3+tAIBdBI3VV4HGarPs5AW/Bq ylMNJrn6uK/pV/AKEuDPWRbL1JS711fyOBKM7wZ83lsSsf6LY7+yq/4q2UnYF2GCUJ3EQkmdwZG tJ28Y3pLCnE/do0fzCCcvOI2rKx1VFuUl2JXleYbL/GII7zztv0nB6qZ/HwGme/j4N0cJoC1neJ WzAlm4A5YIA5DCd08yNBPZkVRytFZeQqyls6a7mnFKqYNNFasbAur7ldbd2Iqw+MX8FBS/WVn0R AJO4j3EgCLTqScjK5ciCwf07WUOevvGGgfpwOQsqCifGZKlJ7fw3pcYklVvKDfljRzoxDvfw3ia TOJ2hzyb9fR4DDTrni6EcbjpflVhdH+RIAPygssS6d1SRC9tNQcYZxo9TRgi9g5d7CQ5pClORuh Q2swjVO/AYWoq029glmh2Aq14cTRsWvjr9opqm+Ruo85p+hDfQ+zqVEKkGxwZfsN4gOu7MJf3I+ DiwK60agEQUfJDibR8usV4kelsQQnVTFlzr5te+Pn5eqVgmQVNyZ/vdweUCKGiclJjxtcYWM5um qvGvA+ootCouvyw== 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" Now that we have moved the AgcMeanLuminance class and the associated helpers to calculate the maximum achievable shutter time using the frame duration, remove the maxExposureTime parameter from all the IPAs using AgcMeanLuminance. Remove maxExposureTime from all IPA sensor configuration data, as it is now unused and its value depends on the frame duration. Do not remove minExposureTime as this instead effectively represents a sensor parameters which remains stable during a streaming session. Signed-off-by: Jacopo Mondi --- src/ipa/ipu3/algorithms/agc.cpp | 15 +++------------ src/ipa/ipu3/algorithms/agc.h | 4 ---- src/ipa/ipu3/ipa_context.cpp | 3 --- src/ipa/ipu3/ipa_context.h | 1 - src/ipa/ipu3/ipu3.cpp | 2 -- src/ipa/libipa/agc_mean_luminance.cpp | 4 ---- src/ipa/libipa/agc_mean_luminance.h | 1 - src/ipa/mali-c55/algorithms/agc.cpp | 1 - src/ipa/mali-c55/ipa_context.h | 1 - src/ipa/mali-c55/mali-c55.cpp | 2 -- src/ipa/rkisp1/algorithms/agc.cpp | 2 -- src/ipa/rkisp1/ipa_context.cpp | 3 --- src/ipa/rkisp1/ipa_context.h | 1 - src/ipa/rkisp1/rkisp1.cpp | 1 - 14 files changed, 3 insertions(+), 38 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index c9d41f93cff5b81710b76592303f1e0d10697326..8fc2d7310c5c6e08b3eff6cc575c221d21c1131f 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -57,11 +57,6 @@ static constexpr utils::Duration kMaxExposureTime = 60ms; /* Histogram constants */ static constexpr uint32_t knumHistogramBins = 256; -Agc::Agc() - : minExposureTime_(0s), maxExposureTime_(0s) -{ -} - /** * \brief Initialise the AGC algorithm from tuning files * \param[in] context The shared IPA context @@ -101,10 +96,6 @@ int Agc::configure(IPAContext &context, stride_ = configuration.grid.stride; bdsGrid_ = configuration.grid.bdsGrid; - minExposureTime_ = configuration.sensor.minExposureTime; - maxExposureTime_ = std::min(configuration.sensor.maxExposureTime, - kMaxExposureTime); - minAnalogueGain_ = std::max(configuration.sensor.minAnalogueGain, kMinAnalogueGain); maxAnalogueGain_ = configuration.sensor.maxAnalogueGain; @@ -117,10 +108,10 @@ int Agc::configure(IPAContext &context, AgcMeanLuminance::SensorConfiguration sensorConfig; sensorConfig.lineDuration = context.configuration.sensor.lineDuration; - sensorConfig.minExposureTime = minExposureTime_; + sensorConfig.minExposureTime = configuration.sensor.minExposureTime; sensorConfig.minFrameDuration = context.configuration.sensor.minFrameDuration; - sensorConfig.maxFrameDuration = context.configuration.sensor.maxFrameDuration; - sensorConfig.maxExposureTime = maxExposureTime_; + sensorConfig.maxFrameDuration = std::min(context.configuration.sensor.maxFrameDuration, + kMaxExposureTime); sensorConfig.minAnalogueGain = minAnalogueGain_; sensorConfig.maxAnalogueGain = maxAnalogueGain_; diff --git a/src/ipa/ipu3/algorithms/agc.h b/src/ipa/ipu3/algorithms/agc.h index 890c271b44627f337c0d8a85853e1ecc3bae1318..4c2e4e3e5913d87a5af420b17f6c487aeb6cc37a 100644 --- a/src/ipa/ipu3/algorithms/agc.h +++ b/src/ipa/ipu3/algorithms/agc.h @@ -27,7 +27,6 @@ namespace ipa::ipu3::algorithms { class Agc : public Algorithm, public AgcMeanLuminance { public: - Agc(); ~Agc() = default; int init(IPAContext &context, const YamlObject &tuningData) override; @@ -42,9 +41,6 @@ private: Histogram parseStatistics(const ipu3_uapi_stats_3a *stats, const ipu3_uapi_grid_config &grid); - utils::Duration minExposureTime_; - utils::Duration maxExposureTime_; - double minAnalogueGain_; double maxAnalogueGain_; diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index 47ae750f0ddb859e1a08e75e255b8decacd730a1..4281ffba5f7fc841e47132986631c0ee44c898ff 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -111,9 +111,6 @@ namespace libcamera::ipa::ipu3 { * \var IPASessionConfiguration::agc.minExposureTime * \brief Minimum exposure time supported with the configured sensor * - * \var IPASessionConfiguration::agc.maxExposureTime - * \brief Maximum exposure time supported with the configured sensor - * * \var IPASessionConfiguration::agc.minAnalogueGain * \brief Minimum analogue gain supported with the configured sensor * diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index 33f3fe3ae1b503d47887873046def0835cd53894..11b9c286d56ac6f30d446e3721f7e5db4364a6fb 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -38,7 +38,6 @@ struct IPASessionConfiguration { utils::Duration lineDuration; Size size; utils::Duration minExposureTime; - utils::Duration maxExposureTime; utils::Duration minFrameDuration; utils::Duration maxFrameDuration; double minAnalogueGain; diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 0c8651c5235f9e4e9944eb88595aeef41f016310..61e8030d722f430e702a8b8f7e2880caa961fa8e 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -206,7 +206,6 @@ void IPAIPU3::updateSessionConfiguration(const IPACameraSensorInfo &sensorInfo, const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second; int32_t minExposure = v4l2Exposure.min().get(); - int32_t maxExposure = v4l2Exposure.max().get(); const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; int32_t minGain = v4l2Gain.min().get(); @@ -226,7 +225,6 @@ void IPAIPU3::updateSessionConfiguration(const IPACameraSensorInfo &sensorInfo, * \todo take VBLANK into account for maximum exposure time */ context_.configuration.sensor.minExposureTime = minExposure * context_.configuration.sensor.lineDuration; - context_.configuration.sensor.maxExposureTime = maxExposure * context_.configuration.sensor.lineDuration; context_.configuration.sensor.minFrameDuration = frameHeights[0] * context_.configuration.sensor.lineDuration; context_.configuration.sensor.maxFrameDuration = frameHeights[1] * diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 725a23ef2f6f612c6d3408701246db7415fd8327..b930e1f7240d4936aa8dc850657bbbf9c2f3a11f 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -115,9 +115,6 @@ static constexpr double kMaxRelativeLuminanceTarget = 0.95; * \var AgcMeanLuminance::SensorConfiguration::minExposureTime * \brief The sensor minimum exposure time in microseconds * - * \var AgcMeanLuminance::SensorConfiguration::maxExposureTime - * \brief The sensor maximum exposure time in microseconds - * * \var AgcMeanLuminance::SensorConfiguration::minFrameDuration * \brief The sensor minimum frame duration in microseconds * @@ -372,7 +369,6 @@ void AgcMeanLuminance::configure(const SensorConfiguration &config, ExposureModeHelper::SensorConfiguration sensorConfig; sensorConfig.lineDuration_ = config.lineDuration; sensorConfig.minExposureTime_ = config.minExposureTime; - sensorConfig.maxExposureTime_ = config.maxExposureTime; sensorConfig.minFrameDuration_ = config.minFrameDuration; sensorConfig.maxFrameDuration_ = config.maxFrameDuration; sensorConfig.minGain_ = config.minAnalogueGain; diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index acbefc4e5765413bc803417eae1dbd0a943bc95e..93a0959bbd9e0d6ec42248f2d3b19253ad389ae6 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -45,7 +45,6 @@ public: struct SensorConfiguration { utils::Duration lineDuration; utils::Duration minExposureTime; - utils::Duration maxExposureTime; utils::Duration minFrameDuration; utils::Duration maxFrameDuration; double minAnalogueGain; diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 91b1438f7e5ca0498373c86fd75b91f9c5a81c3f..a0b55694aad292f8a080d8266470797ac0cc2c25 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -176,7 +176,6 @@ int Agc::configure(IPAContext &context, AgcMeanLuminance::SensorConfiguration sensorConfig; sensorConfig.lineDuration = context.configuration.sensor.lineDuration; sensorConfig.minExposureTime = context.configuration.sensor.minShutterSpeed; - sensorConfig.maxExposureTime = context.configuration.sensor.maxShutterSpeed; sensorConfig.minFrameDuration = context.configuration.sensor.minFrameDuration; sensorConfig.maxFrameDuration = context.configuration.sensor.maxFrameDuration; sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain; diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index 3b64cb7571729d4af162def5b2316331b3561af1..828103f21451d9f7f4998c3faedc8fb6a1e7a2ec 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -29,7 +29,6 @@ struct IPASessionConfiguration { utils::Duration lineDuration; uint32_t blackLevel; utils::Duration minShutterSpeed; - utils::Duration maxShutterSpeed; utils::Duration minFrameDuration; utils::Duration maxFrameDuration; double minAnalogueGain; diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp index 491ae71a06dbede967bfbe1bcdcab25d177ad691..02f5dfb76eae073858ec688746b7e12ec072e567 100644 --- a/src/ipa/mali-c55/mali-c55.cpp +++ b/src/ipa/mali-c55/mali-c55.cpp @@ -170,7 +170,6 @@ void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info, const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second; int32_t minExposure = v4l2Exposure.min().get(); - int32_t maxExposure = v4l2Exposure.max().get(); int32_t defExposure = v4l2Exposure.def().get(); const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; @@ -191,7 +190,6 @@ void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info, utils::Duration lineDuration = info.minLineLength * 1.0s / info.pixelRate; context_.configuration.sensor.lineDuration = lineDuration; context_.configuration.sensor.minShutterSpeed = minExposure * lineDuration; - context_.configuration.sensor.maxShutterSpeed = maxExposure * lineDuration; context_.configuration.sensor.minFrameDuration = frameHeights[0] * lineDuration; context_.configuration.sensor.maxFrameDuration = frameHeights[1] * lineDuration; context_.configuration.sensor.minAnalogueGain = context_.camHelper->gain(minGain); diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index b9a94ba03c910f73420579dd6737d8d46b26e576..a2ecd5c46a1fbcb728e23ed83b37b89fcdb80d84 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -203,8 +203,6 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) AgcMeanLuminance::SensorConfiguration sensorConfig; sensorConfig.lineDuration = context.configuration.sensor.lineDuration; sensorConfig.minExposureTime = context.configuration.sensor.minExposureTime; - sensorConfig.maxExposureTime = context.configuration.sensor.maxExposureTime; - sensorConfig.minFrameDuration = context.configuration.sensor.minFrameDuration; sensorConfig.maxFrameDuration = context.configuration.sensor.maxFrameDuration; sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain; sensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp index 15cb0afe9fe8d266d645a27cc3a3e440a0dd2413..40c5d244cf524e9ca3455183ee02237892e70312 100644 --- a/src/ipa/rkisp1/ipa_context.cpp +++ b/src/ipa/rkisp1/ipa_context.cpp @@ -89,9 +89,6 @@ namespace libcamera::ipa::rkisp1 { * \var IPASessionConfiguration::sensor.minExposureTime * \brief Minimum exposure time supported with the sensor * - * \var IPASessionConfiguration::sensor.maxExposureTime - * \brief Maximum exposure time supported with the sensor - * * \var IPASessionConfiguration::sensor.minAnalogueGain * \brief Minimum analogue gain supported with the sensor * diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index 5fe727bd0b508617d993d226ae785056a3771ce0..52764dbc0a8f579332f0785baf86ba7a1f6db9e2 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -61,7 +61,6 @@ struct IPASessionConfiguration { struct { utils::Duration minExposureTime; - utils::Duration maxExposureTime; utils::Duration minFrameDuration; utils::Duration maxFrameDuration; double minAnalogueGain; diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index f25e477f0fb77241bd1ccddb7778205e58bdc8a9..4da7cf36400df897ef4392fb264b1f6401391ad0 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -269,7 +269,6 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, * on the sensor, update it with the controls. */ context_.configuration.sensor.minExposureTime = minExposure * lineDuration; - context_.configuration.sensor.maxExposureTime = maxExposure * lineDuration; context_.configuration.sensor.minFrameDuration = frameHeights[0] * lineDuration; context_.configuration.sensor.maxFrameDuration = frameHeights[1] * lineDuration; context_.configuration.sensor.minAnalogueGain = context_.camHelper->gain(minGain); From patchwork Fri Nov 14 14:17:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25069 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 A2BC6C3336 for ; Fri, 14 Nov 2025 14:17:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 433F560AC8; Fri, 14 Nov 2025 15:17:43 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="SdNmKW1p"; 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 6224560AB5 for ; Fri, 14 Nov 2025 15:17:24 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 68DC8BD2; Fri, 14 Nov 2025 15:15:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129723; bh=sHkzxU7P8tUG9VmY0HfmH5BmzTEMZ9oHFezgSarUqJI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=SdNmKW1pDylCoL0h6JuwhF3SXhf8tdAdm2LPuT3Z6DtxdofOYQAK9rC/B1G9KKC6T ER6pj13OjHZ+htZK06RP8+uSGJ/rVclsoNgDlVvSnLsvsPJ/omO9+zlkb6Vp9Cb9/U 0lvkSkfCcyC9XWLp48cWcu2ZLzryoIV/YVtRxadc= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:12 +0100 Subject: [PATCH v3 17/19] ipa: rkisp1: agc: Calculate frame duration using cam helper MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-17-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=2539; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=sHkzxU7P8tUG9VmY0HfmH5BmzTEMZ9oHFezgSarUqJI=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznsiUYfYot8Z/IbPRgQ66+o83qnmtLGgQMJT gjc+4413raJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc57AAKCRByNAaPFqFW PD6HD/wLbfr+0btKgzQTTQCR4LsIHKt+sX2fcxZCEFGbm7jSIWr1FESnkKazK6kU3IjZS3OqehI DEGC9YukE7Ilz2EcneT6nD5ZWyah6YxJgU3AK3Sm5J/UYHU1H7HxXEhXMbHQ5NMSBJDThOKg73j QXA/6M64p/inq0msJOU5L/c7tLhl8K6ozTHlHXN8m4ACA42bPJuhXhy/M7z+NmnG5Kl4FnrphEd bHPLISHpAnD+KtPz+wcjHizI3bKSDtfdMfSDBkHw8c/+ytzR+qPfstQG5S4OtfyT3VM8geueTnc W37tRtQ3EZzlRkN5j28mHeWtCM9ojS2xx97ojrcMfwoxIwvla/njL2zKifi6deVXjwcNkA2TmMC 2uQcta+egpm3AhclhsZ1cxii9etvmOn6XgMqDyHVY/23etENyZLeQnxbQAG0Nb6ALJvHxQ2Me1c aV9u8GJyml8mPfp69hY6iotSPwfn1PGjPMpnTds6U/zliBXgsZoHfcBBpgx3w/WFijTkNQbNtNR 9YkmhOGt3o7esTlREyPN/Y3AnSnjEacC5VQ5wej2TuFLZtpBsXnWsAyGu4YjjceJmU2SvrUyIyM xsuS01jBNIU/FAEsktxmOxqWYPQdnBMH0YzbDVE+ABaWDcaH+flap6EB7fNafsWil+Ex3CZu55+ tW08UisHZVE+gGw== 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" The RkISP1 IPA is the only IPA that derives from AgcMeanLuminance that populates the FrameDuration control. The way frame duration is calculated is by using the shutter time as returned by the Agc helpers and use that value to populate the FrameDuration metadata and calculate the vblank. This is however not correct as the exposure time shall be shorter than the frame duration by a sensor-specific margin. Use the camera sensor helper to calculate the minimum frame duration required to achieve the newly calculated shutter time. Signed-off-by: Jacopo Mondi --- src/ipa/rkisp1/algorithms/agc.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index a2ecd5c46a1fbcb728e23ed83b37b89fcdb80d84..10498eb6357d8917e299ac24f6c8ba8c33af4eae 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -501,21 +501,26 @@ double Agc::estimateLuminance(double gain) const * \brief Process frame duration and compute vblank * \param[in] context The shared IPA context * \param[in] frameContext The current frame context - * \param[in] frameDuration The target frame duration + * \param[in] shutterTime The target shutter duration * - * Compute and populate vblank from the target frame duration. + * Compute and populate vblank from a frame duration that allows to achieve the + * desired \a shutterTime */ void Agc::processFrameDuration(IPAContext &context, IPAFrameContext &frameContext, - utils::Duration frameDuration) + utils::Duration shutterTime) { - IPACameraSensorInfo &sensorInfo = context.sensorInfo; utils::Duration lineDuration = context.configuration.sensor.lineDuration; + utils::Duration frameDuration = + std::max(context.camHelper->minFrameDuration(shutterTime, lineDuration), + context.activeState.agc.minFrameDuration); - frameContext.agc.vblank = (frameDuration / lineDuration) - sensorInfo.outputSize.height; + frameContext.agc.vblank = (frameDuration / lineDuration) + - context.sensorInfo.outputSize.height; /* Update frame duration accounting for line length quantization. */ - frameContext.agc.frameDuration = (sensorInfo.outputSize.height + frameContext.agc.vblank) * lineDuration; + frameContext.agc.frameDuration = (context.sensorInfo.outputSize.height + + frameContext.agc.vblank) * lineDuration; } /** From patchwork Fri Nov 14 14:17:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25070 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 63296C3337 for ; Fri, 14 Nov 2025 14:17:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E7DC960ABA; Fri, 14 Nov 2025 15:17:43 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="wbyvHsUX"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CB54560ABA for ; Fri, 14 Nov 2025 15:17:24 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C86591F37; Fri, 14 Nov 2025 15:15:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129724; bh=h8FMFv/BsfC3u4TGP5N6VoUOd9JL4a85sWM8Gy+/rjs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=wbyvHsUXGRpC4by0p+TJItgaJNSBWadvRGvFjwS983PobJMRr8XZ1Vglu6gPczygo e3vw6LZxIGDiT+0GTLxzb03GJ96y6gPZtmk1B/5BObpbtAnBS9SxXNOJLeSZ/mB2T6 ChUY8P7pJQ6S2UbA2hrNzdUrSAuhGiVZtoae38LU= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:13 +0100 Subject: [PATCH v3 18/19] ipa: libipa: agc: Initialize a sensible frame duration MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-18-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=5199; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=h8FMFv/BsfC3u4TGP5N6VoUOd9JL4a85sWM8Gy+/rjs=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznsEddLKFNFfl9vWvFoI6CAFOdWVGdzpegVy lRSLD1/pk6JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc57AAKCRByNAaPFqFW PDQ5D/4zVHEE1O/zQgHXy1zZdxs5e8011aT+U9w7aA1wPL26LdnwwWrUhEkjuLx5HIfvrJLegL/ +RKsMCe2kQKSFQGqqGfdY8zucBo0q9K+FVb08MzMO0yx/ES/xpGoMsIiVLJwpjkY9GFEfLPOLZF lS/IP0DByUBNOy8XrgTUuyfDGMWUgXW3km+FfB0h+8yy1O8lRO213M7A/NksgMCNPLj++N3np73 N9RjJZQ50rrJ7wMbxkrznAtPFUtZzw/kj+tbI2TBJMZQAp2XHVUHFhXLX8bo+wvsn/CwWniM3+b lrER80u9PaDctSzp3P9fJzC+/ms5aNok5ra+URAAiZw0xLbpw1EPTJfuMzEIJOdcxzSH8eJeOP0 QxK27H+owccPJ7Wu5zFK/ZARERWcgNMfOjb50oXr7qREhyXvL/v0QnHGiqO9jO+gL1A4fFAuoI1 4iBsV0Z2kZX0MdrzCdleRceVccwYTQwiFtE07/I8yyRmmGPbrAKm/Q7UgtH26npO5fefdhG3hx4 8ofLGjvj50ks6oCEqxQW536tchm61wDySFHKEyx6eddJqEQAl5EWa1kvk7Bw3msggibnxizBvw3 o2lELUjWUYD9ZhPdzVV1PvaR3cL0ESPHXC4lpoe6jVf+KXDVoJabM9anEu8uoK3X7wx8UZgKpnk pjM4QDEIH1F33vg== 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" The AGC algorithm implemented by AgcMeanLuminance splits the desired exposure between shutter time, analogue and digital gains. It tries to maximize the shutter time up to the specified limits and then increases the gains. When initializing the algorithm at configure() time the IPA modules have access to the Camera's absolute limits, which might include a very long maximum frame duration. If such duration is used to clamp the maximum exposure, the algorithm will maximize the shutter time, resulting in a very low frame rate. Try to pick a slighlty saner default of 30, 15 or 10 fps to limit the maximum shutter time with to provide a reasonable out-of-the-box behaviour for users. The new frame duration limit calculated by the AGC algorithm is passed back to the IPA module that shall use it to compute the right blankings to configure the camera with. At the moment, only the RkISP1 IPA actually controls the frame duration, and so it is the only IPA that needs to use the frame duration as computed by the AGC algorithm. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/agc_mean_luminance.cpp | 27 ++++++++++++++++++++++++--- src/ipa/libipa/agc_mean_luminance.h | 4 ++-- src/ipa/rkisp1/algorithms/agc.cpp | 3 ++- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index b930e1f7240d4936aa8dc850657bbbf9c2f3a11f..f6768958c0d3d76108561b9455a15c715a89048e 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -353,10 +353,29 @@ int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData) * This function configures the exposure mode helpers by providing them the * sensor configuration parameters and the sensor helper, so they can correctly * take quantization effects into account. + * + * It computes a reasonable frame duration in the sensor's limits and returns it + * to the IPA for configuring the sensor's blankings. + * */ -void AgcMeanLuminance::configure(const SensorConfiguration &config, - const CameraSensorHelper *sensorHelper) +utils::Duration AgcMeanLuminance::configure(const SensorConfiguration &config, + const CameraSensorHelper *sensorHelper) { + + utils::Duration minFrameDuration = config.minFrameDuration; + utils::Duration maxFrameDuration = config.maxFrameDuration; + + static constexpr utils::Duration duration30fps = 33333 * 1us; + static constexpr utils::Duration duration15fps = 66666 * 1us; + static constexpr utils::Duration duration10fps = 100000 * 1us; + utils::Duration frameDuration = minFrameDuration < duration30fps + ? duration30fps + : minFrameDuration < duration15fps + ? duration15fps + : minFrameDuration < duration10fps + ? duration10fps : minFrameDuration; + frameDuration = std::min(frameDuration, maxFrameDuration); + for (auto &[id, helper] : exposureModeHelpers_) { /* * Translate from the SensorConfiguration to the @@ -370,7 +389,7 @@ void AgcMeanLuminance::configure(const SensorConfiguration &config, sensorConfig.lineDuration_ = config.lineDuration; sensorConfig.minExposureTime_ = config.minExposureTime; sensorConfig.minFrameDuration_ = config.minFrameDuration; - sensorConfig.maxFrameDuration_ = config.maxFrameDuration; + sensorConfig.maxFrameDuration_ = frameDuration; sensorConfig.minGain_ = config.minAnalogueGain; sensorConfig.maxGain_ = config.maxAnalogueGain; @@ -378,6 +397,8 @@ void AgcMeanLuminance::configure(const SensorConfiguration &config, } resetFrameCount(); + + return frameDuration; } /** diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 93a0959bbd9e0d6ec42248f2d3b19253ad389ae6..d66da7671c313e5acb959018b08a82335b7170b8 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -51,8 +51,8 @@ public: double maxAnalogueGain; }; - void configure(const SensorConfiguration &config, - const CameraSensorHelper *sensorHelper); + utils::Duration configure(const SensorConfiguration &config, + const CameraSensorHelper *sensorHelper); int parseTuningData(const YamlObject &tuningData); void setExposureCompensation(double gain) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 10498eb6357d8917e299ac24f6c8ba8c33af4eae..eac193b9aadfb17b7b6a02dcff2f3aa0b56dff84 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -207,7 +207,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain; sensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; - AgcMeanLuminance::configure(sensorConfig, context.camHelper.get()); + context.activeState.agc.maxFrameDuration = + AgcMeanLuminance::configure(sensorConfig, context.camHelper.get()); context.activeState.agc.automatic.yTarget = effectiveYTarget(); From patchwork Fri Nov 14 14:17:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25071 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 EA5FEC3317 for ; Fri, 14 Nov 2025 14:17:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8DB6460B36; Fri, 14 Nov 2025 15:17:44 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="geZeZKZi"; 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 3093760AAE for ; Fri, 14 Nov 2025 15:17:25 +0100 (CET) Received: from [192.168.1.101] (93-61-96-190.ip145.fastwebnet.it [93.61.96.190]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3575A1440; Fri, 14 Nov 2025 15:15:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1763129724; bh=/ayLsgiUn+4RgUbAlkJSWkzwC+YENlcz0ZqLsTLxqu4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=geZeZKZiY+SQQZoALOsLVSnc7mr5ms4NsoV7rlj+NlvwYjUuEFoCRRQ0BLkHGo33L /PCzOTaVFJo085mTiqlp4deUC3j4ad8x8OilrbM9sIUZgziA7Cv4Nn98LU1189buTm vWxS2iwMZq/Kt3aWnPBaUfmMiuWQy92WV9FrglxI= From: Jacopo Mondi Date: Fri, 14 Nov 2025 15:17:14 +0100 Subject: [PATCH DNI v3 19/19] ipa: mali: Handle FrameDurationLimits MIME-Version: 1.0 Message-Id: <20251114-exposure-limits-v3-19-b7c07feba026@ideasonboard.com> References: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> In-Reply-To: <20251114-exposure-limits-v3-0-b7c07feba026@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , Robert Mader , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=7237; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=/ayLsgiUn+4RgUbAlkJSWkzwC+YENlcz0ZqLsTLxqu4=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpFznsDwh97j/J5MXWYFD/iURRijUefYb1vT/eg wZcoeHI5XqJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaRc57AAKCRByNAaPFqFW PPhYEACSKOSjRjHC1zpDf2MjDix7YcN1VqwI9DiI248OEcubTmOegrvdtza6j5MkdtsiG9R2RSc RDFWqwLo2NUdfVVuhj7Tjff0uWo/McgXXXKSyDGaaZSv0BAFGnGsfyeA0b/HXip/jBbPpxAYPps sBhuZMJOMAqLc8panIiNnPdSeLf0SOknxIJydtWM1wHRpCKMfWSzuE1WFYfUwDTRsqaI79YL6o5 D0ee9nZMe3nlVpWZQ8tmWSuZ4iyhEk7zHN01vWjrlRbIniQKAkWPssMC6/UY2hGPmUEZXVLA0hU ZUhEiU47wyhqRb15Fd6ngFRegWla3XPOb9N8I+SUQUpVoprZErbO2BaVIgbqb7mpXOZulWPMRQ4 aTZL/MALSM2MZaeuf/QJlnI4l6AmfgNyq8heJQ9k/AUmUVn48YAty4P7A/jqd2XvDLjRqSEFQ8k 79guYYnIdG7AzjCmTSX1kXQKMKtIK2vYBJ4twQvHR8DmEZWjcYXW7eKxpcjEJUMDc0x/giQtVhd BuduXeRjQ4ga/xLKyjvEOEUkNjZd7uR02XYCKcckP9Y7MIb6LRrebAames0EpDTBKWpP3BHGwO4 f6pxhAWBmUYwOWkDR0kvSjQjxWRqRmfbLs8MuAlIkCs80rai7tfDyOhT25/PH2EmRQfsz/ygwND BymxDwmr6JlFo4g== 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" Handle the FrameDurationLimits control for the Mali C55 platform. The frame duration is: 1) Adjuted at configure() time to what the AGC algorithm computed 2) Stored at queueRequest() time in the active state and in the per-frame context 3) Computed at process() time by the AGC calculateNewEV() function and used to compute the desired vblank The VBLANK control is now handled by both the IPA and the pipeline handler and correctly programmed on the sensor. Signed-off-by: Jacopo Mondi --- src/ipa/mali-c55/algorithms/agc.cpp | 50 +++++++++++++++++++++++++++++++++---- src/ipa/mali-c55/ipa_context.h | 6 +++++ src/ipa/mali-c55/mali-c55.cpp | 10 +++++--- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index a0b55694aad292f8a080d8266470797ac0cc2c25..92a58417f9cdf11aa1cd7ec7167bf4818060c85e 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -181,9 +181,10 @@ int Agc::configure(IPAContext &context, sensorConfig.minAnalogueGain = context.configuration.sensor.minAnalogueGain; sensorConfig.maxAnalogueGain = context.configuration.sensor.maxAnalogueGain; - AgcMeanLuminance::configure(sensorConfig, context.camHelper.get()); - - /* \todo Update AGC limits when FrameDurationLimits is passed in */ + context.activeState.agc.maxFrameDuration = + AgcMeanLuminance::configure(sensorConfig, context.camHelper.get()); + context.activeState.agc.minFrameDuration = + context.configuration.sensor.minFrameDuration; return 0; } @@ -209,6 +210,27 @@ void Agc::queueRequest(IPAContext &context, const uint32_t frame, << " AGC"; } + const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits); + if (frameDurationLimits) { + /* Limit the control value to the sensor constraints. */ + int64_t sensorMinFrameDuration = + context.configuration.sensor.minFrameDuration.get(); + int64_t sensorMaxFrameDuration = + context.configuration.sensor.maxFrameDuration.get(); + + int64_t minFrameDuration = + std::clamp((*frameDurationLimits).front(), + sensorMinFrameDuration, sensorMaxFrameDuration); + int64_t maxFrameDuration = + std::clamp((*frameDurationLimits).back(), + sensorMinFrameDuration, sensorMaxFrameDuration); + + agc.minFrameDuration = std::chrono::microseconds(minFrameDuration); + agc.maxFrameDuration = std::chrono::microseconds(maxFrameDuration); + } + frameContext.agc.minFrameDuration = agc.minFrameDuration; + frameContext.agc.maxFrameDuration = agc.maxFrameDuration; + /* * If the automatic exposure and gain is enabled we have no further work * to do here... @@ -372,6 +394,14 @@ void Agc::process(IPAContext &context, return; } + /* + * Update the AGC limits using the frame duration. + * + * \todo Handle ExposureTime and AnalogueGain controls to support + * manual mode. + */ + setExposureLimits({}, {}, frameContext.agc.maxFrameDuration, {}); + statistics_.parseStatistics(stats); context.activeState.agc.temperatureK = estimateCCT({ { statistics_.rHist.interQuantileMean(0, 1), statistics_.gHist.interQuantileMean(0, 1), @@ -401,11 +431,21 @@ void Agc::process(IPAContext &context, << "Divided up shutter, analogue gain and digital gain are " << shutterTime << ", " << aGain << " and " << dGain; - activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration; + /* Use the frame duration to calculate the desired vblank. */ + utils::Duration lineDuration = configuration.sensor.lineDuration; + utils::Duration frameDuration = + context.camHelper->minFrameDuration(shutterTime, lineDuration); + + frameContext.agc.vblank = (frameDuration / lineDuration) + - context.sensorInfo.outputSize.height; + + /* Populate the active state. */ + activeState.agc.automatic.exposure = shutterTime / lineDuration; activeState.agc.automatic.sensorGain = aGain; activeState.agc.automatic.ispGain = dGain; - metadata.set(controls::ExposureTime, currentShutter.get()); + metadata.set(controls::FrameDuration, frameDuration.get()); + metadata.set(controls::ExposureTime, shutterTime.get()); metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain); metadata.set(controls::DigitalGain, frameContext.agc.ispGain); 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 828103f21451d9f7f4998c3faedc8fb6a1e7a2ec..4b76ac25ec4a2e1d2e07642148547303cf4c6031 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -10,6 +10,8 @@ #include #include +#include + #include "libcamera/internal/bayer_format.h" #include @@ -67,6 +69,9 @@ struct IPAFrameContext : public FrameContext { uint32_t exposure; double sensorGain; double ispGain; + uint32_t vblank; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; } agc; struct { @@ -81,6 +86,7 @@ struct IPAContext { { } + IPACameraSensorInfo sensorInfo; IPASessionConfiguration configuration; IPAActiveState activeState; diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp index 02f5dfb76eae073858ec688746b7e12ec072e567..60b5ee8d3060e9f3a4794550fe4140d58125a925 100644 --- a/src/ipa/mali-c55/mali-c55.cpp +++ b/src/ipa/mali-c55/mali-c55.cpp @@ -68,7 +68,7 @@ private: void updateControls(const IPACameraSensorInfo &sensorInfo, const ControlInfoMap &sensorControls, ControlInfoMap *ipaControls); - void setControls(); + void setControls(unsigned int frame); std::map buffers_; @@ -126,14 +126,17 @@ int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig if (ret) return ret; + context_.sensorInfo = ipaConfig.sensorInfo; updateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls); return 0; } -void IPAMaliC55::setControls() +void IPAMaliC55::setControls(unsigned int frame) { + IPAFrameContext &frameContext = context_.frameContexts.get(frame); IPAActiveState &activeState = context_.activeState; + uint32_t vblank = frameContext.agc.vblank; uint32_t exposure; uint32_t gain; @@ -148,6 +151,7 @@ void IPAMaliC55::setControls() ControlList ctrls(sensorControls_); ctrls.set(V4L2_CID_EXPOSURE, static_cast(exposure)); ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast(gain)); + ctrls.set(V4L2_CID_VBLANK, static_cast(vblank)); setSensorControls.emit(ctrls); } @@ -375,7 +379,7 @@ void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId, algo->process(context_, request, frameContext, stats, metadata); } - setControls(); + setControls(request); statsProcessed.emit(request, metadata); }