{"id":27491,"url":"https://patchwork.libcamera.org/api/patches/27491/?format=json","web_url":"https://patchwork.libcamera.org/patch/27491/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260723154327.1357866-43-barnabas.pocze@ideasonboard.com>","date":"2026-07-23T15:43:25","name":"[RFC,v2,42/43] ipa: libipa: agc: Work without `CameraSensorHelper`","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"d45e6e7775a2c3f7b5ee8c6ace817e528115bc28","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/?format=json","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/27491/mbox/","series":[{"id":6075,"url":"https://patchwork.libcamera.org/api/series/6075/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=6075","date":"2026-07-23T15:42:43","name":"ipa: libipa: agc rework","version":2,"mbox":"https://patchwork.libcamera.org/series/6075/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/27491/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27491/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 71BEFC3308\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 23 Jul 2026 15:44:28 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 055E667F5B;\n\tThu, 23 Jul 2026 17:44:28 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2672E67F07\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 23 Jul 2026 17:43:41 +0200 (CEST)","from pb-laptop.local (185.182.215.156.nat.pool.zt.hu\n\t[185.182.215.156])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3C8322C0B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 23 Jul 2026 17:42:40 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Wg9GNl61\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1784821360;\n\tbh=maa82bq6221z5M7tMgjIaPDPLQQ5RzcDFWXl9wN5/HQ=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=Wg9GNl61oZ7I711rn5KHbm11w8Od8oB0HOodpnbs21qoTKglGZTdR6qBk3Ev4868w\n\t5zpyV8Mk69+ddg9qgvAPW0fg7YvLoaY9YItZKi7QU9YVoNUwF0f1Jp0rdjpXo6Uouy\n\twfQZ0pa91on0zv8JFoKTvqwffLMJk+ojPOjG2BpQ=","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Subject":"[RFC PATCH v2 42/43] ipa: libipa: agc: Work without\n\t`CameraSensorHelper`","Date":"Thu, 23 Jul 2026 17:43:25 +0200","Message-ID":"<20260723154327.1357866-43-barnabas.pocze@ideasonboard.com>","X-Mailer":"git-send-email 2.55.0","In-Reply-To":"<20260723154327.1357866-1-barnabas.pocze@ideasonboard.com>","References":"<20260723154327.1357866-1-barnabas.pocze@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Use the agc algorithm extracted from the simple ipa module (AgcMSV)\nto provide some kind of operation when a `CameraSensorHelper` is\nnot available.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/ipa/ipu3/algorithms/agc.cpp     |   4 +-\n src/ipa/libipa/agc.cpp              | 242 ++++++++++++++++++----------\n src/ipa/libipa/agc.h                |   9 +-\n src/ipa/mali-c55/algorithms/agc.cpp |   4 +-\n src/ipa/rkisp1/algorithms/agc.cpp   |   4 +-\n 5 files changed, 170 insertions(+), 93 deletions(-)","diff":"diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp\nindex c9ea02ed00..975f82eaf3 100644\n--- a/src/ipa/ipu3/algorithms/agc.cpp\n+++ b/src/ipa/ipu3/algorithms/agc.cpp\n@@ -68,12 +68,11 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData)\n {\n \tint ret;\n \n-\tret = agc_.init(tuningData);\n+\tret = agc_.init(tuningData, context.camHelper.get());\n \tif (ret)\n \t\treturn ret;\n \n \tret = agc_.configure(context.configuration.agc, context.activeState.agc, {\n-\t\t.sensor = context.camHelper.get(),\n \t\t.sensorInfo = context.sensorInfo,\n \t\t.sensorControls = context.sensorControls,\n \t\t.ctrlMap = context.ctrlMap,\n@@ -98,7 +97,6 @@ int Agc::configure(IPAContext &context,\n \tbdsGrid_ = context.configuration.grid.bdsGrid;\n \n \treturn agc_.configure(context.configuration.agc, context.activeState.agc, {\n-\t\t.sensor = context.camHelper.get(),\n \t\t.sensorInfo = context.sensorInfo,\n \t\t.sensorControls = context.sensorControls,\n \t\t.ctrlMap = context.ctrlMap,\ndiff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp\nindex 27edc96fc6..683da93662 100644\n--- a/src/ipa/libipa/agc.cpp\n+++ b/src/ipa/libipa/agc.cpp\n@@ -11,10 +11,12 @@\n #include <array>\n #include <chrono>\n #include <optional>\n+#include <variant>\n \n #include <linux/v4l2-controls.h>\n \n #include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n \n #include <libcamera/control_ids.h>\n #include <libcamera/controls.h>\n@@ -56,6 +58,9 @@ LOG_DEFINE_CATEGORY(Agc)\n  * \\var agc::Session::maxAnalogueGain\n  * \\brief Maximum analogue gain supported with the configured sensor\n  *\n+ * \\var agc::Session::defAnalogueGain\n+ * \\brief Default analogue gain of the configured sensor\n+ *\n  * \\var agc::Session::minFrameDuration\n  * \\brief Minimum frame duration supported with the configured sensor\n  *\n@@ -190,9 +195,6 @@ LOG_DEFINE_CATEGORY(Agc)\n  * \\struct AgcAlgorithm::ConfigurationParams\n  * \\brief Parameters for AgcAlgorithm::configure()\n  *\n- * \\var AgcAlgorithm::ConfigurationParams::sensor\n- * \\brief CameraSensorHelper for the sensor\n- *\n  * \\var AgcAlgorithm::ConfigurationParams::sensorInfo\n  * \\brief Details of the sensor\n  *\n@@ -231,13 +233,14 @@ LOG_DEFINE_CATEGORY(Agc)\n \n namespace {\n \n+[[nodiscard]] uint32_t clampExposure(uint32_t exposure, const agc::Session &session)\n+{\n+\treturn std::clamp(exposure, session.minExposure, session.maxExposure);\n+}\n+\n [[nodiscard]] uint32_t clampExposure(utils::Duration exposureTime, const agc::Session &session)\n {\n-\treturn std::clamp<uint32_t>(\n-\t\texposureTime / session.lineDuration,\n-\t\tsession.minExposure,\n-\t\tsession.maxExposure\n-\t);\n+\treturn clampExposure(exposureTime / session.lineDuration, session);\n }\n \n } /* namespace */\n@@ -245,11 +248,18 @@ namespace {\n /**\n  * \\brief Load tuning data\n  */\n-int AgcAlgorithm::init(const ValueNode &tuningData)\n+int AgcAlgorithm::init(const ValueNode &tuningData, CameraSensorHelper *sensor)\n {\n-\tint ret = impl_.parseTuningData(tuningData);\n-\tif (ret)\n-\t\treturn ret;\n+\tif (sensor) {\n+\t\tauto& impl = impl_.emplace<AgcMeanLuminance>();\n+\t\tint ret = impl.parseTuningData(tuningData);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t} else {\n+\t\timpl_.emplace<AgcMSV>();\n+\t}\n+\n+\tsensor_ = sensor;\n \n \treturn 0;\n }\n@@ -283,10 +293,14 @@ int AgcAlgorithm::configure(agc::Session &session, agc::ActiveState &state, cons\n \t};\n \n \t/* Compute the analogue gain limits. */\n+\tconst auto extractGain = [&](const ControlValue &v) {\n+\t\tauto gainCode = v.get<int32_t>();\n+\t\treturn sensor_ ? sensor_->gain(gainCode) : gainCode;\n+\t};\n \tconst ControlInfo &v4l2Gain = config.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n-\tfloat minGain = config.sensor->gain(v4l2Gain.min().get<int32_t>());\n-\tfloat maxGain = config.sensor->gain(v4l2Gain.max().get<int32_t>());\n-\tfloat defGain = config.sensor->gain(v4l2Gain.def().get<int32_t>());\n+\tfloat minGain = extractGain(v4l2Gain.min());\n+\tfloat maxGain = extractGain(v4l2Gain.max());\n+\tfloat defGain = extractGain(v4l2Gain.def());\n \tconfig.ctrlMap[&controls::AnalogueGain] = ControlInfo{\n \t\tminGain,\n \t\tmaxGain,\n@@ -345,30 +359,19 @@ int AgcAlgorithm::configure(agc::Session &session, agc::ActiveState &state, cons\n \tsession.maxExposureTime = maxExposure * session.lineDuration;\n \tsession.minAnalogueGain = minGain;\n \tsession.maxAnalogueGain = maxGain;\n-\n-\timpl_.configure(session.lineDuration, config.sensor);\n-\timpl_.setLimits(session.minExposureTime, session.maxExposureTime,\n-\t\t\tsession.minAnalogueGain, session.maxAnalogueGain,\n-\t\t\t{});\n-\timpl_.resetFrameCount();\n+\tsession.defAnalogueGain = defGain;\n \n \t/* Configure the default exposure and gain. */\n \tstate = {};\n \tstate.automatic.gain = session.minAnalogueGain;\n \tstate.automatic.exposure = clampExposure(defExposure * session.lineDuration, session);\n \tstate.automatic.quantizationGain = 1;\n-\tstate.automatic.yTarget = impl_.effectiveYTarget(0, 1);\n \tstate.manual.gain = state.automatic.gain;\n \tstate.manual.exposure = state.automatic.exposure;\n \tstate.autoExposureEnabled = session.autoAllowed;\n \tstate.autoGainEnabled = session.autoAllowed;\n \tstate.exposureValue = 0;\n \n-\tstate.constraintMode =\n-\t\tstatic_cast<controls::AeConstraintModeEnum>(impl_.constraintModes().begin()->first);\n-\tstate.exposureMode =\n-\t\tstatic_cast<controls::AeExposureModeEnum>(impl_.exposureModeHelpers().begin()->first);\n-\n \tstate.minFrameDuration = session.minFrameDuration;\n \tstate.maxFrameDuration = session.maxFrameDuration;\n \n@@ -397,25 +400,63 @@ int AgcAlgorithm::configure(agc::Session &session, agc::ActiveState &state, cons\n \t\tsession.autoAllowed,\n \t};\n \n-\tif (session.autoAllowed) {\n-\t\tconfig.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f);\n-\n-\t\t{\n-\t\t\tstd::vector<ControlValue> options;\n-\t\t\tfor (const auto &[id, _] : impl_.constraintModes())\n-\t\t\t\toptions.emplace_back(id);\n-\n-\t\t\tconfig.ctrlMap[&controls::AeConstraintMode] = ControlInfo(options);\n-\t\t}\n-\n-\t\t{\n-\t\t\tstd::vector<ControlValue> options;\n-\t\t\tfor (const auto &[id, _] : impl_.exposureModeHelpers())\n-\t\t\t\toptions.emplace_back(id);\n-\n-\t\t\tconfig.ctrlMap[&controls::AeExposureMode] = ControlInfo(options);\n-\t\t}\n-\t} else {\n+\tstd::visit(utils::overloaded{\n+\t\t[&](AgcMSV&) {\n+\t\t\t/* no constraint/exposure mode support */\n+\t\t\tstate.constraintMode = controls::AeConstraintModeEnum::ConstraintNormal;\n+\t\t\tstate.exposureMode = controls::AeExposureModeEnum::ExposureNormal;\n+\n+\t\t\tstate.automatic.quantizationGain = 1; /* no CameraSensorHelper */\n+\t\t\tstate.automatic.yTarget = (2.5 - 1) / (5 - 1);\n+\n+\t\t\tif (session.autoAllowed) {\n+\t\t\t\tconfig.ctrlMap[&controls::AeConstraintMode] = ControlInfo(\n+\t\t\t\t\tstd::array{ ControlValue(state.constraintMode) }\n+\t\t\t\t);\n+\n+\t\t\t\tconfig.ctrlMap[&controls::AeExposureMode] = ControlInfo(\n+\t\t\t\t\tstd::array{ ControlValue(state.exposureMode) }\n+\t\t\t\t);\n+\t\t\t}\n+\t\t},\n+\t\t[&](AgcMeanLuminance& impl) {\n+\t\t\tstate.constraintMode =\n+\t\t\t\tstatic_cast<controls::AeConstraintModeEnum>(impl.constraintModes().begin()->first);\n+\t\t\tstate.exposureMode =\n+\t\t\t\tstatic_cast<controls::AeExposureModeEnum>(impl.exposureModeHelpers().begin()->first);\n+\n+\t\t\tstate.automatic.yTarget = impl.effectiveYTarget(0, 1);\n+\n+\t\t\tASSERT(sensor_);\n+\t\t\timpl.configure(session.lineDuration, sensor_);\n+\t\t\timpl.setLimits(session.minExposureTime, session.maxExposureTime,\n+\t\t\t\t       session.minAnalogueGain, session.maxAnalogueGain,\n+\t\t\t\t       {});\n+\t\t\timpl.resetFrameCount();\n+\n+\t\t\tif (session.autoAllowed) {\n+\t\t\t\tconfig.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f);\n+\n+\t\t\t\t{\n+\t\t\t\t\tstd::vector<ControlValue> options;\n+\t\t\t\t\tfor (const auto &[id, _] : impl.constraintModes())\n+\t\t\t\t\t\toptions.emplace_back(id);\n+\n+\t\t\t\t\tconfig.ctrlMap[&controls::AeConstraintMode] = ControlInfo(options);\n+\t\t\t\t}\n+\n+\t\t\t\t{\n+\t\t\t\t\tstd::vector<ControlValue> options;\n+\t\t\t\t\tfor (const auto &[id, _] : impl.exposureModeHelpers())\n+\t\t\t\t\t\toptions.emplace_back(id);\n+\n+\t\t\t\t\tconfig.ctrlMap[&controls::AeExposureMode] = ControlInfo(options);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t},\n+\t}, impl_);\n+\n+\tif (!session.autoAllowed) {\n \t\tconfig.ctrlMap.erase(&controls::ExposureValue);\n \t\tconfig.ctrlMap.erase(&controls::AeConstraintMode);\n \t\tconfig.ctrlMap.erase(&controls::AeExposureMode);\n@@ -607,41 +648,80 @@ void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state,\n \t\t\tmaxAnalogueGain = frameContext.gain;\n \t\t}\n \n-\t\t/*\n-\t\t* The Agc algorithm needs to know the effective exposure value that was\n-\t\t* applied to the sensor when the statistics were collected.\n-\t\t*/\n-\t\tutils::Duration effectiveExposureValue =\n-\t\t\tlineDuration * params->exposure * params->gain;\n-\n-\t\timpl_.setLimits(minExposureTime, maxExposureTime,\n-\t\t\t\tminAnalogueGain, maxAnalogueGain,\n-\t\t\t\tstd::move(params->additionalConstraints));\n-\n-\t\tconst auto &newEv = impl_.calculateNewEv({\n-\t\t\t.traits = params->traits,\n-\t\t\t.yHist = params->yHist,\n-\t\t\t.effectiveExposureValue = effectiveExposureValue,\n-\t\t\t.constraintModeIndex = frameContext.constraintMode,\n-\t\t\t.exposureModeIndex = frameContext.exposureMode,\n-\t\t\t.lux = params->lux,\n-\t\t\t.exposureCompensation = pow(2.0, frameContext.exposureValue),\n-\t\t});\n-\n-\t\tLOG(Agc, Debug)\n-\t\t\t<< \"exposure-time:\" << newEv.exposureTime\n-\t\t\t<< \" analogue-gain\" << newEv.analogueGain\n-\t\t\t<< \" quantization-gain\" << newEv.quantizationGain\n-\t\t\t<< \" digital-gain: \" << newEv.digitalGain\n-\t\t;\n-\n-\t\t/* Update the estimated exposure and gain. */\n-\t\tstate.automatic.exposure = clampExposure(newEv.exposureTime, session);\n-\t\tstate.automatic.gain = newEv.analogueGain;\n-\t\tstate.automatic.quantizationGain = newEv.quantizationGain;\n-\t\tstate.automatic.yTarget = newEv.yTarget;\n-\n-\t\tnewExposureTime = newEv.exposureTime;\n+\t\tstd::visit(utils::overloaded{\n+\t\t\t[&](AgcMSV& impl) {\n+\t\t\t\timpl.setLimits({\n+\t\t\t\t\t.exposure = {\n+\t\t\t\t\t\tuint32_t(minExposureTime / lineDuration),\n+\t\t\t\t\t\tuint32_t(maxExposureTime / lineDuration),\n+\t\t\t\t\t},\n+\t\t\t\t\t.gain = {\n+\t\t\t\t\t\tminAnalogueGain,\n+\t\t\t\t\t\tmaxAnalogueGain,\n+\t\t\t\t\t},\n+\t\t\t\t\t/* gain codes -> step size of 1 */\n+\t\t\t\t\t.gainMinStep = 1,\n+\t\t\t\t\t/* assume default gain is close to 1.0 */\n+\t\t\t\t\t.gain1 = session.defAnalogueGain,\n+\t\t\t\t});\n+\n+\t\t\t\tconst auto& newEv = impl.calculateNewEv({\n+\t\t\t\t\t.yHist = params->yHist,\n+\t\t\t\t\t.exposure = params->exposure,\n+\t\t\t\t\t.gain = params->gain,\n+\t\t\t\t});\n+\n+\t\t\t\tstate.automatic.exposure = newEv.exposure;\n+\t\t\t\tstate.automatic.gain = newEv.analogueGain;\n+\n+\t\t\t\tnewExposureTime = newEv.exposure * lineDuration;\n+\n+\t\t\t\tLOG(Agc, Debug)\n+\t\t\t\t\t<< \"exposure-time:\" << newExposureTime\n+\t\t\t\t\t<< \" analogue-gain:\" << newEv.analogueGain\n+\t\t\t\t;\n+\t\t\t},\n+\t\t\t[&](AgcMeanLuminance& impl) {\n+\t\t\t\t/*\n+\t\t\t\t * The Agc algorithm needs to know the effective exposure value that was\n+\t\t\t\t * applied to the sensor when the statistics were collected.\n+\t\t\t\t */\n+\t\t\t\tutils::Duration effectiveExposureValue =\n+\t\t\t\t\tlineDuration * params->exposure * params->gain;\n+\n+\t\t\t\timpl.setLimits(minExposureTime, maxExposureTime,\n+\t\t\t\t\t       minAnalogueGain, maxAnalogueGain,\n+\t\t\t\t\t       std::move(params->additionalConstraints));\n+\n+\t\t\t\tconst auto &newEv = impl.calculateNewEv({\n+\t\t\t\t\t.traits = params->traits,\n+\t\t\t\t\t.yHist = params->yHist,\n+\t\t\t\t\t.effectiveExposureValue = effectiveExposureValue,\n+\t\t\t\t\t.constraintModeIndex = frameContext.constraintMode,\n+\t\t\t\t\t.exposureModeIndex = frameContext.exposureMode,\n+\t\t\t\t\t.lux = params->lux,\n+\t\t\t\t\t.exposureCompensation = pow(2.0, frameContext.exposureValue),\n+\t\t\t\t});\n+\n+\t\t\t\tLOG(Agc, Debug)\n+\t\t\t\t\t<< \"exposure-time:\" << newEv.exposureTime\n+\t\t\t\t\t<< \" analogue-gain:\" << newEv.analogueGain\n+\t\t\t\t\t<< \" quantization-gain:\" << newEv.quantizationGain\n+\t\t\t\t\t<< \" digital-gain:\" << newEv.digitalGain\n+\t\t\t\t;\n+\n+\t\t\t\t/* Update the estimated exposure and gain. */\n+\t\t\t\tstate.automatic.exposure = newEv.exposureTime / lineDuration;\n+\t\t\t\tstate.automatic.gain = newEv.analogueGain;\n+\t\t\t\tstate.automatic.quantizationGain = newEv.quantizationGain;\n+\t\t\t\tstate.automatic.yTarget = newEv.yTarget;\n+\n+\t\t\t\tnewExposureTime = newEv.exposureTime;\n+\t\t\t},\n+\t\t}, impl_);\n+\n+\t\t/* Avoid out of range values due to rounding, etc. */\n+\t\tstate.automatic.exposure = clampExposure(state.automatic.exposure, session);\n \t}\n \n \t/*\ndiff --git a/src/ipa/libipa/agc.h b/src/ipa/libipa/agc.h\nindex c11dbf80cd..52896b7851 100644\n--- a/src/ipa/libipa/agc.h\n+++ b/src/ipa/libipa/agc.h\n@@ -9,6 +9,7 @@\n \n #include <optional>\n #include <utility>\n+#include <variant>\n \n #include <linux/v4l2-controls.h>\n \n@@ -18,6 +19,7 @@\n #include <libcamera/ipa/core_ipa_interface.h>\n \n #include \"agc_mean_luminance.h\"\n+#include \"agc_msv.h\"\n #include \"camera_sensor_helper.h\"\n #include \"histogram.h\"\n \n@@ -55,6 +57,7 @@ struct Session {\n \tutils::Duration maxExposureTime;\n \tdouble minAnalogueGain;\n \tdouble maxAnalogueGain;\n+\tdouble defAnalogueGain;\n \tutils::Duration minFrameDuration;\n \tutils::Duration maxFrameDuration;\n \n@@ -112,14 +115,13 @@ class AgcAlgorithm\n {\n public:\n \tstruct ConfigurationParams {\n-\t\tconst CameraSensorHelper *sensor;\n \t\tconst IPACameraSensorInfo &sensorInfo;\n \t\tconst ControlInfoMap &sensorControls;\n \t\tControlInfoMap::Map &ctrlMap;\n \t\tbool autoAllowed = true;\n \t};\n \n-\tint init(const ValueNode &tuningData);\n+\tint init(const ValueNode &tuningData, CameraSensorHelper *sensor);\n \n \tint configure(agc::Session &session, agc::ActiveState &state, const ConfigurationParams &config);\n \n@@ -141,7 +143,8 @@ public:\n \t\t     std::optional<ProcessParams> &&params, ControlList &metadata);\n \n private:\n-\tAgcMeanLuminance impl_;\n+\tstd::variant<AgcMSV, AgcMeanLuminance> impl_;\n+\tCameraSensorHelper *sensor_ = nullptr;\n };\n \n } /* namespace ipa */\ndiff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp\nindex 8af4df53e7..00fb7bfa70 100644\n--- a/src/ipa/mali-c55/algorithms/agc.cpp\n+++ b/src/ipa/mali-c55/algorithms/agc.cpp\n@@ -123,12 +123,11 @@ Agc::Agc()\n \n int Agc::init(IPAContext &context, const ValueNode &tuningData)\n {\n-\tint ret = agc_.init(tuningData);\n+\tint ret = agc_.init(tuningData, context.camHelper.get());\n \tif (ret)\n \t\treturn ret;\n \n \tret = agc_.configure(context.configuration.agc, context.activeState.agc, {\n-\t\t.sensor = context.camHelper.get(),\n \t\t.sensorInfo = context.sensorInfo,\n \t\t.sensorControls = context.sensorControls,\n \t\t.ctrlMap = context.ctrlMap,\n@@ -148,7 +147,6 @@ int Agc::configure(IPAContext &context,\n \t\treturn ret;\n \n \tret = agc_.configure(context.configuration.agc, context.activeState.agc, {\n-\t\t.sensor = context.camHelper.get(),\n \t\t.sensorInfo = context.sensorInfo,\n \t\t.sensorControls = context.sensorControls,\n \t\t.ctrlMap = context.ctrlMap,\ndiff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp\nindex 4c2a066e86..41a8cd581f 100644\n--- a/src/ipa/rkisp1/algorithms/agc.cpp\n+++ b/src/ipa/rkisp1/algorithms/agc.cpp\n@@ -136,12 +136,11 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData)\n {\n \tint ret;\n \n-\tret = agc_.init(tuningData);\n+\tret = agc_.init(tuningData, context.camHelper.get());\n \tif (ret)\n \t\treturn ret;\n \n \tret = agc_.configure(context.configuration.agc, context.activeState.agc, {\n-\t\t.sensor = context.camHelper.get(),\n \t\t.sensorInfo = context.sensorInfo,\n \t\t.sensorControls = context.sensorControls,\n \t\t.ctrlMap = context.ctrlMap,\n@@ -167,7 +166,6 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData)\n int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)\n {\n \tint ret = agc_.configure(context.configuration.agc, context.activeState.agc, {\n-\t\t.sensor = context.camHelper.get(),\n \t\t.sensorInfo = context.sensorInfo,\n \t\t.sensorControls = context.sensorControls,\n \t\t.ctrlMap = context.ctrlMap,\n","prefixes":["RFC","v2","42/43"]}