[{"id":39184,"web_url":"https://patchwork.libcamera.org/comment/39184/","msgid":"<20260618195059.GF2890159@ragnatech.se>","date":"2026-06-18T19:50:59","subject":"Re: [PATCH 09/14] ipa: libipa: Introduce V4L2Stats","submitter":{"id":230,"url":"https://patchwork.libcamera.org/api/people/230/","name":"Niklas Söderlund","email":"niklas.soderlund+renesas@ragnatech.se"},"content":"Hi Jacopo,\n\nThanks for your work.\n\nOn 2026-06-18 12:18:48 +0200, Jacopo Mondi wrote:\n> Add a V4L2Stats class similar in spirit to the existing V4L2Params\n> class to allow IPA modules to easily sub-class it to access ISP\n> statistics blocks serialized into a v4l2_isp_buffer.\n> \n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nReviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>\n\n> ---\n>  src/ipa/libipa/meson.build    |   2 +\n>  src/ipa/libipa/v4l2_stats.cpp | 241 ++++++++++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/v4l2_stats.h   |  67 ++++++++++++\n>  3 files changed, 310 insertions(+)\n> \n> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> index edf8eabd8b78..635abad778e6 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -20,6 +20,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> @@ -42,6 +43,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('..')\n> diff --git a/src/ipa/libipa/v4l2_stats.cpp b/src/ipa/libipa/v4l2_stats.cpp\n> new file mode 100644\n> index 000000000000..64f546d85069\n> --- /dev/null\n> +++ b/src/ipa/libipa/v4l2_stats.cpp\n> @@ -0,0 +1,241 @@\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> +#include <libcamera/base/log.h>\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +LOG_DEFINE_CATEGORY(V4L2Stats)\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 for the V4L2 ISP statistics buffer\n> + * format and allows users to retrieve an ISP statistics block.\n> + *\n> + * IPA implementations using these helpers should define an enumeration of ISP\n> + * blocks supported by the IPA module and use a set of common abstractions 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 V4L2StatsBase\n> + * \\brief Base class for V4L2Stats\n> + *\n> + * The V4L2StatsBase is an integral part of V4L2Stats. It serves as a\n> + * container for all code that does not depend on the V4L2Stats template\n> + * arguments, to avoid duplicate copies of inline code.\n> + */\n> +\n> +/**\n> + * \\brief Construct an instance of V4L2StatsBase\n> + * \\param[in] data Reference to the v4l2-buffer memory mapped area\n> + * \\param[in] version The ISP parameters version the implementation supports\n> + *\n> + * Parse the statistics buffer and construct a cache that maps the block type to\n> + * the memory location of the statistics block in the buffer.\n> + *\n> + * After construction users of this class shall check the validity of the\n> + * constructed instance using operator bool().\n> + */\n> +V4L2StatsBase::V4L2StatsBase(Span<uint8_t> data, unsigned int version)\n> +\t: data_(data), valid_(false)\n> +{\n> +\tconst struct v4l2_isp_buffer *stats =\n> +\t\treinterpret_cast<const struct v4l2_isp_buffer *>(data_.data());\n> +\n> +\tif (data_.size() - sizeof(*stats) < stats->data_size) {\n> +\t\tLOG(V4L2Stats, Error)\n> +\t\t\t<< \"Stats buffer size mismatch: \" << stats->data_size;\n> +\t\treturn;\n> +\t}\n> +\n> +\tif (version != stats->version) {\n> +\t\tLOG(V4L2Stats, Error)\n> +\t\t\t<< \"Unsupported v4l2-isp version: \" << stats->version;\n> +\t\treturn;\n> +\t}\n> +\n> +\t/* Construct the cache for easier lookup. */\n> +\tsize_t left = stats->data_size;\n> +\tconst __u8 *d = stats->data;\n> +\n> +\twhile (left > 0) {\n> +\t\tconst struct v4l2_isp_block_header *header =\n> +\t\t\treinterpret_cast<const struct v4l2_isp_block_header *>(d);\n> +\n> +\t\tif (left < sizeof(*header) || header->size < sizeof(*header)) {\n> +\t\t\tLOG(V4L2Stats, Error)\n> +\t\t\t\t<< \"Block type \" << header->type\n> +\t\t\t\t<< \" size is not valid\";\n> +\t\t\treturn;\n> +\t\t}\n> +\n> +\t\tif (left < header->size) {\n> +\t\t\tLOG(V4L2Stats, Error)\n> +\t\t\t\t<< \"Not enough space for block type \" << header->type;\n> +\t\t\treturn;\n> +\t\t}\n> +\n> +\t\tauto [it, inserted] = cache_.try_emplace(header->type, d, header->size);\n> +\t\tif (!inserted) {\n> +\t\t\tLOG(V4L2Stats, Error)\n> +\t\t\t\t<< \"Duplicated block type \" << header->type;\n> +\t\t\treturn;\n> +\t\t}\n> +\n> +\t\td += header->size;\n> +\t\tleft -= header->size;\n> +\t}\n> +\n> +\tvalid_ = true;\n> +}\n> +\n> +/**\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> + * Retrieve a span to the statistics block memory location by accessing the\n> + * cache built at class construction time.\n> + *\n> + * \\return The memory location of the ISP statistics block, or an empty Span\n> + * if \\a blockType is not supported\n> + */\n> +Span<const uint8_t> V4L2StatsBase::block(unsigned int blockType, size_t blockSize) const\n> +{\n> +\tconst auto it = cache_.find(blockType);\n> +\tif (it == cache_.end()) {\n> +\t\tLOG(V4L2Stats, Error) << \"Unsupported stats block type: \"\n> +\t\t\t\t      << blockType;\n> +\t\treturn {};\n> +\t}\n> +\n> +\tconst struct v4l2_isp_block_header *header =\n> +\t\treinterpret_cast<const struct v4l2_isp_block_header *>(it->second.data());\n> +\tif (header->size != blockSize) {\n> +\t\tLOG(V4L2Stats, Error)\n> +\t\t\t<< \"Block type \" << blockType\n> +\t\t\t<< \" size mistmatch: expected \"\n> +\t\t\t<< blockSize << \" got:\"\n> +\t\t\t<< header->size;\n> +\t\treturn {};\n> +\t}\n> +\n> +\treturn it->second;\n> +}\n> +\n> +/**\n> + * \\fn V4L2StatsBase::operator bool()\n> + * \\brief Retrieve if a statistics block is valid\n> + * \\return True if the statistics block is valid, false otherwise\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, unsigned int version)\n> + * \t\t: V4L2Stats(data, version)\n> + * \t{\n> + * \t}\n> + * };\n> + *\n> + * \\endcode\n> + *\n> + * Users of this class can then easily access an ISP statistics block using the\n> + * block() function.\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::block() const\n> + * \\brief Retrieve a pointer to an ISP statistics block\n> + * \\return A pointer to the ISP statistics block\n> + */\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/v4l2_stats.h b/src/ipa/libipa/v4l2_stats.h\n> new file mode 100644\n> index 000000000000..b96395e5f551\n> --- /dev/null\n> +++ b/src/ipa/libipa/v4l2_stats.h\n> @@ -0,0 +1,67 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2026, Ideas On Board\n> + *\n> + * V4L2 ISP Statistics\n> + */\n> +\n> +#pragma once\n> +\n> +#include <map>\n> +#include <stdint.h>\n> +\n> +#include <linux/media/v4l2-isp.h>\n> +\n> +#include <libcamera/base/span.h>\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +class V4L2StatsBase\n> +{\n> +public:\n> +\tV4L2StatsBase(Span<uint8_t> data, unsigned int version);\n> +\n> +\tSpan<const uint8_t> block(unsigned int blockType, size_t blockSize) const;\n> +\tconstexpr explicit operator bool()\n> +\t{\n> +\t\treturn valid_;\n> +\t}\n> +\n> +private:\n> +\tstd::map<uint16_t, Span<const uint8_t>> cache_;\n> +\tSpan<uint8_t> data_;\n> +\tbool valid_;\n> +};\n> +\n> +template<typename Traits>\n> +class V4L2Stats : public V4L2StatsBase\n> +{\n> +public:\n> +\tstatic_assert(std::is_same_v<std::underlying_type_t<typename Traits::id_type>, uint16_t>);\n> +\n> +\tV4L2Stats(Span<uint8_t> data, unsigned int version)\n> +\t\t: V4L2StatsBase(data, version)\n> +\t{\n> +\t}\n> +\n> +\ttemplate<typename Traits::id_type Id>\n> +\tconst typename Traits::template id_to_details<Id>::type *\n> +\tblock() 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 = V4L2StatsBase::block(kernelId, sizeof(Type));\n> +\n> +\t\treturn data.size() > 0 ?\n> +\t\t       reinterpret_cast<const Type *>(data.data()) : nullptr;\n> +\t}\n> +};\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> \n> -- \n> 2.54.0\n>","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 44D58BF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 18 Jun 2026 19:51:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 20CB662E9D;\n\tThu, 18 Jun 2026 21:51:04 +0200 (CEST)","from fhigh-b3-smtp.messagingengine.com\n\t(fhigh-b3-smtp.messagingengine.com [202.12.124.154])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C196261754\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 18 Jun 2026 21:51:02 +0200 (CEST)","from phl-compute-09.internal (phl-compute-09.internal\n\t[10.202.2.49])\n\tby mailfhigh.stl.internal (Postfix) with ESMTP id BAF807A0101;\n\tThu, 18 Jun 2026 15:51:01 -0400 (EDT)","from phl-frontend-04 ([10.202.2.163])\n\tby phl-compute-09.internal (MEProxy); Thu, 18 Jun 2026 15:51:01 -0400","by mail.messagingengine.com (Postfix) with ESMTPA; Thu,\n\t18 Jun 2026 15:51:00 -0400 (EDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=ragnatech.se header.i=@ragnatech.se\n\theader.b=\"hUL7l/Ps\"; dkim=pass (2048-bit key;\n\tunprotected) header.d=messagingengine.com\n\theader.i=@messagingengine.com header.b=\"YqfDYrPz\"; \n\tdkim-atps=neutral","DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/relaxed; d=ragnatech.se; h=\n\tcc:cc:content-transfer-encoding:content-type:content-type:date\n\t:date:from:from:in-reply-to:in-reply-to:message-id:mime-version\n\t:references:reply-to:subject:subject:to:to; s=fm3; t=1781812261;\n\tx=1781898661; bh=4pb5sb3eObG+AY/0FFyx+HemklKRUQ2NvEH+djPjeKg=; b=\n\thUL7l/PsOkUGzvrajxjgtcLW0M9Wi7/2q1jxWEqdf9nFTZp6AKpwgWYqV9qGjYqi\n\t7xk4P2lYDGzBZuENssy52u4UeqSpJmVODQF2tXYeJ0aRlHovVnrtynB6pM/1zn63\n\to93/cCubfm8nhofEeZBulJA/OKXvI0zJTQvw+15C2JhI69ze4m8GU0LZdOHqrRK+\n\tYYK/FNa7+qmrPc+CayKnY70uvUO22alUv6pHd/InmbBJxFM1Rmm3/UcYazhKfMPE\n\tpWhtYKx7oPTtFNhwsJ/0hxFJmr/6KeEHlg75+b2ZiWMS6lGFmcNvITyYtHC/GnnC\n\tha+bDCOPAhj0xLO4FdjE4w==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=\n\tmessagingengine.com; h=cc:cc:content-transfer-encoding\n\t:content-type:content-type:date:date:feedback-id:feedback-id\n\t:from:from:in-reply-to:in-reply-to:message-id:mime-version\n\t:references:reply-to:subject:subject:to:to:x-me-proxy\n\t:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t=1781812261; x=\n\t1781898661; bh=4pb5sb3eObG+AY/0FFyx+HemklKRUQ2NvEH+djPjeKg=; b=Y\n\tqfDYrPz4/W9kUFbJkPtejxtxd8uyVC7exEMok9zg6IWgRO06wBczwPrfbgIMv2zF\n\tYRiK54DtoUNFoUqr/u/biGsdJFoeX+E23cm96i1Co7Lqy2B/TJtZIikFdr5RrVX5\n\tNCa8EtgYJMx1kjDMH2XZYFgjSYjb3Iz+lgaNE3GEE7IKfJj/5Dq301/qGBihOp0d\n\tEMIapR5WFLqihk6r72d4k/58LBv06muJYUCJ9Bi4jr/Bv1u8qZBTJD7XceTcs6Yd\n\tv59i2Au4nQ/mcnBGbMCKEHtWMg39boLD+B8ALrwCx8lzMHMgKv7VMMei0OQC0VX5\n\tuev8ozh+NpUK6aTMijz1Q=="],"X-ME-Sender":"<xms:JUw0aiCeWhBd0bx84t26uhmwo0DEm5QLNdYDwL5DS_ZWXCb-qa7xog>\n\t<xme:JUw0aj8L-Jd5m4rephxtn9wNVpX4d34hmvtEpphl31cG6JGIsuJoTC4LxZPetRIvW\n\tn_qN36G2VgCc4xBzoJiklE19ul3JxKvyRPwCJT3rQyrSZVc4KKDeEKf>","X-ME-Received":"<xmr:JUw0ai9sScNSNH_IJd18YME-uzaYHpMHBhHP-NJJtaS7PqD_DbZnriv3Hz6FMI3E2kWr3J3SmJfWsUMkGpnP1vf5LA9z>","X-ME-Proxy-Cause":"dmFkZTEiGw83C1T39u6vjNJyEd4uDAhfrtu9GH8ds4SCtpJ6+1hDm+jIif90fTUB7zelvH\n\tvSJ+HhCb8ITlq0VQY/XYJ8k69MI/V+z12SEbdiyF6BjPfcxsMKys4cqPDAEVCr5QcHXtcd\n\tadejLIMa9k1U32GP9TiNdjJSvFogTMetnUzU6FbZwE25wMxrctUP3VJgt+6PcOma5yugxr\n\taLMFtZhDokX2oioVbjA20MBMZcNFXV0oD3VUg08DNpPmFYV8QiTwfmxHvBu4+39yTMVvaP\n\tkrdYg1ylO2VVB9YyPXGBOYhKttf5+Gpc8nmSOVqDbPJgqJi29j0F0JLCLCdSZiuWtD9sTt\n\tyOgOjia6VH2+TGZCjakQLVfp8gjswWcODS9lR4VQQDAXH/osEms/3Oi9CY/tXZ8OuuaAzO\n\tYnZr4QJSaD/5OElPh+3+pQ3u+S1Ox64qNdPTo06WZwKKsjw+M4yXwvnllqQkeCtmMObjVS\n\tk3mdmH87/IHcN0QrTqx3cb39y42Erv3MFC+o6SCl96bUgohCxZMY3DzF/O1+OewdgWkWKy\n\tr3sojlamSuOiUFIHLmzQTf60ZRdrQQ7acEMDWffAaMOLz4N8tsVTQh6lOn5qT1UajiREUi\n\tF1Wbmfy2/YABh5SovK4sn6LqaLTm4hufBX+CmXI341k6np9CX2Ud+f7VsxnA","X-ME-Proxy":"<xmx:JUw0aqfNk4M3xOIfOKiYW9L3LJ7pYGkfpNH17DuInC6XNQiNn-eABg>\n\t<xmx:JUw0arEVCKNpDuCakAHH8ecd90Nz-zbyEkYDm3a7kWw5KQgGEJsfDw>\n\t<xmx:JUw0aofDqzXIZMSirYn8nMwlDUc_12zmMTB4zylhhoaavMGtZELykg>\n\t<xmx:JUw0alGUFOyIV3SDW1nr2vx5MKJpb7wbWWk-tL_bWC7-x9E5zaLMOA>\n\t<xmx:JUw0atmkYSEL4AsJH4y5G-sgDmnzJjHqvDXttEEDehQT8sqTryHlIhau>","Feedback-ID":"i80c9496c:Fastmail","Date":"Thu, 18 Jun 2026 21:50:59 +0200","From":"Niklas =?utf-8?q?S=C3=B6derlund?=\n\t<niklas.soderlund+renesas@ragnatech.se>","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>","Subject":"Re: [PATCH 09/14] ipa: libipa: Introduce V4L2Stats","Message-ID":"<20260618195059.GF2890159@ragnatech.se>","References":"<20260618-rppx1-ipa-v1-0-32337264cfcd@ideasonboard.com>\n\t<20260618-rppx1-ipa-v1-9-32337264cfcd@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20260618-rppx1-ipa-v1-9-32337264cfcd@ideasonboard.com>","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>"}}]