| Message ID | 20260618-rppx1-ipa-v1-9-32337264cfcd@ideasonboard.com |
|---|---|
| State | New |
| Headers | show |
| Series |
|
| Related | show |
Hi Jacopo, Thanks for your work. On 2026-06-18 12:18:48 +0200, Jacopo Mondi wrote: > 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 <jacopo.mondi@ideasonboard.com> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> > --- > src/ipa/libipa/meson.build | 2 + > src/ipa/libipa/v4l2_stats.cpp | 241 ++++++++++++++++++++++++++++++++++++++++++ > src/ipa/libipa/v4l2_stats.h | 67 ++++++++++++ > 3 files changed, 310 insertions(+) > > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build > index edf8eabd8b78..635abad778e6 100644 > --- a/src/ipa/libipa/meson.build > +++ b/src/ipa/libipa/meson.build > @@ -20,6 +20,7 @@ libipa_headers = files([ > 'pwl.h', > 'quantized.h', > 'v4l2_params.h', > + 'v4l2_stats.h', > ]) > > libipa_sources = files([ > @@ -42,6 +43,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..64f546d85069 > --- /dev/null > +++ b/src/ipa/libipa/v4l2_stats.cpp > @@ -0,0 +1,241 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2026, Ideas On Board > + * > + * V4L2 Statistics > + */ > + > +#include "v4l2_stats.h" > + > +#include <libcamera/base/log.h> > + > +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<uint8_t> data, unsigned int version) > + : data_(data), valid_(false) > +{ > + const struct v4l2_isp_buffer *stats = > + reinterpret_cast<const struct v4l2_isp_buffer *>(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<const struct v4l2_isp_block_header *>(d); > + > + if (left < sizeof(*header) || header->size < sizeof(*header)) { > + LOG(V4L2Stats, Error) > + << "Block type " << header->type > + << " size is not valid"; > + return; > + } > + > + if (left < header->size) { > + LOG(V4L2Stats, Error) > + << "Not enough space for block type " << header->type; > + return; > + } > + > + auto [it, inserted] = cache_.try_emplace(header->type, d, header->size); > + if (!inserted) { > + LOG(V4L2Stats, Error) > + << "Duplicated block type " << header->type; > + return; > + } > + > + 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<const uint8_t> 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<const struct v4l2_isp_block_header *>(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 statistics block is valid > + * \return True if the statistics block is valid, false otherwise > + */ > + > +/** > + * \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<myISPStats B> > + * struct block_type { > + * }; > + * > + * template<> > + * struct block_type<myISPStats::Agc> { > + * using type = struct my_isp_kernel_stats_type_agc; > + * static constexpr kernel_enum_type blockType = MY_ISP_STATS_TYPE_AGC; > + * }; > + * > + * template<> > + * struct block_type<myISPStats::Awb> { > + * 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<id_type Id> using id_to_details = block_type<Id>; > + * }; > + * > + * ... > + * > + * // Derive the V4L2Stats class by providing stats_traits > + * class MyISPStats : public V4L2Stats<stats_traits> > + * { > + * public: > + * MyISPStats::MyISPStats(Span<uint8_t> 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<myISPStats::AWB>(); > + * 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..b96395e5f551 > --- /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 <map> > +#include <stdint.h> > + > +#include <linux/media/v4l2-isp.h> > + > +#include <libcamera/base/span.h> > + > +namespace libcamera { > + > +namespace ipa { > + > +class V4L2StatsBase > +{ > +public: > + V4L2StatsBase(Span<uint8_t> data, unsigned int version); > + > + Span<const uint8_t> block(unsigned int blockType, size_t blockSize) const; > + constexpr explicit operator bool() > + { > + return valid_; > + } > + > +private: > + std::map<uint16_t, Span<const uint8_t>> cache_; > + Span<uint8_t> data_; > + bool valid_; > +}; > + > +template<typename Traits> > +class V4L2Stats : public V4L2StatsBase > +{ > +public: > + static_assert(std::is_same_v<std::underlying_type_t<typename Traits::id_type>, uint16_t>); > + > + V4L2Stats(Span<uint8_t> data, unsigned int version) > + : V4L2StatsBase(data, version) > + { > + } > + > + template<typename Traits::id_type Id> > + const typename Traits::template id_to_details<Id>::type * > + block() const > + { > + using Details = typename Traits::template id_to_details<Id>; > + > + using Type = typename Details::type; > + constexpr auto kernelId = Details::blockType; > + > + auto data = V4L2StatsBase::block(kernelId, sizeof(Type)); > + > + return data.size() > 0 ? > + reinterpret_cast<const Type *>(data.data()) : nullptr; > + } > +}; > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > > -- > 2.54.0 >
diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index edf8eabd8b78..635abad778e6 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -20,6 +20,7 @@ libipa_headers = files([ 'pwl.h', 'quantized.h', 'v4l2_params.h', + 'v4l2_stats.h', ]) libipa_sources = files([ @@ -42,6 +43,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..64f546d85069 --- /dev/null +++ b/src/ipa/libipa/v4l2_stats.cpp @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * V4L2 Statistics + */ + +#include "v4l2_stats.h" + +#include <libcamera/base/log.h> + +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<uint8_t> data, unsigned int version) + : data_(data), valid_(false) +{ + const struct v4l2_isp_buffer *stats = + reinterpret_cast<const struct v4l2_isp_buffer *>(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<const struct v4l2_isp_block_header *>(d); + + if (left < sizeof(*header) || header->size < sizeof(*header)) { + LOG(V4L2Stats, Error) + << "Block type " << header->type + << " size is not valid"; + return; + } + + if (left < header->size) { + LOG(V4L2Stats, Error) + << "Not enough space for block type " << header->type; + return; + } + + auto [it, inserted] = cache_.try_emplace(header->type, d, header->size); + if (!inserted) { + LOG(V4L2Stats, Error) + << "Duplicated block type " << header->type; + return; + } + + 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<const uint8_t> 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<const struct v4l2_isp_block_header *>(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 statistics block is valid + * \return True if the statistics block is valid, false otherwise + */ + +/** + * \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<myISPStats B> + * struct block_type { + * }; + * + * template<> + * struct block_type<myISPStats::Agc> { + * using type = struct my_isp_kernel_stats_type_agc; + * static constexpr kernel_enum_type blockType = MY_ISP_STATS_TYPE_AGC; + * }; + * + * template<> + * struct block_type<myISPStats::Awb> { + * 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<id_type Id> using id_to_details = block_type<Id>; + * }; + * + * ... + * + * // Derive the V4L2Stats class by providing stats_traits + * class MyISPStats : public V4L2Stats<stats_traits> + * { + * public: + * MyISPStats::MyISPStats(Span<uint8_t> 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<myISPStats::AWB>(); + * 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..b96395e5f551 --- /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 <map> +#include <stdint.h> + +#include <linux/media/v4l2-isp.h> + +#include <libcamera/base/span.h> + +namespace libcamera { + +namespace ipa { + +class V4L2StatsBase +{ +public: + V4L2StatsBase(Span<uint8_t> data, unsigned int version); + + Span<const uint8_t> block(unsigned int blockType, size_t blockSize) const; + constexpr explicit operator bool() + { + return valid_; + } + +private: + std::map<uint16_t, Span<const uint8_t>> cache_; + Span<uint8_t> data_; + bool valid_; +}; + +template<typename Traits> +class V4L2Stats : public V4L2StatsBase +{ +public: + static_assert(std::is_same_v<std::underlying_type_t<typename Traits::id_type>, uint16_t>); + + V4L2Stats(Span<uint8_t> data, unsigned int version) + : V4L2StatsBase(data, version) + { + } + + template<typename Traits::id_type Id> + const typename Traits::template id_to_details<Id>::type * + block() const + { + using Details = typename Traits::template id_to_details<Id>; + + using Type = typename Details::type; + constexpr auto kernelId = Details::blockType; + + auto data = V4L2StatsBase::block(kernelId, sizeof(Type)); + + return data.size() > 0 ? + reinterpret_cast<const Type *>(data.data()) : nullptr; + } +}; + +} /* namespace ipa */ + +} /* namespace libcamera */