[libcamera-devel,v2,13/14] libcamera: ipa: rkisp1: Add basic control of auto exposure

Message ID 20190829232653.13214-14-niklas.soderlund@ragnatech.se
State Superseded
Headers show
Series
  • libcamera: ipa: Add basic IPA support
Related show

Commit Message

Niklas Söderlund Aug. 29, 2019, 11:26 p.m. UTC
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
but lacks the support to inform the camera of the status of the AE
control loop.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 src/ipa/ipa_rkisp1.cpp | 165 +++++++++++++++++++++++++++++++++++++++++
 src/ipa/meson.build    |   5 +-
 2 files changed, 168 insertions(+), 2 deletions(-)
 create mode 100644 src/ipa/ipa_rkisp1.cpp

Patch

diff --git a/src/ipa/ipa_rkisp1.cpp b/src/ipa/ipa_rkisp1.cpp
new file mode 100644
index 0000000000000000..063b075d8358c30d
--- /dev/null
+++ b/src/ipa/ipa_rkisp1.cpp
@@ -0,0 +1,165 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * ipa_rkisp1.cpp - RkISP1 Image Processing Algorithms
+ */
+
+#include <algorithm>
+#include <string.h>
+
+#include <linux/rkisp1-config.h>
+
+#include <libcamera/buffer.h>
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libcamera/request.h>
+
+#include "log.h"
+#include "utils.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPARkISP1)
+
+class IPARkISP1 : public IPAInterface
+{
+public:
+	int initSensor(const V4L2ControlInfoMap &controls) override;
+	void processRequest(const void *cookie, const ControlList &controls,
+			    Buffer &parameters) override;
+	void updateStatistics(const void *cookie, Buffer &statistics) override;
+
+private:
+	void setControls();
+
+	uint64_t statFrame_;
+
+	/* Camera sensor controls. */
+	bool autoExposure_;
+	uint64_t exposure_;
+	uint64_t minExposure_;
+	uint64_t maxExposure_;
+	uint64_t gain_;
+	uint64_t minGain_;
+	uint64_t maxGain_;
+};
+
+int IPARkISP1::initSensor(const V4L2ControlInfoMap &controls)
+{
+	statFrame_ = 0;
+
+	const auto itExp = controls.find(V4L2_CID_EXPOSURE);
+	if (itExp == controls.end())
+		return -ENODEV;
+
+	const auto itGain = controls.find(V4L2_CID_ANALOGUE_GAIN);
+	if (itGain == controls.end())
+		return -ENODEV;
+
+	autoExposure_ = true;
+
+	minExposure_ = std::max<uint64_t>(itExp->second.min(), 1);
+	maxExposure_ = itExp->second.max();
+	exposure_ = minExposure_;
+
+	minGain_ = std::max<uint64_t>(itGain->second.min(), 1);
+	maxGain_ = itGain->second.max();
+	gain_ = minGain_;
+
+	LOG(IPARkISP1, Info)
+		<< "Exposure: " << minExposure_ << "-" << maxExposure_
+		<< " Gain: " << minGain_ << "-" << maxGain_;
+
+	setControls();
+
+	return 0;
+}
+
+void IPARkISP1::setControls()
+{
+	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(ctrls);
+}
+
+void IPARkISP1::processRequest(const void *cookie, const ControlList &controls,
+			       Buffer &parameters)
+{
+	rkisp1_isp_params_cfg *params =
+		static_cast<rkisp1_isp_params_cfg *>(parameters.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;
+	}
+
+	queueRequest.emit(cookie);
+}
+
+void IPARkISP1::updateStatistics(const void *cookie, Buffer &statistics)
+{
+	const rkisp1_stat_buffer *stats =
+		static_cast<rkisp1_stat_buffer *>(statistics.mem()->planes()[0].mem());
+	const cifisp_stat *params = &stats->params;
+
+	if ((stats->meas_type & CIFISP_STAT_AUTOEXP) && (statFrame_ % 2 == 0)) {
+		const cifisp_ae_stat *ae = &params->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;
+		double tmp;
+
+		tmp = factor * exposure_ * gain_ / minGain_;
+		exposure_ = utils::clamp<uint64_t>((uint64_t)tmp, minExposure_, maxExposure_);
+
+		tmp = tmp / exposure_ * minGain_;
+		gain_ = utils::clamp<uint64_t>((uint64_t)tmp, minGain_, maxGain_);
+
+		setControls();
+	}
+
+	statFrame_++;
+}
+
+/*
+ * 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 dca7a9461385b68d..59311e6adae90ada 100644
--- a/src/ipa/meson.build
+++ b/src/ipa/meson.build
@@ -1,11 +1,12 @@ 
-ipa_dummy_sources = [
+ipa_sources = [
     ['ipa_dummy', 'ipa_dummy.cpp'],
     ['ipa_dummy_isolate', 'ipa_dummy_isolate.cpp'],
+    ['ipa_rkisp1', 'ipa_rkisp1.cpp'],
 ]
 
 ipa_install_dir = join_paths(get_option('libdir'), 'libcamera')
 
-foreach t : ipa_dummy_sources
+foreach t : ipa_sources
     ipa = shared_module(t[0],
                         t[1],
                         name_prefix : '',