From patchwork Thu Oct 3 17:49:40 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: 2092 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D091361767 for ; Thu, 3 Oct 2019 19:50:43 +0200 (CEST) X-Halon-ID: 34f93834-e606-11e9-837a-0050569116f7 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (unknown [84.172.88.101]) by bin-vsp-out-03.atm.binero.net (Halon) with ESMTPA id 34f93834-e606-11e9-837a-0050569116f7; Thu, 03 Oct 2019 19:49:54 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 3 Oct 2019 19:49:40 +0200 Message-Id: <20191003174941.1296988-11-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191003174941.1296988-1-niklas.soderlund@ragnatech.se> References: <20191003174941.1296988-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 10/11] 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: Thu, 03 Oct 2019 17:50:44 -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 --- include/ipa/rkisp1.h | 23 ++++ src/ipa/ipa_rkisp1.cpp | 278 +++++++++++++++++++++++++++++++++++++++++ src/ipa/meson.build | 13 ++ 3 files changed, 314 insertions(+) create mode 100644 include/ipa/rkisp1.h create mode 100644 src/ipa/ipa_rkisp1.cpp diff --git a/include/ipa/rkisp1.h b/include/ipa/rkisp1.h new file mode 100644 index 0000000000000000..de9c7971e7bc94ad --- /dev/null +++ b/include/ipa/rkisp1.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * rkisp1.h - Image Processing Algorithm interface for RkISP1 + */ +#ifndef __LIBCAMERA_IPA_INTERFACE_RKISP1_H__ +#define __LIBCAMERA_IPA_INTERFACE_RKISP1_H__ + +enum RkISP1BufferType { + RKISP1_BUFFER_PARAM = 1, + RKISP1_BUFFER_STAT = 2, +}; + +enum RkISP1Operations { + RKISP1_IPA_ACTION_V4L2_SET = 1, + RKISP1_IPA_ACTION_QUEUE_BUFFER = 2, + RKISP1_IPA_ACTION_META_DATA = 3, + RKISP1_IPA_EVENT_SIGNAL_BUFFER = 4, + RKISP1_IPA_EVENT_QUEUE_REQUEST = 5, +}; + +#endif /* __LIBCAMERA_IPA_INTERFACE_RKISP1_H__ */ diff --git a/src/ipa/ipa_rkisp1.cpp b/src/ipa/ipa_rkisp1.cpp new file mode 100644 index 0000000000000000..78dd148a66299ab9 --- /dev/null +++ b/src/ipa/ipa_rkisp1.cpp @@ -0,0 +1,278 @@ +/* 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 +#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 &buffers) override; + void processEvent(const IPAOperationData &event) override; + +private: + void signalBuffer(unsigned int type, unsigned int id); + void queueRequest(unsigned int frame, const ControlList &controls); + void updateStatistics(unsigned int frame, BufferMemory &statistics); + + void setControls(unsigned int frame); + void queueBuffer(unsigned int frame, unsigned int type, + unsigned int id); + void metaDataReady(unsigned int frame, unsigned int aeState); + + 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::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.type][buffer.id] = buffer.buffer; + bufferInfo_[buffer.type][buffer.id].planes()[0].mem(); + bufferFree_[buffer.type].push(buffer.id); + } +} + +void IPARkISP1::unmapBuffers(const std::vector &buffers) +{ + for (IPABuffer buffer : buffers) + bufferInfo_[buffer.type].erase(buffer.id); +} + +void IPARkISP1::processEvent(const IPAOperationData &event) +{ + switch (event.operation) { + case RKISP1_IPA_EVENT_SIGNAL_BUFFER: + signalBuffer(event.data[0], event.data[1]); + break; + case RKISP1_IPA_EVENT_QUEUE_REQUEST: + queueRequest(event.data[0], event.controls[0]); + break; + default: + LOG(IPARkISP1, Error) << "Unkown event " << event.operation; + break; + } +} + +void IPARkISP1::signalBuffer(unsigned int type, unsigned int id) +{ + if (type == RKISP1_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_[RKISP1_BUFFER_PARAM].empty()) { + LOG(IPARkISP1, Error) << "Param buffer underrun"; + return; + } + + if (bufferFree_[RKISP1_BUFFER_STAT].empty()) { + LOG(IPARkISP1, Error) << "Statistics buffer underrun"; + return; + } + + unsigned int paramid = bufferFree_[RKISP1_BUFFER_PARAM].front(); + bufferFree_[RKISP1_BUFFER_PARAM].pop(); + unsigned int statid = bufferFree_[RKISP1_BUFFER_STAT].front(); + bufferFree_[RKISP1_BUFFER_STAT].pop(); + + bufferFrame_[RKISP1_BUFFER_PARAM][paramid] = frame; + bufferFrame_[RKISP1_BUFFER_STAT][statid] = frame; + + /* Prepare parameters buffer. */ + BufferMemory &mem = bufferInfo_[RKISP1_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(controls::AeEnable)) { + autoExposure_ = controls.get(controls::AeEnable); + if (autoExposure_) + params->module_ens = CIFISP_MODULE_AEC; + + params->module_en_update = CIFISP_MODULE_AEC; + } + + /* Queue buffers to pipeline. */ + queueBuffer(frame, RKISP1_BUFFER_PARAM, paramid); + queueBuffer(frame, RKISP1_BUFFER_STAT, statid); +} + +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; + 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) { + 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); + } + + 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; + op.data.push_back(frame); + op.data.push_back(V4L2_CID_EXPOSURE); + op.data.push_back(exposure_); + op.data.push_back(V4L2_CID_ANALOGUE_GAIN); + op.data.push_back(gain_); + + queueFrameAction.emit(op); +} + +void IPARkISP1::queueBuffer(unsigned int frame, unsigned int type, + unsigned int id) +{ + IPAOperationData op; + op.operation = RKISP1_IPA_ACTION_QUEUE_BUFFER; + op.data = { frame, type, id }; + + queueFrameAction.emit(op); +} + +void IPARkISP1::metaDataReady(unsigned int frame, unsigned int aeState) +{ + IPAOperationData op; + op.operation = RKISP1_IPA_ACTION_META_DATA; + op.data = { frame, aeState }; + + queueFrameAction.emit(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 */ 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) + '"')