{"id":26052,"url":"https://patchwork.libcamera.org/api/patches/26052/?format=json","web_url":"https://patchwork.libcamera.org/patch/26052/","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":"<20260130080935.2569621-4-paul.elder@ideasonboard.com>","date":"2026-01-30T08:09:34","name":"[v2,3/4] libipa: Add SyncHelper","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"f1e83ecd355bacc64536da1071ca789428e67ed0","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/?format=json","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/26052/mbox/","series":[{"id":5759,"url":"https://patchwork.libcamera.org/api/series/5759/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5759","date":"2026-01-30T08:09:31","name":"Add Sync Layer","version":2,"mbox":"https://patchwork.libcamera.org/series/5759/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/26052/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/26052/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 8C209C3226\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 30 Jan 2026 08:09:58 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4657E61FDA;\n\tFri, 30 Jan 2026 09:09:58 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8411861FC7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 30 Jan 2026 09:09:56 +0100 (CET)","from neptunite.hamster-moth.ts.net (unknown\n\t[IPv6:2404:7a81:160:2100:ec11:5e0c:deb8:1e2d])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4F8BE122A;\n\tFri, 30 Jan 2026 09:09:16 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"bj22LJTe\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769760558;\n\tbh=NTxeZCYj98CSpFNe/g4MpZo45xvZAc6k3TP0XoYClUo=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=bj22LJTeptHlrALiSUrTH4fzuja7X/6POEY7vNuhgzf6P20T0aHu4G2k3pE6lwP7e\n\tpGw5FsE1XE23MGJLTltgN+e+IYwiz94J61fowDtOfO6Qf2OQMYXfTADnKe1EpS1XxW\n\tMo9kKAfsbwr85rctygU+r2p9lUgV9Dg+dKjNflis=","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Paul Elder <paul.elder@ideasonboard.com>, david.plowman@raspberrypi.com, \n\tnaush@raspberrypi.com, kieran.bingham@ideasonboard.com,\n\tstefan.klug@ideasonboard.com","Subject":"[PATCH v2 3/4] libipa: Add SyncHelper","Date":"Fri, 30 Jan 2026 17:09:34 +0900","Message-ID":"<20260130080935.2569621-4-paul.elder@ideasonboard.com>","X-Mailer":"git-send-email 2.47.2","In-Reply-To":"<20260130080935.2569621-1-paul.elder@ideasonboard.com>","References":"<20260130080935.2569621-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","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":"The sync layer allows any Camera to get sync support as long as it\nimplements:\n- SyncAdjustment control\n- FrameDurationLimits control\n- SensorTimestamp metadata\n\nWhile FrameDurationLimits is usually implemented by AGC and\nSensorTimestamp is already implemented by all pipeline handlers, only\nSyncAdjustment is left that needs to be implemented for all platforms.\nIt naturally would be a good idea to implement it in libipa so all IPAs\ncan easily be supported.\n\nAdd SyncHelper that wraps handling of the SyncAdjustment control.\nAlthough it still needs to be plumbed into each IPA, it should mitigate\na lot of future code duplication.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n\n---\nChanges in v2:\n- move functions to header\n- make controlInfo static\n---\n src/ipa/libipa/meson.build     |  2 +\n src/ipa/libipa/sync_helper.cpp | 88 ++++++++++++++++++++++++++++++++++\n src/ipa/libipa/sync_helper.h   | 43 +++++++++++++++++\n 3 files changed, 133 insertions(+)\n create mode 100644 src/ipa/libipa/sync_helper.cpp\n create mode 100644 src/ipa/libipa/sync_helper.h","diff":"diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\nindex 7202df869c2f..44f712879b3f 100644\n--- a/src/ipa/libipa/meson.build\n+++ b/src/ipa/libipa/meson.build\n@@ -17,6 +17,7 @@ libipa_headers = files([\n     'lux.h',\n     'module.h',\n     'pwl.h',\n+    'sync_helper.h',\n     'v4l2_params.h',\n ])\n \n@@ -37,6 +38,7 @@ libipa_sources = files([\n     'lux.cpp',\n     'module.cpp',\n     'pwl.cpp',\n+    'sync_helper.cpp',\n     'v4l2_params.cpp',\n ])\n \ndiff --git a/src/ipa/libipa/sync_helper.cpp b/src/ipa/libipa/sync_helper.cpp\nnew file mode 100644\nindex 000000000000..2df6fb2473be\n--- /dev/null\n+++ b/src/ipa/libipa/sync_helper.cpp\n@@ -0,0 +1,88 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2025 Ideas on Board Oy\n+ *\n+ * Helper class that handles sync\n+ */\n+#include \"sync_helper.h\"\n+\n+#include <algorithm>\n+#include <chrono>\n+#include <libcamera/controls.h>\n+\n+/**\n+ * \\file sync_helper.h\n+ * \\brief Helper class that encapsulates handling sync\n+ */\n+\n+namespace libcamera {\n+\n+namespace ipa {\n+\n+/**\n+ * \\class SyncHelper\n+ * \\brief Class for handling sync\n+ *\n+ * In order for a Camera to support the sync algorithm (via the sync layer), it\n+ * needs to implement the FrameDurationLimits control, the SyncAdjustment\n+ * control, and the SensorTimestamp metadata. The first is handled by AGC, the\n+ * last is handled by the IPA cores, and the second one is handled by this\n+ * helper. It must be plumbed into IPAs, however.\n+ */\n+\n+/**\n+ * \\fn SyncHelper::SyncHelper()\n+ * \\brief Construct an SyncHelper instance\n+ */\n+\n+/**\n+ * \\fn SyncHelper::controlInfo(int64_t maxFrameDuration)\n+ * \\brief Return an entry for the ControlInfoMap of the IPA for SyncAdjustment\n+ * \\param[in] maxFrameDuration The maximum value for FrameDurationLimits\n+ *\n+ * This function creates an entry for SyncAdjustment that can be insterted\n+ * directly into the ControlInfoMap of the IPA.\n+ *\n+ * The SyncAdjustment limits are computed based on the \\a maxFrameDuration.\n+ * Technically the limits of SyncAdjustment depend on the currently set\n+ * FrameDurationLimits, but since they can be set in the same request this\n+ * doesn't really work. Instead report half of the maximum FrameDurationLimits\n+ * as the SyncAdjustment limits.\n+ *\n+ * \\return Entry for ControlInfoMap for SyncAdjustment\n+ */\n+\n+/**\n+ * \\fn SyncHelper::setSync(int32_t sync, utils::Duration minFrameDuration)\n+ * \\brief Set the sync adjustment value\n+ * \\param[in] sync The SyncAdjustment value as passed in by the application, in microseconds\n+ * \\param[in] minFrameDuration The minimum frame duration as set by FrameDurationLimits\n+ *\n+ * This function takes the value of SyncAdjustment as passed in by the\n+ * application, and saves it to be later read by getSync(). The \\a sync value\n+ * will be clamped by \\a minFrameDuration. This\n+ * function is meant to be called by the IPA at queueRequest time.\n+ */\n+\n+/**\n+ * \\fn SyncHelper::getSync()\n+ * \\brief Retrieve the set sync adjustment value\n+ *\n+ * This function returns the SyncAdjustment value stored in setSync(). It is\n+ * meant to be read out by the IPA when the computing the frame duration to\n+ * set, usually by AGC.\n+ *\n+ * \\return The amount of time to adjust the frame duration by for sync\n+ */\n+\n+/**\n+ * \\fn SyncHelper::resetSync()\n+ * \\brief Reset the stored sync adjustment value\n+ *\n+ * This function resets the state of the sync helper, that is it zeros the\n+ * stored frame duration offset.\n+ */\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/sync_helper.h b/src/ipa/libipa/sync_helper.h\nnew file mode 100644\nindex 000000000000..bf01658c259b\n--- /dev/null\n+++ b/src/ipa/libipa/sync_helper.h\n@@ -0,0 +1,43 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2025 Ideas on Board Oy\n+ *\n+ * Helper class that handles sync\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/base/utils.h>\n+\n+#include <libcamera/controls.h>\n+\n+namespace libcamera {\n+\n+namespace ipa {\n+\n+class SyncHelper\n+{\n+public:\n+\tstatic ControlInfo controlInfo(int64_t maxFrameDuration)\n+\t{\n+\t\treturn ControlInfo(static_cast<int32_t>(-maxFrameDuration / 2),\n+\t\t\t\t   static_cast<int32_t>(maxFrameDuration / 2), 0);\n+\t}\n+\n+\tvoid setSync(int32_t sync, utils::Duration minFrameDuration)\n+\t{\n+\t\tutils::Duration value = std::chrono::microseconds(sync);\n+\t\tframeDurationOffset_ = std::clamp(value,\n+\t\t\t\t\t\t  -minFrameDuration, minFrameDuration);\n+\t}\n+\n+\tutils::Duration getSync() const { return frameDurationOffset_; }\n+\tvoid resetSync() { frameDurationOffset_ = utils::Duration(0); }\n+\n+private:\n+\tutils::Duration frameDurationOffset_ = utils::Duration(0);\n+};\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\n","prefixes":["v2","3/4"]}