Show a patch.

GET /api/patches/26637/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 26637,
    "url": "https://patchwork.libcamera.org/api/patches/26637/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/26637/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/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": "<20260505-extensible-stats-v1-2-0b56c7b1bbd6@ideasonboard.com>",
    "date": "2026-05-05T16:11:10",
    "name": "[2/3] ipa: libipa: Introduce V4L2Stats",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "84c886468378e34759ce006ab136082a2ce94b61",
    "submitter": {
        "id": 143,
        "url": "https://patchwork.libcamera.org/api/people/143/?format=api",
        "name": "Jacopo Mondi",
        "email": "jacopo.mondi@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/26637/mbox/",
    "series": [
        {
            "id": 5908,
            "url": "https://patchwork.libcamera.org/api/series/5908/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5908",
            "date": "2026-05-05T16:11:08",
            "name": "ipa: libipa: Add support for V4L2 ISP statistics",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5908/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/26637/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/26637/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 CCE42C32F7\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  5 May 2026 16:11:28 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8FE0163030;\n\tTue,  5 May 2026 18:11:25 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 35B2762DC4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  5 May 2026 18:11:22 +0200 (CEST)",
            "from [192.168.1.83] (unknown\n\t[IPv6:2001:b07:6462:5de2:520d:d7a3:63ca:99e8])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 69ACC874;\n\tTue,  5 May 2026 18:11:19 +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=\"XxBerSDd\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1777997479;\n\tbh=bGgqS43n/jFzC4FRyn+b3LDWNsNBrG90ma0D+MzewpQ=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=XxBerSDdCzJV1pYSpnLFiZ0jukAM0tEhG/eDMwJt3Y0Gq2Qk5TGUcYgIU4E9Rfoi9\n\tofS7BS+aqdoGC+yQMeVA490klfaofzZNhe8b3xg/9MGT3hgfwRsm7MtqA6+qF+BvzM\n\tudjzHD37AYHs/UcxjLlh/I/9MsOjI2skLN6Z7GMM=",
        "From": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>",
        "Date": "Tue, 05 May 2026 18:11:10 +0200",
        "Subject": "[PATCH 2/3] ipa: libipa: Introduce V4L2Stats",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "7bit",
        "Message-Id": "<20260505-extensible-stats-v1-2-0b56c7b1bbd6@ideasonboard.com>",
        "References": "<20260505-extensible-stats-v1-0-0b56c7b1bbd6@ideasonboard.com>",
        "In-Reply-To": "<20260505-extensible-stats-v1-0-0b56c7b1bbd6@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org, \n\tAntoine Bouyer <antoine.bouyer@nxp.com>",
        "Cc": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>",
        "X-Mailer": "b4 0.14.3",
        "X-Developer-Signature": "v=1; a=openpgp-sha256; l=11093;\n\ti=jacopo.mondi@ideasonboard.com; h=from:subject:message-id;\n\tbh=bGgqS43n/jFzC4FRyn+b3LDWNsNBrG90ma0D+MzewpQ=;\n\tb=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBp+hao6oHk4Q0wT5k+uW0q371es/KGiYotaP0zx\n\tpvxL2VkZLWJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCafoWqAAKCRByNAaPFqFW\n\tPMfyD/9Q5CP1rsF1qHs1D8RGXESPjF2xeFar1nsF9EHUIKUZUSVbDerg5FpkrFUDFsDPEGEN6SG\n\t8jGRVG3vjX8KLK4E4TONzzBNII/95i79h9c8YDLwKFMsVh8dUrC2d+pMrZpwdDLiIPapYFeXYVJ\n\tsmQDu4BB97zuI29qbrhA36tEzff4mKROgENyAEZkWcW30df86MePanqChhcGCqKBxqNHIRrHoe2\n\tgt7yhI6O+/fGYrt8TMfHDsj8LE6E/7xir+OwfQ6Tg+yIEoD8/0q31Z6imL0mrXCmLxN3mKyTKEL\n\tY/MdBptLGZP2r5JlURpkKFcVkvGKeVa3CUQXM++U2Lx/dZtR6hGZSEFyanvTm9hzEUSfg9m8JCo\n\t1gILq5Xm3RZB80Syn7pFDowIw8nPxZMLUQPuZjtgxiVNh69CADlV40ndwmCW91KkUpnCY6Ss83D\n\tN91Q2fn7K4CK/wkXaTkGILE0hmNSNVGQmuna9/H3aFiXUNr+YoJQWSFHphVRJHqyEa6k0W2qlNX\n\tBDvXp8H5FCx2SBQ9QYB1RztZCaBVCEQ5oOmI1H4mVYFE/rruks9hdMwTwFusiyhtaTTx8FjI45Z\n\tvAE2XDE3SK0I2DJv/u1z9M6DOA3OK6EMmdOGhG1vQlNXf5Nbp+Z72kGlvBw6y4bXmYuKHV49I+z\n\tUZb5S10S280YEsg==",
        "X-Developer-Key": "i=jacopo.mondi@ideasonboard.com; a=openpgp;\n\tfpr=72392EDC88144A65C701EA9BA5826A2587AD026B",
        "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": "Add a V4L2Stats class similar in spirit to the existing V4L2Params\nclass to allow IPA modules to easily sub-class it to access ISP\nstatistics blocks serialized into a v4l2_isp_buffer.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/libipa/meson.build    |   2 +\n src/ipa/libipa/v4l2_stats.cpp | 226 ++++++++++++++++++++++++++++++++++++++++++\n src/ipa/libipa/v4l2_stats.h   | 124 +++++++++++++++++++++++\n 3 files changed, 352 insertions(+)",
    "diff": "diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\nindex 963c5ee73063..16f4b095f220 100644\n--- a/src/ipa/libipa/meson.build\n+++ b/src/ipa/libipa/meson.build\n@@ -19,6 +19,7 @@ libipa_headers = files([\n     'pwl.h',\n     'quantized.h',\n     'v4l2_params.h',\n+    'v4l2_stats.h',\n ])\n \n libipa_sources = files([\n@@ -40,6 +41,7 @@ libipa_sources = files([\n     'pwl.cpp',\n     'quantized.cpp',\n     'v4l2_params.cpp',\n+    'v4l2_stats.cpp',\n ])\n \n libipa_includes = include_directories('..')\ndiff --git a/src/ipa/libipa/v4l2_stats.cpp b/src/ipa/libipa/v4l2_stats.cpp\nnew file mode 100644\nindex 000000000000..050e2329dfa9\n--- /dev/null\n+++ b/src/ipa/libipa/v4l2_stats.cpp\n@@ -0,0 +1,226 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas On Board\n+ *\n+ * V4L2 Statistics\n+ */\n+\n+#include \"v4l2_stats.h\"\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(V4L2Stats)\n+\n+namespace ipa {\n+\n+/**\n+ * \\file v4l2_stats.cpp\n+ * \\brief Helper class to handle an ISP statistics buffer compatible with\n+ * the generic V4L2 ISP format\n+ *\n+ * The Linux kernel defines a generic buffer format for ISP statistics.\n+ * The format describes a serialisation method that allows userspace to\n+ * access statistics data from a binary buffer.\n+ *\n+ * The V4L2Stats class implements support the V4L2 ISP statistics buffer format\n+ * and allows users to retrieve an ISP statistics blocks, represented as\n+ * V4L2StatsBlock class instances.\n+ *\n+ * IPA implementations using this helpers should define an enumeration of ISP\n+ * blocks supported by the IPA module and use a set of common abstraction to\n+ * help their derived implementation of V4L2Stats translate the enumerated ISP\n+ * block identifiers to the actual type of the statistics data as defined by\n+ * the kernel interface.\n+ */\n+\n+/**\n+ * \\class V4L2StatsBlock\n+ * \\brief Helper class that represents an ISP statistics block\n+ *\n+ * Each ISP measurement block produces a set of statistics data whose memory\n+ * layout is defined by the kernel interface.\n+ *\n+ * This class represents an ISP statistics block entry. It is constructed\n+ * with a reference to the memory area where the statistics block is\n+ * stored in the statistics buffer. The template parameter represents\n+ * the underlying kernel-defined ISP statistics block type and allows its\n+ * user to easily cast it to said type to read the statistics data.\n+ *\n+ * \\sa V4L2Stats::block()\n+ */\n+\n+/**\n+ * \\fn V4L2StatsBlock::V4L2StatsBlock()\n+ * \\brief Construct a V4L2StatsBlock with memory represented by \\a data\n+ * \\param[in] data The memory area where the statistics block is located\n+ */\n+\n+/**\n+ * \\fn V4L2StatsBlock::operator->() const\n+ * \\brief Access the statistics block casting it to the kernel-defined\n+ * ISP block type\n+ *\n+ * The V4L2StatsBlock is templated with the kernel defined ISP block type. This\n+ * function allows users to easily cast a V4L2StatsBlock to the underlying\n+ * kernel-defined type in order to easily access the statistics data.\n+ *\n+ * \\code{.cpp}\n+ *\n+ * // The kernel header defines the ISP statistics types, in example\n+ * // struct my_isp_awb_stats_data {\n+ * //\t\tu16 mean_r;\n+ * //\t\tu16 mean_g;\n+ * //\t\tu16 mean_b;\n+ * //  }\n+ *\n+ * template<> V4L2StatsBlock<struct my_isp_awb_stats_data> awbStats = ...\n+ *\n+ * u16 mean_r = awbStats->mean_r;\n+ * u16 mean_g = awbStats->mean_g;\n+ * u16 mean_b = awbStats->mean_b;\n+ *\n+ * \\endcode\n+ *\n+ * Users of this class shall not create a V4L2StatsBlock manually but should\n+ * use V4L2Stats::block().\n+ */\n+\n+/**\n+ * \\fn V4L2StatsBlock::operator*() const\n+ * \\copydoc V4L2StatsBlock::operator->() const\n+ */\n+\n+/**\n+ * \\fn V4L2StatsBlock::size() const\n+ * \\brief Retrieve the size (in bytes) of the ISP statistics block, including\n+ * the block's header\n+ * \\return The size of the stats block\n+ */\n+\n+/**\n+ * \\var V4L2StatsBlock::data_\n+ * \\brief Memory area where ISP statistics have been serialized\n+ */\n+\n+/**\n+ * \\class V4L2Stats\n+ * \\brief Helper class that represent an ISP statistics buffer\n+ *\n+ * This class represents an ISP statistics buffer. It is constructed with a\n+ * reference to the memory mapped buffer that has been dequeued from the ISP\n+ * driver.\n+ *\n+ * This class is templated with the type of the enumeration of ISP blocks that\n+ * each IPA module is expected to support. IPA modules are expected to derive\n+ * this class by providing a 'stats_traits' type that helps the class associate\n+ * a block type with the actual memory area that represents the ISP statistics\n+ * block.\n+ *\n+ * \\code{.cpp}\n+ *\n+ * // Define the supported ISP statistics blocks\n+ * enum class myISPStats {\n+ *\tAgc,\n+ *\tAwb,\n+ *\t...\n+ * };\n+ *\n+ * // Maps the C++ enum type to the kernel enum type and concrete parameter type\n+ * template<myISPStats B>\n+ * struct block_type {\n+ * };\n+ *\n+ * template<>\n+ * struct block_type<myISPStats::Agc> {\n+ *\tusing type = struct my_isp_kernel_stats_type_agc;\n+ *\tstatic constexpr kernel_enum_type blockType = MY_ISP_STATS_TYPE_AGC;\n+ * };\n+ *\n+ * template<>\n+ * struct block_type<myISPStats::Awb> {\n+ *\tusing type = struct my_isp_kernel_stats_type_awb;\n+ *\tstatic constexpr kernel_enum_type blockType = MY_ISP_STATS_TYPE_AWB;\n+ * };\n+ *\n+ *\n+ * // Convenience type to associate a block id to the 'block_type' overload\n+ * struct stats_traits {\n+ * \tusing id_type = myISPStats;\n+ * \ttemplate<id_type Id> using id_to_details = block_type<Id>;\n+ * };\n+ *\n+ * ...\n+ *\n+ * // Derive the V4L2Stats class by providing stats_traits\n+ * class MyISPStats : public V4L2Stats<stats_traits>\n+ * {\n+ * public:\n+ * \tMyISPStats::MyISPStats(Span<uint8_t> data)\n+ * \t\t: V4L2Stats(data)\n+ * \t{\n+ * \t}\n+ * };\n+ *\n+ * \\endcode\n+ *\n+ * Users of this class can then easily access an ISP statistics block as a\n+ * V4L2StatsBlock instance.\n+ *\n+ * \\code{.cpp}\n+ *\n+ * MyISPStats stats(data);\n+ *\n+ * auto awb = stats.block<myISPStats::AWB>();\n+ * auto mean_r = awb->mean_r;\n+ * auto mean_g = awb->mean_g;\n+ * auto mean_b = awb->mean_b;\n+ * \\endcode\n+ */\n+\n+/**\n+ * \\fn V4L2Stats::V4L2Stats()\n+ * \\brief Construct an instance of V4L2Stats\n+ * \\param[in] data Reference to the v4l2-buffer memory mapped area\n+ * \\param[in] version The expected V4L2 ISP serialization format version\n+ */\n+\n+/**\n+ * \\fn V4L2Stats::bytesused()\n+ * \\brief Retrieve the size of the statistics buffer (in bytes)\n+ * \\return The number of bytes occupied by the ISP statistics\n+ */\n+\n+/**\n+ * \\fn V4L2Stats::block() const\n+ * \\brief Retrieve the location of an ISP statistics block a return it\n+ * \\return A V4L2StatsBlock instance that points to the ISP statistics block\n+ */\n+\n+/**\n+ * \\fn V4L2Stats::block(unsigned int blockType, size_t blockSize) const\n+ * \\brief Retrieve an ISP statistics block a returns a reference to it\n+ * \\param[in] blockType The kernel-defined ISP block identifier, used to\n+ * identify the block header\n+ * \\param[in] blockSize The ISP statistics block size, for validation\n+ *\n+ * Parse the buffer of serialized statistics data and retrieve the location\n+ * of the block with type \\a blockType of size \\a blockSize.\n+ *\n+ * IPA modules that derive the V4L2Stats class shall use this function to\n+ * retrieve the memory area that will be used to construct a V4L2StatsBlock<T>\n+ * before returning it to the caller.\n+ */\n+\n+/**\n+ * \\var V4L2Stats::data_\n+ * \\brief The ISP statistics buffer memory\n+ */\n+\n+/**\n+ * \\var V4L2Stats::used_\n+ * \\brief The number of bytes used by the statistics buffer\n+ */\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/v4l2_stats.h b/src/ipa/libipa/v4l2_stats.h\nnew file mode 100644\nindex 000000000000..404cb606e135\n--- /dev/null\n+++ b/src/ipa/libipa/v4l2_stats.h\n@@ -0,0 +1,124 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2026, Ideas On Board\n+ *\n+ * V4L2 Stats\n+ */\n+\n+#pragma once\n+\n+#include <stdint.h>\n+\n+#include <linux/media/v4l2-isp.h>\n+\n+#include <libcamera/base/log.h>\n+#include <libcamera/base/span.h>\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(V4L2Stats)\n+\n+namespace ipa {\n+\n+template<typename T>\n+class V4L2StatsBlock\n+{\n+public:\n+\tV4L2StatsBlock(const Span<const uint8_t> data)\n+\t\t: data_(data)\n+\t{\n+\t}\n+\n+\tvirtual ~V4L2StatsBlock() {}\n+\n+\tvirtual const T *operator->() const\n+\t{\n+\t\treturn reinterpret_cast<const T *>(data_.data());\n+\t}\n+\n+\tvirtual const T &operator*() const\n+\t{\n+\t\treturn *reinterpret_cast<const T *>(data_.data());\n+\t}\n+\n+\tsize_t size() const\n+\t{\n+\t\treturn data_.size();\n+\t}\n+\n+protected:\n+\tSpan<const uint8_t> data_;\n+};\n+\n+template<typename Traits>\n+class V4L2Stats\n+{\n+public:\n+\tV4L2Stats(Span<uint8_t> data, unsigned int version)\n+\t\t: data_(data)\n+\t{\n+\t\tstruct v4l2_isp_buffer *stats =\n+\t\t\treinterpret_cast<struct v4l2_isp_buffer *>(data_.data());\n+\t\tused_ = stats->data_size;\n+\n+\t\tif (version != stats->version)\n+\t\t\tLOG(V4L2Stats, Error)\n+\t\t\t\t<< \"Unsupported v4l2-isp version: \" << stats->version;\n+\t}\n+\n+\tsize_t bytesused() const { return used_; }\n+\n+\ttemplate<typename Traits::id_type Id>\n+\tauto block() const\n+\t{\n+\t\tusing Details = typename Traits::template id_to_details<Id>;\n+\n+\t\tusing Type = typename Details::type;\n+\t\tconstexpr auto kernelId = Details::blockType;\n+\n+\t\tauto data = block(kernelId, sizeof(Type));\n+\t\treturn V4L2StatsBlock<Type>(data);\n+\t}\n+\n+protected:\n+\tconst Span<const uint8_t> block(unsigned int blockType, size_t blockSize) const\n+\t{\n+\t\tstruct v4l2_isp_buffer *stats =\n+\t\t\treinterpret_cast<struct v4l2_isp_buffer *>(data_.data());\n+\n+\t\t__u8 *data = stats->data;\n+\t\twhile (data < stats->data + stats->data_size) {\n+\t\t\tstruct v4l2_isp_block_header *header =\n+\t\t\t\treinterpret_cast<struct v4l2_isp_block_header *>(data);\n+\n+\t\t\tif (header->type != blockType) {\n+\t\t\t\tdata += header->size;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (header->size != blockSize) {\n+\t\t\t\tLOG(V4L2Stats, Error)\n+\t\t\t\t\t<< \"Block type \" << blockType\n+\t\t\t\t\t<< \" size mistmatch: expected \"\n+\t\t\t\t\t<< blockSize << \" got:\"\n+\t\t\t\t\t<< header->size;\n+\t\t\t\treturn {};\n+\t\t\t}\n+\n+\t\t\tSpan<const uint8_t> block(data, header->size);\n+\t\t\treturn block;\n+\t\t}\n+\n+\t\tLOG(V4L2Stats, Error) << \"Unsupported stats block type: \"\n+\t\t\t\t      << blockType;\n+\n+\t\treturn {};\n+\t}\n+\n+\tSpan<uint8_t> data_;\n+\tsize_t used_;\n+};\n+\n+} /* namespace ipa */\n+\n+} /* namespace libcamera */\n",
    "prefixes": [
        "2/3"
    ]
}