Patch Detail
Show a patch.
GET /api/1.1/patches/27187/?format=api
{ "id": 27187, "url": "https://patchwork.libcamera.org/api/1.1/patches/27187/?format=api", "web_url": "https://patchwork.libcamera.org/patch/27187/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "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-9-barnabas.pocze@ideasonboard.com>", "date": "2026-07-03T15:38:10", "name": "[RFC,v1,08/17] ipa: rkisp1: Move AGC related controls into AGC algorithm", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "a1f51c3f85a1c421928fd2a190a662558c5f8655", "submitter": { "id": 216, "url": "https://patchwork.libcamera.org/api/1.1/people/216/?format=api", "name": "Barnabás Pőcze", "email": "barnabas.pocze@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/27187/mbox/", "series": [ { "id": 6036, "url": "https://patchwork.libcamera.org/api/1.1/series/6036/?format=api", "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/27187/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/27187/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 5BB13C3303\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 3 Jul 2026 15:38:41 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0BEC66600B;\n\tFri, 3 Jul 2026 17:38:35 +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 82C7765FD8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 3 Jul 2026 17:38:24 +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 6F3E51121\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 3 Jul 2026 17:37:38 +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=\"WcZeFPHX\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783093058;\n\tbh=nxvZ/KS10UA5ATQQMLxrZN3L87OapVP3zburJqk2ZhI=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=WcZeFPHX444nK5nIMdmpb8CmNLiuyVKrFPnQwxnA6XNcx4W/V8SZ5cmYNed3OqC93\n\tGkHE1THaHnnsoi91DivEvICHiUBJUsuaRO312a9AMILwh50YyLB5Jti4rO3KW0gVk0\n\tM60igCaFL5fild8LchTcSzCX0PxD2ZAlHcn6h1y4=", "From": "=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Subject": "[RFC PATCH v1 08/17] ipa: rkisp1: Move AGC related controls into AGC\n\talgorithm", "Date": "Fri, 3 Jul 2026 17:38:10 +0200", "Message-ID": "<20260703153819.1088752-9-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": "Move the `ControlInfo` setup and related initialization into the AGC\nalgorithm instead of having it in the main IPA file.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/ipa/rkisp1/algorithms/agc.cpp | 91 ++++++++++++++++++++++++++++++-\n src/ipa/rkisp1/rkisp1.cpp | 88 +-----------------------------\n 2 files changed, 91 insertions(+), 88 deletions(-)", "diff": "diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp\nindex 540b64ff99..689d045b7a 100644\n--- a/src/ipa/rkisp1/algorithms/agc.cpp\n+++ b/src/ipa/rkisp1/algorithms/agc.cpp\n@@ -33,13 +33,96 @@ using namespace std::literals::chrono_literals;\n \n namespace ipa::rkisp1::algorithms {\n \n+LOG_DEFINE_CATEGORY(RkISP1Agc)\n+\n+namespace {\n+\n+void reconfigure(IPAContext &context)\n+{\n+\tcontext.configuration.sensor.lineDuration =\n+\t\tcontext.sensorInfo.minLineLength * 1.0s / context.sensorInfo.pixelRate;\n+\n+\tdouble lineDurationUs = context.configuration.sensor.lineDuration.get<std::micro>();\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+\n+\tconst ControlInfo &v4l2Exposure = context.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+\tcontext.ctrlMap[&controls::ExposureTime] = ControlInfo{\n+\t\tstatic_cast<int32_t>(minExposure * lineDurationUs),\n+\t\tstatic_cast<int32_t>(maxExposure * lineDurationUs),\n+\t\tstatic_cast<int32_t>(defExposure * lineDurationUs),\n+\t};\n+\n+\t/* Compute the analogue gain limits. */\n+\tconst ControlInfo &v4l2Gain = context.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n+\tfloat minGain = context.camHelper->gain(v4l2Gain.min().get<int32_t>());\n+\tfloat maxGain = context.camHelper->gain(v4l2Gain.max().get<int32_t>());\n+\tfloat defGain = context.camHelper->gain(v4l2Gain.def().get<int32_t>());\n+\tcontext.ctrlMap[&controls::AnalogueGain] = ControlInfo{\n+\t\tminGain,\n+\t\tmaxGain,\n+\t\tdefGain,\n+\t};\n+\n+\tLOG(RkISP1Agc, Debug)\n+\t\t<< \"Exposure: [\" << minExposure << \", \" << maxExposure\n+\t\t<< \"], gain: [\" << minGain << \", \" << maxGain << \"]\";\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 = context.sensorControls.find(V4L2_CID_HBLANK)->second;\n+\tuint32_t hblank = v4l2HBlank.def().get<int32_t>();\n+\tuint32_t lineLength = context.sensorInfo.outputSize.width + hblank;\n+\n+\tconst ControlInfo &v4l2VBlank = context.sensorControls.find(V4L2_CID_VBLANK)->second;\n+\tstd::array<uint32_t, 3> frameHeights{\n+\t\tv4l2VBlank.min().get<int32_t>() + context.sensorInfo.outputSize.height,\n+\t\tv4l2VBlank.max().get<int32_t>() + context.sensorInfo.outputSize.height,\n+\t\tv4l2VBlank.def().get<int32_t>() + context.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 / (context.sensorInfo.pixelRate / 1000000U);\n+\t}\n+\n+\tcontext.ctrlMap[&controls::FrameDurationLimits] = ControlInfo{\n+\t\tframeDurations[0],\n+\t\tframeDurations[1],\n+\t\tSpan<const int64_t, 2>{ { frameDurations[2], frameDurations[2] } },\n+\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 exposure time and analogue gain. As it depends\n+\t * on the sensor, update it with the controls.\n+\t *\n+\t * \\todo take VBLANK into account for maximum exposure time\n+\t */\n+\tcontext.configuration.sensor.minExposureTime = minExposure * context.configuration.sensor.lineDuration;\n+\tcontext.configuration.sensor.maxExposureTime = maxExposure * context.configuration.sensor.lineDuration;\n+\tcontext.configuration.sensor.minAnalogueGain = context.camHelper->gain(minGain);\n+\tcontext.configuration.sensor.maxAnalogueGain = context.camHelper->gain(maxGain);\n+}\n+\n+} /* namespace */\n+\n /**\n * \\class Agc\n * \\brief A mean-based auto-exposure algorithm\n */\n \n-LOG_DEFINE_CATEGORY(RkISP1Agc)\n-\n int Agc::parseMeteringModes(IPAContext &context, const ValueNode &tuningData)\n {\n \tif (!tuningData.isDictionary())\n@@ -160,6 +243,8 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData)\n \tcontext.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f);\n \tcontext.ctrlMap.merge(agc_.controls());\n \n+\treconfigure(context);\n+\n \treturn 0;\n }\n \n@@ -172,6 +257,8 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData)\n */\n int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)\n {\n+\treconfigure(context);\n+\n \t/* Configure the default exposure and gain. */\n \tcontext.activeState.agc.automatic.gain = context.configuration.sensor.minAnalogueGain;\n \tcontext.activeState.agc.automatic.exposure =\ndiff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp\nindex 66a91c4d79..38f55b1d86 100644\n--- a/src/ipa/rkisp1/rkisp1.cpp\n+++ b/src/ipa/rkisp1/rkisp1.cpp\n@@ -169,9 +169,6 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,\n \t\treturn -ENODEV;\n \t}\n \n-\tcontext_.configuration.sensor.lineDuration =\n-\t\tsensorInfo.minLineLength * 1.0s / sensorInfo.pixelRate;\n-\n \t/* Load the tuning data file. */\n \tFile file(settings.configurationFile);\n \tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n@@ -227,18 +224,6 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,\n \tcontext_.sensorInfo = ipaConfig.sensorInfo;\n \tcontext_.sensorControls = ipaConfig.sensorControls;\n \n-\tconst auto itExp = context_.sensorControls.find(V4L2_CID_EXPOSURE);\n-\tint32_t minExposure = itExp->second.min().get<int32_t>();\n-\tint32_t maxExposure = itExp->second.max().get<int32_t>();\n-\n-\tconst auto itGain = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN);\n-\tint32_t minGain = itGain->second.min().get<int32_t>();\n-\tint32_t maxGain = itGain->second.max().get<int32_t>();\n-\n-\tLOG(IPARkISP1, Debug)\n-\t\t<< \"Exposure: [\" << minExposure << \", \" << maxExposure\n-\t\t<< \"], gain: [\" << minGain << \", \" << maxGain << \"]\";\n-\n \t/* Clear the IPA context before the streaming session. */\n \tcontext_.configuration = {};\n \tcontext_.activeState = {};\n@@ -247,27 +232,6 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,\n \tcontext_.configuration.paramFormat = ipaConfig.paramFormat;\n \n \tcontext_.configuration.sensor.size = context_.sensorInfo.outputSize;\n-\tcontext_.configuration.sensor.lineDuration = context_.sensorInfo.minLineLength * 1.0s\n-\t\t\t\t\t\t\t/ context_.sensorInfo.pixelRate;\n-\n-\t/* Update the camera controls using the new sensor settings. */\n-\tupdateControls(ipaControls);\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 exposure time and analogue gain. As it depends\n-\t * on the sensor, update it with the controls.\n-\t *\n-\t * \\todo take VBLANK into account for maximum exposure time\n-\t */\n-\tcontext_.configuration.sensor.minExposureTime =\n-\t\tminExposure * context_.configuration.sensor.lineDuration;\n-\tcontext_.configuration.sensor.maxExposureTime =\n-\t\tmaxExposure * context_.configuration.sensor.lineDuration;\n-\tcontext_.configuration.sensor.minAnalogueGain =\n-\t\tcontext_.camHelper->gain(minGain);\n-\tcontext_.configuration.sensor.maxAnalogueGain =\n-\t\tcontext_.camHelper->gain(maxGain);\n \n \tcontext_.configuration.raw = std::any_of(streamConfig.begin(), streamConfig.end(),\n \t\t[](auto &cfg) -> bool {\n@@ -289,6 +253,8 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,\n \t\t\treturn ret;\n \t}\n \n+\tupdateControls(ipaControls);\n+\n \treturn 0;\n }\n \n@@ -386,56 +352,6 @@ void IPARkISP1::updateControls(ControlInfoMap *ipaControls)\n {\n \tControlInfoMap::Map ctrlMap = rkisp1Controls;\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 = context_.configuration.sensor.lineDuration.get<std::micro>();\n-\tconst ControlInfo &v4l2Exposure = context_.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.emplace(std::piecewise_construct,\n-\t\t\tstd::forward_as_tuple(&controls::ExposureTime),\n-\t\t\tstd::forward_as_tuple(minExposure, maxExposure, defExposure));\n-\n-\t/* Compute the analogue gain limits. */\n-\tconst ControlInfo &v4l2Gain = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n-\tfloat minGain = context_.camHelper->gain(v4l2Gain.min().get<int32_t>());\n-\tfloat maxGain = context_.camHelper->gain(v4l2Gain.max().get<int32_t>());\n-\tfloat defGain = context_.camHelper->gain(v4l2Gain.def().get<int32_t>());\n-\tctrlMap.emplace(std::piecewise_construct,\n-\t\t\tstd::forward_as_tuple(&controls::AnalogueGain),\n-\t\t\tstd::forward_as_tuple(minGain, maxGain, defGain));\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 = context_.sensorControls.find(V4L2_CID_HBLANK)->second;\n-\tuint32_t hblank = v4l2HBlank.def().get<int32_t>();\n-\tuint32_t lineLength = context_.sensorInfo.outputSize.width + hblank;\n-\n-\tconst ControlInfo &v4l2VBlank = context_.sensorControls.find(V4L2_CID_VBLANK)->second;\n-\tstd::array<uint32_t, 3> frameHeights{\n-\t\tv4l2VBlank.min().get<int32_t>() + context_.sensorInfo.outputSize.height,\n-\t\tv4l2VBlank.max().get<int32_t>() + context_.sensorInfo.outputSize.height,\n-\t\tv4l2VBlank.def().get<int32_t>() + context_.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 / (context_.sensorInfo.pixelRate / 1000000U);\n-\t}\n-\n-\t/* \\todo Move this (and other agc-related controls) to agc */\n-\tcontext_.ctrlMap[&controls::FrameDurationLimits] =\n-\t\tControlInfo(frameDurations[0], frameDurations[1],\n-\t\t\t ControlValue(Span<const int64_t, 2>{ { frameDurations[2], frameDurations[2] } }));\n-\n \tctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end());\n \t*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);\n }\n", "prefixes": [ "RFC", "v1", "08/17" ] }