{"id":27191,"url":"https://patchwork.libcamera.org/api/1.1/patches/27191/?format=json","web_url":"https://patchwork.libcamera.org/patch/27191/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/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":"<20260703153819.1088752-13-barnabas.pocze@ideasonboard.com>","date":"2026-07-03T15:38:14","name":"[RFC,v1,12/17] ipa: mali-c55: Port to `AgcMeanLuminanceAlgorithm`","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"c4148d0d6d85b5cd8ed5b3480963e206e9dcfd30","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/1.1/people/216/?format=json","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/27191/mbox/","series":[{"id":6036,"url":"https://patchwork.libcamera.org/api/1.1/series/6036/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=6036","date":"2026-07-03T15:38:02","name":"ipa: libipa: agc rework","version":1,"mbox":"https://patchwork.libcamera.org/series/6036/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/27191/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27191/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 27CDDC3261\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Jul 2026 15:38:44 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6B52965FEF;\n\tFri,  3 Jul 2026 17:38:40 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6A50965FE9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Jul 2026 17:38:25 +0200 (CEST)","from pb-laptop.local (185.221.140.128.nat.pool.zt.hu\n\t[185.221.140.128])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 54F6B1121\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Jul 2026 17:37:39 +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=\"FNA1Mj4A\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783093059;\n\tbh=Yq3fSQ3nwQmUtEPaiAY44uP5soMd+QiQsd5MJF9LopE=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=FNA1Mj4AIe0RVPJvV6iw/WG/adVtPgcphKiVQfGvOR4cj4ovF36fQczlvOf2ahPfy\n\t/58WHbcvX44m26xlFOyvx+2jCwF1b0GPytVDVCI4dV0Wg9D77hN4C4bvMBhrau9pWV\n\tb+ngbAtSZ4IDPkEssR3MFRgfcCfsJxEG1GKgVHOs=","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Subject":"[RFC PATCH v1 12/17] ipa: mali-c55: Port to\n\t`AgcMeanLuminanceAlgorithm`","Date":"Fri,  3 Jul 2026 17:38:14 +0200","Message-ID":"<20260703153819.1088752-13-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","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":"Closes: https://gitlab.freedesktop.org/camera/libcamera/-/work_items/262\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/ipa/mali-c55/algorithms/agc.cpp | 135 ++++++-----------------\n src/ipa/mali-c55/algorithms/agc.h   |   2 +-\n src/ipa/mali-c55/ipa_context.h      |  35 +++---\n src/ipa/mali-c55/mali-c55.cpp       | 163 ++++++----------------------\n 4 files changed, 79 insertions(+), 256 deletions(-)","diff":"diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp\nindex 8d104e01f2..386541fd58 100644\n--- a/src/ipa/mali-c55/algorithms/agc.cpp\n+++ b/src/ipa/mali-c55/algorithms/agc.cpp\n@@ -123,12 +123,19 @@ Agc::Agc()\n \n int Agc::init(IPAContext &context, const ValueNode &tuningData)\n {\n-\tint ret = agc_.parseTuningData(tuningData);\n+\tint ret = agc_.init(tuningData);\n \tif (ret)\n \t\treturn ret;\n \n-\tcontext.ctrlMap[&controls::AeEnable] = ControlInfo(false, true);\n-\tcontext.ctrlMap.merge(agc_.controls());\n+\tret = agc_.configure(context.configuration.agc, context.activeState.agc, {\n+\t\t.sensor = *context.camHelper,\n+\t\t.sensorInfo = context.sensorInfo,\n+\t\t.sensorControls = context.sensorControls,\n+\t\t.ctrlMap = context.ctrlMap,\n+\t\t.autoAllowed = true, // \\todo if not raw?\n+\t});\n+\tif (ret)\n+\t\treturn ret;\n \n \treturn 0;\n }\n@@ -140,79 +147,24 @@ int Agc::configure(IPAContext &context,\n \tif (ret)\n \t\treturn ret;\n \n-\t/*\n-\t * Defaults; we use whatever the sensor's default exposure is and the\n-\t * minimum analogue gain. AEGC is _active_ by default.\n-\t */\n-\tcontext.activeState.agc.autoEnabled = true;\n-\tcontext.activeState.agc.automatic.sensorGain = context.configuration.agc.minAnalogueGain;\n-\tcontext.activeState.agc.automatic.exposure = context.configuration.agc.defaultExposure;\n-\tcontext.activeState.agc.manual.sensorGain = context.configuration.agc.minAnalogueGain;\n-\tcontext.activeState.agc.manual.exposure = context.configuration.agc.defaultExposure;\n-\tcontext.activeState.agc.constraintMode = agc_.constraintModes().begin()->first;\n-\tcontext.activeState.agc.exposureMode = agc_.exposureModeHelpers().begin()->first;\n-\n-\t/* \\todo Run this again when FrameDurationLimits is passed in */\n-\tagc_.setLimits(context.configuration.agc.minShutterSpeed,\n-\t\t       context.configuration.agc.maxShutterSpeed,\n-\t\t       context.configuration.agc.minAnalogueGain,\n-\t\t       context.configuration.agc.maxAnalogueGain,\n-\t\t       {});\n-\n-\tagc_.resetFrameCount();\n+\tret = agc_.configure(context.configuration.agc, context.activeState.agc, {\n+\t\t.sensor = *context.camHelper,\n+\t\t.sensorInfo = context.sensorInfo,\n+\t\t.sensorControls = context.sensorControls,\n+\t\t.ctrlMap = context.ctrlMap,\n+\t\t.autoAllowed = true, // \\todo if not raw?\n+\t});\n+\tif (ret)\n+\t\treturn ret;\n \n \treturn 0;\n }\n \n-void Agc::queueRequest(IPAContext &context, const uint32_t frame,\n-\t\t       [[maybe_unused]] IPAFrameContext &frameContext,\n+void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n+\t\t       IPAFrameContext &frameContext,\n \t\t       const ControlList &controls)\n {\n-\tauto &agc = context.activeState.agc;\n-\n-\tconst auto &constraintMode = controls.get(controls::AeConstraintMode);\n-\tagc.constraintMode = constraintMode.value_or(agc.constraintMode);\n-\n-\tconst auto &exposureMode = controls.get(controls::AeExposureMode);\n-\tagc.exposureMode = exposureMode.value_or(agc.exposureMode);\n-\n-\tconst auto &agcEnable = controls.get(controls::AeEnable);\n-\tif (agcEnable && *agcEnable != agc.autoEnabled) {\n-\t\tagc.autoEnabled = *agcEnable;\n-\n-\t\tLOG(MaliC55Agc, Info)\n-\t\t\t<< (agc.autoEnabled ? \"Enabling\" : \"Disabling\")\n-\t\t\t<< \" AGC\";\n-\t}\n-\n-\t/*\n-\t * If the automatic exposure and gain is enabled we have no further work\n-\t * to do here...\n-\t */\n-\tif (agc.autoEnabled)\n-\t\treturn;\n-\n-\t/*\n-\t * ...otherwise we need to look for exposure and gain controls and use\n-\t * those to set the activeState.\n-\t */\n-\tconst auto &exposure = controls.get(controls::ExposureTime);\n-\tif (exposure) {\n-\t\tagc.manual.exposure = *exposure * 1.0us / context.configuration.sensor.lineDuration;\n-\n-\t\tLOG(MaliC55Agc, Debug)\n-\t\t\t<< \"Exposure set to \" << agc.manual.exposure\n-\t\t\t<< \" on request sequence \" << frame;\n-\t}\n-\n-\tconst auto &analogueGain = controls.get(controls::AnalogueGain);\n-\tif (analogueGain) {\n-\t\tagc.manual.sensorGain = *analogueGain;\n-\n-\t\tLOG(MaliC55Agc, Debug)\n-\t\t\t<< \"Analogue gain set to \" << agc.manual.sensorGain\n-\t\t\t<< \" on request sequence \" << frame;\n-\t}\n+\tagc_.queueRequest(context.configuration.agc, context.activeState.agc, frameContext.agc, controls);\n }\n \n void Agc::fillParamsBuffer(MaliC55Params *params, enum MaliC55Blocks type)\n@@ -265,9 +217,11 @@ void Agc::fillWeightsArrayBuffer(MaliC55Params *params, const enum MaliC55Blocks\n \tstd::fill(weights.begin(), weights.end(), 1);\n }\n \n-void Agc::prepare([[maybe_unused]] IPAContext &context, const uint32_t frame,\n-\t\t  [[maybe_unused]]  IPAFrameContext &frameContext, MaliC55Params *params)\n+void Agc::prepare(IPAContext &context, const uint32_t frame,\n+\t\t  IPAFrameContext &frameContext, MaliC55Params *params)\n {\n+\tagc_.prepare(context.activeState.agc, frameContext.agc);\n+\n \tif (frame > 0)\n \t\treturn;\n \n@@ -310,9 +264,6 @@ void Agc::process(IPAContext &context,\n \t\t  const mali_c55_stats_buffer *stats,\n \t\t  [[maybe_unused]] ControlList &metadata)\n {\n-\tIPASessionConfiguration &configuration = context.configuration;\n-\tIPAActiveState &activeState = context.activeState;\n-\n \tif (!stats) {\n \t\tLOG(MaliC55Agc, Error) << \"No statistics buffer passed to Agc\";\n \t\treturn;\n@@ -323,34 +274,12 @@ void Agc::process(IPAContext &context,\n \t\t\t\t\t\t\t       statistics_.gHist.interQuantileMean(0, 1),\n \t\t\t\t\t\t\t       statistics_.bHist.interQuantileMean(0, 1) } });\n \n-\t/*\n-\t * The Agc algorithm needs to know the effective exposure value that was\n-\t * applied to the sensor when the statistics were collected.\n-\t */\n-\tuint32_t exposure = frameContext.agc.exposure;\n-\tdouble analogueGain = frameContext.agc.sensorGain;\n-\tutils::Duration currentShutter = exposure * configuration.sensor.lineDuration;\n-\tutils::Duration effectiveExposureValue = currentShutter * analogueGain;\n-\tAgcTraits agcTraits(statistics_);\n-\n-\n-\tutils::Duration shutterTime;\n-\tdouble aGain, qGain, dGain;\n-\tstd::tie(shutterTime, aGain, qGain, dGain) =\n-\t\tagc_.calculateNewEv(activeState.agc.constraintMode,\n-\t\t\t\t    activeState.agc.exposureMode, statistics_.yHist,\n-\t\t\t\t    effectiveExposureValue, agcTraits);\n-\n-\tLOG(MaliC55Agc, Debug)\n-\t\t<< \"Divided up shutter, analogue gain and digital gain are \"\n-\t\t<< shutterTime << \", \" << aGain << \" and \" << dGain;\n-\n-\tactiveState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration;\n-\tactiveState.agc.automatic.sensorGain = aGain;\n-\n-\tmetadata.set(controls::ExposureTime, currentShutter.get<std::micro>());\n-\tmetadata.set(controls::AnalogueGain, frameContext.agc.sensorGain);\n-\tmetadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK);\n+\tagc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{\n+\t\t.traits = AgcTraits(statistics_),\n+\t\t.hist = statistics_.yHist,\n+\t\t.exposure = frameContext.sensor.exposure,\n+\t\t.gain = frameContext.sensor.gain,\n+\t}}, metadata);\n }\n \n REGISTER_IPA_ALGORITHM(Agc, \"Agc\")\ndiff --git a/src/ipa/mali-c55/algorithms/agc.h b/src/ipa/mali-c55/algorithms/agc.h\nindex ec7fe28c17..995a176a19 100644\n--- a/src/ipa/mali-c55/algorithms/agc.h\n+++ b/src/ipa/mali-c55/algorithms/agc.h\n@@ -68,7 +68,7 @@ private:\n \tvoid fillWeightsArrayBuffer(MaliC55Params *params, enum MaliC55Blocks type);\n \n \tAgcStatistics statistics_;\n-\tAgcMeanLuminance agc_;\n+\tAgcMeanLuminanceAlgorithm agc_;\n };\n \n } /* namespace ipa::mali_c55::algorithms */\ndiff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h\nindex 075d6f66ef..0d8c0f986c 100644\n--- a/src/ipa/mali-c55/ipa_context.h\n+++ b/src/ipa/mali-c55/ipa_context.h\n@@ -12,6 +12,8 @@\n \n #include \"libcamera/internal/bayer_format.h\"\n \n+#include <libipa/agc_mean_luminance.h>\n+#include <libipa/camera_sensor_helper.h>\n #include <libipa/fc_queue.h>\n \n #include \"libipa/fixedpoint.h\"\n@@ -21,34 +23,17 @@ namespace libcamera {\n namespace ipa::mali_c55 {\n \n struct IPASessionConfiguration {\n-\tstruct {\n-\t\tutils::Duration minShutterSpeed;\n-\t\tutils::Duration maxShutterSpeed;\n-\t\tuint32_t defaultExposure;\n-\t\tdouble minAnalogueGain;\n-\t\tdouble maxAnalogueGain;\n+\tstruct Agc : AgcMeanLuminanceAlgorithm::Session {\n \t} agc;\n \n \tstruct {\n \t\tBayerFormat::Order bayerOrder;\n-\t\tutils::Duration lineDuration;\n \t\tuint32_t blackLevel;\n \t} sensor;\n };\n \n struct IPAActiveState {\n-\tstruct {\n-\t\tstruct {\n-\t\t\tuint32_t exposure;\n-\t\t\tdouble sensorGain;\n-\t\t} automatic;\n-\t\tstruct {\n-\t\t\tuint32_t exposure;\n-\t\t\tdouble sensorGain;\n-\t\t} manual;\n-\t\tbool autoEnabled;\n-\t\tuint32_t constraintMode;\n-\t\tuint32_t exposureMode;\n+\tstruct Agc : AgcMeanLuminanceAlgorithm::ActiveState {\n \t\tuint32_t temperatureK;\n \t} agc;\n \n@@ -59,10 +44,13 @@ struct IPAActiveState {\n };\n \n struct IPAFrameContext : public FrameContext {\n+\tstruct Agc : AgcMeanLuminanceAlgorithm::FrameContext {\n+\t} agc;\n+\n \tstruct {\n \t\tuint32_t exposure;\n-\t\tdouble sensorGain;\n-\t} agc;\n+\t\tdouble gain;\n+\t} sensor;\n \n \tstruct {\n \t\tUQ<4, 8> rGain;\n@@ -77,10 +65,15 @@ struct IPAContext {\n \t}\n \n \tIPASessionConfiguration configuration;\n+\tIPACameraSensorInfo sensorInfo;\n \tIPAActiveState activeState;\n \n \tFCQueue<IPAFrameContext> frameContexts;\n \n+\tControlInfoMap sensorControls;\n+\n+\tstd::unique_ptr<CameraSensorHelper> camHelper;\n+\n \tControlInfoMap::Map ctrlMap;\n };\n \ndiff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp\nindex 1d3af0627f..3374316fa6 100644\n--- a/src/ipa/mali-c55/mali-c55.cpp\n+++ b/src/ipa/mali-c55/mali-c55.cpp\n@@ -65,21 +65,11 @@ protected:\n \tstd::string logPrefix() const override;\n \n private:\n-\tvoid updateSessionConfiguration(const IPACameraSensorInfo &info,\n-\t\t\t\t\tconst ControlInfoMap &sensorControls,\n-\t\t\t\t\tBayerFormat::Order bayerOrder);\n-\tvoid updateControls(const IPACameraSensorInfo &sensorInfo,\n-\t\t\t    const ControlInfoMap &sensorControls,\n-\t\t\t    ControlInfoMap *ipaControls);\n-\tvoid setControls();\n+\tvoid updateControls(ControlInfoMap *ipaControls);\n+\tvoid setControls(const IPAFrameContext &frameContext);\n \n \tstd::map<unsigned int, MappedFrameBuffer> buffers_;\n \n-\tControlInfoMap sensorControls_;\n-\n-\t/* Interface to the Camera Helper */\n-\tstd::unique_ptr<CameraSensorHelper> camHelper_;\n-\n \t/* Local parameter storage */\n \tstruct IPAContext context_;\n };\n@@ -101,8 +91,8 @@ std::string IPAMaliC55::logPrefix() const\n int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,\n \t\t     ControlInfoMap *ipaControls)\n {\n-\tcamHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n-\tif (!camHelper_) {\n+\tcontext_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n+\tif (!context_.camHelper) {\n \t\tLOG(IPAMaliC55, Error)\n \t\t\t<< \"Failed to create camera sensor helper for \"\n \t\t\t<< settings.sensorModel;\n@@ -128,30 +118,24 @@ int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig\n \t\treturn -EINVAL;\n \t}\n \n+\tcontext_.sensorControls = ipaConfig.sensorControls;\n+\tcontext_.sensorInfo = ipaConfig.sensorInfo;\n+\n \tint ret = createAlgorithms(context_, (*data)[\"algorithms\"]);\n \tif (ret)\n \t\treturn ret;\n \n-\tupdateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls);\n+\tupdateControls(ipaControls);\n \n \treturn 0;\n }\n \n-void IPAMaliC55::setControls()\n+void IPAMaliC55::setControls(const IPAFrameContext &frameContext)\n {\n-\tIPAActiveState &activeState = context_.activeState;\n-\tuint32_t exposure;\n-\tuint32_t gain;\n-\n-\tif (activeState.agc.autoEnabled) {\n-\t\texposure = activeState.agc.automatic.exposure;\n-\t\tgain = camHelper_->gainCode(activeState.agc.automatic.sensorGain);\n-\t} else {\n-\t\texposure = activeState.agc.manual.exposure;\n-\t\tgain = camHelper_->gainCode(activeState.agc.manual.sensorGain);\n-\t}\n+\tuint32_t exposure = frameContext.agc.exposure;\n+\tuint32_t gain = context_.camHelper->gainCode(frameContext.agc.gain);\n \n-\tControlList ctrls(sensorControls_);\n+\tControlList ctrls(context_.sensorControls);\n \tctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));\n \tctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));\n \n@@ -168,111 +152,19 @@ void IPAMaliC55::stop()\n \tcontext_.frameContexts.clear();\n }\n \n-void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info,\n-\t\t\t\t\t    const ControlInfoMap &sensorControls,\n-\t\t\t\t\t    BayerFormat::Order bayerOrder)\n-{\n-\tcontext_.configuration.sensor.bayerOrder = bayerOrder;\n-\n-\tconst ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;\n-\tint32_t minExposure = v4l2Exposure.min().get<int32_t>();\n-\tint32_t maxExposure = v4l2Exposure.max().get<int32_t>();\n-\tint32_t defExposure = v4l2Exposure.def().get<int32_t>();\n-\n-\tconst ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n-\tint32_t minGain = v4l2Gain.min().get<int32_t>();\n-\tint32_t maxGain = v4l2Gain.max().get<int32_t>();\n-\n-\t/*\n-\t * When the AGC computes the new exposure values for a frame, it needs\n-\t * to know the limits for shutter speed and analogue gain.\n-\t * As it depends on the sensor, update it with the controls.\n-\t *\n-\t * \\todo take VBLANK into account for maximum shutter speed\n-\t */\n-\tcontext_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate;\n-\tcontext_.configuration.agc.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration;\n-\tcontext_.configuration.agc.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration;\n-\tcontext_.configuration.agc.defaultExposure = defExposure;\n-\tcontext_.configuration.agc.minAnalogueGain = camHelper_->gain(minGain);\n-\tcontext_.configuration.agc.maxAnalogueGain = camHelper_->gain(maxGain);\n-\n-\tif (camHelper_->blackLevel().has_value()) {\n-\t\t/*\n-\t\t * The black level from CameraSensorHelper is a 16-bit value.\n-\t\t * The Mali-C55 ISP expects 20-bit settings, so we shift it to\n-\t\t * the appropriate width\n-\t\t */\n-\t\tcontext_.configuration.sensor.blackLevel =\n-\t\t\tcamHelper_->blackLevel().value() << 4;\n-\t}\n-}\n-\n-void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,\n-\t\t\t\tconst ControlInfoMap &sensorControls,\n-\t\t\t\tControlInfoMap *ipaControls)\n+void IPAMaliC55::updateControls(ControlInfoMap *ipaControls)\n {\n \tControlInfoMap::Map ctrlMap;\n \n-\t/*\n-\t * Compute the frame duration limits.\n-\t *\n-\t * The frame length is computed assuming a fixed line length combined\n-\t * with the vertical frame sizes.\n-\t */\n-\tconst ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second;\n-\tuint32_t hblank = v4l2HBlank.def().get<int32_t>();\n-\tuint32_t lineLength = sensorInfo.outputSize.width + hblank;\n-\n-\tconst ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second;\n-\tstd::array<uint32_t, 3> frameHeights{\n-\t\tv4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height,\n-\t\tv4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height,\n-\t\tv4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height,\n-\t};\n-\n-\tstd::array<int64_t, 3> frameDurations;\n-\tfor (unsigned int i = 0; i < frameHeights.size(); ++i) {\n-\t\tuint64_t frameSize = lineLength * frameHeights[i];\n-\t\tframeDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U);\n-\t}\n-\n-\tctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0],\n-\t\t\t\t\t\t\t      frameDurations[1],\n-\t\t\t\t\t\t\t      Span<const int64_t, 2>{ { frameDurations[2], frameDurations[2] } });\n-\n-\t/*\n-\t * Compute exposure time limits from the V4L2_CID_EXPOSURE control\n-\t * limits and the line duration.\n-\t */\n-\tdouble lineDuration = sensorInfo.minLineLength / sensorInfo.pixelRate;\n-\n-\tconst ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;\n-\tint32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;\n-\tint32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration;\n-\tint32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration;\n-\tctrlMap[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure, defExposure);\n-\n-\t/* Compute the analogue gain limits. */\n-\tconst ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n-\tfloat minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>());\n-\tfloat maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>());\n-\tfloat defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>());\n-\tctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain);\n-\n-\t/*\n-\t * Merge in any controls that we support either statically or from the\n-\t * algorithms.\n-\t */\n \tctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end());\n-\n \t*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);\n }\n \n int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,\n \t\t\t  ControlInfoMap *ipaControls)\n {\n-\tsensorControls_ = ipaConfig.sensorControls;\n+\tcontext_.sensorControls = ipaConfig.sensorControls;\n+\tcontext_.sensorInfo = ipaConfig.sensorInfo;\n \n \t/* Clear the IPA context before the streaming session. */\n \tcontext_.configuration = {};\n@@ -281,9 +173,16 @@ int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,\n \n \tconst IPACameraSensorInfo &info = ipaConfig.sensorInfo;\n \n-\tupdateSessionConfiguration(info, ipaConfig.sensorControls,\n-\t\t\t\t   static_cast<BayerFormat::Order>(bayerOrder));\n-\tupdateControls(info, ipaConfig.sensorControls, ipaControls);\n+\tcontext_.configuration.sensor.bayerOrder = static_cast<BayerFormat::Order>(bayerOrder);\n+\n+\tif (auto bl = context_.camHelper->blackLevel()) {\n+\t\t/*\n+\t\t * The black level from CameraSensorHelper is a 16-bit value.\n+\t\t * The Mali-C55 ISP expects 20-bit settings, so we shift it to\n+\t\t * the appropriate width\n+\t\t */\n+\t\tcontext_.configuration.sensor.blackLevel = *bl << 4;\n+\t}\n \n \tfor (const auto &a : algorithms()) {\n \t\tAlgorithm *algo = static_cast<Algorithm *>(a.get());\n@@ -293,6 +192,8 @@ int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,\n \t\t\treturn ret;\n \t}\n \n+\tupdateControls(ipaControls);\n+\n \treturn 0;\n }\n \n@@ -352,10 +253,10 @@ void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,\n \tstats = reinterpret_cast<mali_c55_stats_buffer *>(\n \t\tbuffers_.at(bufferId).planes()[0].data());\n \n-\tframeContext.agc.exposure =\n-\t\tsensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n-\tframeContext.agc.sensorGain =\n-\t\tcamHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());\n+\tframeContext.sensor = {\n+\t\t.exposure = static_cast<uint32_t>(sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>()),\n+\t\t.gain = context_.camHelper->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>()),\n+\t};\n \n \tControlList metadata(controls::controls);\n \n@@ -365,7 +266,7 @@ void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,\n \t\talgo->process(context_, request, frameContext, stats, metadata);\n \t}\n \n-\tsetControls();\n+\tsetControls(frameContext);\n \n \tstatsProcessed.emit(request, metadata);\n }\n","prefixes":["RFC","v1","12/17"]}