From patchwork Mon Oct 14 15:47:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 21623 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 81FE3C32F4 for ; Mon, 14 Oct 2024 15:47:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A663D6537C; Mon, 14 Oct 2024 17:47:56 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="OgGPz9xY"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DE4646537E for ; Mon, 14 Oct 2024 17:47:51 +0200 (CEST) Received: from Monstersaurus.lgs-net.com (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EB3FA1BAE; Mon, 14 Oct 2024 17:46:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1728920771; bh=drBSXRzmjMPrQnNbI+yZGFBPw6McSxyxZ7MizNbASiE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OgGPz9xYbMRDM2Uv71/nnVGgzwbRTN9JexRt26qSJsYgidCRa2R6P2SvZiwWJxH3j 4y/HkNwnnoZ+DLykTosZ/3yq++ZSM/03bVXqFB2O5VOqknUQpW1vWFA61hc9HLCDTM DcesLL/iUot2FGfYiw06gsq4eVjT/av7Rp+t9vCI= From: Kieran Bingham To: libcamera devel Cc: Paul Elder , Kieran Bingham Subject: [PATCH 3/3] rkisp1: Honor the FrameDurationLimits control Date: Mon, 14 Oct 2024 16:47:47 +0100 Message-Id: <20241014154747.2295253-4-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241014154747.2295253-1-kieran.bingham@ideasonboard.com> References: <20241014154747.2295253-1-kieran.bingham@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" From: Paul Elder Add support to rkisp1 for controlling the framerate via the FrameDurationLimits control. Signed-off-by: Paul Elder Signed-off-by: Kieran Bingham Reviewed-by: Umang Jain --- src/ipa/rkisp1/algorithms/agc.cpp | 52 ++++++++++++++++++------ src/ipa/rkisp1/ipa_context.cpp | 16 +++++++- src/ipa/rkisp1/ipa_context.h | 4 ++ src/ipa/rkisp1/rkisp1.cpp | 2 + src/libcamera/pipeline/rkisp1/rkisp1.cpp | 7 ++++ 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index e23ab120b3e2..088ecf42c480 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -180,6 +180,7 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) /* 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()); /* @@ -267,10 +268,21 @@ void Agc::queueRequest(IPAContext &context, const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits); if (frameDurationLimits) { - utils::Duration maxFrameDuration = - std::chrono::milliseconds((*frameDurationLimits).back()); - agc.maxFrameDuration = maxFrameDuration; + /* 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()); + + agc.minFrameDuration = std::chrono::microseconds(minFrameDuration); + agc.maxFrameDuration = std::chrono::microseconds(maxFrameDuration); } + frameContext.agc.minFrameDuration = agc.minFrameDuration; frameContext.agc.maxFrameDuration = agc.maxFrameDuration; } @@ -330,15 +342,8 @@ void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext, * frameContext.sensor.exposure; metadata.set(controls::AnalogueGain, frameContext.sensor.gain); metadata.set(controls::ExposureTime, exposureTime.get()); + metadata.set(controls::FrameDuration, frameContext.agc.frameDuration.get()); metadata.set(controls::AeEnable, frameContext.agc.autoEnabled); - - /* \todo Use VBlank value calculated from each frame exposure. */ - uint32_t vTotal = context.configuration.sensor.size.height - + context.configuration.sensor.defVBlank; - utils::Duration frameDuration = context.configuration.sensor.lineDuration - * vTotal; - metadata.set(controls::FrameDuration, frameDuration.get()); - metadata.set(controls::AeMeteringMode, frameContext.agc.meteringMode); metadata.set(controls::AeExposureMode, frameContext.agc.exposureMode); metadata.set(controls::AeConstraintMode, frameContext.agc.constraintMode); @@ -400,6 +405,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, return; } + IPACameraSensorInfo &sensorInfo = context.sensorInfo; utils::Duration lineDuration = context.configuration.sensor.lineDuration; /* @@ -418,11 +424,19 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, [](uint32_t x) { return x >> 4; }); expMeans_ = { params->ae.exp_mean, context.hw->numAeCells }; + /* + * Limit the allowed shutter speeds for the exposure helper based on + * the frame duration limits. + */ + utils::Duration minShutterSpeed = + std::clamp(frameContext.agc.minFrameDuration, + context.configuration.sensor.minShutterSpeed, + context.configuration.sensor.maxShutterSpeed); utils::Duration maxShutterSpeed = std::clamp(frameContext.agc.maxFrameDuration, context.configuration.sensor.minShutterSpeed, context.configuration.sensor.maxShutterSpeed); - setLimits(context.configuration.sensor.minShutterSpeed, + setLimits(minShutterSpeed, maxShutterSpeed, context.configuration.sensor.minAnalogueGain, context.configuration.sensor.maxAnalogueGain); @@ -451,6 +465,20 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, activeState.agc.automatic.exposure = shutterTime / lineDuration; activeState.agc.automatic.gain = aGain; + /* + * Determine what our FrameDuration must be and adapt VBLANK to suit + * this if we need to expand the shutter time calculated to fill the + * remaining time so that we do not run faster than the minimum frame + * duration (maximum requested frame rate) when we have short exposures. + */ + utils::Duration frameDuration = std::max(frameContext.agc.minFrameDuration, + shutterTime); + + frameContext.agc.vblank = (frameDuration / lineDuration) - sensorInfo.outputSize.height; + + /* Update accounting for line length quantization. */ + frameContext.agc.frameDuration = (sensorInfo.outputSize.height + frameContext.agc.vblank) * lineDuration; + fillMetadata(context, frameContext, metadata); expMeans_ = {}; } diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp index 14d0c02a2b32..c5eb0d64ec29 100644 --- a/src/ipa/rkisp1/ipa_context.cpp +++ b/src/ipa/rkisp1/ipa_context.cpp @@ -177,6 +177,9 @@ namespace libcamera::ipa::rkisp1 { * \var IPAActiveState::agc.meteringMode * \brief Metering mode as set by the AeMeteringMode control * + * \var IPAActiveState::agc.minFrameDuration + * \brief Minimum frame duration as set by the FrameDurationLimits control + * * \var IPAActiveState::agc.maxFrameDuration * \brief Maximum frame duration as set by the FrameDurationLimits control */ @@ -297,7 +300,9 @@ namespace libcamera::ipa::rkisp1 { * \brief Automatic Gain Control parameters for this frame * * The exposure and gain are provided by the AGC algorithm, and are to be - * applied to the sensor in order to take effect for this frame. + * applied to the sensor in order to take effect for this frame. Additionally + * the vertical blanking period is determined to maintain a consistent frame + * rate matched to the FrameDurationLimits as set by the user. * * \var IPAFrameContext::agc.exposure * \brief Exposure time expressed as a number of lines computed by the algorithm @@ -307,6 +312,9 @@ namespace libcamera::ipa::rkisp1 { * * The gain should be adapted to the sensor specific gain code before applying. * + * \var IPAFrameContext::agc.vblank + * \brief Vertical blanking parameter computed by the algorithm + * * \var IPAFrameContext::agc.autoEnabled * \brief Manual/automatic AGC state as set by the AeEnable control * @@ -319,9 +327,15 @@ namespace libcamera::ipa::rkisp1 { * \var IPAFrameContext::agc.meteringMode * \brief Metering mode as set by the AeMeteringMode control * + * \var IPAFrameContext::agc.minFrameDuration + * \brief Minimum frame duration as set by the FrameDurationLimits control + * * \var IPAFrameContext::agc.maxFrameDuration * \brief Maximum frame duration as set by the FrameDurationLimits control * + * \var IPAFrameContext::agc.frameDuration + * \brief The actual FrameDuration used by the algorithm for the frame + * * \var IPAFrameContext::agc.updateMetering * \brief Indicate if new ISP AGC metering parameters need to be applied */ diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index e274d9b01e1c..60c4d647f1ef 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -79,6 +79,7 @@ struct IPAActiveState { controls::AeConstraintModeEnum constraintMode; controls::AeExposureModeEnum exposureMode; controls::AeMeteringModeEnum meteringMode; + utils::Duration minFrameDuration; utils::Duration maxFrameDuration; } agc; @@ -128,11 +129,14 @@ struct IPAFrameContext : public FrameContext { struct { uint32_t exposure; double gain; + uint32_t vblank; bool autoEnabled; controls::AeConstraintModeEnum constraintMode; controls::AeExposureModeEnum exposureMode; controls::AeMeteringModeEnum meteringMode; + utils::Duration minFrameDuration; utils::Duration maxFrameDuration; + utils::Duration frameDuration; bool updateMetering; } agc; diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 47777ece783f..17d56fb4e901 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -449,10 +449,12 @@ void IPARkISP1::setControls(unsigned int frame) IPAFrameContext &frameContext = context_.frameContexts.get(frame); uint32_t exposure = frameContext.agc.exposure; uint32_t gain = context_.camHelper->gainCode(frameContext.agc.gain); + uint32_t vblank = frameContext.agc.vblank; 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(frame, ctrls); } diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 42961c120036..1ec12185bb78 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -394,6 +394,12 @@ void RkISP1CameraData::paramFilled(unsigned int frame, unsigned int bytesused) void RkISP1CameraData::setSensorControls([[maybe_unused]] unsigned int frame, const ControlList &sensorControls) { + if (frame == 0) { + ControlList controls = sensorControls; + sensor_->setControls(&controls); + return; + } + delayedCtrls_->push(sensorControls); } @@ -1138,6 +1144,7 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor) std::unordered_map params = { { V4L2_CID_ANALOGUE_GAIN, { 1, false } }, { V4L2_CID_EXPOSURE, { 2, false } }, + { V4L2_CID_VBLANK, { 1, false } }, }; data->delayedCtrls_ =