From patchwork Fri Oct 17 09:00: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: 24695 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 18A45C32CE for ; Fri, 17 Oct 2025 09:00:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B1202606A4; Fri, 17 Oct 2025 11:00:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="PFzaoL3/"; 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 3E0D1606A3 for ; Fri, 17 Oct 2025 11:00:16 +0200 (CEST) Received: from [192.168.1.182] (93-46-82-201.ip106.fastwebnet.it [93.46.82.201]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C59041E2F; Fri, 17 Oct 2025 10:58:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1760691515; bh=EJcNXPRfts0MnODBTGcYhq7MQ3EqFRL97hh4t6jRWnM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=PFzaoL3/zi0p2bxAkohLny68KdstOr5PWYbrJiC1oBAUZcGjj5zYHDFhJ4igDN8hC 85RmwbQ9rlUQc04baNxuowzPlt3582TakoNNcpaPTQqzyEqjQmbCbnmvyr5btm/7TZ /pBOUM3tQevzCt9O+bE5HVauhk/PuFXS/HLi6Ifw= From: Jacopo Mondi Date: Fri, 17 Oct 2025 11:00:06 +0200 Subject: [PATCH 2/3] libcamera: rkisp1: Update camera::controls() on limit changes MIME-Version: 1.0 Message-Id: <20251017-exposure-limits-v1-2-6288cd86e719@ideasonboard.com> References: <20251017-exposure-limits-v1-0-6288cd86e719@ideasonboard.com> In-Reply-To: <20251017-exposure-limits-v1-0-6288cd86e719@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=6379; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=EJcNXPRfts0MnODBTGcYhq7MQ3EqFRL97hh4t6jRWnM=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBo8gWfgDagLSWyzlIHi517Pg/KU/PtkrmBsS3CN i5qqDwVaCqJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaPIFnwAKCRByNAaPFqFW POHBD/9W5DRzUnEt6lR4BNjCUgXKIfgmT9yvt6nfsl6tzUlBIofk2ERChqGayQdIPV4eEGl7BNj PdxKOxjro8LB5D4VEpWA8ret4j1nuk4mgsc7o/4awHsikkG3f8YYxcE9+0nbBEhHvBLF2rk/ZAk RGdQ3fQxUphesTC9aI279wqO95jjzcAft5W+r3yZ5VcfdI0ObsaqoI2+Tc5U+rUcsYGeQqYJsZQ f5MTvWlhfniwvSJE3tRPX+TIPOWbvsNDLDHPcNyruehbTY0Kzdmo34cLO1Ots/T4nP9BFwWr+E2 FCBm+cLKfqZJ9BnweeidSDNrsrZVZeQ1infDAEtn/9PBe1d9RSKgVnfiq0CwhNSB//Xt00xbPfP 4SJ1YthLcn/R5e9WejMeel8qPpfNItl9Wt6G9zqoE/hUg9lB8rANX8pgwcmU04f+KL6vvD/QQED G9Hjbv0IoFU1qwneJCDQdfiY8Gib4VjXPr47ReIojjnkyeB5meqOXZJK8KDjGcEG1DpJRpANrIr hLJnGD9bJYv/P3HRge7IY58lUnPQ8E00lT9a59iw6g/MldDsucSfDQ0Ixt6jix0cga1aTAc9yCN xcI2gvH9f5uuv3jztG3iNqDpopIev2t7Npaxzvxtwht9gbGyNV/4SQDwmQhCbftTpNmF6mVuhCr qCYEAlOmKSyvRCQ== 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 limits (min and max values) for the Camera controls are registered at Camera configuration time and never updated. Some controls, like FrameDurationLimits have a direct impact on the limits of other controls, such as the ExposureTime and as they can be configured by the application, this has to be taken into account. Currently, when a user changes the frame duration, the limits for both the ExposureTime and FrameDurationControls are not updated. The timing at which the controls limits should be updated is also critical: the new control values take effect once the Request they belong to is completed. To support this operation model introduce a new IPA function 'updateControlsLimits()' which the pipeline handler calls before completing a Request. Store the exposure time limits in the FrameContext (frame duration limits were already there) and update the Camera::controls() control info map with the limits as computed for the Request that has just completed. Signed-off-by: Jacopo Mondi --- include/libcamera/ipa/rkisp1.mojom | 2 ++ src/ipa/rkisp1/algorithms/agc.cpp | 3 +++ src/ipa/rkisp1/ipa_context.h | 3 +++ src/ipa/rkisp1/rkisp1.cpp | 26 ++++++++++++++++++++++++++ src/libcamera/pipeline/rkisp1/rkisp1.cpp | 3 +++ 5 files changed, 37 insertions(+) diff --git a/include/libcamera/ipa/rkisp1.mojom b/include/libcamera/ipa/rkisp1.mojom index 068e898848c4943282b4a6a05362a99016560afd..8d9446f5cbe9851886832f5cf7fd41d1a6d23a11 100644 --- a/include/libcamera/ipa/rkisp1.mojom +++ b/include/libcamera/ipa/rkisp1.mojom @@ -27,6 +27,8 @@ interface IPARkISP1Interface { map streamConfig) => (int32 ret, libcamera.ControlInfoMap ipaControls); + updateControlsLimits(uint32 frame) => (int32 ret, libcamera.ControlInfoMap ipaControls); + mapBuffers(array buffers); unmapBuffers(array ids); diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index f5a3c917cb6909f6ef918e5ee8e46cf97ba55010..0fd7541c9f1ab9b4cbae1f8fa60c39b032c3bcd1 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -585,6 +585,8 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, * frameContext.agc.exposure; maxExposureTime = minExposureTime; } + frameContext.agc.minExposureTime = minExposureTime; + frameContext.agc.maxExposureTime = maxExposureTime; if (frameContext.agc.autoGainEnabled) { minAnalogueGain = context.configuration.sensor.minAnalogueGain; @@ -606,6 +608,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, * applied to the sensor when the statistics were collected. */ utils::Duration exposureTime = lineDuration * frameContext.sensor.exposure; + frameContext.agc.exposureTime = exposureTime; double analogueGain = frameContext.sensor.gain; utils::Duration effectiveExposureValue = exposureTime * analogueGain; diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index af66a749052bc82bbbe7fbb0c4626f2422700926..54ff5c114f5c6cdfaf515475a3892f76e2e022d6 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -143,6 +143,9 @@ struct IPAActiveState { struct IPAFrameContext : public FrameContext { struct { + utils::Duration minExposureTime; + utils::Duration maxExposureTime; + utils::Duration exposureTime; uint32_t exposure; double gain; double exposureValue; diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 54bd1434e0f4e34834beb1f9e9c39b77590f8b34..ad5c24a7159a8634aae883915b54ae0f4456692b 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -62,6 +62,8 @@ public: int configure(const IPAConfigInfo &ipaConfig, const std::map &streamConfig, ControlInfoMap *ipaControls) override; + int updateControlsLimits(const uint32_t frame, + ControlInfoMap *ipaControls) override; void mapBuffers(const std::vector &buffers) override; void unmapBuffers(const std::vector &ids) override; @@ -294,6 +296,30 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, return 0; } +int IPARkISP1::updateControlsLimits(const uint32_t frame, ControlInfoMap *ipaControls) +{ + IPAFrameContext &frameContext = context_.frameContexts.get(frame); + + /* + * Update the exposure time and frame duration limits with the + * settings computed by the AGC for the frame at hand. + */ + assert(ipaControls->find(&controls::ExposureTime) != ipaControls->end()); + assert(ipaControls->find(&controls::FrameDurationLimits) != ipaControls->end()); + + ControlValue eMin(static_cast(frameContext.agc.minExposureTime.get())); + ControlValue eMax(static_cast(frameContext.agc.maxExposureTime.get())); + ControlValue eDef(static_cast(frameContext.agc.exposureTime.get())); + ipaControls->at(controls::ExposureTime.id()) = ControlInfo(eMin, eMax, eDef); + + ControlValue fMin(static_cast(frameContext.agc.minFrameDuration.get())); + ControlValue fMax(static_cast(frameContext.agc.maxFrameDuration.get())); + ControlValue fDef(static_cast(frameContext.agc.frameDuration.get())); + ipaControls->at(controls::FrameDurationLimits.id()) = ControlInfo(fMin, fMax, fDef); + + return 0; +} + void IPARkISP1::mapBuffers(const std::vector &buffers) { for (const IPABuffer &buffer : buffers) { diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index ecd13831539fdf5cb79da2ea4b33a353514328ae..ad1ff7a8be8d6e3268e21ec92ae4e07500b38c66 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -1494,6 +1494,9 @@ void PipelineHandlerRkISP1::tryCompleteRequest(RkISP1FrameInfo *info) if (!isRaw_ && !info->paramDequeued) return; + /* Update controls before completing the request */ + data->ipa_->updateControlsLimits(info->frame, &data->controlInfo_); + data->frameInfo_.destroy(info->frame); completeRequest(request);