From patchwork Fri May 8 16:48:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 26696 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id EB85ABE173 for ; Fri, 8 May 2026 16:49:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5271C62FEA; Fri, 8 May 2026 18:49:03 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="p9wZopgb"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 19DDB62FEA for ; Fri, 8 May 2026 18:49:00 +0200 (CEST) Received: from [192.168.1.7] (net-93-65-100-155.cust.vodafonedsl.it [93.65.100.155]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 185DA1B0C; Fri, 8 May 2026 18:48:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1778258935; bh=szU5xhnmuC2awTXjEVJDengDDKnLwzecQQwbP1zkErI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=p9wZopgbRf4MSYmc8BKx7IqwXIiC+EvzviSt7BHch/0F5RnQ+p237hTcikWL4yefY SIiWzdMmI5HrZe3SnCPUWcubTk3kZPicvLkTsaSFdGNzPqN4VUxgjQTvGz1gGlTDty pssxLJLD66ke8jznOV5F7BliAWDLk8mRf5fMlnrE= From: Jacopo Mondi Date: Fri, 08 May 2026 18:48:52 +0200 Subject: [PATCH v3 2/2] ipa: libipa: Introduce V4L2Stats MIME-Version: 1.0 Message-Id: <20260508-extensible-stats-v3-2-f2174ab4e124@ideasonboard.com> References: <20260508-extensible-stats-v3-0-f2174ab4e124@ideasonboard.com> In-Reply-To: <20260508-extensible-stats-v3-0-f2174ab4e124@ideasonboard.com> To: libcamera-devel@lists.libcamera.org, Antoine Bouyer Cc: Jacopo Mondi , Kieran Bingham X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=10638; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=szU5xhnmuC2awTXjEVJDengDDKnLwzecQQwbP1zkErI=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBp/hP6Mt4n3s63HSOLO7wLohNhwY6SGLQORfiVE Yx6tLqIWi+JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaf4T+gAKCRByNAaPFqFW PKkHD/4g+TAqfOWs2lmzAdD78AOeJ3cFc/9w2CBmfoF6EGBBZBj6z8Wsf4J1h2UZnDMtJnEGFWA H2gfTBW18+jycoQtUk6cKYx4IiT/lJEWQLz3vwhNU3DoDdmRRH/fBs/1Ej2sqmEH2BsfGOndD3K F4raT6C9PMdgtw0I5SfJK2N1YtZuRwOy7MzNinFgAM6fXXknrVG6OHt/1k77n5B34XsBX3hZz3w FB6R3ZUcW/2RDYd4vSBbY1QE7G1B5QNuCYOEVDC++CoMdEU7gjvkY771RweZFZ9uM3+toj176ex Q8jD7oo0sDQ/KLh9TknVTWSxlbZVrAnQzPgrbWl49R2aMW+spGmhJ27pQqfxzA+RtRRnVQ6lbMl 97s1wS8mzsZrA7KpOvj/e/VfD1o6gV4cgqg+UEuMGMKcFjkEx5Ajqopq9pq2VE1rEowajS8FEGQ Zd5rqBFi7+tYTKxl0cXlUpDTGZKb3XuX1cP40605YWRW9x4jylsM/BmroRaE1LZ5Er7tq0KlfRl ht+PyZfbp9tSOnF7Bwo5QgwTgVQIp9ZnASnUkbkhIVF+brbKHoeXw/XUxw8zBSOs4TOA1y0GrbN o9wX/pClbCrVUbREnuXYnK/xY3JWG+mJ4dCgIHBUEIA+jwv5e+ODwEVA8nrxnink6fPbHYZEMiy 8q8sc6rYz9Hz2rQ== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a V4L2Stats class similar in spirit to the existing V4L2Params class to allow IPA modules to easily sub-class it to access ISP statistics blocks serialized into a v4l2_isp_buffer. Signed-off-by: Jacopo Mondi Reviewed-by: Kieran Bingham --- src/ipa/libipa/meson.build | 2 + src/ipa/libipa/v4l2_stats.cpp | 256 ++++++++++++++++++++++++++++++++++++++++++ src/ipa/libipa/v4l2_stats.h | 67 +++++++++++ 3 files changed, 325 insertions(+) diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index 963c5ee73063..16f4b095f220 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -19,6 +19,7 @@ libipa_headers = files([ 'pwl.h', 'quantized.h', 'v4l2_params.h', + 'v4l2_stats.h', ]) libipa_sources = files([ @@ -40,6 +41,7 @@ libipa_sources = files([ 'pwl.cpp', 'quantized.cpp', 'v4l2_params.cpp', + 'v4l2_stats.cpp', ]) libipa_includes = include_directories('..') diff --git a/src/ipa/libipa/v4l2_stats.cpp b/src/ipa/libipa/v4l2_stats.cpp new file mode 100644 index 000000000000..0f807b2431a4 --- /dev/null +++ b/src/ipa/libipa/v4l2_stats.cpp @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * V4L2 ISP Statistics + */ + +#include "v4l2_stats.h" + +#include + +namespace libcamera { + +namespace ipa { + +LOG_DEFINE_CATEGORY(V4L2Stats) + +/** + * \file v4l2_stats.cpp + * \brief Helper class to handle an ISP statistics buffer compatible with + * the generic V4L2 ISP format + * + * The Linux kernel defines a generic buffer format for ISP statistics. + * The format describes a serialisation method that allows userspace to + * access statistics data from a binary buffer. + * + * The V4L2Stats class implements support for the V4L2 ISP statistics buffer + * format and allows users to retrieve an ISP statistics block. + * + * IPA implementations using these helpers should define an enumeration of ISP + * blocks supported by the IPA module and use a set of common abstractions to + * help their derived implementation of V4L2Stats translate the enumerated ISP + * block identifiers to the actual type of the statistics data as defined by + * the kernel interface. + */ + +/** + * \class V4L2StatsBase + * \brief Base class for V4L2Stats + * + * The V4L2StatsBase is an integral part of V4L2Stats. It serves as a + * container for all code that does not depend on the V4L2Stats template + * arguments, to avoid duplicate copies of inline code. + */ + +/** + * \brief Construct an instance of V4L2StatsBase + * \param[in] data Reference to the v4l2-buffer memory mapped area + * \param[in] version The ISP parameters version the implementation supports + * + * Parse the statistics buffer and construct a cache that maps the block type to + * the memory location of the statistics block in the buffer. + * + * After construction users of this class shall check the validity of the + * constructed instance using operator bool(). + */ +V4L2StatsBase::V4L2StatsBase(Span data, unsigned int version) + : data_(data), valid_(false) +{ + const struct v4l2_isp_buffer *stats = + reinterpret_cast(data_.data()); + + if (data_.size() - sizeof(*stats) < stats->data_size) { + LOG(V4L2Stats, Error) + << "Stats buffer size mismatch: " << stats->data_size; + return; + } + + if (version != stats->version) { + LOG(V4L2Stats, Error) + << "Unsupported v4l2-isp version: " << stats->version; + return; + } + + /* Construct the cache for easier lookup. */ + size_t left = stats->data_size; + const __u8 *d = stats->data; + + while (left > 0) { + const struct v4l2_isp_block_header *header = + reinterpret_cast(d); + + if (header->size > stats->data + stats->data_size - d) { + LOG(V4L2Stats, Error) + << "Block type " << header->type + << " size is not valid"; + return; + } + + cache_[header->type] = Span(d, header->size); + d += header->size; + left -= header->size; + } + + valid_ = true; +} + +/** + * \brief Retrieve an ISP statistics block a returns a reference to it + * \param[in] blockType The kernel-defined ISP block identifier, used to + * identify the block header + * \param[in] blockSize The ISP statistics block size, for validation + * + * Retrieve a span to the statistics block memory location by accessing the + * cache built at class construction time. + * + * \return The memory location of the ISP statistics block, or an empty Span + * if \a blockType is not supported + */ +Span V4L2StatsBase::block(unsigned int blockType, size_t blockSize) const +{ + const auto it = cache_.find(blockType); + if (it == cache_.end()) { + LOG(V4L2Stats, Error) << "Unsupported stats block type: " + << blockType; + return {}; + } + + const struct v4l2_isp_block_header *header = + reinterpret_cast(it->second.data()); + if (header->size != blockSize) { + LOG(V4L2Stats, Error) + << "Block type " << blockType + << " size mistmatch: expected " + << blockSize << " got:" + << header->size; + return {}; + } + + return it->second; +} + +/** + * \fn V4L2StatsBase::operator bool() + * \brief Retrieve if a V4L2StatsBase has been successfully constructed + * \return True if the instance has been constructed successfully, false + * otherwise + */ + +/** + * \var V4L2StatsBase::cache_ + * \brief Map the statistics block types to the memory area where stats are + * located + */ + +/** + * \var V4L2StatsBase::data_ + * \brief The ISP statistics buffer memory + */ + +/** + * \var V4L2StatsBase::valid_ + * \brief Flag to signal valid construction of a V4L2StatsBase instance + */ + +/** + * \class V4L2Stats + * \brief Helper class that represent an ISP statistics buffer + * + * This class represents an ISP statistics buffer. It is constructed with a + * reference to the memory mapped buffer that has been dequeued from the ISP + * driver. + * + * This class is templated with the type of the enumeration of ISP blocks that + * each IPA module is expected to support. IPA modules are expected to derive + * this class by providing a 'stats_traits' type that helps the class associate + * a block type with the actual memory area that represents the ISP statistics + * block. + * + * \code{.cpp} + * + * // Define the supported ISP statistics blocks + * enum class myISPStats { + * Agc, + * Awb, + * ... + * }; + * + * // Maps the C++ enum type to the kernel enum type and concrete parameter type + * template + * struct block_type { + * }; + * + * template<> + * struct block_type { + * using type = struct my_isp_kernel_stats_type_agc; + * static constexpr kernel_enum_type blockType = MY_ISP_STATS_TYPE_AGC; + * }; + * + * template<> + * struct block_type { + * using type = struct my_isp_kernel_stats_type_awb; + * static constexpr kernel_enum_type blockType = MY_ISP_STATS_TYPE_AWB; + * }; + * + * + * // Convenience type to associate a block id to the 'block_type' overload + * struct stats_traits { + * using id_type = myISPStats; + * template using id_to_details = block_type; + * }; + * + * ... + * + * // Derive the V4L2Stats class by providing stats_traits; return an + * // std::optional to make it easy to check if the class has been constructed + * // correctly + * class MyISPStats : public V4L2Stats + * { + * public: + * static std::optional from(Span data) + * { + * MyISPStats s(data, V4L2_ISP_VERSION_V..); + * + * return s ? std::make_optional(s) : std::nullopt; + * } + * + * private: + * MyISPStats::MyISPStats(Span data, unsigned int version) + * : V4L2Stats(data, version) + * { + * } + * }; + * + * \endcode + * + * Users of this class can then easily access an ISP statistics block using the + * block() function. + * + * \code{.cpp} + * + * MyISPStats stats(data); + * + * auto awb = stats.block(); + * auto mean_r = awb->mean_r; + * auto mean_g = awb->mean_g; + * auto mean_b = awb->mean_b; + * \endcode + */ + +/** + * \fn V4L2Stats::V4L2Stats() + * \brief Construct an instance of V4L2Stats + * \param[in] data Reference to the v4l2-buffer memory mapped area + * \param[in] version The expected V4L2 ISP serialization format version + */ + +/** + * \fn V4L2Stats::block() const + * \brief Retrieve a pointer to an ISP statistics block + * \return A pointer to the ISP statistics block + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/v4l2_stats.h b/src/ipa/libipa/v4l2_stats.h new file mode 100644 index 000000000000..88e23f363f36 --- /dev/null +++ b/src/ipa/libipa/v4l2_stats.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * V4L2 ISP Statistics + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace libcamera { + +namespace ipa { + +class V4L2StatsBase +{ +protected: + V4L2StatsBase(Span data, unsigned int version); + + Span block(unsigned int blockType, size_t blockSize) const; + constexpr explicit operator bool() + { + return valid_; + } + + std::map> cache_; + Span data_; + bool valid_; +}; + +template +class V4L2Stats : public V4L2StatsBase +{ +public: + static_assert(std::is_same_v, uint16_t>); + + template + const typename Traits::template id_to_details::type * + block() const + { + using Details = typename Traits::template id_to_details; + + using Type = typename Details::type; + constexpr auto kernelId = Details::blockType; + + auto data = V4L2StatsBase::block(kernelId, sizeof(Type)); + + return data.size() > 0 ? + reinterpret_cast(data.data()) : nullptr; + } + +protected: + V4L2Stats(Span data, unsigned int version) + : V4L2StatsBase(data, version) + { + } +}; + +} /* namespace ipa */ + +} /* namespace libcamera */