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); }