From patchwork Fri Jun 20 12:42:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 23609 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 529B7BDE6B for ; Fri, 20 Jun 2025 12:45:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CBD1C68DEE; Fri, 20 Jun 2025 14:45:01 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="da19/KMT"; dkim-atps=neutral Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AB66861535 for ; Fri, 20 Jun 2025 14:44:58 +0200 (CEST) Received: by mail-wm1-x32d.google.com with SMTP id 5b1f17b1804b1-4535fc0485dso2484525e9.0 for ; Fri, 20 Jun 2025 05:44:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1750423498; x=1751028298; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=frwK0qays/9gk1irCD7nqtI6enlAuzK77v7RfYNMXpU=; b=da19/KMThAlxIvb8cLv1StG1YQfuGgSas6JHMQxO9LDen7V+6o5lzbrMfZ3UrmUvN0 BtDRjjDWOSTFSItHpKCdgRsCBUKKdJ9ijjyebuQbogJy0AJcBhQnwtiw2NYJXIH4KUxq vaWlTebCHh/8PDmsqLs+QfX+fBJEIvwrXzRkOFmeogdS4Dk9kPu9L+iN4JaWzSO80kJi w/gMSjUf7jfK9mNMcFu/1zZ61iSUkHW+appJc+FpWy8hn/MoEXax+s8Lt3kjcB4DA/bc UdQRkpUYo/ijTmwL1eW45YXPZaD2VZIS6XhsauuHMmHS/UlhO2q93XnUJf8ZsTFzhwl+ SfPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750423498; x=1751028298; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=frwK0qays/9gk1irCD7nqtI6enlAuzK77v7RfYNMXpU=; b=wRci9rvwBLad7WUBvHF6XS2ggsqcz399M3LxsqY5UUMVfUsr3ks2n0jAUvlXeqLE61 bvAu/Kp4kFhcj/fXuEYSO4Ny+WTGrdmcwFOnpVlzWVGNhfYFBCnNeKlr05/e2irHOxy7 9URGn+AeijVKCnblLLClp0UQIURkvJW2fWHFQdLqPv9vW1ULcwwS/ymHIBxC/en9Ak8B g1JB2/YAZZe1eA0FSiauCozGA9MUMgV/lERyT3n3tDdSMgABOm6Nb4EBGf4ApKW3rhOm bFEi8XuzJkWEgSvBYICYfDYFeJ7WT+4ajvTy7t8+pzMc61zIQzNJgo+tTvHn3lAMSJ/O eRaQ== X-Gm-Message-State: AOJu0YzSxiWUBvYeZQRFW5HNKffLmsMJBVXUR3zkyAzas/LeWgPswZ// Cc4F1ulYd2oN3ZudvF7IFzUKdGndfcdAWMc1t5yZ+n4Su7BoDpB/EKI4nptrJ4GRo/ywXwRa06W QXt4B X-Gm-Gg: ASbGncsxETV4ctw+ZC+hM66RIUux38PmOH8c1fJ5QvNiSRDSRGD7m3OeIIchuirdnfe r6fGwWfnc+zXz2Fe3OkUP1xX9CGSFuoXiuQnF0EqxY+xYb+Dl74SR+K6xG7sOtPV0iFrImRNY4B 0yq65scKxvWs0boKJOJa0E7F2YRIbpcZ4e7lMV8bWxU6BFDceWaNx/OsYLMyTYZRA7wLjEWKG0e eLhnDjsBev+Gh+onH4L1WKnmLlZs465gOGvsenyJ1Pe0R08Gzox0i1gOiAK0H0rv2JXHED18BYE 3+NQ9D0b7H1jm2eAPSww1I0FrqJgRlVTDhQIRr/arR2dexleHrSoSxOubXU3VXevYk4rBUx1pQI +joLfhw== X-Google-Smtp-Source: AGHT+IGhUDNGpV48uQ1ya2kZSs4uUM1kiclr7X4KmS9Qa0h9i+PybKGH6fZm9D0DZWjpkLZc8aGSZw== X-Received: by 2002:a05:600c:1d0a:b0:451:dee4:cd07 with SMTP id 5b1f17b1804b1-4536478e1e7mr10076895e9.0.1750423497870; Fri, 20 Jun 2025 05:44:57 -0700 (PDT) Received: from NAUSH-P-DELL.pitowers.org ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-45361461375sm41561525e9.14.2025.06.20.05.44.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Jun 2025 05:44:57 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Nick Hollinghurst , Naushir Patuck Subject: [PATCH v1 1/8] ipa: rpi: Defer initialising AF LensPosition ControlInfo and value Date: Fri, 20 Jun 2025 13:42:22 +0100 Message-ID: <20250620124452.557855-2-naush@raspberrypi.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250620124452.557855-1-naush@raspberrypi.com> References: <20250620124452.557855-1-naush@raspberrypi.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: Nick Hollinghurst This fixes two small bugs: We previously populated LensPosition's ControlInfo with hard-coded values, ignoring the tuning file. Now we query the AfAlgorithm to get limits (over all AF ranges) and default (for AfRangeNormal). We previously sent a default position to the lens driver, even when a user-specified starting position would follow. Defer doing this, to reduce unnecessary lens movement at startup (for some drivers). Fixes: https://bugs.libcamera.org/show_bug.cgi?id=258 Signed-off-by: Nick Hollinghurst Signed-off-by: Naushir Patuck Reviewed-by: Naushir Patuck --- src/ipa/rpi/common/ipa_base.cpp | 53 +++++++++++++++++---------- src/ipa/rpi/controller/af_algorithm.h | 8 +++- src/ipa/rpi/controller/rpi/af.cpp | 16 +++++++- src/ipa/rpi/controller/rpi/af.h | 4 +- 4 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp index 6565f5366312..a5bdcbb5838c 100644 --- a/src/ipa/rpi/common/ipa_base.cpp +++ b/src/ipa/rpi/common/ipa_base.cpp @@ -233,25 +233,6 @@ int32_t IpaBase::configure(const IPACameraSensorInfo &sensorInfo, const ConfigPa agcStatus.analogueGain = defaultAnalogueGain; applyAGC(&agcStatus, ctrls); - /* - * Set the lens to the default (typically hyperfocal) position - * on first start. - */ - if (lensPresent_) { - RPiController::AfAlgorithm *af = - dynamic_cast(controller_.getAlgorithm("af")); - - if (af) { - float defaultPos = - ipaAfControls.at(&controls::LensPosition).def().get(); - ControlList lensCtrl(lensCtrls_); - int32_t hwpos; - - af->setLensPosition(defaultPos, &hwpos); - lensCtrl.set(V4L2_CID_FOCUS_ABSOLUTE, hwpos); - result->lensControls = std::move(lensCtrl); - } - } } result->sensorControls = std::move(ctrls); @@ -281,8 +262,20 @@ int32_t IpaBase::configure(const IPACameraSensorInfo &sensorInfo, const ConfigPa ctrlMap.merge(ControlInfoMap::Map(ipaColourControls)); /* Declare Autofocus controls, only if we have a controllable lens */ - if (lensPresent_) + if (lensPresent_) { ctrlMap.merge(ControlInfoMap::Map(ipaAfControls)); + RPiController::AfAlgorithm *af = + dynamic_cast(controller_.getAlgorithm("af")); + if (af) { + double min, max, dflt; + af->getLensLimits(min, max); + dflt = af->getDefaultLensPosition(); + ctrlMap[&controls::LensPosition] = + ControlInfo(static_cast(min), + static_cast(max), + static_cast(dflt)); + } + } result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls); @@ -320,6 +313,26 @@ void IpaBase::start(const ControlList &controls, StartResult *result) /* Make a note of this as it tells us the HDR status of the first few frames. */ hdrStatus_ = agcStatus.hdr; + /* + * AF: If no lens position was specified, drive lens to a default position. + * This had to be deferred (not initialised by a constructor) until here + * to ensure that exactly ONE starting position is sent to the lens driver. + * It should be the static API default, not dependent on AF range or mode. + */ + if (firstStart_ && lensPresent_) { + RPiController::AfAlgorithm *af = dynamic_cast( + controller_.getAlgorithm("af")); + if (af && !af->getLensPosition()) { + int32_t hwpos; + double pos = af->getDefaultLensPosition(); + if (af->setLensPosition(pos, &hwpos, true)) { + ControlList lensCtrls(lensCtrls_); + lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, hwpos); + setLensControls.emit(lensCtrls); + } + } + } + /* * Initialise frame counts, and decide how many frames must be hidden or * "mistrusted", which depends on whether this is a startup from cold, diff --git a/src/ipa/rpi/controller/af_algorithm.h b/src/ipa/rpi/controller/af_algorithm.h index ad9b575450e3..382609f9b2d8 100644 --- a/src/ipa/rpi/controller/af_algorithm.h +++ b/src/ipa/rpi/controller/af_algorithm.h @@ -33,6 +33,10 @@ public: * * getMode() is provided mainly for validating controls. * getLensPosition() is provided for populating DeviceStatus. + * + * getDefaultlensPosition() and getLensLimits() were added for + * populating ControlInfoMap. They return the static API limits + * which should be independent of the current range or mode. */ enum AfRange { AfRangeNormal = 0, @@ -66,7 +70,9 @@ public: } virtual void setMode(AfMode mode) = 0; virtual AfMode getMode() const = 0; - virtual bool setLensPosition(double dioptres, int32_t *hwpos) = 0; + virtual double getDefaultLensPosition() const = 0; + virtual void getLensLimits(double &min, double &max) const = 0; + virtual bool setLensPosition(double dioptres, int32_t *hwpos, bool force = false) = 0; virtual std::optional getLensPosition() const = 0; virtual void triggerScan() = 0; virtual void cancelScan() = 0; diff --git a/src/ipa/rpi/controller/rpi/af.cpp b/src/ipa/rpi/controller/rpi/af.cpp index 2157eb94f427..041cb51db277 100644 --- a/src/ipa/rpi/controller/rpi/af.cpp +++ b/src/ipa/rpi/controller/rpi/af.cpp @@ -715,11 +715,23 @@ void Af::setWindows(libcamera::Span const &wins) invalidateWeights(); } -bool Af::setLensPosition(double dioptres, int *hwpos) +double Af::getDefaultLensPosition() const +{ + return cfg_.ranges[AfRangeNormal].focusDefault; +} + +void Af::getLensLimits(double &min, double &max) const +{ + /* Limits for manual focus are set by map, not by ranges */ + min = cfg_.map.domain().start; + max = cfg_.map.domain().end; +} + +bool Af::setLensPosition(double dioptres, int *hwpos, bool force) { bool changed = false; - if (mode_ == AfModeManual) { + if (mode_ == AfModeManual || force) { LOG(RPiAf, Debug) << "setLensPosition: " << dioptres; ftarget_ = cfg_.map.domain().clamp(dioptres); changed = !(initted_ && fsmooth_ == ftarget_); diff --git a/src/ipa/rpi/controller/rpi/af.h b/src/ipa/rpi/controller/rpi/af.h index 317a51f3ecc3..d8ece1ab119c 100644 --- a/src/ipa/rpi/controller/rpi/af.h +++ b/src/ipa/rpi/controller/rpi/af.h @@ -54,7 +54,9 @@ public: void setWindows(libcamera::Span const &wins) override; void setMode(AfMode mode) override; AfMode getMode() const override; - bool setLensPosition(double dioptres, int32_t *hwpos) override; + double getDefaultLensPosition() const override; + void getLensLimits(double &min, double &max) const override; + bool setLensPosition(double dioptres, int32_t *hwpos, bool force) override; std::optional getLensPosition() const override; void triggerScan() override; void cancelScan() override;