af9a8d372b
This code adds support for the ipu6 webcams via libcamera, based on the work in https://copr.fedorainfracloud.org/coprs/jwrdegoede/ipu6-softisp/. It's supposed to be included in your NixOS configuration imports. Change-Id: Ifb71999ad61161fa23506b97cb449f73fb1270e3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/10709 Tested-by: BuildkiteCI Reviewed-by: flokli <flokli@flokli.de> Autosubmit: flokli <flokli@flokli.de>
483 lines
14 KiB
Diff
483 lines
14 KiB
Diff
From 21f1dd954a44b4e8f81abbfea276e4b60f8a8297 Mon Sep 17 00:00:00 2001
|
|
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
|
Date: Thu, 23 Nov 2023 16:47:15 +0300
|
|
Subject: [PATCH 13/25] libcamera: software_isp: add Simple SoftwareIsp
|
|
implementation
|
|
|
|
The implementation of SoftwareIsp handles creation of Soft IPA
|
|
and interactions with it, so that the pipeline handler wouldn't
|
|
need to care about the Soft IPA.
|
|
|
|
Doxygen documentation by Dennis Bonke.
|
|
|
|
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
|
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
|
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
|
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
|
Tested-by: Pavel Machek <pavel@ucw.cz>
|
|
---
|
|
.../internal/software_isp/meson.build | 1 +
|
|
.../internal/software_isp/swisp_simple.h | 163 ++++++++++++
|
|
src/libcamera/software_isp/meson.build | 19 ++
|
|
src/libcamera/software_isp/swisp_simple.cpp | 238 ++++++++++++++++++
|
|
4 files changed, 421 insertions(+)
|
|
create mode 100644 include/libcamera/internal/software_isp/swisp_simple.h
|
|
create mode 100644 src/libcamera/software_isp/swisp_simple.cpp
|
|
|
|
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
|
index b5a0d737..cf21ace5 100644
|
|
--- a/include/libcamera/internal/software_isp/meson.build
|
|
+++ b/include/libcamera/internal/software_isp/meson.build
|
|
@@ -4,6 +4,7 @@ libcamera_internal_headers += files([
|
|
'debayer.h',
|
|
'debayer_cpu.h',
|
|
'debayer_params.h',
|
|
+ 'swisp_simple.h',
|
|
'swisp_stats.h',
|
|
'swstats.h',
|
|
'swstats_cpu.h',
|
|
diff --git a/include/libcamera/internal/software_isp/swisp_simple.h b/include/libcamera/internal/software_isp/swisp_simple.h
|
|
new file mode 100644
|
|
index 00000000..87613c23
|
|
--- /dev/null
|
|
+++ b/include/libcamera/internal/software_isp/swisp_simple.h
|
|
@@ -0,0 +1,163 @@
|
|
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
+/*
|
|
+ * Copyright (C) 2023, Linaro Ltd
|
|
+ *
|
|
+ * swisp_simple.h - Simple software ISP implementation
|
|
+ */
|
|
+
|
|
+#pragma once
|
|
+
|
|
+#include <libcamera/base/log.h>
|
|
+#include <libcamera/base/thread.h>
|
|
+
|
|
+#include <libcamera/pixel_format.h>
|
|
+
|
|
+#include <libcamera/ipa/soft_ipa_interface.h>
|
|
+#include <libcamera/ipa/soft_ipa_proxy.h>
|
|
+
|
|
+#include "libcamera/internal/dma_heaps.h"
|
|
+#include "libcamera/internal/software_isp.h"
|
|
+#include "libcamera/internal/software_isp/debayer_cpu.h"
|
|
+
|
|
+namespace libcamera {
|
|
+
|
|
+/**
|
|
+ * \brief Class for the Simple Software ISP.
|
|
+ *
|
|
+ * Implementation of the SoftwareIsp interface.
|
|
+ */
|
|
+class SwIspSimple : public SoftwareIsp
|
|
+{
|
|
+public:
|
|
+ /**
|
|
+ * \brief Constructor for the SwIspSimple object.
|
|
+ *
|
|
+ * \param[in] pipe The pipeline handler in use.
|
|
+ * \param[in] sensorControls The sensor controls.
|
|
+ */
|
|
+ SwIspSimple(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
|
|
+ ~SwIspSimple() {}
|
|
+
|
|
+ /**
|
|
+ * \brief Load a configuration from a file.
|
|
+ * \param[in] filename The file to load from.
|
|
+ *
|
|
+ * \return 0 on success.
|
|
+ */
|
|
+ int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
|
|
+
|
|
+ /**
|
|
+ * \brief Gets if there is a valid debayer object.
|
|
+ *
|
|
+ * \returns true if there is, false otherwise.
|
|
+ */
|
|
+ bool isValid() const;
|
|
+
|
|
+ /**
|
|
+ * \brief Get the supported output formats.
|
|
+ * \param[in] input The input format.
|
|
+ *
|
|
+ * \return all supported output formats or an empty vector if there are none.
|
|
+ */
|
|
+ std::vector<PixelFormat> formats(PixelFormat input);
|
|
+
|
|
+ /**
|
|
+ * \brief Get the supported output sizes for the given input format and size.
|
|
+ * \param[in] inputFormat The input format.
|
|
+ * \param[in] inputSize The input size.
|
|
+ *
|
|
+ * \return The valid size ranges or an empty range if there are none.
|
|
+ */
|
|
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
|
|
+
|
|
+ /**
|
|
+ * \brief Get the stride and the frame size.
|
|
+ * \param[in] outputFormat The output format.
|
|
+ * \param[in] size The output size.
|
|
+ *
|
|
+ * \return a tuple of the stride and the frame size, or a tuple with 0,0 if there is no valid output config.
|
|
+ */
|
|
+ std::tuple<unsigned int, unsigned int>
|
|
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
|
|
+
|
|
+ /**
|
|
+ * \brief Configure the SwIspSimple object according to the passed in parameters.
|
|
+ * \param[in] inputCfg The input configuration.
|
|
+ * \param[in] outputCfgs The output configurations.
|
|
+ * \param[in] sensorControls The sensor controls.
|
|
+ *
|
|
+ * \return 0 on success, a negative errno on failure.
|
|
+ */
|
|
+ int configure(const StreamConfiguration &inputCfg,
|
|
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
|
+ const ControlInfoMap &sensorControls);
|
|
+
|
|
+ /**
|
|
+ * \brief Exports the buffers for use in processing.
|
|
+ * \param[in] output The number of outputs requested.
|
|
+ * \param[in] count The number of planes.
|
|
+ * \param[out] buffers The exported buffers.
|
|
+ *
|
|
+ * \return count when successful, a negative return value if an error occurred.
|
|
+ */
|
|
+ int exportBuffers(unsigned int output, unsigned int count,
|
|
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers);
|
|
+
|
|
+ /**
|
|
+ * \brief Process the statistics gathered.
|
|
+ * \param[in] sensorControls The sensor controls.
|
|
+ */
|
|
+ void processStats(const ControlList &sensorControls);
|
|
+
|
|
+ /**
|
|
+ * \brief Starts the Software ISP worker.
|
|
+ *
|
|
+ * \return 0 on success, any other value indicates an error.
|
|
+ */
|
|
+ int start();
|
|
+
|
|
+ /**
|
|
+ * \brief Stops the Software ISP worker.
|
|
+ */
|
|
+ void stop();
|
|
+
|
|
+ /**
|
|
+ * \brief Queues buffers for processing.
|
|
+ * \param[in] input The input framebuffer.
|
|
+ * \param[in] outputs The output framebuffers.
|
|
+ *
|
|
+ * \return 0 on success, a negative errno on failure
|
|
+ */
|
|
+ int queueBuffers(FrameBuffer *input,
|
|
+ const std::map<unsigned int, FrameBuffer *> &outputs);
|
|
+
|
|
+ /**
|
|
+ * \brief Get the signal for when the sensor controls are set.
|
|
+ *
|
|
+ * \return The control list of the sensor controls.
|
|
+ */
|
|
+ Signal<const ControlList &> &getSignalSetSensorControls();
|
|
+
|
|
+ /**
|
|
+ * \brief Process the input framebuffer.
|
|
+ * \param[in] input The input framebuffer.
|
|
+ * \param[out] output The output framebuffer.
|
|
+ */
|
|
+ void process(FrameBuffer *input, FrameBuffer *output);
|
|
+
|
|
+private:
|
|
+ void saveIspParams(int dummy);
|
|
+ void statsReady(int dummy);
|
|
+ void inputReady(FrameBuffer *input);
|
|
+ void outputReady(FrameBuffer *output);
|
|
+
|
|
+ std::unique_ptr<DebayerCpu> debayer_;
|
|
+ Thread ispWorkerThread_;
|
|
+ SharedMemObject<DebayerParams> sharedParams_;
|
|
+ DebayerParams debayerParams_;
|
|
+ DmaHeap dmaHeap_;
|
|
+
|
|
+ std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
|
|
+};
|
|
+
|
|
+} /* namespace libcamera */
|
|
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
|
|
index 6d7a44d7..9464f2fd 100644
|
|
--- a/src/libcamera/software_isp/meson.build
|
|
+++ b/src/libcamera/software_isp/meson.build
|
|
@@ -6,3 +6,22 @@ libcamera_sources += files([
|
|
'swstats.cpp',
|
|
'swstats_cpu.cpp',
|
|
])
|
|
+
|
|
+# Software ISP is enabled for 'simple' pipeline handler.
|
|
+# The 'pipelines' option value should be
|
|
+# 'simple/<Software ISP implementation>' e.g.:
|
|
+# -Dpipelines=simple/simple
|
|
+# The source file should be named swisp_<Software ISP implementation>.cpp,
|
|
+# e.g. 'swisp_simple.cpp'.
|
|
+
|
|
+foreach pipeline : pipelines
|
|
+ pipeline = pipeline.split('/')
|
|
+ if pipeline.length() == 2 and pipeline[0] == 'simple'
|
|
+ libcamera_sources += files([
|
|
+ 'swisp_' + pipeline[1] + '.cpp',
|
|
+ ])
|
|
+ # the 'break' below can be removed if/when multiple
|
|
+ # Software ISP implementations are allowed in single build
|
|
+ break
|
|
+ endif
|
|
+endforeach
|
|
diff --git a/src/libcamera/software_isp/swisp_simple.cpp b/src/libcamera/software_isp/swisp_simple.cpp
|
|
new file mode 100644
|
|
index 00000000..0884166e
|
|
--- /dev/null
|
|
+++ b/src/libcamera/software_isp/swisp_simple.cpp
|
|
@@ -0,0 +1,238 @@
|
|
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
+/*
|
|
+ * Copyright (C) 2023, Linaro Ltd
|
|
+ *
|
|
+ * swisp_simple.cpp - Simple software ISP implementation
|
|
+ */
|
|
+
|
|
+#include "libcamera/internal/software_isp/swisp_simple.h"
|
|
+
|
|
+#include <sys/mman.h>
|
|
+#include <sys/types.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <libcamera/formats.h>
|
|
+#include <libcamera/stream.h>
|
|
+
|
|
+#include "libcamera/internal/bayer_format.h"
|
|
+#include "libcamera/internal/framebuffer.h"
|
|
+#include "libcamera/internal/ipa_manager.h"
|
|
+#include "libcamera/internal/mapped_framebuffer.h"
|
|
+
|
|
+namespace libcamera {
|
|
+
|
|
+SwIspSimple::SwIspSimple(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
|
|
+ : SoftwareIsp(pipe, sensorControls), debayer_(nullptr), debayerParams_{ 256, 256, 256, 0.5f },
|
|
+ dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
|
|
+{
|
|
+ if (!dmaHeap_.isValid()) {
|
|
+ LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object";
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params");
|
|
+ if (!sharedParams_.fd().isValid()) {
|
|
+ LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ std::unique_ptr<SwStatsCpu> stats;
|
|
+
|
|
+ stats = std::make_unique<SwStatsCpu>();
|
|
+ if (!stats) {
|
|
+ LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ stats->statsReady.connect(this, &SwIspSimple::statsReady);
|
|
+
|
|
+ debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
|
|
+ if (!debayer_) {
|
|
+ LOG(SoftwareIsp, Error) << "Failed to create DebayerCpu object";
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ debayer_->inputBufferReady.connect(this, &SwIspSimple::inputReady);
|
|
+ debayer_->outputBufferReady.connect(this, &SwIspSimple::outputReady);
|
|
+
|
|
+ ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
|
|
+ if (!ipa_) {
|
|
+ LOG(SoftwareIsp, Error)
|
|
+ << "Creating IPA for software ISP failed";
|
|
+ debayer_.reset();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
|
|
+ debayer_->getStatsFD(),
|
|
+ sharedParams_.fd(),
|
|
+ sensorControls);
|
|
+ if (ret) {
|
|
+ LOG(SoftwareIsp, Error) << "IPA init failed";
|
|
+ debayer_.reset();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ipa_->setIspParams.connect(this, &SwIspSimple::saveIspParams);
|
|
+
|
|
+ debayer_->moveToThread(&ispWorkerThread_);
|
|
+}
|
|
+
|
|
+void SwIspSimple::processStats(const ControlList &sensorControls)
|
|
+{
|
|
+ ASSERT(ipa_);
|
|
+ ipa_->processStats(sensorControls);
|
|
+}
|
|
+
|
|
+Signal<const ControlList &> &SwIspSimple::getSignalSetSensorControls()
|
|
+{
|
|
+ ASSERT(ipa_);
|
|
+ return ipa_->setSensorControls;
|
|
+}
|
|
+
|
|
+bool SwIspSimple::isValid() const
|
|
+{
|
|
+ return !!debayer_;
|
|
+}
|
|
+
|
|
+std::vector<PixelFormat> SwIspSimple::formats(PixelFormat inputFormat)
|
|
+{
|
|
+ ASSERT(debayer_ != nullptr);
|
|
+
|
|
+ return debayer_->formats(inputFormat);
|
|
+}
|
|
+
|
|
+SizeRange SwIspSimple::sizes(PixelFormat inputFormat, const Size &inputSize)
|
|
+{
|
|
+ ASSERT(debayer_ != nullptr);
|
|
+
|
|
+ return debayer_->sizes(inputFormat, inputSize);
|
|
+}
|
|
+
|
|
+std::tuple<unsigned int, unsigned int>
|
|
+SwIspSimple::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
|
|
+{
|
|
+ ASSERT(debayer_ != nullptr);
|
|
+
|
|
+ return debayer_->strideAndFrameSize(outputFormat, size);
|
|
+}
|
|
+
|
|
+int SwIspSimple::configure(const StreamConfiguration &inputCfg,
|
|
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
|
+ const ControlInfoMap &sensorControls)
|
|
+{
|
|
+ ASSERT(ipa_ != nullptr && debayer_ != nullptr);
|
|
+
|
|
+ int ret = ipa_->configure(sensorControls);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return debayer_->configure(inputCfg, outputCfgs);
|
|
+}
|
|
+
|
|
+int SwIspSimple::exportBuffers(unsigned int output, unsigned int count,
|
|
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
|
|
+{
|
|
+ ASSERT(debayer_ != nullptr);
|
|
+
|
|
+ /* single output for now */
|
|
+ if (output >= 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (unsigned int i = 0; i < count; i++) {
|
|
+ const std::string name = "frame-" + std::to_string(i);
|
|
+ const size_t frameSize = debayer_->frameSize();
|
|
+
|
|
+ FrameBuffer::Plane outPlane;
|
|
+ outPlane.fd = SharedFD(dmaHeap_.alloc(name.c_str(), frameSize));
|
|
+ if (!outPlane.fd.isValid()) {
|
|
+ LOG(SoftwareIsp, Error)
|
|
+ << "failed to allocate a dma_buf";
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ outPlane.offset = 0;
|
|
+ outPlane.length = frameSize;
|
|
+
|
|
+ std::vector<FrameBuffer::Plane> planes{ outPlane };
|
|
+ buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+int SwIspSimple::queueBuffers(FrameBuffer *input,
|
|
+ const std::map<unsigned int, FrameBuffer *> &outputs)
|
|
+{
|
|
+ unsigned int mask = 0;
|
|
+
|
|
+ /*
|
|
+ * Validate the outputs as a sanity check: at least one output is
|
|
+ * required, all outputs must reference a valid stream and no two
|
|
+ * outputs can reference the same stream.
|
|
+ */
|
|
+ if (outputs.empty())
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (auto [index, buffer] : outputs) {
|
|
+ if (!buffer)
|
|
+ return -EINVAL;
|
|
+ if (index >= 1) /* only single stream atm */
|
|
+ return -EINVAL;
|
|
+ if (mask & (1 << index))
|
|
+ return -EINVAL;
|
|
+
|
|
+ mask |= 1 << index;
|
|
+ }
|
|
+
|
|
+ process(input, outputs.at(0));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int SwIspSimple::start()
|
|
+{
|
|
+ int ret = ipa_->start();
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ispWorkerThread_.start();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void SwIspSimple::stop()
|
|
+{
|
|
+ ispWorkerThread_.exit();
|
|
+ ispWorkerThread_.wait();
|
|
+
|
|
+ ipa_->stop();
|
|
+}
|
|
+
|
|
+void SwIspSimple::process(FrameBuffer *input, FrameBuffer *output)
|
|
+{
|
|
+ debayer_->invokeMethod(&DebayerCpu::process,
|
|
+ ConnectionTypeQueued, input, output, debayerParams_);
|
|
+}
|
|
+
|
|
+void SwIspSimple::saveIspParams([[maybe_unused]] int dummy)
|
|
+{
|
|
+ debayerParams_ = *sharedParams_;
|
|
+}
|
|
+
|
|
+void SwIspSimple::statsReady(int dummy)
|
|
+{
|
|
+ ispStatsReady.emit(dummy);
|
|
+}
|
|
+
|
|
+void SwIspSimple::inputReady(FrameBuffer *input)
|
|
+{
|
|
+ inputBufferReady.emit(input);
|
|
+}
|
|
+
|
|
+void SwIspSimple::outputReady(FrameBuffer *output)
|
|
+{
|
|
+ outputBufferReady.emit(output);
|
|
+}
|
|
+
|
|
+REGISTER_SOFTWAREISP(SwIspSimple)
|
|
+
|
|
+} /* namespace libcamera */
|
|
--
|
|
2.43.0
|
|
|