Message ID | 20241115122540.478103-6-dan.scally@ideasonboard.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Quoting Daniel Scally (2024-11-15 12:25:34) > Add a barebones IPA module for the Mali-C55 ISP. In this initial > implementation pretty much only buffer plumbing is implemented. > > Acked-by: Nayden Kanchev <nayden.kanchev@arm.com> > Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> > Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> > --- > Changes in v4: > > - None > > Changes in v3: > > - Fixed some parameter naming inconsistency > > Changes in v2: > > - None > > Documentation/Doxyfile-common.in | 1 + > include/libcamera/ipa/mali-c55.mojom | 34 +++ > include/libcamera/ipa/meson.build | 1 + > meson_options.txt | 2 +- > src/ipa/mali-c55/algorithms/algorithm.h | 39 +++ > src/ipa/mali-c55/algorithms/meson.build | 4 + > src/ipa/mali-c55/data/meson.build | 8 + > src/ipa/mali-c55/data/uncalibrated.yaml | 6 + > src/ipa/mali-c55/ipa_context.cpp | 101 +++++++ > src/ipa/mali-c55/ipa_context.h | 42 +++ > src/ipa/mali-c55/mali-c55.cpp | 339 ++++++++++++++++++++++++ > src/ipa/mali-c55/meson.build | 33 +++ > src/ipa/mali-c55/module.h | 27 ++ > 13 files changed, 636 insertions(+), 1 deletion(-) > create mode 100644 include/libcamera/ipa/mali-c55.mojom > create mode 100644 src/ipa/mali-c55/algorithms/algorithm.h > create mode 100644 src/ipa/mali-c55/algorithms/meson.build > create mode 100644 src/ipa/mali-c55/data/meson.build > create mode 100644 src/ipa/mali-c55/data/uncalibrated.yaml > create mode 100644 src/ipa/mali-c55/ipa_context.cpp > create mode 100644 src/ipa/mali-c55/ipa_context.h > create mode 100644 src/ipa/mali-c55/mali-c55.cpp > create mode 100644 src/ipa/mali-c55/meson.build > create mode 100644 src/ipa/mali-c55/module.h > > diff --git a/Documentation/Doxyfile-common.in b/Documentation/Doxyfile-common.in > index a70aee43..045c19dd 100644 > --- a/Documentation/Doxyfile-common.in > +++ b/Documentation/Doxyfile-common.in > @@ -32,6 +32,7 @@ RECURSIVE = YES > EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \ > @TOP_BUILDDIR@/include/libcamera/ipa/*_proxy.h \ > @TOP_BUILDDIR@/include/libcamera/ipa/ipu3_*.h \ > + @TOP_BUILDDIR@/include/libcamera/ipa/mali-c55_*.h \ > @TOP_BUILDDIR@/include/libcamera/ipa/raspberrypi_*.h \ > @TOP_BUILDDIR@/include/libcamera/ipa/rkisp1_*.h \ > @TOP_BUILDDIR@/include/libcamera/ipa/vimc_*.h > diff --git a/include/libcamera/ipa/mali-c55.mojom b/include/libcamera/ipa/mali-c55.mojom > new file mode 100644 > index 00000000..5d7eb4ee > --- /dev/null > +++ b/include/libcamera/ipa/mali-c55.mojom > @@ -0,0 +1,34 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > + > +module ipa.mali_c55; > + > +import "include/libcamera/ipa/core.mojom"; > + > +struct IPAConfigInfo { > + libcamera.IPACameraSensorInfo sensorInfo; > + libcamera.ControlInfoMap sensorControls; > +}; > + > +interface IPAMaliC55Interface { > + init(libcamera.IPASettings settings, IPAConfigInfo configInfo) > + => (int32 ret, libcamera.ControlInfoMap ipaControls); > + start() => (int32 ret); > + stop(); > + > + configure(IPAConfigInfo configInfo, uint8 bayerOrder) > + => (int32 ret, libcamera.ControlInfoMap ipaControls); > + > + mapBuffers(array<libcamera.IPABuffer> buffers, bool readOnly); > + unmapBuffers(array<libcamera.IPABuffer> buffers); > + > + [async] queueRequest(uint32 request, libcamera.ControlList reqControls); > + [async] fillParams(uint32 request, uint32 bufferId); > + [async] processStats(uint32 request, uint32 bufferId, > + libcamera.ControlList sensorControls); > +}; > + > +interface IPAMaliC55EventInterface { > + paramsComputed(uint32 request); > + statsProcessed(uint32 request, libcamera.ControlList metadata); > + setSensorControls(libcamera.ControlList sensorControls); > +}; > diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build > index bf55e124..3129f119 100644 > --- a/include/libcamera/ipa/meson.build > +++ b/include/libcamera/ipa/meson.build > @@ -64,6 +64,7 @@ libcamera_ipa_headers += custom_target('core_ipa_serializer_h', > # Mapping from pipeline handler name to mojom file > pipeline_ipa_mojom_mapping = { > 'ipu3': 'ipu3.mojom', > + 'mali-c55': 'mali-c55.mojom', > 'rkisp1': 'rkisp1.mojom', > 'rpi/vc4': 'raspberrypi.mojom', > 'simple': 'soft.mojom', > diff --git a/meson_options.txt b/meson_options.txt > index 7aa41249..5365601c 100644 > --- a/meson_options.txt > +++ b/meson_options.txt > @@ -32,7 +32,7 @@ option('gstreamer', > > option('ipas', > type : 'array', > - choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'], > + choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'], > description : 'Select which IPA modules to build') > > option('lc-compliance', > diff --git a/src/ipa/mali-c55/algorithms/algorithm.h b/src/ipa/mali-c55/algorithms/algorithm.h > new file mode 100644 > index 00000000..36a3bff0 > --- /dev/null > +++ b/src/ipa/mali-c55/algorithms/algorithm.h > @@ -0,0 +1,39 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2024, Ideas On Board > + * > + * algorithm.h - Mali-C55 control algorithm interface > + */ > + > +#pragma once > + > +#include <linux/mali-c55-config.h> > + > +#include <libipa/algorithm.h> > + > +#include "module.h" > + > +namespace libcamera { > + > +namespace ipa::mali_c55 { > + > +class Algorithm : public libcamera::ipa::Algorithm<Module> > +{ > +}; > + > +union mali_c55_params_block { > + struct mali_c55_params_block_header *header; > + struct mali_c55_params_sensor_off_preshading *sensor_offs; > + struct mali_c55_params_aexp_hist *aexp_hist; > + struct mali_c55_params_aexp_weights *aexp_weights; > + struct mali_c55_params_digital_gain *digital_gain; > + struct mali_c55_params_awb_gains *awb_gains; > + struct mali_c55_params_awb_config *awb_config; > + struct mali_c55_params_mesh_shading_config *shading_config; > + struct mali_c55_params_mesh_shading_selection *shading_selection; > + __u8 *data; > +}; > + > +} /* namespace ipa::mali_c55 */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/mali-c55/algorithms/meson.build b/src/ipa/mali-c55/algorithms/meson.build > new file mode 100644 > index 00000000..f2203b15 > --- /dev/null > +++ b/src/ipa/mali-c55/algorithms/meson.build > @@ -0,0 +1,4 @@ > +# SPDX-License-Identifier: CC0-1.0 > + > +mali_c55_ipa_algorithms = files([ > +]) > diff --git a/src/ipa/mali-c55/data/meson.build b/src/ipa/mali-c55/data/meson.build > new file mode 100644 > index 00000000..88109d17 > --- /dev/null > +++ b/src/ipa/mali-c55/data/meson.build > @@ -0,0 +1,8 @@ > +# SPDX-License-Identifier: CC0-1.0 > + > +conf_files = files([ > + 'uncalibrated.yaml' > +]) > + > +install_data(conf_files, > + install_dir : ipa_data_dir / 'mali-c55') > diff --git a/src/ipa/mali-c55/data/uncalibrated.yaml b/src/ipa/mali-c55/data/uncalibrated.yaml > new file mode 100644 > index 00000000..2cdc39a8 > --- /dev/null > +++ b/src/ipa/mali-c55/data/uncalibrated.yaml > @@ -0,0 +1,6 @@ > +# SPDX-License-Identifier: CC0-1.0 > +%YAML 1.1 > +--- > +version: 1 > +algorithms: > +... > diff --git a/src/ipa/mali-c55/ipa_context.cpp b/src/ipa/mali-c55/ipa_context.cpp > new file mode 100644 > index 00000000..99f76ecd > --- /dev/null > +++ b/src/ipa/mali-c55/ipa_context.cpp > @@ -0,0 +1,101 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2024, Ideas On Board > + * > + * ipa_context.cpp - MaliC55 IPA Context > + */ > + > +#include "ipa_context.h" > + > +/** > + * \file ipa_context.h > + * \brief Context and state information shared between the algorithms > + */ > + > +namespace libcamera::ipa::mali_c55 { > + > +/** > + * \struct IPASessionConfiguration > + * \brief Session configuration for the IPA module > + * > + * The session configuration contains all IPA configuration parameters that > + * remain constant during the capture session, from IPA module start to stop. > + * It is typically set during the configure() operation of the IPA module, but > + * may also be updated in the start() operation. > + */ > + > +/** > + * \struct IPAActiveState > + * \brief Active state for algorithms > + * > + * The active state contains all algorithm-specific data that needs to be > + * maintained by algorithms across frames. Unlike the session configuration, > + * the active state is mutable and constantly updated by algorithms. The active > + * state is accessible through the IPAContext structure. > + * > + * The active state stores two distinct categories of information: > + * > + * - The consolidated value of all algorithm controls. Requests passed to > + * the queueRequest() function store values for controls that the > + * application wants to modify for that particular frame, and the > + * queueRequest() function updates the active state with those values. > + * The active state thus contains a consolidated view of the value of all > + * controls handled by the algorithm. > + * > + * - The value of parameters computed by the algorithm when running in auto > + * mode. Algorithms running in auto mode compute new parameters every > + * time statistics buffers are received (either synchronously, or > + * possibly in a background thread). The latest computed value of those > + * parameters is stored in the active state in the process() function. > + * > + * Each of the members in the active state belongs to a specific algorithm. A > + * member may be read by any algorithm, but shall only be written by its owner. > + */ > + > +/** > + * \struct IPAFrameContext > + * \brief Per-frame context for algorithms > + * > + * The frame context stores two distinct categories of information: > + * > + * - The value of the controls to be applied to the frame. These values are > + * typically set in the queueRequest() function, from the consolidated > + * control values stored in the active state. The frame context thus stores > + * values for all controls related to the algorithm, not limited to the > + * controls specified in the corresponding request, but consolidated from all > + * requests that have been queued so far. > + * > + * For controls that can be set manually or computed by an algorithm > + * (depending on the algorithm operation mode), such as for instance the > + * colour gains for the AWB algorithm, the control value will be stored in > + * the frame context in the queueRequest() function only when operating in > + * manual mode. When operating in auto mode, the values are computed by the > + * algorithm in process(), stored in the active state, and copied to the > + * frame context in prepare(), just before being stored in the ISP parameters > + * buffer. > + * > + * The queueRequest() function can also store ancillary data in the frame > + * context, such as flags to indicate if (and what) control values have > + * changed compared to the previous request. > + * > + * - Status information computed by the algorithm for a frame. For instance, > + * the colour temperature estimated by the AWB algorithm from ISP statistics > + * calculated on a frame is stored in the frame context for that frame in > + * the process() function. > + */ > + > +/** > + * \struct IPAContext > + * \brief Global IPA context data shared between all algorithms > + * > + * \var IPAContext::configuration > + * \brief The IPA session configuration, immutable during the session > + * > + * \var IPAContext::activeState > + * \brief The IPA active state, storing the latest state for all algorithms > + * > + * \var IPAContext::frameContexts > + * \brief Ring buffer of per-frame contexts > + */ > + > +} /* namespace libcamera::ipa::mali_c55 */ > diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h > new file mode 100644 > index 00000000..9e408a17 > --- /dev/null > +++ b/src/ipa/mali-c55/ipa_context.h > @@ -0,0 +1,42 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2024, Ideas On Board > + * > + * ipa_context.h - Mali-C55 IPA Context > + */ > + > +#pragma once > + > +#include <libcamera/controls.h> > + > +#include <libipa/fc_queue.h> > + > +namespace libcamera { > + > +namespace ipa::mali_c55 { > + > +struct IPASessionConfiguration { > +}; > + > +struct IPAActiveState { > +}; > + > +struct IPAFrameContext : public FrameContext { > + struct { > + uint32_t exposure; > + double sensorGain; > + } agc; > +}; > + > +struct IPAContext { > + IPASessionConfiguration configuration; > + IPAActiveState activeState; > + > + FCQueue<IPAFrameContext> frameContexts; > + > + ControlInfoMap::Map ctrlMap; > +}; > + > +} /* namespace ipa::mali_c55 */ > + > +} /* namespace libcamera*/ > diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp > new file mode 100644 > index 00000000..7efc0124 > --- /dev/null > +++ b/src/ipa/mali-c55/mali-c55.cpp > @@ -0,0 +1,339 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2023, Ideas on Board Oy > + * > + * mali-c55.cpp - Mali-C55 ISP image processing algorithms > + */ > + > +#include <map> > +#include <string.h> > +#include <vector> > + > +#include <linux/mali-c55-config.h> > +#include <linux/v4l2-controls.h> > + > +#include <libcamera/base/file.h> > +#include <libcamera/base/log.h> > + > +#include <libcamera/control_ids.h> > +#include <libcamera/ipa/ipa_interface.h> > +#include <libcamera/ipa/ipa_module_info.h> > +#include <libcamera/ipa/mali-c55_ipa_interface.h> > + > +#include "libcamera/internal/bayer_format.h" > +#include "libcamera/internal/mapped_framebuffer.h" > +#include "libcamera/internal/yaml_parser.h" > + > +#include "algorithms/algorithm.h" > +#include "libipa/camera_sensor_helper.h" > + > +#include "ipa_context.h" > + > +namespace libcamera { > + > +LOG_DEFINE_CATEGORY(IPAMaliC55) > + > +namespace ipa::mali_c55 { > + > +/* Maximum number of frame contexts to be held */ > +static constexpr uint32_t kMaxFrameContexts = 16; > + > +class IPAMaliC55 : public IPAMaliC55Interface, public Module > +{ > +public: > + IPAMaliC55(); > + > + int init(const IPASettings &settings, const IPAConfigInfo &ipaConfig, > + ControlInfoMap *ipaControls) override; > + int start() override; > + void stop() override; > + int configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder, > + ControlInfoMap *ipaControls) override; > + void mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) override; > + void unmapBuffers(const std::vector<IPABuffer> &buffers) override; > + void queueRequest(const uint32_t request, const ControlList &controls) override; > + void fillParams(unsigned int request, uint32_t bufferId) override; > + void processStats(unsigned int request, unsigned int bufferId, > + const ControlList &sensorControls) override; > + > +protected: > + std::string logPrefix() const override; > + > +private: > + void updateControls(const IPACameraSensorInfo &sensorInfo, > + const ControlInfoMap &sensorControls, > + ControlInfoMap *ipaControls); > + void setControls(); > + > + std::map<unsigned int, MappedFrameBuffer> buffers_; > + > + ControlInfoMap sensorControls_; > + > + /* Interface to the Camera Helper */ > + std::unique_ptr<CameraSensorHelper> camHelper_; > + > + /* Local parameter storage */ > + struct IPAContext context_; > +}; > + > +namespace { > + > +} /* namespace */ > + > +IPAMaliC55::IPAMaliC55() > + : context_({ {}, {}, { kMaxFrameContexts }, {} }) > +{ > +} This is the only quibble I'd have (but can be updated on top if you prefer, if we don't need another version). See https://patchwork.libcamera.org/patch/22093/ > + > +std::string IPAMaliC55::logPrefix() const > +{ > + return "mali-c55"; > +} > + > +int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig, > + ControlInfoMap *ipaControls) > +{ > + camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel); > + if (!camHelper_) { > + LOG(IPAMaliC55, Error) > + << "Failed to create camera sensor helper for " > + << settings.sensorModel; > + return -ENODEV; > + } > + > + File file(settings.configurationFile); > + if (!file.open(File::OpenModeFlag::ReadOnly)) { > + int ret = file.error(); > + LOG(IPAMaliC55, Error) > + << "Failed to open configuration file " > + << settings.configurationFile << ": " << strerror(-ret); > + return ret; > + } > + > + std::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file); > + if (!data) > + return -EINVAL; > + > + if (!data->contains("algorithms")) { > + LOG(IPAMaliC55, Error) > + << "Tuning file doesn't contain any algorithm"; > + return -EINVAL; > + } > + > + int ret = createAlgorithms(context_, (*data)["algorithms"]); > + if (ret) > + return ret; > + > + updateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls); > + > + return 0; > +} > + > +void IPAMaliC55::setControls() > +{ > + ControlList ctrls(sensorControls_); > + > + setSensorControls.emit(ctrls); > +} > + > +int IPAMaliC55::start() > +{ > + return 0; > +} > + > +void IPAMaliC55::stop() > +{ > + context_.frameContexts.clear(); > +} > + > +void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo, > + const ControlInfoMap &sensorControls, > + ControlInfoMap *ipaControls) > +{ > + ControlInfoMap::Map ctrlMap; > + > + /* > + * Compute the frame duration limits. > + * > + * The frame length is computed assuming a fixed line length combined > + * with the vertical frame sizes. > + */ > + const ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second; > + uint32_t hblank = v4l2HBlank.def().get<int32_t>(); > + uint32_t lineLength = sensorInfo.outputSize.width + hblank; > + > + const ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second; > + std::array<uint32_t, 3> frameHeights{ > + v4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height, > + v4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height, > + v4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height, > + }; > + > + std::array<int64_t, 3> frameDurations; > + for (unsigned int i = 0; i < frameHeights.size(); ++i) { > + uint64_t frameSize = lineLength * frameHeights[i]; > + frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U); > + } > + > + ctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0], > + frameDurations[1], > + frameDurations[2]); > + > + /* > + * Compute exposure time limits from the V4L2_CID_EXPOSURE control > + * limits and the line duration. > + */ > + double lineDuration = sensorInfo.minLineLength / sensorInfo.pixelRate; > + > + const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second; > + int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration; > + int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration; > + int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration; > + ctrlMap[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure, defExposure); > + > + /* Compute the analogue gain limits. */ > + const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; > + float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>()); > + float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>()); > + float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>()); > + ctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain); If all of that is the same as every other libipa IPA then I think theres some opportunity to refactor into a helper here, or otherwise into the common AEGC components ... but can be on top still. > + > + /* > + * Merge in any controls that we support either statically or from the > + * algorithms. > + */ > + ctrlMap.merge(context_.ctrlMap); > + > + *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls); > +} > + > +int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, > + [[maybe_unused]] uint8_t bayerOrder, > + ControlInfoMap *ipaControls) > +{ > + sensorControls_ = ipaConfig.sensorControls; > + > + /* Clear the IPA context before the streaming session. */ > + context_.configuration = {}; > + context_.activeState = {}; > + context_.frameContexts.clear(); > + > + const IPACameraSensorInfo &info = ipaConfig.sensorInfo; > + > + updateControls(info, ipaConfig.sensorControls, ipaControls); > + > + for (auto const &a : algorithms()) { > + Algorithm *algo = static_cast<Algorithm *>(a.get()); > + > + int ret = algo->configure(context_, info); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +void IPAMaliC55::mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) > +{ > + for (const IPABuffer &buffer : buffers) { > + const FrameBuffer fb(buffer.planes); I think the MappedFrameBuffer should probably be split out here and error checked at time of mapping, to at least report a warning/Error message, but we're in a void function ... hrm. Probably something to add to a todo list ? But we don't expect these to fail... Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > + buffers_.emplace( > + buffer.id, > + MappedFrameBuffer( > + &fb, > + readOnly ? MappedFrameBuffer::MapFlag::Read > + : MappedFrameBuffer::MapFlag::ReadWrite)); > + } > +} > + > +void IPAMaliC55::unmapBuffers(const std::vector<IPABuffer> &buffers) > +{ > + for (const IPABuffer &buffer : buffers) { > + auto it = buffers_.find(buffer.id); > + if (it == buffers_.end()) > + continue; > + > + buffers_.erase(buffer.id); > + } > +} > + > +void IPAMaliC55::queueRequest(const uint32_t request, const ControlList &controls) > +{ > + IPAFrameContext &frameContext = context_.frameContexts.alloc(request); > + > + for (auto const &a : algorithms()) { > + Algorithm *algo = static_cast<Algorithm *>(a.get()); > + > + algo->queueRequest(context_, request, frameContext, controls); > + } > +} > + > +void IPAMaliC55::fillParams(unsigned int request, > + [[maybe_unused]] uint32_t bufferId) > +{ > + struct mali_c55_params_buffer *params; > + IPAFrameContext &frameContext = context_.frameContexts.get(request); > + > + params = reinterpret_cast<mali_c55_params_buffer *>( > + buffers_.at(bufferId).planes()[0].data()); > + memset(params, 0, sizeof(mali_c55_params_buffer)); > + > + params->version = MALI_C55_PARAM_BUFFER_V1; > + > + for (auto const &algo : algorithms()) { > + algo->prepare(context_, request, frameContext, params); > + > + ASSERT(params->total_size <= MALI_C55_PARAMS_MAX_SIZE); > + } > + > + paramsComputed.emit(request); > +} > + > +void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId, > + const ControlList &sensorControls) > +{ > + IPAFrameContext &frameContext = context_.frameContexts.get(request); > + const mali_c55_stats_buffer *stats = nullptr; > + > + stats = reinterpret_cast<mali_c55_stats_buffer *>( > + buffers_.at(bufferId).planes()[0].data()); > + > + frameContext.agc.exposure = > + sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>(); > + frameContext.agc.sensorGain = > + camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>()); > + > + ControlList metadata(controls::controls); > + > + for (auto const &a : algorithms()) { > + Algorithm *algo = static_cast<Algorithm *>(a.get()); > + > + algo->process(context_, request, frameContext, stats, metadata); > + } > + > + setControls(); > + > + statsProcessed.emit(request, metadata); > +} > + > +} /* namespace ipa::mali_c55 */ > + > +/* > + * External IPA module interface > + */ > +extern "C" { > +const struct IPAModuleInfo ipaModuleInfo = { > + IPA_MODULE_API_VERSION, > + 1, > + "mali-c55", > + "mali-c55", > +}; > + > +IPAInterface *ipaCreate() > +{ > + return new ipa::mali_c55::IPAMaliC55(); > +} > + > +} /* extern "C" */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/mali-c55/meson.build b/src/ipa/mali-c55/meson.build > new file mode 100644 > index 00000000..864d90ec > --- /dev/null > +++ b/src/ipa/mali-c55/meson.build > @@ -0,0 +1,33 @@ > +# SPDX-License-Identifier: CC0-1.0 > + > +subdir('algorithms') > +subdir('data') > + > +ipa_name = 'ipa_mali_c55' > + > +mali_c55_ipa_sources = files([ > + 'ipa_context.cpp', > + 'mali-c55.cpp' > +]) > + > +mali_c55_ipa_sources += mali_c55_ipa_algorithms > + > +mod = shared_module(ipa_name, > + mali_c55_ipa_sources, > + name_prefix : '', > + include_directories : [ipa_includes, libipa_includes], > + dependencies : libcamera_private, > + link_with : libipa, > + install : true, > + install_dir : ipa_install_dir) > + > +if ipa_sign_module > + custom_target(ipa_name + '.so.sign', > + input : mod, > + output : ipa_name + '.so.sign', > + command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'], > + install : false, > + build_by_default : true) > +endif > + > +ipa_names += ipa_name > diff --git a/src/ipa/mali-c55/module.h b/src/ipa/mali-c55/module.h > new file mode 100644 > index 00000000..1d85ec1f > --- /dev/null > +++ b/src/ipa/mali-c55/module.h > @@ -0,0 +1,27 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2024, Ideas On Board > + * > + * module.h - Mali-C55 IPA Module > + */ > + > +#pragma once > + > +#include <linux/mali-c55-config.h> > + > +#include <libcamera/ipa/mali-c55_ipa_interface.h> > + > +#include <libipa/module.h> > + > +#include "ipa_context.h" > + > +namespace libcamera { > + > +namespace ipa::mali_c55 { > + > +using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo, > + mali_c55_params_buffer, mali_c55_stats_buffer>; > + > +} /* namespace ipa::mali_c55 */ > + > +} /* namespace libcamera*/ > -- > 2.30.2 >
Hi Kieran - thanks for the review On 06/12/2024 13:51, Kieran Bingham wrote: > Quoting Daniel Scally (2024-11-15 12:25:34) >> Add a barebones IPA module for the Mali-C55 ISP. In this initial >> implementation pretty much only buffer plumbing is implemented. >> >> Acked-by: Nayden Kanchev <nayden.kanchev@arm.com> >> Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> >> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> >> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> >> --- >> Changes in v4: >> >> - None >> >> Changes in v3: >> >> - Fixed some parameter naming inconsistency >> >> Changes in v2: >> >> - None >> >> Documentation/Doxyfile-common.in | 1 + >> include/libcamera/ipa/mali-c55.mojom | 34 +++ >> include/libcamera/ipa/meson.build | 1 + >> meson_options.txt | 2 +- >> src/ipa/mali-c55/algorithms/algorithm.h | 39 +++ >> src/ipa/mali-c55/algorithms/meson.build | 4 + >> src/ipa/mali-c55/data/meson.build | 8 + >> src/ipa/mali-c55/data/uncalibrated.yaml | 6 + >> src/ipa/mali-c55/ipa_context.cpp | 101 +++++++ >> src/ipa/mali-c55/ipa_context.h | 42 +++ >> src/ipa/mali-c55/mali-c55.cpp | 339 ++++++++++++++++++++++++ >> src/ipa/mali-c55/meson.build | 33 +++ >> src/ipa/mali-c55/module.h | 27 ++ >> 13 files changed, 636 insertions(+), 1 deletion(-) >> create mode 100644 include/libcamera/ipa/mali-c55.mojom >> create mode 100644 src/ipa/mali-c55/algorithms/algorithm.h >> create mode 100644 src/ipa/mali-c55/algorithms/meson.build >> create mode 100644 src/ipa/mali-c55/data/meson.build >> create mode 100644 src/ipa/mali-c55/data/uncalibrated.yaml >> create mode 100644 src/ipa/mali-c55/ipa_context.cpp >> create mode 100644 src/ipa/mali-c55/ipa_context.h >> create mode 100644 src/ipa/mali-c55/mali-c55.cpp >> create mode 100644 src/ipa/mali-c55/meson.build >> create mode 100644 src/ipa/mali-c55/module.h >> >> diff --git a/Documentation/Doxyfile-common.in b/Documentation/Doxyfile-common.in >> index a70aee43..045c19dd 100644 >> --- a/Documentation/Doxyfile-common.in >> +++ b/Documentation/Doxyfile-common.in >> @@ -32,6 +32,7 @@ RECURSIVE = YES >> EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \ >> @TOP_BUILDDIR@/include/libcamera/ipa/*_proxy.h \ >> @TOP_BUILDDIR@/include/libcamera/ipa/ipu3_*.h \ >> + @TOP_BUILDDIR@/include/libcamera/ipa/mali-c55_*.h \ >> @TOP_BUILDDIR@/include/libcamera/ipa/raspberrypi_*.h \ >> @TOP_BUILDDIR@/include/libcamera/ipa/rkisp1_*.h \ >> @TOP_BUILDDIR@/include/libcamera/ipa/vimc_*.h >> diff --git a/include/libcamera/ipa/mali-c55.mojom b/include/libcamera/ipa/mali-c55.mojom >> new file mode 100644 >> index 00000000..5d7eb4ee >> --- /dev/null >> +++ b/include/libcamera/ipa/mali-c55.mojom >> @@ -0,0 +1,34 @@ >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> + >> +module ipa.mali_c55; >> + >> +import "include/libcamera/ipa/core.mojom"; >> + >> +struct IPAConfigInfo { >> + libcamera.IPACameraSensorInfo sensorInfo; >> + libcamera.ControlInfoMap sensorControls; >> +}; >> + >> +interface IPAMaliC55Interface { >> + init(libcamera.IPASettings settings, IPAConfigInfo configInfo) >> + => (int32 ret, libcamera.ControlInfoMap ipaControls); >> + start() => (int32 ret); >> + stop(); >> + >> + configure(IPAConfigInfo configInfo, uint8 bayerOrder) >> + => (int32 ret, libcamera.ControlInfoMap ipaControls); >> + >> + mapBuffers(array<libcamera.IPABuffer> buffers, bool readOnly); >> + unmapBuffers(array<libcamera.IPABuffer> buffers); >> + >> + [async] queueRequest(uint32 request, libcamera.ControlList reqControls); >> + [async] fillParams(uint32 request, uint32 bufferId); >> + [async] processStats(uint32 request, uint32 bufferId, >> + libcamera.ControlList sensorControls); >> +}; >> + >> +interface IPAMaliC55EventInterface { >> + paramsComputed(uint32 request); >> + statsProcessed(uint32 request, libcamera.ControlList metadata); >> + setSensorControls(libcamera.ControlList sensorControls); >> +}; >> diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build >> index bf55e124..3129f119 100644 >> --- a/include/libcamera/ipa/meson.build >> +++ b/include/libcamera/ipa/meson.build >> @@ -64,6 +64,7 @@ libcamera_ipa_headers += custom_target('core_ipa_serializer_h', >> # Mapping from pipeline handler name to mojom file >> pipeline_ipa_mojom_mapping = { >> 'ipu3': 'ipu3.mojom', >> + 'mali-c55': 'mali-c55.mojom', >> 'rkisp1': 'rkisp1.mojom', >> 'rpi/vc4': 'raspberrypi.mojom', >> 'simple': 'soft.mojom', >> diff --git a/meson_options.txt b/meson_options.txt >> index 7aa41249..5365601c 100644 >> --- a/meson_options.txt >> +++ b/meson_options.txt >> @@ -32,7 +32,7 @@ option('gstreamer', >> >> option('ipas', >> type : 'array', >> - choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'], >> + choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'], >> description : 'Select which IPA modules to build') >> >> option('lc-compliance', >> diff --git a/src/ipa/mali-c55/algorithms/algorithm.h b/src/ipa/mali-c55/algorithms/algorithm.h >> new file mode 100644 >> index 00000000..36a3bff0 >> --- /dev/null >> +++ b/src/ipa/mali-c55/algorithms/algorithm.h >> @@ -0,0 +1,39 @@ >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> +/* >> + * Copyright (C) 2024, Ideas On Board >> + * >> + * algorithm.h - Mali-C55 control algorithm interface >> + */ >> + >> +#pragma once >> + >> +#include <linux/mali-c55-config.h> >> + >> +#include <libipa/algorithm.h> >> + >> +#include "module.h" >> + >> +namespace libcamera { >> + >> +namespace ipa::mali_c55 { >> + >> +class Algorithm : public libcamera::ipa::Algorithm<Module> >> +{ >> +}; >> + >> +union mali_c55_params_block { >> + struct mali_c55_params_block_header *header; >> + struct mali_c55_params_sensor_off_preshading *sensor_offs; >> + struct mali_c55_params_aexp_hist *aexp_hist; >> + struct mali_c55_params_aexp_weights *aexp_weights; >> + struct mali_c55_params_digital_gain *digital_gain; >> + struct mali_c55_params_awb_gains *awb_gains; >> + struct mali_c55_params_awb_config *awb_config; >> + struct mali_c55_params_mesh_shading_config *shading_config; >> + struct mali_c55_params_mesh_shading_selection *shading_selection; >> + __u8 *data; >> +}; >> + >> +} /* namespace ipa::mali_c55 */ >> + >> +} /* namespace libcamera */ >> diff --git a/src/ipa/mali-c55/algorithms/meson.build b/src/ipa/mali-c55/algorithms/meson.build >> new file mode 100644 >> index 00000000..f2203b15 >> --- /dev/null >> +++ b/src/ipa/mali-c55/algorithms/meson.build >> @@ -0,0 +1,4 @@ >> +# SPDX-License-Identifier: CC0-1.0 >> + >> +mali_c55_ipa_algorithms = files([ >> +]) >> diff --git a/src/ipa/mali-c55/data/meson.build b/src/ipa/mali-c55/data/meson.build >> new file mode 100644 >> index 00000000..88109d17 >> --- /dev/null >> +++ b/src/ipa/mali-c55/data/meson.build >> @@ -0,0 +1,8 @@ >> +# SPDX-License-Identifier: CC0-1.0 >> + >> +conf_files = files([ >> + 'uncalibrated.yaml' >> +]) >> + >> +install_data(conf_files, >> + install_dir : ipa_data_dir / 'mali-c55') >> diff --git a/src/ipa/mali-c55/data/uncalibrated.yaml b/src/ipa/mali-c55/data/uncalibrated.yaml >> new file mode 100644 >> index 00000000..2cdc39a8 >> --- /dev/null >> +++ b/src/ipa/mali-c55/data/uncalibrated.yaml >> @@ -0,0 +1,6 @@ >> +# SPDX-License-Identifier: CC0-1.0 >> +%YAML 1.1 >> +--- >> +version: 1 >> +algorithms: >> +... >> diff --git a/src/ipa/mali-c55/ipa_context.cpp b/src/ipa/mali-c55/ipa_context.cpp >> new file mode 100644 >> index 00000000..99f76ecd >> --- /dev/null >> +++ b/src/ipa/mali-c55/ipa_context.cpp >> @@ -0,0 +1,101 @@ >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> +/* >> + * Copyright (C) 2024, Ideas On Board >> + * >> + * ipa_context.cpp - MaliC55 IPA Context >> + */ >> + >> +#include "ipa_context.h" >> + >> +/** >> + * \file ipa_context.h >> + * \brief Context and state information shared between the algorithms >> + */ >> + >> +namespace libcamera::ipa::mali_c55 { >> + >> +/** >> + * \struct IPASessionConfiguration >> + * \brief Session configuration for the IPA module >> + * >> + * The session configuration contains all IPA configuration parameters that >> + * remain constant during the capture session, from IPA module start to stop. >> + * It is typically set during the configure() operation of the IPA module, but >> + * may also be updated in the start() operation. >> + */ >> + >> +/** >> + * \struct IPAActiveState >> + * \brief Active state for algorithms >> + * >> + * The active state contains all algorithm-specific data that needs to be >> + * maintained by algorithms across frames. Unlike the session configuration, >> + * the active state is mutable and constantly updated by algorithms. The active >> + * state is accessible through the IPAContext structure. >> + * >> + * The active state stores two distinct categories of information: >> + * >> + * - The consolidated value of all algorithm controls. Requests passed to >> + * the queueRequest() function store values for controls that the >> + * application wants to modify for that particular frame, and the >> + * queueRequest() function updates the active state with those values. >> + * The active state thus contains a consolidated view of the value of all >> + * controls handled by the algorithm. >> + * >> + * - The value of parameters computed by the algorithm when running in auto >> + * mode. Algorithms running in auto mode compute new parameters every >> + * time statistics buffers are received (either synchronously, or >> + * possibly in a background thread). The latest computed value of those >> + * parameters is stored in the active state in the process() function. >> + * >> + * Each of the members in the active state belongs to a specific algorithm. A >> + * member may be read by any algorithm, but shall only be written by its owner. >> + */ >> + >> +/** >> + * \struct IPAFrameContext >> + * \brief Per-frame context for algorithms >> + * >> + * The frame context stores two distinct categories of information: >> + * >> + * - The value of the controls to be applied to the frame. These values are >> + * typically set in the queueRequest() function, from the consolidated >> + * control values stored in the active state. The frame context thus stores >> + * values for all controls related to the algorithm, not limited to the >> + * controls specified in the corresponding request, but consolidated from all >> + * requests that have been queued so far. >> + * >> + * For controls that can be set manually or computed by an algorithm >> + * (depending on the algorithm operation mode), such as for instance the >> + * colour gains for the AWB algorithm, the control value will be stored in >> + * the frame context in the queueRequest() function only when operating in >> + * manual mode. When operating in auto mode, the values are computed by the >> + * algorithm in process(), stored in the active state, and copied to the >> + * frame context in prepare(), just before being stored in the ISP parameters >> + * buffer. >> + * >> + * The queueRequest() function can also store ancillary data in the frame >> + * context, such as flags to indicate if (and what) control values have >> + * changed compared to the previous request. >> + * >> + * - Status information computed by the algorithm for a frame. For instance, >> + * the colour temperature estimated by the AWB algorithm from ISP statistics >> + * calculated on a frame is stored in the frame context for that frame in >> + * the process() function. >> + */ >> + >> +/** >> + * \struct IPAContext >> + * \brief Global IPA context data shared between all algorithms >> + * >> + * \var IPAContext::configuration >> + * \brief The IPA session configuration, immutable during the session >> + * >> + * \var IPAContext::activeState >> + * \brief The IPA active state, storing the latest state for all algorithms >> + * >> + * \var IPAContext::frameContexts >> + * \brief Ring buffer of per-frame contexts >> + */ >> + >> +} /* namespace libcamera::ipa::mali_c55 */ >> diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h >> new file mode 100644 >> index 00000000..9e408a17 >> --- /dev/null >> +++ b/src/ipa/mali-c55/ipa_context.h >> @@ -0,0 +1,42 @@ >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> +/* >> + * Copyright (C) 2024, Ideas On Board >> + * >> + * ipa_context.h - Mali-C55 IPA Context >> + */ >> + >> +#pragma once >> + >> +#include <libcamera/controls.h> >> + >> +#include <libipa/fc_queue.h> >> + >> +namespace libcamera { >> + >> +namespace ipa::mali_c55 { >> + >> +struct IPASessionConfiguration { >> +}; >> + >> +struct IPAActiveState { >> +}; >> + >> +struct IPAFrameContext : public FrameContext { >> + struct { >> + uint32_t exposure; >> + double sensorGain; >> + } agc; >> +}; >> + >> +struct IPAContext { >> + IPASessionConfiguration configuration; >> + IPAActiveState activeState; >> + >> + FCQueue<IPAFrameContext> frameContexts; >> + >> + ControlInfoMap::Map ctrlMap; >> +}; >> + >> +} /* namespace ipa::mali_c55 */ >> + >> +} /* namespace libcamera*/ >> diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp >> new file mode 100644 >> index 00000000..7efc0124 >> --- /dev/null >> +++ b/src/ipa/mali-c55/mali-c55.cpp >> @@ -0,0 +1,339 @@ >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> +/* >> + * Copyright (C) 2023, Ideas on Board Oy >> + * >> + * mali-c55.cpp - Mali-C55 ISP image processing algorithms >> + */ >> + >> +#include <map> >> +#include <string.h> >> +#include <vector> >> + >> +#include <linux/mali-c55-config.h> >> +#include <linux/v4l2-controls.h> >> + >> +#include <libcamera/base/file.h> >> +#include <libcamera/base/log.h> >> + >> +#include <libcamera/control_ids.h> >> +#include <libcamera/ipa/ipa_interface.h> >> +#include <libcamera/ipa/ipa_module_info.h> >> +#include <libcamera/ipa/mali-c55_ipa_interface.h> >> + >> +#include "libcamera/internal/bayer_format.h" >> +#include "libcamera/internal/mapped_framebuffer.h" >> +#include "libcamera/internal/yaml_parser.h" >> + >> +#include "algorithms/algorithm.h" >> +#include "libipa/camera_sensor_helper.h" >> + >> +#include "ipa_context.h" >> + >> +namespace libcamera { >> + >> +LOG_DEFINE_CATEGORY(IPAMaliC55) >> + >> +namespace ipa::mali_c55 { >> + >> +/* Maximum number of frame contexts to be held */ >> +static constexpr uint32_t kMaxFrameContexts = 16; >> + >> +class IPAMaliC55 : public IPAMaliC55Interface, public Module >> +{ >> +public: >> + IPAMaliC55(); >> + >> + int init(const IPASettings &settings, const IPAConfigInfo &ipaConfig, >> + ControlInfoMap *ipaControls) override; >> + int start() override; >> + void stop() override; >> + int configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder, >> + ControlInfoMap *ipaControls) override; >> + void mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) override; >> + void unmapBuffers(const std::vector<IPABuffer> &buffers) override; >> + void queueRequest(const uint32_t request, const ControlList &controls) override; >> + void fillParams(unsigned int request, uint32_t bufferId) override; >> + void processStats(unsigned int request, unsigned int bufferId, >> + const ControlList &sensorControls) override; >> + >> +protected: >> + std::string logPrefix() const override; >> + >> +private: >> + void updateControls(const IPACameraSensorInfo &sensorInfo, >> + const ControlInfoMap &sensorControls, >> + ControlInfoMap *ipaControls); >> + void setControls(); >> + >> + std::map<unsigned int, MappedFrameBuffer> buffers_; >> + >> + ControlInfoMap sensorControls_; >> + >> + /* Interface to the Camera Helper */ >> + std::unique_ptr<CameraSensorHelper> camHelper_; >> + >> + /* Local parameter storage */ >> + struct IPAContext context_; >> +}; >> + >> +namespace { >> + >> +} /* namespace */ >> + >> +IPAMaliC55::IPAMaliC55() >> + : context_({ {}, {}, { kMaxFrameContexts }, {} }) >> +{ >> +} > This is the only quibble I'd have (but can be updated on top if you > prefer, if we don't need another version). > > > See https://patchwork.libcamera.org/patch/22093/ Yeah sure > >> + >> +std::string IPAMaliC55::logPrefix() const >> +{ >> + return "mali-c55"; >> +} >> + >> +int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig, >> + ControlInfoMap *ipaControls) >> +{ >> + camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel); >> + if (!camHelper_) { >> + LOG(IPAMaliC55, Error) >> + << "Failed to create camera sensor helper for " >> + << settings.sensorModel; >> + return -ENODEV; >> + } >> + >> + File file(settings.configurationFile); >> + if (!file.open(File::OpenModeFlag::ReadOnly)) { >> + int ret = file.error(); >> + LOG(IPAMaliC55, Error) >> + << "Failed to open configuration file " >> + << settings.configurationFile << ": " << strerror(-ret); >> + return ret; >> + } >> + >> + std::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file); >> + if (!data) >> + return -EINVAL; >> + >> + if (!data->contains("algorithms")) { >> + LOG(IPAMaliC55, Error) >> + << "Tuning file doesn't contain any algorithm"; >> + return -EINVAL; >> + } >> + >> + int ret = createAlgorithms(context_, (*data)["algorithms"]); >> + if (ret) >> + return ret; >> + >> + updateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls); >> + >> + return 0; >> +} >> + >> +void IPAMaliC55::setControls() >> +{ >> + ControlList ctrls(sensorControls_); >> + >> + setSensorControls.emit(ctrls); >> +} >> + >> +int IPAMaliC55::start() >> +{ >> + return 0; >> +} >> + >> +void IPAMaliC55::stop() >> +{ >> + context_.frameContexts.clear(); >> +} >> + >> +void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo, >> + const ControlInfoMap &sensorControls, >> + ControlInfoMap *ipaControls) >> +{ >> + ControlInfoMap::Map ctrlMap; >> + >> + /* >> + * Compute the frame duration limits. >> + * >> + * The frame length is computed assuming a fixed line length combined >> + * with the vertical frame sizes. >> + */ >> + const ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second; >> + uint32_t hblank = v4l2HBlank.def().get<int32_t>(); >> + uint32_t lineLength = sensorInfo.outputSize.width + hblank; >> + >> + const ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second; >> + std::array<uint32_t, 3> frameHeights{ >> + v4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height, >> + v4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height, >> + v4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height, >> + }; >> + >> + std::array<int64_t, 3> frameDurations; >> + for (unsigned int i = 0; i < frameHeights.size(); ++i) { >> + uint64_t frameSize = lineLength * frameHeights[i]; >> + frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U); >> + } >> + >> + ctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0], >> + frameDurations[1], >> + frameDurations[2]); >> + >> + /* >> + * Compute exposure time limits from the V4L2_CID_EXPOSURE control >> + * limits and the line duration. >> + */ >> + double lineDuration = sensorInfo.minLineLength / sensorInfo.pixelRate; >> + >> + const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second; >> + int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration; >> + int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration; >> + int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration; >> + ctrlMap[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure, defExposure); >> + >> + /* Compute the analogue gain limits. */ >> + const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; >> + float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>()); >> + float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>()); >> + float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>()); >> + ctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain); > If all of that is the same as every other libipa IPA then I think theres > some opportunity to refactor into a helper here, or otherwise into the > common AEGC components ... but can be on top still. I will stick this on the to-do list > > >> + >> + /* >> + * Merge in any controls that we support either statically or from the >> + * algorithms. >> + */ >> + ctrlMap.merge(context_.ctrlMap); >> + >> + *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls); >> +} >> + >> +int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, >> + [[maybe_unused]] uint8_t bayerOrder, >> + ControlInfoMap *ipaControls) >> +{ >> + sensorControls_ = ipaConfig.sensorControls; >> + >> + /* Clear the IPA context before the streaming session. */ >> + context_.configuration = {}; >> + context_.activeState = {}; >> + context_.frameContexts.clear(); >> + >> + const IPACameraSensorInfo &info = ipaConfig.sensorInfo; >> + >> + updateControls(info, ipaConfig.sensorControls, ipaControls); >> + >> + for (auto const &a : algorithms()) { >> + Algorithm *algo = static_cast<Algorithm *>(a.get()); >> + >> + int ret = algo->configure(context_, info); >> + if (ret) >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +void IPAMaliC55::mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) >> +{ >> + for (const IPABuffer &buffer : buffers) { >> + const FrameBuffer fb(buffer.planes); > I think the MappedFrameBuffer should probably be split out here and > error checked at time of mapping, to at least report a warning/Error > message, but we're in a void function ... hrm. > > > Probably something to add to a todo list ? Sure, it doesn't have to be a void though, we can change that. I suspect this is a similar problem for the other IPA modules too. > > But we don't expect these to fail... > > > Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Thanks > >> + buffers_.emplace( >> + buffer.id, >> + MappedFrameBuffer( >> + &fb, >> + readOnly ? MappedFrameBuffer::MapFlag::Read >> + : MappedFrameBuffer::MapFlag::ReadWrite)); >> + } >> +} >> + >> +void IPAMaliC55::unmapBuffers(const std::vector<IPABuffer> &buffers) >> +{ >> + for (const IPABuffer &buffer : buffers) { >> + auto it = buffers_.find(buffer.id); >> + if (it == buffers_.end()) >> + continue; >> + >> + buffers_.erase(buffer.id); >> + } >> +} >> + >> +void IPAMaliC55::queueRequest(const uint32_t request, const ControlList &controls) >> +{ >> + IPAFrameContext &frameContext = context_.frameContexts.alloc(request); >> + >> + for (auto const &a : algorithms()) { >> + Algorithm *algo = static_cast<Algorithm *>(a.get()); >> + >> + algo->queueRequest(context_, request, frameContext, controls); >> + } >> +} >> + >> +void IPAMaliC55::fillParams(unsigned int request, >> + [[maybe_unused]] uint32_t bufferId) >> +{ >> + struct mali_c55_params_buffer *params; >> + IPAFrameContext &frameContext = context_.frameContexts.get(request); >> + >> + params = reinterpret_cast<mali_c55_params_buffer *>( >> + buffers_.at(bufferId).planes()[0].data()); >> + memset(params, 0, sizeof(mali_c55_params_buffer)); >> + >> + params->version = MALI_C55_PARAM_BUFFER_V1; >> + >> + for (auto const &algo : algorithms()) { >> + algo->prepare(context_, request, frameContext, params); >> + >> + ASSERT(params->total_size <= MALI_C55_PARAMS_MAX_SIZE); >> + } >> + >> + paramsComputed.emit(request); >> +} >> + >> +void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId, >> + const ControlList &sensorControls) >> +{ >> + IPAFrameContext &frameContext = context_.frameContexts.get(request); >> + const mali_c55_stats_buffer *stats = nullptr; >> + >> + stats = reinterpret_cast<mali_c55_stats_buffer *>( >> + buffers_.at(bufferId).planes()[0].data()); >> + >> + frameContext.agc.exposure = >> + sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>(); >> + frameContext.agc.sensorGain = >> + camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>()); >> + >> + ControlList metadata(controls::controls); >> + >> + for (auto const &a : algorithms()) { >> + Algorithm *algo = static_cast<Algorithm *>(a.get()); >> + >> + algo->process(context_, request, frameContext, stats, metadata); >> + } >> + >> + setControls(); >> + >> + statsProcessed.emit(request, metadata); >> +} >> + >> +} /* namespace ipa::mali_c55 */ >> + >> +/* >> + * External IPA module interface >> + */ >> +extern "C" { >> +const struct IPAModuleInfo ipaModuleInfo = { >> + IPA_MODULE_API_VERSION, >> + 1, >> + "mali-c55", >> + "mali-c55", >> +}; >> + >> +IPAInterface *ipaCreate() >> +{ >> + return new ipa::mali_c55::IPAMaliC55(); >> +} >> + >> +} /* extern "C" */ >> + >> +} /* namespace libcamera */ >> diff --git a/src/ipa/mali-c55/meson.build b/src/ipa/mali-c55/meson.build >> new file mode 100644 >> index 00000000..864d90ec >> --- /dev/null >> +++ b/src/ipa/mali-c55/meson.build >> @@ -0,0 +1,33 @@ >> +# SPDX-License-Identifier: CC0-1.0 >> + >> +subdir('algorithms') >> +subdir('data') >> + >> +ipa_name = 'ipa_mali_c55' >> + >> +mali_c55_ipa_sources = files([ >> + 'ipa_context.cpp', >> + 'mali-c55.cpp' >> +]) >> + >> +mali_c55_ipa_sources += mali_c55_ipa_algorithms >> + >> +mod = shared_module(ipa_name, >> + mali_c55_ipa_sources, >> + name_prefix : '', >> + include_directories : [ipa_includes, libipa_includes], >> + dependencies : libcamera_private, >> + link_with : libipa, >> + install : true, >> + install_dir : ipa_install_dir) >> + >> +if ipa_sign_module >> + custom_target(ipa_name + '.so.sign', >> + input : mod, >> + output : ipa_name + '.so.sign', >> + command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'], >> + install : false, >> + build_by_default : true) >> +endif >> + >> +ipa_names += ipa_name >> diff --git a/src/ipa/mali-c55/module.h b/src/ipa/mali-c55/module.h >> new file mode 100644 >> index 00000000..1d85ec1f >> --- /dev/null >> +++ b/src/ipa/mali-c55/module.h >> @@ -0,0 +1,27 @@ >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> +/* >> + * Copyright (C) 2024, Ideas On Board >> + * >> + * module.h - Mali-C55 IPA Module >> + */ >> + >> +#pragma once >> + >> +#include <linux/mali-c55-config.h> >> + >> +#include <libcamera/ipa/mali-c55_ipa_interface.h> >> + >> +#include <libipa/module.h> >> + >> +#include "ipa_context.h" >> + >> +namespace libcamera { >> + >> +namespace ipa::mali_c55 { >> + >> +using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo, >> + mali_c55_params_buffer, mali_c55_stats_buffer>; >> + >> +} /* namespace ipa::mali_c55 */ >> + >> +} /* namespace libcamera*/ >> -- >> 2.30.2 >>
diff --git a/Documentation/Doxyfile-common.in b/Documentation/Doxyfile-common.in index a70aee43..045c19dd 100644 --- a/Documentation/Doxyfile-common.in +++ b/Documentation/Doxyfile-common.in @@ -32,6 +32,7 @@ RECURSIVE = YES EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \ @TOP_BUILDDIR@/include/libcamera/ipa/*_proxy.h \ @TOP_BUILDDIR@/include/libcamera/ipa/ipu3_*.h \ + @TOP_BUILDDIR@/include/libcamera/ipa/mali-c55_*.h \ @TOP_BUILDDIR@/include/libcamera/ipa/raspberrypi_*.h \ @TOP_BUILDDIR@/include/libcamera/ipa/rkisp1_*.h \ @TOP_BUILDDIR@/include/libcamera/ipa/vimc_*.h diff --git a/include/libcamera/ipa/mali-c55.mojom b/include/libcamera/ipa/mali-c55.mojom new file mode 100644 index 00000000..5d7eb4ee --- /dev/null +++ b/include/libcamera/ipa/mali-c55.mojom @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +module ipa.mali_c55; + +import "include/libcamera/ipa/core.mojom"; + +struct IPAConfigInfo { + libcamera.IPACameraSensorInfo sensorInfo; + libcamera.ControlInfoMap sensorControls; +}; + +interface IPAMaliC55Interface { + init(libcamera.IPASettings settings, IPAConfigInfo configInfo) + => (int32 ret, libcamera.ControlInfoMap ipaControls); + start() => (int32 ret); + stop(); + + configure(IPAConfigInfo configInfo, uint8 bayerOrder) + => (int32 ret, libcamera.ControlInfoMap ipaControls); + + mapBuffers(array<libcamera.IPABuffer> buffers, bool readOnly); + unmapBuffers(array<libcamera.IPABuffer> buffers); + + [async] queueRequest(uint32 request, libcamera.ControlList reqControls); + [async] fillParams(uint32 request, uint32 bufferId); + [async] processStats(uint32 request, uint32 bufferId, + libcamera.ControlList sensorControls); +}; + +interface IPAMaliC55EventInterface { + paramsComputed(uint32 request); + statsProcessed(uint32 request, libcamera.ControlList metadata); + setSensorControls(libcamera.ControlList sensorControls); +}; diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index bf55e124..3129f119 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -64,6 +64,7 @@ libcamera_ipa_headers += custom_target('core_ipa_serializer_h', # Mapping from pipeline handler name to mojom file pipeline_ipa_mojom_mapping = { 'ipu3': 'ipu3.mojom', + 'mali-c55': 'mali-c55.mojom', 'rkisp1': 'rkisp1.mojom', 'rpi/vc4': 'raspberrypi.mojom', 'simple': 'soft.mojom', diff --git a/meson_options.txt b/meson_options.txt index 7aa41249..5365601c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -32,7 +32,7 @@ option('gstreamer', option('ipas', type : 'array', - choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'], + choices : ['ipu3', 'mali-c55', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'], description : 'Select which IPA modules to build') option('lc-compliance', diff --git a/src/ipa/mali-c55/algorithms/algorithm.h b/src/ipa/mali-c55/algorithms/algorithm.h new file mode 100644 index 00000000..36a3bff0 --- /dev/null +++ b/src/ipa/mali-c55/algorithms/algorithm.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas On Board + * + * algorithm.h - Mali-C55 control algorithm interface + */ + +#pragma once + +#include <linux/mali-c55-config.h> + +#include <libipa/algorithm.h> + +#include "module.h" + +namespace libcamera { + +namespace ipa::mali_c55 { + +class Algorithm : public libcamera::ipa::Algorithm<Module> +{ +}; + +union mali_c55_params_block { + struct mali_c55_params_block_header *header; + struct mali_c55_params_sensor_off_preshading *sensor_offs; + struct mali_c55_params_aexp_hist *aexp_hist; + struct mali_c55_params_aexp_weights *aexp_weights; + struct mali_c55_params_digital_gain *digital_gain; + struct mali_c55_params_awb_gains *awb_gains; + struct mali_c55_params_awb_config *awb_config; + struct mali_c55_params_mesh_shading_config *shading_config; + struct mali_c55_params_mesh_shading_selection *shading_selection; + __u8 *data; +}; + +} /* namespace ipa::mali_c55 */ + +} /* namespace libcamera */ diff --git a/src/ipa/mali-c55/algorithms/meson.build b/src/ipa/mali-c55/algorithms/meson.build new file mode 100644 index 00000000..f2203b15 --- /dev/null +++ b/src/ipa/mali-c55/algorithms/meson.build @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: CC0-1.0 + +mali_c55_ipa_algorithms = files([ +]) diff --git a/src/ipa/mali-c55/data/meson.build b/src/ipa/mali-c55/data/meson.build new file mode 100644 index 00000000..88109d17 --- /dev/null +++ b/src/ipa/mali-c55/data/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: CC0-1.0 + +conf_files = files([ + 'uncalibrated.yaml' +]) + +install_data(conf_files, + install_dir : ipa_data_dir / 'mali-c55') diff --git a/src/ipa/mali-c55/data/uncalibrated.yaml b/src/ipa/mali-c55/data/uncalibrated.yaml new file mode 100644 index 00000000..2cdc39a8 --- /dev/null +++ b/src/ipa/mali-c55/data/uncalibrated.yaml @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: CC0-1.0 +%YAML 1.1 +--- +version: 1 +algorithms: +... diff --git a/src/ipa/mali-c55/ipa_context.cpp b/src/ipa/mali-c55/ipa_context.cpp new file mode 100644 index 00000000..99f76ecd --- /dev/null +++ b/src/ipa/mali-c55/ipa_context.cpp @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas On Board + * + * ipa_context.cpp - MaliC55 IPA Context + */ + +#include "ipa_context.h" + +/** + * \file ipa_context.h + * \brief Context and state information shared between the algorithms + */ + +namespace libcamera::ipa::mali_c55 { + +/** + * \struct IPASessionConfiguration + * \brief Session configuration for the IPA module + * + * The session configuration contains all IPA configuration parameters that + * remain constant during the capture session, from IPA module start to stop. + * It is typically set during the configure() operation of the IPA module, but + * may also be updated in the start() operation. + */ + +/** + * \struct IPAActiveState + * \brief Active state for algorithms + * + * The active state contains all algorithm-specific data that needs to be + * maintained by algorithms across frames. Unlike the session configuration, + * the active state is mutable and constantly updated by algorithms. The active + * state is accessible through the IPAContext structure. + * + * The active state stores two distinct categories of information: + * + * - The consolidated value of all algorithm controls. Requests passed to + * the queueRequest() function store values for controls that the + * application wants to modify for that particular frame, and the + * queueRequest() function updates the active state with those values. + * The active state thus contains a consolidated view of the value of all + * controls handled by the algorithm. + * + * - The value of parameters computed by the algorithm when running in auto + * mode. Algorithms running in auto mode compute new parameters every + * time statistics buffers are received (either synchronously, or + * possibly in a background thread). The latest computed value of those + * parameters is stored in the active state in the process() function. + * + * Each of the members in the active state belongs to a specific algorithm. A + * member may be read by any algorithm, but shall only be written by its owner. + */ + +/** + * \struct IPAFrameContext + * \brief Per-frame context for algorithms + * + * The frame context stores two distinct categories of information: + * + * - The value of the controls to be applied to the frame. These values are + * typically set in the queueRequest() function, from the consolidated + * control values stored in the active state. The frame context thus stores + * values for all controls related to the algorithm, not limited to the + * controls specified in the corresponding request, but consolidated from all + * requests that have been queued so far. + * + * For controls that can be set manually or computed by an algorithm + * (depending on the algorithm operation mode), such as for instance the + * colour gains for the AWB algorithm, the control value will be stored in + * the frame context in the queueRequest() function only when operating in + * manual mode. When operating in auto mode, the values are computed by the + * algorithm in process(), stored in the active state, and copied to the + * frame context in prepare(), just before being stored in the ISP parameters + * buffer. + * + * The queueRequest() function can also store ancillary data in the frame + * context, such as flags to indicate if (and what) control values have + * changed compared to the previous request. + * + * - Status information computed by the algorithm for a frame. For instance, + * the colour temperature estimated by the AWB algorithm from ISP statistics + * calculated on a frame is stored in the frame context for that frame in + * the process() function. + */ + +/** + * \struct IPAContext + * \brief Global IPA context data shared between all algorithms + * + * \var IPAContext::configuration + * \brief The IPA session configuration, immutable during the session + * + * \var IPAContext::activeState + * \brief The IPA active state, storing the latest state for all algorithms + * + * \var IPAContext::frameContexts + * \brief Ring buffer of per-frame contexts + */ + +} /* namespace libcamera::ipa::mali_c55 */ diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h new file mode 100644 index 00000000..9e408a17 --- /dev/null +++ b/src/ipa/mali-c55/ipa_context.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas On Board + * + * ipa_context.h - Mali-C55 IPA Context + */ + +#pragma once + +#include <libcamera/controls.h> + +#include <libipa/fc_queue.h> + +namespace libcamera { + +namespace ipa::mali_c55 { + +struct IPASessionConfiguration { +}; + +struct IPAActiveState { +}; + +struct IPAFrameContext : public FrameContext { + struct { + uint32_t exposure; + double sensorGain; + } agc; +}; + +struct IPAContext { + IPASessionConfiguration configuration; + IPAActiveState activeState; + + FCQueue<IPAFrameContext> frameContexts; + + ControlInfoMap::Map ctrlMap; +}; + +} /* namespace ipa::mali_c55 */ + +} /* namespace libcamera*/ diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp new file mode 100644 index 00000000..7efc0124 --- /dev/null +++ b/src/ipa/mali-c55/mali-c55.cpp @@ -0,0 +1,339 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Ideas on Board Oy + * + * mali-c55.cpp - Mali-C55 ISP image processing algorithms + */ + +#include <map> +#include <string.h> +#include <vector> + +#include <linux/mali-c55-config.h> +#include <linux/v4l2-controls.h> + +#include <libcamera/base/file.h> +#include <libcamera/base/log.h> + +#include <libcamera/control_ids.h> +#include <libcamera/ipa/ipa_interface.h> +#include <libcamera/ipa/ipa_module_info.h> +#include <libcamera/ipa/mali-c55_ipa_interface.h> + +#include "libcamera/internal/bayer_format.h" +#include "libcamera/internal/mapped_framebuffer.h" +#include "libcamera/internal/yaml_parser.h" + +#include "algorithms/algorithm.h" +#include "libipa/camera_sensor_helper.h" + +#include "ipa_context.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(IPAMaliC55) + +namespace ipa::mali_c55 { + +/* Maximum number of frame contexts to be held */ +static constexpr uint32_t kMaxFrameContexts = 16; + +class IPAMaliC55 : public IPAMaliC55Interface, public Module +{ +public: + IPAMaliC55(); + + int init(const IPASettings &settings, const IPAConfigInfo &ipaConfig, + ControlInfoMap *ipaControls) override; + int start() override; + void stop() override; + int configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder, + ControlInfoMap *ipaControls) override; + void mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) override; + void unmapBuffers(const std::vector<IPABuffer> &buffers) override; + void queueRequest(const uint32_t request, const ControlList &controls) override; + void fillParams(unsigned int request, uint32_t bufferId) override; + void processStats(unsigned int request, unsigned int bufferId, + const ControlList &sensorControls) override; + +protected: + std::string logPrefix() const override; + +private: + void updateControls(const IPACameraSensorInfo &sensorInfo, + const ControlInfoMap &sensorControls, + ControlInfoMap *ipaControls); + void setControls(); + + std::map<unsigned int, MappedFrameBuffer> buffers_; + + ControlInfoMap sensorControls_; + + /* Interface to the Camera Helper */ + std::unique_ptr<CameraSensorHelper> camHelper_; + + /* Local parameter storage */ + struct IPAContext context_; +}; + +namespace { + +} /* namespace */ + +IPAMaliC55::IPAMaliC55() + : context_({ {}, {}, { kMaxFrameContexts }, {} }) +{ +} + +std::string IPAMaliC55::logPrefix() const +{ + return "mali-c55"; +} + +int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig, + ControlInfoMap *ipaControls) +{ + camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel); + if (!camHelper_) { + LOG(IPAMaliC55, Error) + << "Failed to create camera sensor helper for " + << settings.sensorModel; + return -ENODEV; + } + + File file(settings.configurationFile); + if (!file.open(File::OpenModeFlag::ReadOnly)) { + int ret = file.error(); + LOG(IPAMaliC55, Error) + << "Failed to open configuration file " + << settings.configurationFile << ": " << strerror(-ret); + return ret; + } + + std::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file); + if (!data) + return -EINVAL; + + if (!data->contains("algorithms")) { + LOG(IPAMaliC55, Error) + << "Tuning file doesn't contain any algorithm"; + return -EINVAL; + } + + int ret = createAlgorithms(context_, (*data)["algorithms"]); + if (ret) + return ret; + + updateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls); + + return 0; +} + +void IPAMaliC55::setControls() +{ + ControlList ctrls(sensorControls_); + + setSensorControls.emit(ctrls); +} + +int IPAMaliC55::start() +{ + return 0; +} + +void IPAMaliC55::stop() +{ + context_.frameContexts.clear(); +} + +void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo, + const ControlInfoMap &sensorControls, + ControlInfoMap *ipaControls) +{ + ControlInfoMap::Map ctrlMap; + + /* + * Compute the frame duration limits. + * + * The frame length is computed assuming a fixed line length combined + * with the vertical frame sizes. + */ + const ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second; + uint32_t hblank = v4l2HBlank.def().get<int32_t>(); + uint32_t lineLength = sensorInfo.outputSize.width + hblank; + + const ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second; + std::array<uint32_t, 3> frameHeights{ + v4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height, + v4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height, + v4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height, + }; + + std::array<int64_t, 3> frameDurations; + for (unsigned int i = 0; i < frameHeights.size(); ++i) { + uint64_t frameSize = lineLength * frameHeights[i]; + frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U); + } + + ctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0], + frameDurations[1], + frameDurations[2]); + + /* + * Compute exposure time limits from the V4L2_CID_EXPOSURE control + * limits and the line duration. + */ + double lineDuration = sensorInfo.minLineLength / sensorInfo.pixelRate; + + const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second; + int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration; + int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration; + int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration; + ctrlMap[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure, defExposure); + + /* Compute the analogue gain limits. */ + const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; + float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>()); + float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>()); + float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>()); + ctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain); + + /* + * Merge in any controls that we support either statically or from the + * algorithms. + */ + ctrlMap.merge(context_.ctrlMap); + + *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls); +} + +int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, + [[maybe_unused]] uint8_t bayerOrder, + ControlInfoMap *ipaControls) +{ + sensorControls_ = ipaConfig.sensorControls; + + /* Clear the IPA context before the streaming session. */ + context_.configuration = {}; + context_.activeState = {}; + context_.frameContexts.clear(); + + const IPACameraSensorInfo &info = ipaConfig.sensorInfo; + + updateControls(info, ipaConfig.sensorControls, ipaControls); + + for (auto const &a : algorithms()) { + Algorithm *algo = static_cast<Algorithm *>(a.get()); + + int ret = algo->configure(context_, info); + if (ret) + return ret; + } + + return 0; +} + +void IPAMaliC55::mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) +{ + for (const IPABuffer &buffer : buffers) { + const FrameBuffer fb(buffer.planes); + buffers_.emplace( + buffer.id, + MappedFrameBuffer( + &fb, + readOnly ? MappedFrameBuffer::MapFlag::Read + : MappedFrameBuffer::MapFlag::ReadWrite)); + } +} + +void IPAMaliC55::unmapBuffers(const std::vector<IPABuffer> &buffers) +{ + for (const IPABuffer &buffer : buffers) { + auto it = buffers_.find(buffer.id); + if (it == buffers_.end()) + continue; + + buffers_.erase(buffer.id); + } +} + +void IPAMaliC55::queueRequest(const uint32_t request, const ControlList &controls) +{ + IPAFrameContext &frameContext = context_.frameContexts.alloc(request); + + for (auto const &a : algorithms()) { + Algorithm *algo = static_cast<Algorithm *>(a.get()); + + algo->queueRequest(context_, request, frameContext, controls); + } +} + +void IPAMaliC55::fillParams(unsigned int request, + [[maybe_unused]] uint32_t bufferId) +{ + struct mali_c55_params_buffer *params; + IPAFrameContext &frameContext = context_.frameContexts.get(request); + + params = reinterpret_cast<mali_c55_params_buffer *>( + buffers_.at(bufferId).planes()[0].data()); + memset(params, 0, sizeof(mali_c55_params_buffer)); + + params->version = MALI_C55_PARAM_BUFFER_V1; + + for (auto const &algo : algorithms()) { + algo->prepare(context_, request, frameContext, params); + + ASSERT(params->total_size <= MALI_C55_PARAMS_MAX_SIZE); + } + + paramsComputed.emit(request); +} + +void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId, + const ControlList &sensorControls) +{ + IPAFrameContext &frameContext = context_.frameContexts.get(request); + const mali_c55_stats_buffer *stats = nullptr; + + stats = reinterpret_cast<mali_c55_stats_buffer *>( + buffers_.at(bufferId).planes()[0].data()); + + frameContext.agc.exposure = + sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>(); + frameContext.agc.sensorGain = + camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>()); + + ControlList metadata(controls::controls); + + for (auto const &a : algorithms()) { + Algorithm *algo = static_cast<Algorithm *>(a.get()); + + algo->process(context_, request, frameContext, stats, metadata); + } + + setControls(); + + statsProcessed.emit(request, metadata); +} + +} /* namespace ipa::mali_c55 */ + +/* + * External IPA module interface + */ +extern "C" { +const struct IPAModuleInfo ipaModuleInfo = { + IPA_MODULE_API_VERSION, + 1, + "mali-c55", + "mali-c55", +}; + +IPAInterface *ipaCreate() +{ + return new ipa::mali_c55::IPAMaliC55(); +} + +} /* extern "C" */ + +} /* namespace libcamera */ diff --git a/src/ipa/mali-c55/meson.build b/src/ipa/mali-c55/meson.build new file mode 100644 index 00000000..864d90ec --- /dev/null +++ b/src/ipa/mali-c55/meson.build @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: CC0-1.0 + +subdir('algorithms') +subdir('data') + +ipa_name = 'ipa_mali_c55' + +mali_c55_ipa_sources = files([ + 'ipa_context.cpp', + 'mali-c55.cpp' +]) + +mali_c55_ipa_sources += mali_c55_ipa_algorithms + +mod = shared_module(ipa_name, + mali_c55_ipa_sources, + name_prefix : '', + include_directories : [ipa_includes, libipa_includes], + dependencies : libcamera_private, + link_with : libipa, + install : true, + install_dir : ipa_install_dir) + +if ipa_sign_module + custom_target(ipa_name + '.so.sign', + input : mod, + output : ipa_name + '.so.sign', + command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'], + install : false, + build_by_default : true) +endif + +ipa_names += ipa_name diff --git a/src/ipa/mali-c55/module.h b/src/ipa/mali-c55/module.h new file mode 100644 index 00000000..1d85ec1f --- /dev/null +++ b/src/ipa/mali-c55/module.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas On Board + * + * module.h - Mali-C55 IPA Module + */ + +#pragma once + +#include <linux/mali-c55-config.h> + +#include <libcamera/ipa/mali-c55_ipa_interface.h> + +#include <libipa/module.h> + +#include "ipa_context.h" + +namespace libcamera { + +namespace ipa::mali_c55 { + +using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo, + mali_c55_params_buffer, mali_c55_stats_buffer>; + +} /* namespace ipa::mali_c55 */ + +} /* namespace libcamera*/