diff options
-rw-r--r-- | ipu3.cpp | 241 | ||||
-rw-r--r-- | meson.build | 83 |
2 files changed, 324 insertions, 0 deletions
diff --git a/ipu3.cpp b/ipu3.cpp new file mode 100644 index 0000000..827a980 --- /dev/null +++ b/ipu3.cpp @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipu3.cpp - IPU3 Image Processing Algorithms + */ + +#include <stdint.h> +#include <sys/mman.h> + +#include <linux/intel-ipu3.h> +#include <linux/v4l2-controls.h> + +#include <libcamera/buffer.h> +#include <libcamera/control_ids.h> +#include <libcamera/ipa/ipa_interface.h> +#include <libcamera/ipa/ipa_module_info.h> +#include <libcamera/ipa/ipu3_ipa_interface.h> +#include <libcamera/request.h> + +#include <libcamera/base/log.h> + +#include "libcamera-helpers/mapped_buffer.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(IPAIPU3) + +namespace ipa::ipu3 { + +class IPAIPU3 : public IPAIPU3Interface +{ +public: + int init([[maybe_unused]] const IPASettings &settings) override + { + return 0; + } + int start() override; + void stop() override {} + + int configure(const IPAConfigInfo &configInfo) override; + + void mapBuffers(const std::vector<IPABuffer> &buffers) override; + void unmapBuffers(const std::vector<unsigned int> &ids) override; + void processEvent(const IPU3Event &event) override; + +private: + void processControls(unsigned int frame, const ControlList &controls); + void fillParams(unsigned int frame, ipu3_uapi_params *params); + void parseStatistics(unsigned int frame, + int64_t frameTimestamp, + const ipu3_uapi_stats_3a *stats); + + void setControls(unsigned int frame); + + std::map<unsigned int, MappedFrameBuffer> buffers_; + + ControlInfoMap ctrls_; + + IPACameraSensorInfo sensorInfo_; + + /* Camera sensor controls. */ + uint32_t exposure_; + uint32_t minExposure_; + uint32_t maxExposure_; + uint32_t gain_; + uint32_t minGain_; + uint32_t maxGain_; +}; + +int IPAIPU3::start() +{ + setControls(0); + + return 0; +} + +int IPAIPU3::configure(const IPAConfigInfo &configInfo) +{ + if (configInfo.entityControls.empty()) { + LOG(IPAIPU3, Error) << "No controls provided"; + return -ENODATA; + } + + sensorInfo_ = configInfo.sensorInfo; + + ctrls_ = configInfo.entityControls.at(0); + + const auto itExp = ctrls_.find(V4L2_CID_EXPOSURE); + if (itExp == ctrls_.end()) { + LOG(IPAIPU3, Error) << "Can't find exposure control"; + return -EINVAL; + } + + const auto itGain = ctrls_.find(V4L2_CID_ANALOGUE_GAIN); + if (itGain == ctrls_.end()) { + LOG(IPAIPU3, Error) << "Can't find gain control"; + return -EINVAL; + } + + minExposure_ = std::max(itExp->second.min().get<int32_t>(), 1); + maxExposure_ = itExp->second.max().get<int32_t>(); + exposure_ = maxExposure_; + + minGain_ = std::max(itGain->second.min().get<int32_t>(), 1); + maxGain_ = itGain->second.max().get<int32_t>(); + gain_ = maxGain_; + + return 0; +} + +void IPAIPU3::mapBuffers(const std::vector<IPABuffer> &buffers) +{ + for (const IPABuffer &buffer : buffers) { + const FrameBuffer fb(buffer.planes); + buffers_.emplace(buffer.id, + MappedFrameBuffer(&fb, PROT_READ | PROT_WRITE)); + } +} + +void IPAIPU3::unmapBuffers(const std::vector<unsigned int> &ids) +{ + for (unsigned int id : ids) { + auto it = buffers_.find(id); + if (it == buffers_.end()) + continue; + + buffers_.erase(it); + } +} + +void IPAIPU3::processEvent(const IPU3Event &event) +{ + switch (event.op) { + case EventProcessControls: { + processControls(event.frame, event.controls); + break; + } + case EventStatReady: { + auto it = buffers_.find(event.bufferId); + if (it == buffers_.end()) { + LOG(IPAIPU3, Error) << "Could not find stats buffer!"; + return; + } + + Span<uint8_t> mem = it->second.maps()[0]; + const ipu3_uapi_stats_3a *stats = + reinterpret_cast<ipu3_uapi_stats_3a *>(mem.data()); + + parseStatistics(event.frame, event.frameTimestamp, stats); + break; + } + case EventFillParams: { + auto it = buffers_.find(event.bufferId); + if (it == buffers_.end()) { + LOG(IPAIPU3, Error) << "Could not find param buffer!"; + return; + } + + Span<uint8_t> mem = it->second.maps()[0]; + ipu3_uapi_params *params = + reinterpret_cast<ipu3_uapi_params *>(mem.data()); + + fillParams(event.frame, params); + break; + } + default: + LOG(IPAIPU3, Error) << "Unknown event " << event.op; + break; + } +} + +void IPAIPU3::processControls([[maybe_unused]] unsigned int frame, + [[maybe_unused]] const ControlList &controls) +{ + /* \todo Start processing for 'frame' based on 'controls'. */ +} + +void IPAIPU3::fillParams(unsigned int frame, ipu3_uapi_params *params) +{ + /* Prepare parameters buffer. */ + memset(params, 0, sizeof(*params)); + + /* \todo Fill in parameters buffer. */ + + IPU3Action op; + op.op = ActionParamFilled; + + queueFrameAction.emit(frame, op); +} + +void IPAIPU3::parseStatistics(unsigned int frame, + [[maybe_unused]] int64_t frameTimestamp, + [[maybe_unused]] const ipu3_uapi_stats_3a *stats) +{ + ControlList ctrls(controls::controls); + + /* \todo React to statistics and update internal state machine. */ + /* \todo Add meta-data information to ctrls. */ + + IPU3Action op; + op.op = ActionMetadataReady; + op.controls = ctrls; + + queueFrameAction.emit(frame, op); +} + +void IPAIPU3::setControls(unsigned int frame) +{ + IPU3Action op; + op.op = ActionSetSensorControls; + + ControlList ctrls(ctrls_); + ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure_)); + ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain_)); + op.controls = ctrls; + + queueFrameAction.emit(frame, op); +} + +} /* namespace ipa::ipu3 */ + +/* + * External IPA module interface + */ + +extern "C" { +const struct IPAModuleInfo ipaModuleInfo = { + IPA_MODULE_API_VERSION, + 1, + "PipelineHandlerIPU3", + "ipu3", +}; + +IPAInterface *ipaCreate() +{ + return new ipa::ipu3::IPAIPU3(); +} +} + +} /* namespace libcamera */ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..80fb18d --- /dev/null +++ b/meson.build @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: CC0-1.0 +project('ipu3-ipa', 'c', 'cpp', + meson_version : '>= 0.55', + version : '0.0.0', + default_options : [ + 'werror=true', + 'warning_level=2', + 'cpp_std=c++17', + ], + license : 'Apache-2.0') + +ipa_name = 'intel-ipu3-ipa' + +ipa_install_dir = get_option('libdir') / 'libcamera' + +cc = meson.get_compiler('c') +libcamera_dep = dependency('libcamera') +libcamera_base = dependency('libcamera-base') +libatomic = cc.find_library('atomic', required : false) + +config_h = configuration_data() + +if cc.has_header_symbol('stdlib.h', 'secure_getenv', prefix : '#define _GNU_SOURCE') + config_h.set('HAVE_SECURE_GETENV', 1) +endif + +configure_file(output : 'config.h', configuration : config_h) +common_arguments = [ + '-DLIBCAMERA_BASE_PRIVATE', + '-Wshadow', + '-include', 'config.h', +] + +ipa_includes = [ + include_directories('include'), +] + +c_arguments = [] +cpp_arguments = [] + +if cc.get_id() == 'clang' + if cc.version().version_compare('<5') + error('clang version is too old, ipa-ipu3 requires 5.0 or newer') + endif + + # Use libc++ by default if available instead of libstdc++ when compiling + # with clang. + if cc.find_library('libc++', required: false).found() + cpp_arguments += [ + '-stdlib=libc++', + ] + endif + + cpp_arguments += [ + '-Wextra-semi', + ] +endif + +c_arguments += common_arguments +cpp_arguments += common_arguments + +add_project_arguments(c_arguments, language : 'c') +add_project_arguments(cpp_arguments, language : 'cpp') + +ipu3_ipa_files = files([ + 'ipu3.cpp', +]) + +ipu3_ipa_deps = [ + libatomic, + libcamera_base, + libcamera_dep, +] + +subdir('src') + +mod = shared_module(ipa_name, + [ipu3_ipa_files, libcamera_helpers], + name_prefix : '', + include_directories : ipa_includes, + dependencies : ipu3_ipa_deps, + install : true, + install_dir : ipa_install_dir) |