From patchwork Mon Aug 17 11:43:21 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: 27798 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 8C400C3263 for ; Mon, 17 Aug 2026 11:44:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F0B29682DB; Mon, 17 Aug 2026 13:44:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="J0wOEghO"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 801A66824E for ; Mon, 17 Aug 2026 13:43:59 +0200 (CEST) Received: from pb-laptop.local (catv-89-132-78-151.catv.fixed.one.hu [89.132.78.151]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3CBA813BA; Mon, 17 Aug 2026 13:42:40 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1786966960; bh=PSanywnDLbyX9E7YISTgLvrAcQx3564Q+j/SqZKXt/0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=J0wOEghODZlHCDD4L1vVd795OHC/SAE8xCSt8F/n+RXdxOcfTPi0BVYUxTFspNkRA iiUh7C6k/NVt+6zB51zbda6vROjFO4nfqgDhovNORCpVbCt+Fk232dBy4UE2gWr2HA 8vTvdjrahfFVXI85OcgrwkaWErSGqXP35XYFg7j4= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi Subject: [PATCH v5 20/47] ipa: Simplify sensor exposure/gain setting/getting Date: Mon, 17 Aug 2026 13:43:21 +0200 Message-ID: <20260817114349.994123-21-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260817114349.994123-1-barnabas.pocze@ideasonboard.com> References: <20260817114349.994123-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 extraction and preparation of `V4L2_CID_{EXPOSURE,ANALOGUE_GAIN}` into separate functions. This also implements support for not having a a camera sensor helper. Signed-off-by: Barnabás Pőcze Acked-by: Jacopo Mondi --- src/ipa/ipu3/ipu3.cpp | 13 ++++----- src/ipa/libipa/agc.cpp | 45 +++++++++++++++++++++++++++++ src/ipa/libipa/agc.h | 52 ++++++++++++++++++++++++++++++++++ src/ipa/libipa/meson.build | 2 ++ src/ipa/mali-c55/mali-c55.cpp | 18 ++++++------ src/ipa/rkisp1/rkisp1.cpp | 12 ++++---- src/ipa/simple/soft_simple.cpp | 16 ++++------- 7 files changed, 123 insertions(+), 35 deletions(-) create mode 100644 src/ipa/libipa/agc.cpp create mode 100644 src/ipa/libipa/agc.h diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 4bdc4b7677..b7897269bb 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -36,6 +36,7 @@ #include "libcamera/internal/mapped_framebuffer.h" #include "libcamera/internal/yaml_parser.h" +#include "libipa/agc.h" #include "libipa/camera_sensor_helper.h" #include "ipa_context.h" @@ -596,8 +597,8 @@ void IPAIPU3::processStats(const uint32_t frame, IPAFrameContext &frameContext = context_.frameContexts.get(frame); - frameContext.sensor.exposure = sensorControls.get(V4L2_CID_EXPOSURE).get(); - frameContext.sensor.gain = camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get()); + std::tie(frameContext.sensor.exposure, frameContext.sensor.gain) + = agc::extractControls(sensorControls, camHelper_.get()); ControlList metadata(controls::controls); @@ -642,12 +643,10 @@ void IPAIPU3::queueRequest(const uint32_t frame, const ControlList &controls) */ void IPAIPU3::setControls(unsigned int frame) { - int32_t exposure = context_.activeState.agc.exposure; - int32_t gain = camHelper_->gainCode(context_.activeState.agc.gain); - ControlList ctrls(sensorCtrls_); - ctrls.set(V4L2_CID_EXPOSURE, exposure); - ctrls.set(V4L2_CID_ANALOGUE_GAIN, gain); + agc::prepareControls(ctrls, camHelper_.get(), + context_.activeState.agc.exposure, + context_.activeState.agc.gain); ControlList lensCtrls(lensCtrls_); lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp new file mode 100644 index 0000000000..415a6d831f --- /dev/null +++ b/src/ipa/libipa/agc.cpp @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026 Ideas On Board + * + * Auto exposure/gain algorithm for implementing the IPA-specific AGC algorithms + */ + +#include "agc.h" + +namespace libcamera { + +namespace ipa { + +namespace agc { + +/** + * \fn extractControls() + * \param[in] controls The controls list to extract from + * \param[in] sensor The CameraSensorHelper + * + * This function extracts \a V4L2_CID_EXPOSURE and \a V4L2_CID_ANALOGUE_GAIN + * from \a controls and then returns the exposure and gain values. The gain + * code is mapped to the real gain value if \a sensor is provided, otherwise + * the gain code is returned. + * + * \return A pair of exposure and analogue gain extracted from \a controls + */ + +/** + * \fn prepareControls() + * \param[out] controls The controls list to extract from + * \param[in] sensor The CameraSensorHelper + * \param[in] exposure The exposure (in lines) + * \param[in] gain The analogue gain + * + * This function sets \a V4L2_CID_EXPOSURE and \a V4L2_CID_ANALOGUE_GAIN + * in \a controls. The gain is mapped to the gain code if \a sensor is provided, + * otherwise the gain value will be used directly. + */ + +} /* namespace agc */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/agc.h b/src/ipa/libipa/agc.h new file mode 100644 index 0000000000..4789c06ef8 --- /dev/null +++ b/src/ipa/libipa/agc.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026 Ideas on Board Oy + * + * Auto exposure/gain algorithm for implementing the IPA-specific AGC algorithms + */ + +#pragma once + +#include + +#include + +#include + +#include "camera_sensor_helper.h" + +namespace libcamera { + +namespace ipa { + +namespace agc { + +[[nodiscard]] +inline std::pair +extractControls(const ControlList &controls, const CameraSensorHelper *sensor) +{ + auto exposure = controls.get(V4L2_CID_EXPOSURE).get(); + auto gainCode = controls.get(V4L2_CID_ANALOGUE_GAIN).get(); + + return { + static_cast(exposure), + sensor ? sensor->gain(gainCode) : gainCode, + }; +} + +inline void +prepareControls(ControlList &controls, const CameraSensorHelper *sensor, + int32_t exposure, double gain) +{ + controls.set(V4L2_CID_EXPOSURE, exposure); + controls.set(V4L2_CID_ANALOGUE_GAIN, + static_cast(sensor + ? sensor->gainCode(gain) + : static_cast(gain))); +} + +} /* namespace agc */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index cbfa7cd409..fa49abcd55 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 libipa_headers = files([ + 'agc.h', 'agc_mean_luminance.h', 'algorithm.h', 'awb_bayes.h', @@ -27,6 +28,7 @@ libipa_headers = files([ ]) libipa_sources = files([ + 'agc.cpp', 'agc_mean_luminance.cpp', 'algorithm.cpp', 'awb_bayes.cpp', diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp index da0dbe4b31..a13146d8a6 100644 --- a/src/ipa/mali-c55/mali-c55.cpp +++ b/src/ipa/mali-c55/mali-c55.cpp @@ -27,6 +27,7 @@ #include "libcamera/internal/yaml_parser.h" #include "algorithms/algorithm.h" +#include "libipa/agc.h" #include "libipa/camera_sensor_helper.h" #include "ipa_context.h" @@ -143,20 +144,19 @@ void IPAMaliC55::setControls() { IPAActiveState &activeState = context_.activeState; uint32_t exposure; - uint32_t gain; + double gain; if (activeState.agc.autoEnabled) { exposure = activeState.agc.automatic.exposure; - gain = camHelper_->gainCode(activeState.agc.automatic.sensorGain); + gain = activeState.agc.automatic.sensorGain; } else { exposure = activeState.agc.manual.exposure; - gain = camHelper_->gainCode(activeState.agc.manual.sensorGain); + gain = activeState.agc.manual.sensorGain; } ControlList ctrls(sensorControls_); - ctrls.set(V4L2_CID_EXPOSURE, static_cast(exposure)); - ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast(gain)); - + agc::prepareControls(ctrls, camHelper_.get(), + exposure, gain); setSensorControls.emit(ctrls); } @@ -354,10 +354,8 @@ void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId, stats = reinterpret_cast( buffers_.at(bufferId).planes()[0].data()); - frameContext.agc.exposure = - sensorControls.get(V4L2_CID_EXPOSURE).get(); - frameContext.agc.sensorGain = - camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get()); + std::tie(frameContext.agc.exposure, frameContext.agc.sensorGain) + = agc::extractControls(sensorControls, camHelper_.get()); ControlList metadata(controls::controls); diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 38f55b1d86..98ec5a5748 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -31,6 +31,7 @@ #include "libcamera/internal/yaml_parser.h" #include "algorithms/algorithm.h" +#include "libipa/agc.h" #include "ipa_context.h" #include "params.h" @@ -328,10 +329,8 @@ void IPARkISP1::processStats(const uint32_t frame, const uint32_t bufferId, stats = reinterpret_cast( mappedBuffers_.at(bufferId).planes()[0].data()); - frameContext.sensor.exposure = - sensorControls.get(V4L2_CID_EXPOSURE).get(); - frameContext.sensor.gain = - context_.camHelper->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get()); + std::tie(frameContext.sensor.exposure, frameContext.sensor.gain) + = agc::extractControls(sensorControls, context_.camHelper.get()); ControlList metadata(controls::controls); @@ -365,7 +364,6 @@ 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; LOG(IPARkISP1, Debug) @@ -373,8 +371,8 @@ void IPARkISP1::setControls(unsigned int frame) << ", gain " << frameContext.agc.gain << ", vblank " << vblank; ControlList ctrls(context_.sensorControls); - ctrls.set(V4L2_CID_EXPOSURE, static_cast(exposure)); - ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast(gain)); + agc::prepareControls(ctrls, context_.camHelper.get(), + exposure, frameContext.agc.gain); ctrls.set(V4L2_CID_VBLANK, static_cast(vblank)); setSensorControls.emit(frame, ctrls); diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 3932daaa1f..01c41e3a8c 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -27,6 +27,7 @@ #include "libcamera/internal/yaml_parser.h" #include "algorithms/adjust.h" +#include "libipa/agc.h" #include "libipa/camera_sensor_helper.h" #include "module.h" @@ -301,10 +302,8 @@ void IPASoftSimple::processStats(const uint32_t frame, { IPAFrameContext &frameContext = context_.frameContexts.get(frame); - frameContext.sensor.exposure = - sensorControls.get(V4L2_CID_EXPOSURE).get(); - int32_t again = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get(); - frameContext.sensor.gain = camHelper_ ? camHelper_->gain(again) : again; + std::tie(frameContext.sensor.exposure, frameContext.sensor.gain) + = agc::extractControls(sensorControls, camHelper_.get()); ControlList metadata(controls::controls); for (const auto &algo : algorithms()) @@ -312,13 +311,8 @@ void IPASoftSimple::processStats(const uint32_t frame, metadataReady.emit(frame, metadata); ControlList ctrls(sensorInfoMap_); - - int32_t againNew = camHelper_ - ? camHelper_->gainCode(frameContext.agc.gain) - : static_cast(frameContext.agc.gain); - ctrls.set(V4L2_CID_EXPOSURE, frameContext.agc.exposure); - ctrls.set(V4L2_CID_ANALOGUE_GAIN, againNew); - + agc::prepareControls(ctrls, camHelper_.get(), + frameContext.agc.exposure, frameContext.agc.gain); setSensorControls.emit(ctrls); }