From bf0915a4e808247fc26d817e66cc20e6d15067d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Fri, 27 Sep 2019 04:44:16 +0200 Subject: libcamera: ipa: rkisp1: Add basic control of auto exposure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an IPA which controls the exposure time and analog gain for a sensor connected to the rkisp1 pipeline. The IPA supports turning AE on and off and informing the camera of the status of the AE control loop. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/ipa/meson.build | 2 + src/ipa/rkisp1/meson.build | 7 ++ src/ipa/rkisp1/rkisp1.cpp | 254 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 src/ipa/rkisp1/meson.build create mode 100644 src/ipa/rkisp1/rkisp1.cpp (limited to 'src/ipa') diff --git a/src/ipa/meson.build b/src/ipa/meson.build index ac16e1da..4f2a4577 100644 --- a/src/ipa/meson.build +++ b/src/ipa/meson.build @@ -22,3 +22,5 @@ endforeach config_h.set('IPA_MODULE_DIR', '"' + join_paths(get_option('prefix'), ipa_install_dir) + '"') + +subdir('rkisp1') diff --git a/src/ipa/rkisp1/meson.build b/src/ipa/rkisp1/meson.build new file mode 100644 index 00000000..1cab319c --- /dev/null +++ b/src/ipa/rkisp1/meson.build @@ -0,0 +1,7 @@ +rkisp1_ipa = shared_module('ipa_rkisp1', + 'rkisp1.cpp', + name_prefix : '', + include_directories : ipa_includes, + dependencies : libcamera_dep, + install : true, + install_dir : ipa_install_dir) diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp new file mode 100644 index 00000000..4f5fdb14 --- /dev/null +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * rkisp1.cpp - RkISP1 Image Processing Algorithms + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "utils.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(IPARkISP1) + +class IPARkISP1 : public IPAInterface +{ +public: + int init() override { return 0; } + + void configure(const std::map &streamConfig, + const std::map &entityControls) override; + void mapBuffers(const std::vector &buffers) override; + void unmapBuffers(const std::vector &ids) override; + void processEvent(const IPAOperationData &event) override; + +private: + void queueRequest(unsigned int frame, rkisp1_isp_params_cfg *params, + const ControlList &controls); + void updateStatistics(unsigned int frame, + const rkisp1_stat_buffer *stats); + + void setControls(unsigned int frame); + void metadataReady(unsigned int frame, unsigned int aeState); + + std::map bufferInfo_; + + /* Camera sensor controls. */ + bool autoExposure_; + uint32_t exposure_; + uint32_t minExposure_; + uint32_t maxExposure_; + uint32_t gain_; + uint32_t minGain_; + uint32_t maxGain_; +}; + +void IPARkISP1::configure(const std::map &streamConfig, + const std::map &entityControls) +{ + if (entityControls.empty()) + return; + + const V4L2ControlInfoMap &ctrls = entityControls.at(0); + + const auto itExp = ctrls.find(V4L2_CID_EXPOSURE); + if (itExp == ctrls.end()) { + LOG(IPARkISP1, Error) << "Can't find exposure control"; + return; + } + + const auto itGain = ctrls.find(V4L2_CID_ANALOGUE_GAIN); + if (itGain == ctrls.end()) { + LOG(IPARkISP1, Error) << "Can't find gain control"; + return; + } + + autoExposure_ = true; + + minExposure_ = std::max(itExp->second.range().min().get(), 1); + maxExposure_ = itExp->second.range().max().get(); + exposure_ = minExposure_; + + minGain_ = std::max(itGain->second.range().min().get(), 1); + maxGain_ = itGain->second.range().max().get(); + gain_ = minGain_; + + LOG(IPARkISP1, Info) + << "Exposure: " << minExposure_ << "-" << maxExposure_ + << " Gain: " << minGain_ << "-" << maxGain_; + + setControls(0); +} + +void IPARkISP1::mapBuffers(const std::vector &buffers) +{ + for (IPABuffer buffer : buffers) { + bufferInfo_[buffer.id] = buffer.memory; + bufferInfo_[buffer.id].planes()[0].mem(); + } +} + +void IPARkISP1::unmapBuffers(const std::vector &ids) +{ + for (unsigned int id : ids) + bufferInfo_.erase(id); +} + +void IPARkISP1::processEvent(const IPAOperationData &event) +{ + switch (event.operation) { + case RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER: { + unsigned int frame = event.data[0]; + unsigned int bufferId = event.data[1]; + + const rkisp1_stat_buffer *stats = + static_cast(bufferInfo_[bufferId].planes()[0].mem()); + + updateStatistics(frame, stats); + break; + } + case RKISP1_IPA_EVENT_QUEUE_REQUEST: { + unsigned int frame = event.data[0]; + unsigned int bufferId = event.data[1]; + + rkisp1_isp_params_cfg *params = + static_cast(bufferInfo_[bufferId].planes()[0].mem()); + + queueRequest(frame, params, event.controls[0]); + break; + } + default: + LOG(IPARkISP1, Error) << "Unkown event " << event.operation; + break; + } +} + +void IPARkISP1::queueRequest(unsigned int frame, rkisp1_isp_params_cfg *params, + const ControlList &controls) +{ + /* Prepare parameters buffer. */ + memset(params, 0, sizeof(*params)); + + /* Auto Exposure on/off. */ + if (controls.contains(controls::AeEnable)) { + autoExposure_ = controls.get(controls::AeEnable); + if (autoExposure_) + params->module_ens = CIFISP_MODULE_AEC; + + params->module_en_update = CIFISP_MODULE_AEC; + } + + IPAOperationData op; + op.operation = RKISP1_IPA_ACTION_PARAM_FILLED; + + queueFrameAction.emit(frame, op); +} + +void IPARkISP1::updateStatistics(unsigned int frame, + const rkisp1_stat_buffer *stats) +{ + const cifisp_stat *params = &stats->params; + unsigned int aeState = 0; + + if (stats->meas_type & CIFISP_STAT_AUTOEXP) { + const cifisp_ae_stat *ae = ¶ms->ae; + + const unsigned int target = 60; + + unsigned int value = 0; + unsigned int num = 0; + for (int i = 0; i < CIFISP_AE_MEAN_MAX; i++) { + if (ae->exp_mean[i] <= 15) + continue; + + value += ae->exp_mean[i]; + num++; + } + value /= num; + + double factor = (double)target / value; + + if (frame % 3 == 0) { + double exposure; + + exposure = factor * exposure_ * gain_ / minGain_; + exposure_ = utils::clamp((uint64_t)exposure, + minExposure_, + maxExposure_); + + exposure = exposure / exposure_ * minGain_; + gain_ = utils::clamp((uint64_t)exposure, + minGain_, maxGain_); + + setControls(frame + 1); + } + + aeState = fabs(factor - 1.0f) < 0.05f ? 2 : 1; + } + + metadataReady(frame, aeState); +} + +void IPARkISP1::setControls(unsigned int frame) +{ + IPAOperationData op; + op.operation = RKISP1_IPA_ACTION_V4L2_SET; + + V4L2ControlList ctrls; + ctrls.add(V4L2_CID_EXPOSURE, exposure_); + ctrls.add(V4L2_CID_ANALOGUE_GAIN, gain_); + op.v4l2controls.push_back(ctrls); + + queueFrameAction.emit(frame, op); +} + +void IPARkISP1::metadataReady(unsigned int frame, unsigned int aeState) +{ + ControlList ctrls(nullptr); + + if (aeState) + ctrls.set(controls::AeLocked, aeState == 2); + + IPAOperationData op; + op.operation = RKISP1_IPA_ACTION_METADATA; + op.controls.push_back(ctrls); + + queueFrameAction.emit(frame, op); +} + +/* + * External IPA module interface + */ + +extern "C" { +const struct IPAModuleInfo ipaModuleInfo = { + IPA_MODULE_API_VERSION, + 1, + "PipelineHandlerRkISP1", + "RkISP1 IPA", + "LGPL-2.1-or-later", +}; + +IPAInterface *ipaCreate() +{ + return new IPARkISP1(); +} +}; + +}; /* namespace libcamera */ -- cgit v1.2.1