From patchwork Fri Sep 27 02:44:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2035 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CB0386191D for ; Fri, 27 Sep 2019 04:45:35 +0200 (CEST) X-Halon-ID: d2599c4a-e0d0-11e9-bdc3-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (unknown [84.172.88.101]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id d2599c4a-e0d0-11e9-bdc3-005056917a89; Fri, 27 Sep 2019 04:45:10 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Fri, 27 Sep 2019 04:44:16 +0200 Message-Id: <20190927024417.725906-13-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190927024417.725906-1-niklas.soderlund@ragnatech.se> References: <20190927024417.725906-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 12/13] libcamera: ipa: rkisp1: Add basic control of auto exposure X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 27 Sep 2019 02:45:36 -0000 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 --- src/ipa/ipa_rkisp1.cpp | 228 +++++++++++++++++++++++++++++++++++++++++ src/ipa/meson.build | 13 +++ 2 files changed, 241 insertions(+) create mode 100644 src/ipa/ipa_rkisp1.cpp diff --git a/src/ipa/ipa_rkisp1.cpp b/src/ipa/ipa_rkisp1.cpp new file mode 100644 index 0000000000000000..f90465516c6aff87 --- /dev/null +++ b/src/ipa/ipa_rkisp1.cpp @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * ipa_rkisp1.cpp - RkISP1 Image Processing Algorithms + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "log.h" +#include "utils.h" + +#define BUFFER_PARAM 1 +#define BUFFER_STAT 2 + +namespace libcamera { + +LOG_DEFINE_CATEGORY(IPARkISP1) + +class IPARkISP1 : public IPAInterface +{ +public: + int init() override { return 0; } + + void initSensor(const V4L2ControlInfoMap &controls) override; + void initBuffers(unsigned int type, + const std::vector &buffers) override; + void signalBuffer(unsigned int type, unsigned int id) override; + void queueRequest(unsigned int frame, const ControlList &controls) override; + +private: + void setControls(unsigned int frame); + void updateStatistics(unsigned int frame, BufferMemory &statistics); + + std::map> bufferInfo_; + std::map> bufferFrame_; + std::map> bufferFree_; + + /* Camera sensor controls. */ + bool autoExposure_; + uint64_t exposure_; + uint64_t minExposure_; + uint64_t maxExposure_; + uint64_t gain_; + uint64_t minGain_; + uint64_t maxGain_; +}; + +void IPARkISP1::initSensor(const V4L2ControlInfoMap &controls) +{ + const auto itExp = controls.find(V4L2_CID_EXPOSURE); + if (itExp == controls.end()) { + LOG(IPARkISP1, Error) << "Can't find exposure control"; + return; + } + + const auto itGain = controls.find(V4L2_CID_ANALOGUE_GAIN); + if (itGain == controls.end()) { + LOG(IPARkISP1, Error) << "Can't find gain control"; + return; + } + + autoExposure_ = true; + + minExposure_ = std::max(itExp->second.min(), 1); + maxExposure_ = itExp->second.max(); + exposure_ = minExposure_; + + minGain_ = std::max(itGain->second.min(), 1); + maxGain_ = itGain->second.max(); + gain_ = minGain_; + + LOG(IPARkISP1, Info) + << "Exposure: " << minExposure_ << "-" << maxExposure_ + << " Gain: " << minGain_ << "-" << maxGain_; + + setControls(0); +} + +void IPARkISP1::initBuffers(unsigned int type, + const std::vector &buffers) +{ + bufferInfo_[type].clear(); + for (unsigned int i = 0; i < buffers.size(); i++) { + bufferInfo_[type][i] = buffers[i]; + bufferFree_[type].push(i); + } +} + +void IPARkISP1::signalBuffer(unsigned int type, unsigned int id) +{ + if (type == BUFFER_STAT) { + unsigned int frame = bufferFrame_[type][id]; + BufferMemory &mem = bufferInfo_[type][id]; + updateStatistics(frame, mem); + } + + bufferFree_[type].push(id); +} + +void IPARkISP1::queueRequest(unsigned int frame, const ControlList &controls) +{ + /* Find buffers. */ + if (bufferFree_[BUFFER_PARAM].empty()) { + LOG(IPARkISP1, Error) << "Param buffer underrun"; + return; + } + + if (bufferFree_[BUFFER_STAT].empty()) { + LOG(IPARkISP1, Error) << "Statistics buffer underrun"; + return; + } + + unsigned int paramid = bufferFree_[BUFFER_PARAM].front(); + bufferFree_[BUFFER_PARAM].pop(); + unsigned int statid = bufferFree_[BUFFER_STAT].front(); + bufferFree_[BUFFER_STAT].pop(); + + bufferFrame_[BUFFER_PARAM][paramid] = frame; + bufferFrame_[BUFFER_STAT][statid] = frame; + + /* Prepare parameters buffer. */ + BufferMemory &mem = bufferInfo_[BUFFER_PARAM][paramid]; + rkisp1_isp_params_cfg *params = + static_cast(mem.planes()[0].mem()); + + memset(params, 0, sizeof(*params)); + + /* Auto Exposure on/off. */ + if (controls.contains(AeEnable)) { + autoExposure_ = controls[AeEnable].getBool(); + if (autoExposure_) + params->module_ens = CIFISP_MODULE_AEC; + + params->module_en_update = CIFISP_MODULE_AEC; + } + + /* Queue buffers to pipeline. */ + queueBuffer.emit(frame, BUFFER_PARAM, paramid); + queueBuffer.emit(frame, BUFFER_STAT, statid); +} + +void IPARkISP1::setControls(unsigned int frame) +{ + V4L2ControlList ctrls; + ctrls.add(V4L2_CID_EXPOSURE); + ctrls.add(V4L2_CID_ANALOGUE_GAIN); + ctrls[V4L2_CID_EXPOSURE]->setValue(exposure_); + ctrls[V4L2_CID_ANALOGUE_GAIN]->setValue(gain_); + + updateSensor.emit(frame, ctrls); +} + +void IPARkISP1::updateStatistics(unsigned int frame, BufferMemory &statistics) +{ + const rkisp1_stat_buffer *stats = + static_cast(statistics.planes()[0].mem()); + const cifisp_stat *params = &stats->params; + IPAMetaData metaData = {}; + + 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) { + value += ae->exp_mean[i]; + num++; + } + } + value /= num; + + double factor = (double)target / value; + + if (frame % 3 == 0) { + double tmp; + + tmp = factor * exposure_ * gain_ / minGain_; + exposure_ = utils::clamp((uint64_t)tmp, minExposure_, maxExposure_); + + tmp = tmp / exposure_ * minGain_; + gain_ = utils::clamp((uint64_t)tmp, minGain_, maxGain_); + + setControls(frame + 1); + } + + metaData.aeState = fabs(factor - 1.0f) < 0.05f ? + AeState::Converged : AeState::Searching; + } else { + metaData.aeState = AeState::Inactive; + } + + metaDataReady.emit(frame, metaData); +} + +/* + * 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 */ diff --git a/src/ipa/meson.build b/src/ipa/meson.build index 10448c2ffc76af4b..eb5b852eec282735 100644 --- a/src/ipa/meson.build +++ b/src/ipa/meson.build @@ -3,6 +3,10 @@ ipa_dummy_sources = [ ['ipa_dummy_isolate', 'Proprietary'], ] +ipa_sources = [ + ['ipa_rkisp1', 'ipa_rkisp1.cpp', 'LGPL-2.1-or-later'], +] + ipa_install_dir = join_paths(get_option('libdir'), 'libcamera') foreach t : ipa_dummy_sources @@ -14,5 +18,14 @@ foreach t : ipa_dummy_sources cpp_args : '-DLICENSE="' + t[1] + '"') endforeach +foreach t : ipa_sources + ipa = shared_module(t[0], t[1], + name_prefix : '', + include_directories : includes, + install : true, + install_dir : ipa_install_dir, + cpp_args : '-DLICENSE="' + t[2] + '"') +endforeach + config_h.set('IPA_MODULE_DIR', '"' + join_paths(get_option('prefix'), ipa_install_dir) + '"')