From patchwork Fri Jul 3 15:38:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 27187 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 5BB13C3303 for ; Fri, 3 Jul 2026 15:38:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0BEC66600B; Fri, 3 Jul 2026 17:38:35 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="WcZeFPHX"; 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 82C7765FD8 for ; Fri, 3 Jul 2026 17:38:24 +0200 (CEST) Received: from pb-laptop.local (185.221.140.128.nat.pool.zt.hu [185.221.140.128]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6F3E51121 for ; Fri, 3 Jul 2026 17:37:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783093058; bh=nxvZ/KS10UA5ATQQMLxrZN3L87OapVP3zburJqk2ZhI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=WcZeFPHX444nK5nIMdmpb8CmNLiuyVKrFPnQwxnA6XNcx4W/V8SZ5cmYNed3OqC93 GkHE1THaHnnsoi91DivEvICHiUBJUsuaRO312a9AMILwh50YyLB5Jti4rO3KW0gVk0 M60igCaFL5fild8LchTcSzCX0PxD2ZAlHcn6h1y4= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v1 08/17] ipa: rkisp1: Move AGC related controls into AGC algorithm Date: Fri, 3 Jul 2026 17:38:10 +0200 Message-ID: <20260703153819.1088752-9-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260703153819.1088752-1-barnabas.pocze@ideasonboard.com> References: <20260703153819.1088752-1-barnabas.pocze@ideasonboard.com> MIME-Version: 1.0 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 `ControlInfo` setup and related initialization into the AGC algorithm instead of having it in the main IPA file. Signed-off-by: Barnabás Pőcze --- src/ipa/rkisp1/algorithms/agc.cpp | 91 ++++++++++++++++++++++++++++++- src/ipa/rkisp1/rkisp1.cpp | 88 +----------------------------- 2 files changed, 91 insertions(+), 88 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 540b64ff99..689d045b7a 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -33,13 +33,96 @@ using namespace std::literals::chrono_literals; namespace ipa::rkisp1::algorithms { +LOG_DEFINE_CATEGORY(RkISP1Agc) + +namespace { + +void reconfigure(IPAContext &context) +{ + context.configuration.sensor.lineDuration = + context.sensorInfo.minLineLength * 1.0s / context.sensorInfo.pixelRate; + + double lineDurationUs = context.configuration.sensor.lineDuration.get(); + + /* + * Compute exposure time limits from the V4L2_CID_EXPOSURE control + * limits and the line duration. + */ + + const ControlInfo &v4l2Exposure = context.sensorControls.find(V4L2_CID_EXPOSURE)->second; + int32_t minExposure = v4l2Exposure.min().get(); + int32_t maxExposure = v4l2Exposure.max().get(); + int32_t defExposure = v4l2Exposure.def().get(); + context.ctrlMap[&controls::ExposureTime] = ControlInfo{ + static_cast(minExposure * lineDurationUs), + static_cast(maxExposure * lineDurationUs), + static_cast(defExposure * lineDurationUs), + }; + + /* Compute the analogue gain limits. */ + const ControlInfo &v4l2Gain = context.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; + float minGain = context.camHelper->gain(v4l2Gain.min().get()); + float maxGain = context.camHelper->gain(v4l2Gain.max().get()); + float defGain = context.camHelper->gain(v4l2Gain.def().get()); + context.ctrlMap[&controls::AnalogueGain] = ControlInfo{ + minGain, + maxGain, + defGain, + }; + + LOG(RkISP1Agc, Debug) + << "Exposure: [" << minExposure << ", " << maxExposure + << "], gain: [" << minGain << ", " << maxGain << "]"; + + /* + * Compute the frame duration limits. + * + * The frame length is computed assuming a fixed line length combined + * with the vertical frame sizes. + */ + const ControlInfo &v4l2HBlank = context.sensorControls.find(V4L2_CID_HBLANK)->second; + uint32_t hblank = v4l2HBlank.def().get(); + uint32_t lineLength = context.sensorInfo.outputSize.width + hblank; + + const ControlInfo &v4l2VBlank = context.sensorControls.find(V4L2_CID_VBLANK)->second; + std::array frameHeights{ + v4l2VBlank.min().get() + context.sensorInfo.outputSize.height, + v4l2VBlank.max().get() + context.sensorInfo.outputSize.height, + v4l2VBlank.def().get() + context.sensorInfo.outputSize.height, + }; + + std::array frameDurations; + for (unsigned int i = 0; i < frameHeights.size(); ++i) { + uint64_t frameSize = lineLength * frameHeights[i]; + frameDurations[i] = frameSize / (context.sensorInfo.pixelRate / 1000000U); + } + + context.ctrlMap[&controls::FrameDurationLimits] = ControlInfo{ + frameDurations[0], + frameDurations[1], + Span{ { frameDurations[2], frameDurations[2] } }, + }; + + /* + * 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); +} + +} /* namespace */ + /** * \class Agc * \brief A mean-based auto-exposure algorithm */ -LOG_DEFINE_CATEGORY(RkISP1Agc) - int Agc::parseMeteringModes(IPAContext &context, const ValueNode &tuningData) { if (!tuningData.isDictionary()) @@ -160,6 +243,8 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData) context.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f); context.ctrlMap.merge(agc_.controls()); + reconfigure(context); + return 0; } @@ -172,6 +257,8 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData) */ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) { + reconfigure(context); + /* Configure the default exposure and gain. */ context.activeState.agc.automatic.gain = context.configuration.sensor.minAnalogueGain; context.activeState.agc.automatic.exposure = diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 66a91c4d79..38f55b1d86 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -169,9 +169,6 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision, return -ENODEV; } - context_.configuration.sensor.lineDuration = - sensorInfo.minLineLength * 1.0s / sensorInfo.pixelRate; - /* Load the tuning data file. */ File file(settings.configurationFile); if (!file.open(File::OpenModeFlag::ReadOnly)) { @@ -227,18 +224,6 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, context_.sensorInfo = ipaConfig.sensorInfo; context_.sensorControls = ipaConfig.sensorControls; - const auto itExp = context_.sensorControls.find(V4L2_CID_EXPOSURE); - int32_t minExposure = itExp->second.min().get(); - int32_t maxExposure = itExp->second.max().get(); - - const auto itGain = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN); - int32_t minGain = itGain->second.min().get(); - int32_t maxGain = itGain->second.max().get(); - - LOG(IPARkISP1, Debug) - << "Exposure: [" << minExposure << ", " << maxExposure - << "], gain: [" << minGain << ", " << maxGain << "]"; - /* Clear the IPA context before the streaming session. */ context_.configuration = {}; context_.activeState = {}; @@ -247,27 +232,6 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, context_.configuration.paramFormat = ipaConfig.paramFormat; context_.configuration.sensor.size = context_.sensorInfo.outputSize; - context_.configuration.sensor.lineDuration = context_.sensorInfo.minLineLength * 1.0s - / context_.sensorInfo.pixelRate; - - /* Update the camera controls using the new sensor settings. */ - updateControls(ipaControls); - - /* - * 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.raw = std::any_of(streamConfig.begin(), streamConfig.end(), [](auto &cfg) -> bool { @@ -289,6 +253,8 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig, return ret; } + updateControls(ipaControls); + return 0; } @@ -386,56 +352,6 @@ void IPARkISP1::updateControls(ControlInfoMap *ipaControls) { ControlInfoMap::Map ctrlMap = rkisp1Controls; - /* - * Compute exposure time limits from the V4L2_CID_EXPOSURE control - * limits and the line duration. - */ - double lineDuration = context_.configuration.sensor.lineDuration.get(); - const ControlInfo &v4l2Exposure = context_.sensorControls.find(V4L2_CID_EXPOSURE)->second; - int32_t minExposure = v4l2Exposure.min().get() * lineDuration; - int32_t maxExposure = v4l2Exposure.max().get() * lineDuration; - int32_t defExposure = v4l2Exposure.def().get() * lineDuration; - ctrlMap.emplace(std::piecewise_construct, - std::forward_as_tuple(&controls::ExposureTime), - std::forward_as_tuple(minExposure, maxExposure, defExposure)); - - /* Compute the analogue gain limits. */ - const ControlInfo &v4l2Gain = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; - 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.emplace(std::piecewise_construct, - std::forward_as_tuple(&controls::AnalogueGain), - std::forward_as_tuple(minGain, maxGain, defGain)); - - /* - * Compute the frame duration limits. - * - * The frame length is computed assuming a fixed line length combined - * with the vertical frame sizes. - */ - const ControlInfo &v4l2HBlank = context_.sensorControls.find(V4L2_CID_HBLANK)->second; - uint32_t hblank = v4l2HBlank.def().get(); - uint32_t lineLength = context_.sensorInfo.outputSize.width + hblank; - - const ControlInfo &v4l2VBlank = context_.sensorControls.find(V4L2_CID_VBLANK)->second; - std::array frameHeights{ - v4l2VBlank.min().get() + context_.sensorInfo.outputSize.height, - v4l2VBlank.max().get() + context_.sensorInfo.outputSize.height, - v4l2VBlank.def().get() + context_.sensorInfo.outputSize.height, - }; - - std::array frameDurations; - for (unsigned int i = 0; i < frameHeights.size(); ++i) { - uint64_t frameSize = lineLength * frameHeights[i]; - frameDurations[i] = frameSize / (context_.sensorInfo.pixelRate / 1000000U); - } - - /* \todo Move this (and other agc-related controls) to agc */ - context_.ctrlMap[&controls::FrameDurationLimits] = - ControlInfo(frameDurations[0], frameDurations[1], - ControlValue(Span{ { frameDurations[2], frameDurations[2] } })); - ctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end()); *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls); }