enable MIPI camera
Signed-off-by: Harald Hoyer <harald@hoyer.xyz>
This commit is contained in:
		
							parent
							
								
									ec22fafec6
								
							
						
					
					
						commit
						897a92ea57
					
				
					 30 changed files with 22984 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
From aa818f7b749122f916be1ced48d1a3a2b3aeb47e Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Tue, 2 Jan 2024 23:47:20 +0300
 | 
			
		||||
Subject: [PATCH 01/25] libcamera: pipeline: simple: fix size adjustment in
 | 
			
		||||
 validate()
 | 
			
		||||
 | 
			
		||||
SimpleCameraConfiguration::validate() adjusts the configuration
 | 
			
		||||
of its streams (if the size is not in the outputSizes) to
 | 
			
		||||
the captureSize. But the captureSize itself can be not in the
 | 
			
		||||
outputSizes, and then the adjusted configuration won't be
 | 
			
		||||
valid resulting in camera configuration failure.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 src/libcamera/pipeline/simple/simple.cpp | 7 +++++--
 | 
			
		||||
 1 file changed, 5 insertions(+), 2 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
index 911051b2..4d0e7255 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
@@ -997,10 +997,13 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
 		if (!pipeConfig_->outputSizes.contains(cfg.size)) {
 | 
			
		||||
+			Size adjustedSize = pipeConfig_->captureSize;
 | 
			
		||||
+			if (!pipeConfig_->outputSizes.contains(adjustedSize))
 | 
			
		||||
+				adjustedSize = pipeConfig_->outputSizes.max;
 | 
			
		||||
 			LOG(SimplePipeline, Debug)
 | 
			
		||||
 				<< "Adjusting size from " << cfg.size
 | 
			
		||||
-				<< " to " << pipeConfig_->captureSize;
 | 
			
		||||
-			cfg.size = pipeConfig_->captureSize;
 | 
			
		||||
+				<< " to " << adjustedSize;
 | 
			
		||||
+			cfg.size = adjustedSize;
 | 
			
		||||
 			status = Adjusted;
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,194 @@
 | 
			
		|||
From ca3bcfde49f069a85f7860f61d8c3bd196f97139 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Tue, 26 Dec 2023 16:55:08 +0300
 | 
			
		||||
Subject: [PATCH 02/25] libcamera: internal: Move dma_heaps.[h,cpp] to common
 | 
			
		||||
 directories
 | 
			
		||||
 | 
			
		||||
DmaHeap class is useful outside the RPi pipeline handler too.
 | 
			
		||||
 | 
			
		||||
Move dma_heaps.h and dma_heaps.cpp to common directories. Update
 | 
			
		||||
the build files and RPi vc4 pipeline handler accordingly.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 .../libcamera/internal}/dma_heaps.h            |  4 ----
 | 
			
		||||
 include/libcamera/internal/meson.build         |  1 +
 | 
			
		||||
 .../{pipeline/rpi/vc4 => }/dma_heaps.cpp       | 18 +++++++-----------
 | 
			
		||||
 src/libcamera/meson.build                      |  1 +
 | 
			
		||||
 src/libcamera/pipeline/rpi/vc4/meson.build     |  1 -
 | 
			
		||||
 src/libcamera/pipeline/rpi/vc4/vc4.cpp         |  5 ++---
 | 
			
		||||
 6 files changed, 11 insertions(+), 19 deletions(-)
 | 
			
		||||
 rename {src/libcamera/pipeline/rpi/vc4 => include/libcamera/internal}/dma_heaps.h (92%)
 | 
			
		||||
 rename src/libcamera/{pipeline/rpi/vc4 => }/dma_heaps.cpp (83%)
 | 
			
		||||
 | 
			
		||||
diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
 | 
			
		||||
similarity index 92%
 | 
			
		||||
rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.h
 | 
			
		||||
rename to include/libcamera/internal/dma_heaps.h
 | 
			
		||||
index 0a4a8d86..cff8f140 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h
 | 
			
		||||
+++ b/include/libcamera/internal/dma_heaps.h
 | 
			
		||||
@@ -13,8 +13,6 @@
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
 
 | 
			
		||||
-namespace RPi {
 | 
			
		||||
-
 | 
			
		||||
 class DmaHeap
 | 
			
		||||
 {
 | 
			
		||||
 public:
 | 
			
		||||
@@ -27,6 +25,4 @@ private:
 | 
			
		||||
 	UniqueFD dmaHeapHandle_;
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
-} /* namespace RPi */
 | 
			
		||||
-
 | 
			
		||||
 } /* namespace libcamera */
 | 
			
		||||
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
 | 
			
		||||
index 7f1f3440..33eb0fb3 100644
 | 
			
		||||
--- a/include/libcamera/internal/meson.build
 | 
			
		||||
+++ b/include/libcamera/internal/meson.build
 | 
			
		||||
@@ -25,6 +25,7 @@ libcamera_internal_headers = files([
 | 
			
		||||
     'device_enumerator.h',
 | 
			
		||||
     'device_enumerator_sysfs.h',
 | 
			
		||||
     'device_enumerator_udev.h',
 | 
			
		||||
+    'dma_heaps.h',
 | 
			
		||||
     'formats.h',
 | 
			
		||||
     'framebuffer.h',
 | 
			
		||||
     'ipa_manager.h',
 | 
			
		||||
diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
 | 
			
		||||
similarity index 83%
 | 
			
		||||
rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
 | 
			
		||||
rename to src/libcamera/dma_heaps.cpp
 | 
			
		||||
index 317b1fc1..7444d9c2 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
 | 
			
		||||
+++ b/src/libcamera/dma_heaps.cpp
 | 
			
		||||
@@ -5,8 +5,6 @@
 | 
			
		||||
  * dma_heaps.h - Helper class for dma-heap allocations.
 | 
			
		||||
  */
 | 
			
		||||
 
 | 
			
		||||
-#include "dma_heaps.h"
 | 
			
		||||
-
 | 
			
		||||
 #include <array>
 | 
			
		||||
 #include <fcntl.h>
 | 
			
		||||
 #include <linux/dma-buf.h>
 | 
			
		||||
@@ -16,6 +14,8 @@
 | 
			
		||||
 
 | 
			
		||||
 #include <libcamera/base/log.h>
 | 
			
		||||
 
 | 
			
		||||
+#include "libcamera/internal/dma_heaps.h"
 | 
			
		||||
+
 | 
			
		||||
 /*
 | 
			
		||||
  * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
 | 
			
		||||
  * to only have to worry about importing.
 | 
			
		||||
@@ -30,9 +30,7 @@ static constexpr std::array<const char *, 2> heapNames = {
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
 
 | 
			
		||||
-LOG_DECLARE_CATEGORY(RPI)
 | 
			
		||||
-
 | 
			
		||||
-namespace RPi {
 | 
			
		||||
+LOG_DEFINE_CATEGORY(DmaHeap)
 | 
			
		||||
 
 | 
			
		||||
 DmaHeap::DmaHeap()
 | 
			
		||||
 {
 | 
			
		||||
@@ -40,7 +38,7 @@ DmaHeap::DmaHeap()
 | 
			
		||||
 		int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
 | 
			
		||||
 		if (ret < 0) {
 | 
			
		||||
 			ret = errno;
 | 
			
		||||
-			LOG(RPI, Debug) << "Failed to open " << name << ": "
 | 
			
		||||
+			LOG(DmaHeap, Debug) << "Failed to open " << name << ": "
 | 
			
		||||
 					<< strerror(ret);
 | 
			
		||||
 			continue;
 | 
			
		||||
 		}
 | 
			
		||||
@@ -50,7 +48,7 @@ DmaHeap::DmaHeap()
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	if (!dmaHeapHandle_.isValid())
 | 
			
		||||
-		LOG(RPI, Error) << "Could not open any dmaHeap device";
 | 
			
		||||
+		LOG(DmaHeap, Error) << "Could not open any dmaHeap device";
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 DmaHeap::~DmaHeap() = default;
 | 
			
		||||
@@ -69,7 +67,7 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
 | 
			
		||||
 
 | 
			
		||||
 	ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
 | 
			
		||||
 	if (ret < 0) {
 | 
			
		||||
-		LOG(RPI, Error) << "dmaHeap allocation failure for "
 | 
			
		||||
+		LOG(DmaHeap, Error) << "dmaHeap allocation failure for "
 | 
			
		||||
 				<< name;
 | 
			
		||||
 		return {};
 | 
			
		||||
 	}
 | 
			
		||||
@@ -77,7 +75,7 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
 | 
			
		||||
 	UniqueFD allocFd(alloc.fd);
 | 
			
		||||
 	ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
 | 
			
		||||
 	if (ret < 0) {
 | 
			
		||||
-		LOG(RPI, Error) << "dmaHeap naming failure for "
 | 
			
		||||
+		LOG(DmaHeap, Error) << "dmaHeap naming failure for "
 | 
			
		||||
 				<< name;
 | 
			
		||||
 		return {};
 | 
			
		||||
 	}
 | 
			
		||||
@@ -85,6 +83,4 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
 | 
			
		||||
 	return allocFd;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-} /* namespace RPi */
 | 
			
		||||
-
 | 
			
		||||
 } /* namespace libcamera */
 | 
			
		||||
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
 | 
			
		||||
index 45f63e93..3c5e43df 100644
 | 
			
		||||
--- a/src/libcamera/meson.build
 | 
			
		||||
+++ b/src/libcamera/meson.build
 | 
			
		||||
@@ -17,6 +17,7 @@ libcamera_sources = files([
 | 
			
		||||
     'delayed_controls.cpp',
 | 
			
		||||
     'device_enumerator.cpp',
 | 
			
		||||
     'device_enumerator_sysfs.cpp',
 | 
			
		||||
+    'dma_heaps.cpp',
 | 
			
		||||
     'fence.cpp',
 | 
			
		||||
     'formats.cpp',
 | 
			
		||||
     'framebuffer.cpp',
 | 
			
		||||
diff --git a/src/libcamera/pipeline/rpi/vc4/meson.build b/src/libcamera/pipeline/rpi/vc4/meson.build
 | 
			
		||||
index cdb049c5..386e2296 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/rpi/vc4/meson.build
 | 
			
		||||
+++ b/src/libcamera/pipeline/rpi/vc4/meson.build
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
 # SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
 
 | 
			
		||||
 libcamera_sources += files([
 | 
			
		||||
-    'dma_heaps.cpp',
 | 
			
		||||
     'vc4.cpp',
 | 
			
		||||
 ])
 | 
			
		||||
 
 | 
			
		||||
diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
 | 
			
		||||
index 26102ea7..3a42e75e 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
 | 
			
		||||
+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
 | 
			
		||||
@@ -12,12 +12,11 @@
 | 
			
		||||
 #include <libcamera/formats.h>
 | 
			
		||||
 
 | 
			
		||||
 #include "libcamera/internal/device_enumerator.h"
 | 
			
		||||
+#include "libcamera/internal/dma_heaps.h"
 | 
			
		||||
 
 | 
			
		||||
 #include "../common/pipeline_base.h"
 | 
			
		||||
 #include "../common/rpi_stream.h"
 | 
			
		||||
 
 | 
			
		||||
-#include "dma_heaps.h"
 | 
			
		||||
-
 | 
			
		||||
 using namespace std::chrono_literals;
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
@@ -87,7 +86,7 @@ public:
 | 
			
		||||
 	RPi::Device<Isp, 4> isp_;
 | 
			
		||||
 
 | 
			
		||||
 	/* DMAHEAP allocation helper. */
 | 
			
		||||
-	RPi::DmaHeap dmaHeap_;
 | 
			
		||||
+	DmaHeap dmaHeap_;
 | 
			
		||||
 	SharedFD lsTable_;
 | 
			
		||||
 
 | 
			
		||||
 	struct Config {
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
From 6d5f3b0b54df4ff66079675a4c1f0f0b76778e22 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Wed, 10 Jan 2024 23:51:25 +0300
 | 
			
		||||
Subject: [PATCH 03/25] libcamera: dma_heaps: extend DmaHeap class to support
 | 
			
		||||
 system heap
 | 
			
		||||
 | 
			
		||||
Add an argument to the constructor to specify dma heaps type(s)
 | 
			
		||||
to use. Can be DmaHeapFlag::Cma and/or DmaHeapFlag::System.
 | 
			
		||||
By default DmaHeapFlag::Cma is used. If both DmaHeapFlag::Cma and
 | 
			
		||||
DmaHeapFlag::System are set, CMA heap is tried first.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 include/libcamera/internal/dma_heaps.h | 12 +++++++-
 | 
			
		||||
 src/libcamera/dma_heaps.cpp            | 39 +++++++++++++++-----------
 | 
			
		||||
 2 files changed, 34 insertions(+), 17 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
 | 
			
		||||
index cff8f140..22aa1007 100644
 | 
			
		||||
--- a/include/libcamera/internal/dma_heaps.h
 | 
			
		||||
+++ b/include/libcamera/internal/dma_heaps.h
 | 
			
		||||
@@ -9,6 +9,7 @@
 | 
			
		||||
 
 | 
			
		||||
 #include <stddef.h>
 | 
			
		||||
 
 | 
			
		||||
+#include <libcamera/base/flags.h>
 | 
			
		||||
 #include <libcamera/base/unique_fd.h>
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
@@ -16,7 +17,14 @@ namespace libcamera {
 | 
			
		||||
 class DmaHeap
 | 
			
		||||
 {
 | 
			
		||||
 public:
 | 
			
		||||
-	DmaHeap();
 | 
			
		||||
+	enum class DmaHeapFlag {
 | 
			
		||||
+		Cma = (1 << 0),
 | 
			
		||||
+		System = (1 << 1),
 | 
			
		||||
+	};
 | 
			
		||||
+
 | 
			
		||||
+	using DmaHeapFlags = Flags<DmaHeapFlag>;
 | 
			
		||||
+
 | 
			
		||||
+	DmaHeap(DmaHeapFlags flags = DmaHeapFlag::Cma);
 | 
			
		||||
 	~DmaHeap();
 | 
			
		||||
 	bool isValid() const { return dmaHeapHandle_.isValid(); }
 | 
			
		||||
 	UniqueFD alloc(const char *name, std::size_t size);
 | 
			
		||||
@@ -25,4 +33,6 @@ private:
 | 
			
		||||
 	UniqueFD dmaHeapHandle_;
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
+LIBCAMERA_FLAGS_ENABLE_OPERATORS(DmaHeap::DmaHeapFlag)
 | 
			
		||||
+
 | 
			
		||||
 } /* namespace libcamera */
 | 
			
		||||
diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
 | 
			
		||||
index 7444d9c2..177de31b 100644
 | 
			
		||||
--- a/src/libcamera/dma_heaps.cpp
 | 
			
		||||
+++ b/src/libcamera/dma_heaps.cpp
 | 
			
		||||
@@ -16,6 +16,8 @@
 | 
			
		||||
 
 | 
			
		||||
 #include "libcamera/internal/dma_heaps.h"
 | 
			
		||||
 
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
 /*
 | 
			
		||||
  * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
 | 
			
		||||
  * to only have to worry about importing.
 | 
			
		||||
@@ -23,28 +25,33 @@
 | 
			
		||||
  * Annoyingly, should the cma heap size be specified on the kernel command line
 | 
			
		||||
  * instead of DT, the heap gets named "reserved" instead.
 | 
			
		||||
  */
 | 
			
		||||
-static constexpr std::array<const char *, 2> heapNames = {
 | 
			
		||||
-	"/dev/dma_heap/linux,cma",
 | 
			
		||||
-	"/dev/dma_heap/reserved"
 | 
			
		||||
+static constexpr std::array<std::pair<DmaHeap::DmaHeapFlag, const char *>, 3> heapNames = {
 | 
			
		||||
+	/* CMA heap names first */
 | 
			
		||||
+	std::make_pair(DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma"),
 | 
			
		||||
+	std::make_pair(DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved"),
 | 
			
		||||
+	std::make_pair(DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system")
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
-namespace libcamera {
 | 
			
		||||
-
 | 
			
		||||
 LOG_DEFINE_CATEGORY(DmaHeap)
 | 
			
		||||
 
 | 
			
		||||
-DmaHeap::DmaHeap()
 | 
			
		||||
+DmaHeap::DmaHeap(DmaHeapFlags flags)
 | 
			
		||||
 {
 | 
			
		||||
-	for (const char *name : heapNames) {
 | 
			
		||||
-		int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
 | 
			
		||||
-		if (ret < 0) {
 | 
			
		||||
-			ret = errno;
 | 
			
		||||
-			LOG(DmaHeap, Debug) << "Failed to open " << name << ": "
 | 
			
		||||
-					<< strerror(ret);
 | 
			
		||||
-			continue;
 | 
			
		||||
-		}
 | 
			
		||||
+	int ret;
 | 
			
		||||
 
 | 
			
		||||
-		dmaHeapHandle_ = UniqueFD(ret);
 | 
			
		||||
-		break;
 | 
			
		||||
+	for (const auto &name : heapNames) {
 | 
			
		||||
+		if (flags & name.first) {
 | 
			
		||||
+			ret = ::open(name.second, O_RDWR | O_CLOEXEC, 0);
 | 
			
		||||
+			if (ret < 0) {
 | 
			
		||||
+				ret = errno;
 | 
			
		||||
+				LOG(DmaHeap, Debug) << "Failed to open " << name.second << ": "
 | 
			
		||||
+						    << strerror(ret);
 | 
			
		||||
+				continue;
 | 
			
		||||
+			}
 | 
			
		||||
+
 | 
			
		||||
+			LOG(DmaHeap, Debug) << "Using " << name.second;
 | 
			
		||||
+			dmaHeapHandle_ = UniqueFD(ret);
 | 
			
		||||
+			break;
 | 
			
		||||
+		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	if (!dmaHeapHandle_.isValid())
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
From 006a4a31a6803e92ec67f48b66da2cdff8b2f6ab Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Sun, 29 Oct 2023 15:56:48 +0300
 | 
			
		||||
Subject: [PATCH 04/25] libcamera: internal: Move SharedMemObject class to a
 | 
			
		||||
 common directory
 | 
			
		||||
 | 
			
		||||
Move SharedMemObject class out of RPi namespace and put it into
 | 
			
		||||
include/libcamera/internal so that everyone could use it.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 include/libcamera/internal/meson.build                        | 1 +
 | 
			
		||||
 .../common => include/libcamera/internal}/shared_mem_object.h | 4 ----
 | 
			
		||||
 2 files changed, 1 insertion(+), 4 deletions(-)
 | 
			
		||||
 rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (98%)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
 | 
			
		||||
index 33eb0fb3..5807dfd9 100644
 | 
			
		||||
--- a/include/libcamera/internal/meson.build
 | 
			
		||||
+++ b/include/libcamera/internal/meson.build
 | 
			
		||||
@@ -39,6 +39,7 @@ libcamera_internal_headers = files([
 | 
			
		||||
     'process.h',
 | 
			
		||||
     'pub_key.h',
 | 
			
		||||
     'request.h',
 | 
			
		||||
+    'shared_mem_object.h',
 | 
			
		||||
     'source_paths.h',
 | 
			
		||||
     'sysfs.h',
 | 
			
		||||
     'v4l2_device.h',
 | 
			
		||||
diff --git a/src/libcamera/pipeline/rpi/common/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
 | 
			
		||||
similarity index 98%
 | 
			
		||||
rename from src/libcamera/pipeline/rpi/common/shared_mem_object.h
 | 
			
		||||
rename to include/libcamera/internal/shared_mem_object.h
 | 
			
		||||
index aa56c220..bfb639ee 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h
 | 
			
		||||
+++ b/include/libcamera/internal/shared_mem_object.h
 | 
			
		||||
@@ -19,8 +19,6 @@
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
 
 | 
			
		||||
-namespace RPi {
 | 
			
		||||
-
 | 
			
		||||
 template<class T>
 | 
			
		||||
 class SharedMemObject
 | 
			
		||||
 {
 | 
			
		||||
@@ -123,6 +121,4 @@ private:
 | 
			
		||||
 	T *obj_;
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
-} /* namespace RPi */
 | 
			
		||||
-
 | 
			
		||||
 } /* namespace libcamera */
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,139 @@
 | 
			
		|||
From cb9ff82efd82af8ae26b2aca4183928c74f7ef34 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Date: Wed, 20 Dec 2023 16:22:29 +0100
 | 
			
		||||
Subject: [PATCH 05/25] libcamera: internal: Document the SharedMemObject class
 | 
			
		||||
 | 
			
		||||
Document the SharedMemObject class.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.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>
 | 
			
		||||
---
 | 
			
		||||
 .../libcamera/internal/shared_mem_object.h    | 53 +++++++++++++++++++
 | 
			
		||||
 1 file changed, 53 insertions(+)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
 | 
			
		||||
index bfb639ee..e862ce48 100644
 | 
			
		||||
--- a/include/libcamera/internal/shared_mem_object.h
 | 
			
		||||
+++ b/include/libcamera/internal/shared_mem_object.h
 | 
			
		||||
@@ -19,10 +19,20 @@
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
 
 | 
			
		||||
+/**
 | 
			
		||||
+ * \class SharedMemObject
 | 
			
		||||
+ * \brief Helper class for shared memory allocations.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Takes a template T which is used to indicate the
 | 
			
		||||
+ * data type of the object stored.
 | 
			
		||||
+ */
 | 
			
		||||
 template<class T>
 | 
			
		||||
 class SharedMemObject
 | 
			
		||||
 {
 | 
			
		||||
 public:
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief The size of the object that is going to be stored here.
 | 
			
		||||
+	 */
 | 
			
		||||
 	static constexpr std::size_t SIZE = sizeof(T);
 | 
			
		||||
 
 | 
			
		||||
 	SharedMemObject()
 | 
			
		||||
@@ -30,6 +40,11 @@ public:
 | 
			
		||||
 	{
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Contstructor for the SharedMemObject.
 | 
			
		||||
+	 * \param[in] name The requested name.
 | 
			
		||||
+	 * \param[in] args Any additional args.
 | 
			
		||||
+	 */
 | 
			
		||||
 	template<class... Args>
 | 
			
		||||
 	SharedMemObject(const std::string &name, Args &&...args)
 | 
			
		||||
 		: name_(name), obj_(nullptr)
 | 
			
		||||
@@ -57,6 +72,10 @@ public:
 | 
			
		||||
 		obj_ = new (mem) T(std::forward<Args>(args)...);
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Move constructor for SharedMemObject.
 | 
			
		||||
+	 * \param[in] rhs The object to move.
 | 
			
		||||
+	 */
 | 
			
		||||
 	SharedMemObject(SharedMemObject<T> &&rhs)
 | 
			
		||||
 	{
 | 
			
		||||
 		this->name_ = std::move(rhs.name_);
 | 
			
		||||
@@ -76,6 +95,10 @@ public:
 | 
			
		||||
 	/* Make SharedMemObject non-copyable for now. */
 | 
			
		||||
 	LIBCAMERA_DISABLE_COPY(SharedMemObject)
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Operator= for SharedMemObject.
 | 
			
		||||
+	 * \param[in] rhs The SharedMemObject object to take the data from.
 | 
			
		||||
+	 */
 | 
			
		||||
 	SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
 | 
			
		||||
 	{
 | 
			
		||||
 		this->name_ = std::move(rhs.name_);
 | 
			
		||||
@@ -85,31 +108,61 @@ public:
 | 
			
		||||
 		return *this;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Operator-> for SharedMemObject.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return the object.
 | 
			
		||||
+	 */
 | 
			
		||||
 	T *operator->()
 | 
			
		||||
 	{
 | 
			
		||||
 		return obj_;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Operator-> for SharedMemObject.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return the object.
 | 
			
		||||
+	 */
 | 
			
		||||
 	const T *operator->() const
 | 
			
		||||
 	{
 | 
			
		||||
 		return obj_;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Operator* for SharedMemObject.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return the object.
 | 
			
		||||
+	 */
 | 
			
		||||
 	T &operator*()
 | 
			
		||||
 	{
 | 
			
		||||
 		return *obj_;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Operator* for SharedMemObject.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return the object.
 | 
			
		||||
+	 */
 | 
			
		||||
 	const T &operator*() const
 | 
			
		||||
 	{
 | 
			
		||||
 		return *obj_;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Gets the file descriptor for the underlaying storage file.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return the file descriptor.
 | 
			
		||||
+	 */
 | 
			
		||||
 	const SharedFD &fd() const
 | 
			
		||||
 	{
 | 
			
		||||
 		return fd_;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Operator bool() for SharedMemObject.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return true if the object is not null, false otherwise.
 | 
			
		||||
+	 */
 | 
			
		||||
 	explicit operator bool() const
 | 
			
		||||
 	{
 | 
			
		||||
 		return !!obj_;
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,354 @@
 | 
			
		|||
From 3fa62a8e2f34c9794ba67e2565db8fef22938fa4 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Sun, 22 Oct 2023 17:49:32 +0300
 | 
			
		||||
Subject: [PATCH 06/25] libcamera: introduce SoftwareIsp class
 | 
			
		||||
 | 
			
		||||
Doxygen documentation by Dennis Bonke.
 | 
			
		||||
 | 
			
		||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 include/libcamera/internal/meson.build    |   1 +
 | 
			
		||||
 include/libcamera/internal/software_isp.h | 231 ++++++++++++++++++++++
 | 
			
		||||
 src/libcamera/meson.build                 |   1 +
 | 
			
		||||
 src/libcamera/software_isp.cpp            |  62 ++++++
 | 
			
		||||
 4 files changed, 295 insertions(+)
 | 
			
		||||
 create mode 100644 include/libcamera/internal/software_isp.h
 | 
			
		||||
 create mode 100644 src/libcamera/software_isp.cpp
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
 | 
			
		||||
index 5807dfd9..1325941d 100644
 | 
			
		||||
--- a/include/libcamera/internal/meson.build
 | 
			
		||||
+++ b/include/libcamera/internal/meson.build
 | 
			
		||||
@@ -40,6 +40,7 @@ libcamera_internal_headers = files([
 | 
			
		||||
     'pub_key.h',
 | 
			
		||||
     'request.h',
 | 
			
		||||
     'shared_mem_object.h',
 | 
			
		||||
+    'software_isp.h',
 | 
			
		||||
     'source_paths.h',
 | 
			
		||||
     'sysfs.h',
 | 
			
		||||
     'v4l2_device.h',
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp.h b/include/libcamera/internal/software_isp.h
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..42ff48ec
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp.h
 | 
			
		||||
@@ -0,0 +1,231 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ *
 | 
			
		||||
+ * software_isp.h - Interface for a software implementation of an ISP
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#pragma once
 | 
			
		||||
+
 | 
			
		||||
+#include <functional>
 | 
			
		||||
+#include <initializer_list>
 | 
			
		||||
+#include <map>
 | 
			
		||||
+#include <memory>
 | 
			
		||||
+#include <string>
 | 
			
		||||
+#include <tuple>
 | 
			
		||||
+#include <vector>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/class.h>
 | 
			
		||||
+#include <libcamera/base/log.h>
 | 
			
		||||
+#include <libcamera/base/signal.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/geometry.h>
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/pipeline_handler.h"
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+class FrameBuffer;
 | 
			
		||||
+class PixelFormat;
 | 
			
		||||
+struct StreamConfiguration;
 | 
			
		||||
+
 | 
			
		||||
+LOG_DECLARE_CATEGORY(SoftwareIsp)
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \brief Base class for the Software ISP.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Base class of the SoftwareIsp interface.
 | 
			
		||||
+ */
 | 
			
		||||
+class SoftwareIsp
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Constructor for the SoftwareIsp object.
 | 
			
		||||
+	 * \param[in] pipe The pipeline handler in use.
 | 
			
		||||
+	 * \param[in] sensorControls The sensor controls.
 | 
			
		||||
+	 */
 | 
			
		||||
+	SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
 | 
			
		||||
+	virtual ~SoftwareIsp();
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Load a configuration from a file.
 | 
			
		||||
+	 * \param[in] filename The file to load from.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return 0 on success.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual int loadConfiguration(const std::string &filename) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Gets if there is a valid debayer object.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \returns true if there is, false otherwise.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual bool isValid() const = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \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.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual std::vector<PixelFormat> formats(PixelFormat input) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \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.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Get the stride and the frame size.
 | 
			
		||||
+	 * \param[in] pixelFormat 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.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual std::tuple<unsigned int, unsigned int>
 | 
			
		||||
+	strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \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.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual int configure(const StreamConfiguration &inputCfg,
 | 
			
		||||
+			      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
 | 
			
		||||
+			      const ControlInfoMap &sensorControls) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \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.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual int exportBuffers(unsigned int output, unsigned int count,
 | 
			
		||||
+				  std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Starts the Software ISP worker.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return 0 on success, any other value indicates an error.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual int start() = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Stops the Software ISP worker.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual void stop() = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \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
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual int queueBuffers(FrameBuffer *input,
 | 
			
		||||
+				 const std::map<unsigned int, FrameBuffer *> &outputs) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Process the statistics gathered.
 | 
			
		||||
+	 * \param[in] sensorControls The sensor controls.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual void processStats(const ControlList &sensorControls) = 0; // rather merge with queueBuffers()?
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Get the signal for when the sensor controls are set.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return The control list of the sensor controls.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual Signal<const ControlList &> &getSignalSetSensorControls() = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Signals that the input buffer is ready.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Signal<FrameBuffer *> inputBufferReady;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Signals that the output buffer is ready.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Signal<FrameBuffer *> outputBufferReady;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Signals that the ISP stats are ready.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * The int parameter isn't actually used.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Signal<int> ispStatsReady;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \brief Base class for the Software ISP Factory.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Base class of the SoftwareIsp Factory.
 | 
			
		||||
+ */
 | 
			
		||||
+class SoftwareIspFactoryBase
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	SoftwareIspFactoryBase();
 | 
			
		||||
+	virtual ~SoftwareIspFactoryBase() = default;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Creates a SoftwareIsp object.
 | 
			
		||||
+	 * \param[in] pipe The pipeline handler in use.
 | 
			
		||||
+	 * \param[in] sensorControls The sensor controls.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return An unique pointer to the created SoftwareIsp object.
 | 
			
		||||
+	 */
 | 
			
		||||
+	static std::unique_ptr<SoftwareIsp> create(PipelineHandler *pipe,
 | 
			
		||||
+						   const ControlInfoMap &sensorControls);
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Gives back a pointer to the factory.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return A static pointer to the factory instance.
 | 
			
		||||
+	 */
 | 
			
		||||
+	static SoftwareIspFactoryBase *&factory();
 | 
			
		||||
+
 | 
			
		||||
+private:
 | 
			
		||||
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(SoftwareIspFactoryBase)
 | 
			
		||||
+
 | 
			
		||||
+	static void registerType(SoftwareIspFactoryBase *factory);
 | 
			
		||||
+	virtual std::unique_ptr<SoftwareIsp> createInstance(PipelineHandler *pipe,
 | 
			
		||||
+							    const ControlInfoMap &sensorControls) const = 0;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \brief Implementation for the Software ISP Factory.
 | 
			
		||||
+ */
 | 
			
		||||
+template<typename _SoftwareIsp>
 | 
			
		||||
+class SoftwareIspFactory : public SoftwareIspFactoryBase
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	SoftwareIspFactory()
 | 
			
		||||
+		: SoftwareIspFactoryBase()
 | 
			
		||||
+	{
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Creates an instance of a SoftwareIsp object.
 | 
			
		||||
+	 * \param[in] pipe The pipeline handler in use.
 | 
			
		||||
+	 * \param[in] sensorControls The sensor controls.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return An unique pointer to the created SoftwareIsp object.
 | 
			
		||||
+	 */
 | 
			
		||||
+	std::unique_ptr<SoftwareIsp> createInstance(PipelineHandler *pipe,
 | 
			
		||||
+						    const ControlInfoMap &sensorControls) const override
 | 
			
		||||
+	{
 | 
			
		||||
+		return std::make_unique<_SoftwareIsp>(pipe, sensorControls);
 | 
			
		||||
+	}
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+#define REGISTER_SOFTWAREISP(softwareIsp) \
 | 
			
		||||
+	static SoftwareIspFactory<softwareIsp> global_##softwareIsp##Factory;
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
 | 
			
		||||
index 3c5e43df..86494663 100644
 | 
			
		||||
--- a/src/libcamera/meson.build
 | 
			
		||||
+++ b/src/libcamera/meson.build
 | 
			
		||||
@@ -41,6 +41,7 @@ libcamera_sources = files([
 | 
			
		||||
     'process.cpp',
 | 
			
		||||
     'pub_key.cpp',
 | 
			
		||||
     'request.cpp',
 | 
			
		||||
+    'software_isp.cpp',
 | 
			
		||||
     'source_paths.cpp',
 | 
			
		||||
     'stream.cpp',
 | 
			
		||||
     'sysfs.cpp',
 | 
			
		||||
diff --git a/src/libcamera/software_isp.cpp b/src/libcamera/software_isp.cpp
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..2ff97d70
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/libcamera/software_isp.cpp
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ *
 | 
			
		||||
+ * software_isp.cpp - Interface for a software implementation of an ISP
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/software_isp.h"
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/log.h>
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+LOG_DEFINE_CATEGORY(SoftwareIsp)
 | 
			
		||||
+
 | 
			
		||||
+SoftwareIsp::SoftwareIsp([[maybe_unused]] PipelineHandler *pipe,
 | 
			
		||||
+			 [[maybe_unused]] const ControlInfoMap &sensorControls)
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+SoftwareIsp::~SoftwareIsp()
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/* SoftwareIspFactoryBase */
 | 
			
		||||
+
 | 
			
		||||
+SoftwareIspFactoryBase::SoftwareIspFactoryBase()
 | 
			
		||||
+{
 | 
			
		||||
+	registerType(this);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SoftwareIspFactoryBase::registerType(SoftwareIspFactoryBase *factory)
 | 
			
		||||
+{
 | 
			
		||||
+	SoftwareIspFactoryBase *®istered =
 | 
			
		||||
+		SoftwareIspFactoryBase::factory();
 | 
			
		||||
+
 | 
			
		||||
+	ASSERT(!registered && factory);
 | 
			
		||||
+	registered = factory;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+SoftwareIspFactoryBase *&SoftwareIspFactoryBase::factory()
 | 
			
		||||
+{
 | 
			
		||||
+	static SoftwareIspFactoryBase *factory;
 | 
			
		||||
+	return factory;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+std::unique_ptr<SoftwareIsp>
 | 
			
		||||
+SoftwareIspFactoryBase::create(PipelineHandler *pipe,
 | 
			
		||||
+			       const ControlInfoMap &sensorControls)
 | 
			
		||||
+{
 | 
			
		||||
+	SoftwareIspFactoryBase *factory = SoftwareIspFactoryBase::factory();
 | 
			
		||||
+	if (!factory)
 | 
			
		||||
+		return nullptr;
 | 
			
		||||
+
 | 
			
		||||
+	std::unique_ptr<SoftwareIsp> swIsp = factory->createInstance(pipe, sensorControls);
 | 
			
		||||
+	if (swIsp->isValid())
 | 
			
		||||
+		return swIsp;
 | 
			
		||||
+
 | 
			
		||||
+	return nullptr;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,382 @@
 | 
			
		|||
From ca3bb6ddf5307537aa05e43d3ec1ff7ffdc0efed Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Thu, 7 Dec 2023 13:30:27 +0100
 | 
			
		||||
Subject: [PATCH 07/25] libcamera: software_isp: Add SwStats base class
 | 
			
		||||
 | 
			
		||||
Add a virtual base class for CPU based software statistics gathering
 | 
			
		||||
implementations.
 | 
			
		||||
 | 
			
		||||
The idea is for the implementations to offer a configure function +
 | 
			
		||||
functions to gather statistics on a line by line basis. This allows
 | 
			
		||||
CPU based software debayering to call into interlace debayering and
 | 
			
		||||
statistics gathering on a line by line bases while the input data
 | 
			
		||||
is still hot in the cache.
 | 
			
		||||
 | 
			
		||||
This base class also allows the user of an implementation to specify
 | 
			
		||||
a window over which to gather statistics instead of processing the
 | 
			
		||||
whole frame; and it allows the implementation to choose to only
 | 
			
		||||
process 1/2, 1/4th, etc. of the lines instead of processing all
 | 
			
		||||
lines (in the window) by setting y_skip_mask_ from configure().
 | 
			
		||||
Skipping columns is left up the line-processing functions provided
 | 
			
		||||
by the implementation.
 | 
			
		||||
 | 
			
		||||
Doxygen documentation by Dennis Bonke.
 | 
			
		||||
 | 
			
		||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Co-authored-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 include/libcamera/internal/meson.build        |   1 +
 | 
			
		||||
 .../internal/software_isp/meson.build         |   6 +
 | 
			
		||||
 .../internal/software_isp/swisp_stats.h       |  34 +++
 | 
			
		||||
 .../libcamera/internal/software_isp/swstats.h | 215 ++++++++++++++++++
 | 
			
		||||
 src/libcamera/meson.build                     |   1 +
 | 
			
		||||
 src/libcamera/software_isp/meson.build        |   5 +
 | 
			
		||||
 src/libcamera/software_isp/swstats.cpp        |  22 ++
 | 
			
		||||
 7 files changed, 284 insertions(+)
 | 
			
		||||
 create mode 100644 include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
 create mode 100644 include/libcamera/internal/software_isp/swisp_stats.h
 | 
			
		||||
 create mode 100644 include/libcamera/internal/software_isp/swstats.h
 | 
			
		||||
 create mode 100644 src/libcamera/software_isp/meson.build
 | 
			
		||||
 create mode 100644 src/libcamera/software_isp/swstats.cpp
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
 | 
			
		||||
index 1325941d..caa533c4 100644
 | 
			
		||||
--- a/include/libcamera/internal/meson.build
 | 
			
		||||
+++ b/include/libcamera/internal/meson.build
 | 
			
		||||
@@ -51,3 +51,4 @@ libcamera_internal_headers = files([
 | 
			
		||||
 ])
 | 
			
		||||
 
 | 
			
		||||
 subdir('converter')
 | 
			
		||||
+subdir('software_isp')
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..1c43acc4
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
+# SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
+
 | 
			
		||||
+libcamera_internal_headers += files([
 | 
			
		||||
+    'swisp_stats.h',
 | 
			
		||||
+    'swstats.h',
 | 
			
		||||
+])
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..07ba7d6a
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/swisp_stats.h
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ *
 | 
			
		||||
+ * swisp_stats.h - Statistics data format used by the software ISP and software IPA
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#pragma once
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \brief Struct that holds the statistics for the Software ISP.
 | 
			
		||||
+ */
 | 
			
		||||
+struct SwIspStats {
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Holds the sum of all sampled red pixels.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned long sumR_;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Holds the sum of all sampled green pixels.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned long sumG_;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Holds the sum of all sampled blue pixels.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned long sumB_;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief A histogram of luminance values.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned int y_histogram[16];
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/swstats.h b/include/libcamera/internal/software_isp/swstats.h
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..dcac7064
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/swstats.h
 | 
			
		||||
@@ -0,0 +1,215 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * swstats.h - software statistics base class
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#pragma once
 | 
			
		||||
+
 | 
			
		||||
+#include <stdint.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/log.h>
 | 
			
		||||
+#include <libcamera/base/signal.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/geometry.h>
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+class PixelFormat;
 | 
			
		||||
+struct SharedFD;
 | 
			
		||||
+struct StreamConfiguration;
 | 
			
		||||
+
 | 
			
		||||
+LOG_DECLARE_CATEGORY(SwStats)
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \class SwStats
 | 
			
		||||
+ * \brief Base class for the software ISP statistics.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Base class for the software ISP statistics.
 | 
			
		||||
+ */
 | 
			
		||||
+class SwStats
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	virtual ~SwStats() = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Gets wether the statistics object is valid.
 | 
			
		||||
+	 * 
 | 
			
		||||
+	 * \return true if it's valid, false otherwise.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual bool isValid() const = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Configure the statistics object for the passed in input format.
 | 
			
		||||
+	 * \param[in] inputCfg The input format
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return 0 on success, a negative errno value on failure.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual int configure(const StreamConfiguration &inputCfg) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Get the file descriptor for the statistics.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return the file descriptor
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual const SharedFD &getStatsFD() = 0;
 | 
			
		||||
+
 | 
			
		||||
+protected:
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Called when there is data to get statistics from.
 | 
			
		||||
+	 * \param[in] src The input data
 | 
			
		||||
+	 */
 | 
			
		||||
+	typedef void (SwStats::*statsProcessFn)(const uint8_t *src[]);
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Called when the statistics gathering is done or when a new frame starts.
 | 
			
		||||
+	 */
 | 
			
		||||
+	typedef void (SwStats::*statsVoidFn)();
 | 
			
		||||
+
 | 
			
		||||
+	/* Variables set by configure(), used every line */
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief The function called when a line is ready for statistics processing.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * Used for line 0 and 1, repeating if there isn't a 3rd and a 4th line in the bayer order.
 | 
			
		||||
+	 */
 | 
			
		||||
+	statsProcessFn stats0_;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief The function called when a line is ready for statistics processing.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * Used for line 3 and 4, only needed if the bayer order has 4 different lines.
 | 
			
		||||
+	 */
 | 
			
		||||
+	statsProcessFn stats2_;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief The memory used per pixel in bits.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned int bpp_;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Skip lines where this bitmask is set in y.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned int y_skip_mask_;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Statistics window, set by setWindow(), used ever line.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Rectangle window_;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief The function called at the start of a frame.
 | 
			
		||||
+	 */
 | 
			
		||||
+	statsVoidFn startFrame_;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief The function called at the end of a frame.
 | 
			
		||||
+	 */
 | 
			
		||||
+	statsVoidFn finishFrame_;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief The size of the bayer pattern.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Size patternSize_;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief The offset of x, applied to window_.x for bayer variants.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * This can either be 0 or 1.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned int x_shift_;
 | 
			
		||||
+
 | 
			
		||||
+public:
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Get the pattern size.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * For some input-formats, e.g. Bayer data, processing is done multiple lines
 | 
			
		||||
+	 * and/or columns at a time. Get width and height at which the (bayer) pattern
 | 
			
		||||
+	 * repeats. Window values are rounded down to a multiple of this and the height
 | 
			
		||||
+	 * also indicates if processLine2() should be called or not.
 | 
			
		||||
+	 * This may only be called after a successful configure() call.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return the pattern size.
 | 
			
		||||
+	 */
 | 
			
		||||
+	const Size &patternSize() { return patternSize_; }
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Specify window coordinates over which to gather statistics.
 | 
			
		||||
+	 * \param[in] window The window object.
 | 
			
		||||
+	 */
 | 
			
		||||
+	void setWindow(Rectangle window)
 | 
			
		||||
+	{
 | 
			
		||||
+		window_ = window;
 | 
			
		||||
+
 | 
			
		||||
+		window_.x &= ~(patternSize_.width - 1);
 | 
			
		||||
+		window_.x += x_shift_;
 | 
			
		||||
+		window_.y &= ~(patternSize_.height - 1);
 | 
			
		||||
+
 | 
			
		||||
+		/* width_ - x_shift_ to make sure the window fits */
 | 
			
		||||
+		window_.width -= x_shift_;
 | 
			
		||||
+		window_.width &= ~(patternSize_.width - 1);
 | 
			
		||||
+		window_.height &= ~(patternSize_.height - 1);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Reset state to start statistics gathering for a new frame.
 | 
			
		||||
+	 * 
 | 
			
		||||
+	 * This may only be called after a successful setWindow() call.
 | 
			
		||||
+	 */
 | 
			
		||||
+	void startFrame()
 | 
			
		||||
+	{
 | 
			
		||||
+		(this->*startFrame_)();
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Process line 0.
 | 
			
		||||
+	 * \param[in] y The y coordinate.
 | 
			
		||||
+	 * \param[in] src The input data.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * This function processes line 0 for input formats with patternSize height == 1.
 | 
			
		||||
+	 * It'll process line 0 and 1 for input formats with patternSize height >= 2.
 | 
			
		||||
+	 * This function may only be called after a successful setWindow() call.
 | 
			
		||||
+	 */
 | 
			
		||||
+	void processLine0(unsigned int y, const uint8_t *src[])
 | 
			
		||||
+	{
 | 
			
		||||
+		if ((y & y_skip_mask_) || y < (unsigned int)window_.y ||
 | 
			
		||||
+		    y >= (window_.y + window_.height))
 | 
			
		||||
+			return;
 | 
			
		||||
+
 | 
			
		||||
+		(this->*stats0_)(src);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Process line 2 and 3.
 | 
			
		||||
+	 * \param[in] y The y coordinate.
 | 
			
		||||
+	 * \param[in] src The input data.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * This function processes line 2 and 3 for input formats with patternSize height == 4.
 | 
			
		||||
+	 * This function may only be called after a successful setWindow() call.
 | 
			
		||||
+	 */
 | 
			
		||||
+	void processLine2(unsigned int y, const uint8_t *src[])
 | 
			
		||||
+	{
 | 
			
		||||
+		if ((y & y_skip_mask_) || y < (unsigned int)window_.y ||
 | 
			
		||||
+		    y >= (window_.y + window_.height))
 | 
			
		||||
+			return;
 | 
			
		||||
+
 | 
			
		||||
+		(this->*stats2_)(src);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Finish statistics calculation for the current frame.
 | 
			
		||||
+	 * 
 | 
			
		||||
+	 * This may only be called after a successful setWindow() call.
 | 
			
		||||
+	 */
 | 
			
		||||
+	void finishFrame()
 | 
			
		||||
+	{
 | 
			
		||||
+		(this->*finishFrame_)();
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Signals that the statistics are ready.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * The int parameter isn't actually used.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Signal<int> statsReady;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
 | 
			
		||||
index 86494663..3d63e8a2 100644
 | 
			
		||||
--- a/src/libcamera/meson.build
 | 
			
		||||
+++ b/src/libcamera/meson.build
 | 
			
		||||
@@ -71,6 +71,7 @@ subdir('converter')
 | 
			
		||||
 subdir('ipa')
 | 
			
		||||
 subdir('pipeline')
 | 
			
		||||
 subdir('proxy')
 | 
			
		||||
+subdir('software_isp')
 | 
			
		||||
 
 | 
			
		||||
 null_dep = dependency('', required : false)
 | 
			
		||||
 
 | 
			
		||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..9359075d
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/libcamera/software_isp/meson.build
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
+# SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
+
 | 
			
		||||
+libcamera_sources += files([
 | 
			
		||||
+	'swstats.cpp',
 | 
			
		||||
+])
 | 
			
		||||
diff --git a/src/libcamera/software_isp/swstats.cpp b/src/libcamera/software_isp/swstats.cpp
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..e65a7ada
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/libcamera/software_isp/swstats.cpp
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * swstats.cpp - software statistics base class
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/software_isp/swstats.h"
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+LOG_DEFINE_CATEGORY(SwStats)
 | 
			
		||||
+
 | 
			
		||||
+SwStats::~SwStats()
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,272 @@
 | 
			
		|||
From c1c43445cd4408010e500fe9d6b6424c77bcf75d Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Fri, 8 Dec 2023 12:50:57 +0100
 | 
			
		||||
Subject: [PATCH 08/25] libcamera: software_isp: Add SwStatsCpu class
 | 
			
		||||
 | 
			
		||||
Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.
 | 
			
		||||
 | 
			
		||||
Co-authored-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Co-authored-by: Pavel Machek <pavel@ucw.cz>
 | 
			
		||||
Signed-off-by: Pavel Machek <pavel@ucw.cz>
 | 
			
		||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Co-authored-by: Marttico <g.martti@gmail.com>
 | 
			
		||||
Signed-off-by: Marttico <g.martti@gmail.com>
 | 
			
		||||
Co-authored-by: Toon Langendam <t.langendam@gmail.com>
 | 
			
		||||
Signed-off-by: Toon Langendam <t.langendam@gmail.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/swstats_cpu.h       |  44 +++++
 | 
			
		||||
 src/libcamera/software_isp/meson.build        |   1 +
 | 
			
		||||
 src/libcamera/software_isp/swstats_cpu.cpp    | 164 ++++++++++++++++++
 | 
			
		||||
 4 files changed, 210 insertions(+)
 | 
			
		||||
 create mode 100644 include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
 create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
index 1c43acc4..1d9e4018 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
@@ -3,4 +3,5 @@
 | 
			
		||||
 libcamera_internal_headers += files([
 | 
			
		||||
     'swisp_stats.h',
 | 
			
		||||
     'swstats.h',
 | 
			
		||||
+    'swstats_cpu.h',
 | 
			
		||||
 ])
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..8bb86e98
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
@@ -0,0 +1,44 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * swstats_cpu.h - CPU based software statistics implementation
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#pragma once
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/shared_mem_object.h"
 | 
			
		||||
+#include "libcamera/internal/software_isp/swisp_stats.h"
 | 
			
		||||
+#include "libcamera/internal/software_isp/swstats.h"
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \class SwStatsCpu
 | 
			
		||||
+ * \brief Implementation for the Software statistics on the CPU.
 | 
			
		||||
+ */
 | 
			
		||||
+class SwStatsCpu : public SwStats
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	SwStatsCpu();
 | 
			
		||||
+	~SwStatsCpu() { }
 | 
			
		||||
+
 | 
			
		||||
+	bool isValid() const { return sharedStats_.fd().isValid(); }
 | 
			
		||||
+	const SharedFD &getStatsFD() { return sharedStats_.fd(); }
 | 
			
		||||
+	int configure(const StreamConfiguration &inputCfg);
 | 
			
		||||
+private:
 | 
			
		||||
+	void statsBGGR10PLine0(const uint8_t *src[]);
 | 
			
		||||
+	void statsGBRG10PLine0(const uint8_t *src[]);
 | 
			
		||||
+	void resetStats(void);
 | 
			
		||||
+	void finishStats(void);
 | 
			
		||||
+
 | 
			
		||||
+	SharedMemObject<SwIspStats> sharedStats_;
 | 
			
		||||
+	SwIspStats stats_;
 | 
			
		||||
+	bool swap_lines_;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
 | 
			
		||||
index 9359075d..d31c6217 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/meson.build
 | 
			
		||||
+++ b/src/libcamera/software_isp/meson.build
 | 
			
		||||
@@ -2,4 +2,5 @@
 | 
			
		||||
 
 | 
			
		||||
 libcamera_sources += files([
 | 
			
		||||
 	'swstats.cpp',
 | 
			
		||||
+	'swstats_cpu.cpp',
 | 
			
		||||
 ])
 | 
			
		||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..59453d07
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
@@ -0,0 +1,164 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * swstats_cpu.cpp - CPU based software statistics implementation
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/software_isp/swstats_cpu.h"
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/log.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/stream.h>
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/bayer_format.h"
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+SwStatsCpu::SwStatsCpu()
 | 
			
		||||
+	: SwStats()
 | 
			
		||||
+{
 | 
			
		||||
+	sharedStats_ = SharedMemObject<SwIspStats>("softIsp_stats");
 | 
			
		||||
+	if (!sharedStats_.fd().isValid())
 | 
			
		||||
+		LOG(SwStats, Error)
 | 
			
		||||
+			<< "Failed to create shared memory for statistics";
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/* for brightness values in the 0 to 255 range: */
 | 
			
		||||
+static const unsigned int BRIGHT_LVL = 200U << 8;
 | 
			
		||||
+static const unsigned int TOO_BRIGHT_LVL = 240U << 8;
 | 
			
		||||
+
 | 
			
		||||
+static const unsigned int RED_Y_MUL = 77; /* 0.30 * 256 */
 | 
			
		||||
+static const unsigned int GREEN_Y_MUL = 150; /* 0.59 * 256 */
 | 
			
		||||
+static const unsigned int BLUE_Y_MUL = 29; /* 0.11 * 256 */
 | 
			
		||||
+
 | 
			
		||||
+#define SWISP_LINARO_START_LINE_STATS(pixel_t) \
 | 
			
		||||
+	pixel_t r, g, g2, b;                   \
 | 
			
		||||
+	unsigned int y_val;                    \
 | 
			
		||||
+                                               \
 | 
			
		||||
+	unsigned int sumR = 0;                 \
 | 
			
		||||
+	unsigned int sumG = 0;                 \
 | 
			
		||||
+	unsigned int sumB = 0;
 | 
			
		||||
+
 | 
			
		||||
+#define SWISP_LINARO_ACCUMULATE_LINE_STATS(div) \
 | 
			
		||||
+	sumR += r;                              \
 | 
			
		||||
+	sumG += g;                              \
 | 
			
		||||
+	sumB += b;                              \
 | 
			
		||||
+                                                \
 | 
			
		||||
+	y_val = r * RED_Y_MUL;                  \
 | 
			
		||||
+	y_val += g * GREEN_Y_MUL;               \
 | 
			
		||||
+	y_val += b * BLUE_Y_MUL;                \
 | 
			
		||||
+	stats_.y_histogram[y_val / (256 * 16 * (div))]++;
 | 
			
		||||
+
 | 
			
		||||
+#define SWISP_LINARO_FINISH_LINE_STATS() \
 | 
			
		||||
+	stats_.sumR_ += sumR;            \
 | 
			
		||||
+	stats_.sumG_ += sumG;            \
 | 
			
		||||
+	stats_.sumB_ += sumB;
 | 
			
		||||
+
 | 
			
		||||
+static inline __attribute__((always_inline)) void
 | 
			
		||||
+statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bggr, SwIspStats &stats_)
 | 
			
		||||
+{
 | 
			
		||||
+	const int width_in_bytes = width * 5 / 4;
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_START_LINE_STATS(uint8_t)
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < width_in_bytes; x += 5) {
 | 
			
		||||
+		if (bggr) {
 | 
			
		||||
+			/* BGGR */
 | 
			
		||||
+			b = src0[x];
 | 
			
		||||
+			g = src0[x + 1];
 | 
			
		||||
+			g2 = src1[x];
 | 
			
		||||
+			r = src1[x + 1];
 | 
			
		||||
+		} else {
 | 
			
		||||
+			/* GBRG */
 | 
			
		||||
+			g = src0[x];
 | 
			
		||||
+			b = src0[x + 1];
 | 
			
		||||
+			r = src1[x];
 | 
			
		||||
+			g2 = src1[x + 1];
 | 
			
		||||
+		}
 | 
			
		||||
+		g = (g + g2) / 2;
 | 
			
		||||
+
 | 
			
		||||
+		SWISP_LINARO_ACCUMULATE_LINE_STATS(1)
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_FINISH_LINE_STATS()
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
 | 
			
		||||
+	const uint8_t *src1 = src[2] + window_.x * 5 / 4;
 | 
			
		||||
+
 | 
			
		||||
+	if (swap_lines_)
 | 
			
		||||
+		std::swap(src0, src1);
 | 
			
		||||
+
 | 
			
		||||
+	statsBayer10P(window_.width, src0, src1, true, stats_);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
 | 
			
		||||
+	const uint8_t *src1 = src[2] + window_.x * 5 / 4;
 | 
			
		||||
+
 | 
			
		||||
+	if (swap_lines_)
 | 
			
		||||
+		std::swap(src0, src1);
 | 
			
		||||
+
 | 
			
		||||
+	statsBayer10P(window_.width, src0, src1, false, stats_);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SwStatsCpu::resetStats(void)
 | 
			
		||||
+{
 | 
			
		||||
+	stats_.sumR_ = 0;
 | 
			
		||||
+	stats_.sumB_ = 0;
 | 
			
		||||
+	stats_.sumG_ = 0;
 | 
			
		||||
+	std::fill_n(stats_.y_histogram, 16, 0);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SwStatsCpu::finishStats(void)
 | 
			
		||||
+{
 | 
			
		||||
+	*sharedStats_ = stats_;
 | 
			
		||||
+	statsReady.emit(0);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
 | 
			
		||||
+{
 | 
			
		||||
+	BayerFormat bayerFormat =
 | 
			
		||||
+		BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
 | 
			
		||||
+
 | 
			
		||||
+	startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats;
 | 
			
		||||
+	finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats;
 | 
			
		||||
+
 | 
			
		||||
+	if (bayerFormat.bitDepth == 10 &&
 | 
			
		||||
+	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
 | 
			
		||||
+		bpp_ = 10;
 | 
			
		||||
+		patternSize_.height = 2;
 | 
			
		||||
+		patternSize_.width = 4; /* 5 bytes per *4* pixels */
 | 
			
		||||
+		y_skip_mask_ = 0x02; /* Skip every 3th and 4th line */
 | 
			
		||||
+		x_shift_ = 0;
 | 
			
		||||
+
 | 
			
		||||
+		switch (bayerFormat.order) {
 | 
			
		||||
+		case BayerFormat::BGGR:
 | 
			
		||||
+		case BayerFormat::GRBG:
 | 
			
		||||
+			stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR10PLine0;
 | 
			
		||||
+			swap_lines_ = bayerFormat.order == BayerFormat::GRBG;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		case BayerFormat::GBRG:
 | 
			
		||||
+		case BayerFormat::RGGB:
 | 
			
		||||
+			stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsGBRG10PLine0;
 | 
			
		||||
+			swap_lines_ = bayerFormat.order == BayerFormat::RGGB;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		default:
 | 
			
		||||
+			break;
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	LOG(SwStats, Info)
 | 
			
		||||
+		<< "Unsupported input format " << inputCfg.pixelFormat.toString();
 | 
			
		||||
+	return -EINVAL;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,272 @@
 | 
			
		|||
From 8fc77447c0d76b0b52b19d23674049181c6cf8d2 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Mon, 11 Dec 2023 14:46:53 +0100
 | 
			
		||||
Subject: [PATCH 09/25] libcamera: software_isp: Add Debayer base class
 | 
			
		||||
 | 
			
		||||
Add a base class for debayer implementations. This is intended to be
 | 
			
		||||
suitable for both GPU (or otherwise) accelerated debayer implementations
 | 
			
		||||
as well as CPU based debayering.
 | 
			
		||||
 | 
			
		||||
Doxygen documentation by Dennis Bonke.
 | 
			
		||||
 | 
			
		||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Co-authored-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 .../libcamera/internal/software_isp/debayer.h | 132 ++++++++++++++++++
 | 
			
		||||
 .../internal/software_isp/debayer_params.h    |  43 ++++++
 | 
			
		||||
 .../internal/software_isp/meson.build         |   2 +
 | 
			
		||||
 src/libcamera/software_isp/debayer.cpp        |  22 +++
 | 
			
		||||
 src/libcamera/software_isp/meson.build        |   1 +
 | 
			
		||||
 5 files changed, 200 insertions(+)
 | 
			
		||||
 create mode 100644 include/libcamera/internal/software_isp/debayer.h
 | 
			
		||||
 create mode 100644 include/libcamera/internal/software_isp/debayer_params.h
 | 
			
		||||
 create mode 100644 src/libcamera/software_isp/debayer.cpp
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/debayer.h b/include/libcamera/internal/software_isp/debayer.h
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..39e6f393
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/debayer.h
 | 
			
		||||
@@ -0,0 +1,132 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * debayer.h - debayering base class
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#pragma once
 | 
			
		||||
+
 | 
			
		||||
+#include <stdint.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/log.h>
 | 
			
		||||
+#include <libcamera/base/signal.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/geometry.h>
 | 
			
		||||
+#include <libcamera/stream.h>
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/software_isp/debayer_params.h"
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+class FrameBuffer;
 | 
			
		||||
+
 | 
			
		||||
+LOG_DECLARE_CATEGORY(Debayer)
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \class Debayer
 | 
			
		||||
+ * \brief Base debayering class
 | 
			
		||||
+ *
 | 
			
		||||
+ * Base class that provides functions for setting up the debayering process.
 | 
			
		||||
+ */
 | 
			
		||||
+class Debayer
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	virtual ~Debayer() = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Configure the debayer object according to the passed in parameters.
 | 
			
		||||
+	 * \param[in] inputCfg The input configuration.
 | 
			
		||||
+	 * \param[in] outputCfgs The output configurations.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return 0 on success, a negative errno on failure.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual int configure(const StreamConfiguration &inputCfg,
 | 
			
		||||
+			      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Get the width and height at which the bayer pattern repeats.
 | 
			
		||||
+	 * \param[in] inputFormat The input format.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return pattern size or an empty size for unsupported inputFormats.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual Size patternSize(PixelFormat inputFormat) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Get the supported output formats.
 | 
			
		||||
+	 * \param[in] inputFormat The input format.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return all supported output formats or an empty vector if there are none.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual std::vector<PixelFormat> formats(PixelFormat inputFormat) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \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.
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual std::tuple<unsigned int, unsigned int>
 | 
			
		||||
+		strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Process the bayer data into the requested format.
 | 
			
		||||
+	 * \param[in] input The input buffer.
 | 
			
		||||
+	 * \param[in] output The output buffer.
 | 
			
		||||
+	 * \param[in] params The parameters to be used in debayering.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \note DebayerParams is passed by value deliberately so that a copy is passed
 | 
			
		||||
+	 * when this is run in another thread by invokeMethod().
 | 
			
		||||
+	 */
 | 
			
		||||
+	virtual void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params) = 0;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \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)
 | 
			
		||||
+	{
 | 
			
		||||
+		Size pattern_size = patternSize(inputFormat);
 | 
			
		||||
+
 | 
			
		||||
+		if (pattern_size.isNull())
 | 
			
		||||
+			return {};
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * For debayer interpolation a border of pattern-height x pattern-width
 | 
			
		||||
+		 * is kept around the entire image. Combined with a minimum-size of
 | 
			
		||||
+		 * pattern-height x pattern-width this means the input-size needs to be
 | 
			
		||||
+		 * at least (3 * pattern-height) x (3 * pattern-width).
 | 
			
		||||
+		 */
 | 
			
		||||
+		if (inputSize.width < (3 * pattern_size.width) ||
 | 
			
		||||
+		    inputSize.height < (3 * pattern_size.height)) {
 | 
			
		||||
+			LOG(Debayer, Warning)
 | 
			
		||||
+				<< "Input format size too small: " << inputSize.toString();
 | 
			
		||||
+			return {};
 | 
			
		||||
+		}
 | 
			
		||||
+
 | 
			
		||||
+		return SizeRange(Size(pattern_size.width, pattern_size.height),
 | 
			
		||||
+				 Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
 | 
			
		||||
+				      (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1)),
 | 
			
		||||
+				 pattern_size.width, pattern_size.height);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Signals when the input buffer is ready.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Signal<FrameBuffer *> inputBufferReady;
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Signals when the output buffer is ready.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Signal<FrameBuffer *> outputBufferReady;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..8f515304
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/debayer_params.h
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * swstats.h - software statistics base class
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#pragma once
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \brief Struct to hold the debayer parameters.
 | 
			
		||||
+ */
 | 
			
		||||
+struct DebayerParams {
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Red Gain.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned int gainR;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Green Gain.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned int gainG;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Blue Gain.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned int gainB;
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Gamma correction, 1.0 is no correction.
 | 
			
		||||
+	 */
 | 
			
		||||
+	float gamma;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
index 1d9e4018..7e40925e 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
 # SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
 
 | 
			
		||||
 libcamera_internal_headers += files([
 | 
			
		||||
+    'debayer.h',
 | 
			
		||||
+    'debayer_params.h',
 | 
			
		||||
     'swisp_stats.h',
 | 
			
		||||
     'swstats.h',
 | 
			
		||||
     'swstats_cpu.h',
 | 
			
		||||
diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..442da1ac
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/libcamera/software_isp/debayer.cpp
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * debayer.cpp - debayer base class
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/software_isp/debayer.h"
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+LOG_DEFINE_CATEGORY(Debayer)
 | 
			
		||||
+
 | 
			
		||||
+Debayer::~Debayer()
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
 | 
			
		||||
index d31c6217..d4ae5ac7 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/meson.build
 | 
			
		||||
+++ b/src/libcamera/software_isp/meson.build
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
 # SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
 
 | 
			
		||||
 libcamera_sources += files([
 | 
			
		||||
+	'debayer.cpp',
 | 
			
		||||
 	'swstats.cpp',
 | 
			
		||||
 	'swstats_cpu.cpp',
 | 
			
		||||
 ])
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,727 @@
 | 
			
		|||
From 7eb7164ed7d90ea4cf9ec7e4f35fa8efa25f35e9 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Mon, 11 Dec 2023 17:00:17 +0100
 | 
			
		||||
Subject: [PATCH 10/25] libcamera: software_isp: Add DebayerCpu class
 | 
			
		||||
 | 
			
		||||
Add CPU based debayering implementation. This initial implementation
 | 
			
		||||
only supports debayering packed 10 bits per pixel bayer data in
 | 
			
		||||
the 4 standard bayer orders.
 | 
			
		||||
 | 
			
		||||
Doxygen documentation by Dennis Bonke.
 | 
			
		||||
 | 
			
		||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Co-authored-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Co-authored-by: Pavel Machek <pavel@ucw.cz>
 | 
			
		||||
Signed-off-by: Pavel Machek <pavel@ucw.cz>
 | 
			
		||||
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/debayer_cpu.h       | 131 +++++
 | 
			
		||||
 .../internal/software_isp/meson.build         |   1 +
 | 
			
		||||
 src/libcamera/software_isp/debayer_cpu.cpp    | 528 ++++++++++++++++++
 | 
			
		||||
 src/libcamera/software_isp/meson.build        |   1 +
 | 
			
		||||
 4 files changed, 661 insertions(+)
 | 
			
		||||
 create mode 100644 include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
 create mode 100644 src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..78573f44
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
@@ -0,0 +1,131 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * debayer_cpu.h - CPU based debayering header
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#pragma once
 | 
			
		||||
+
 | 
			
		||||
+#include <memory>
 | 
			
		||||
+#include <stdint.h>
 | 
			
		||||
+#include <vector>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/object.h>
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/software_isp/swstats_cpu.h"
 | 
			
		||||
+#include "libcamera/internal/software_isp/debayer.h"
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * \class DebayerCpu
 | 
			
		||||
+ * \brief Class for debayering on the CPU
 | 
			
		||||
+ *
 | 
			
		||||
+ * Implementation for CPU based debayering
 | 
			
		||||
+ */
 | 
			
		||||
+class DebayerCpu : public Debayer, public Object
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	/*
 | 
			
		||||
+	  * FIXME this should be a plain (implementation independent)  SwStats
 | 
			
		||||
+	  * this can be fixed once getStats() is dropped.
 | 
			
		||||
+	  */
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Constructs a DebayerCpu object.
 | 
			
		||||
+	 * \param[in] stats Pointer to the stats object to use.
 | 
			
		||||
+	 */
 | 
			
		||||
+	DebayerCpu(std::unique_ptr<SwStatsCpu> stats);
 | 
			
		||||
+	~DebayerCpu();
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * Setup the Debayer object according to the passed in parameters.
 | 
			
		||||
+	 * Return 0 on success, a negative errno value on failure
 | 
			
		||||
+	 * (unsupported parameters).
 | 
			
		||||
+	 */
 | 
			
		||||
+	int configure(const StreamConfiguration &inputCfg,
 | 
			
		||||
+		      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs);
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * Get width and height at which the bayer-pattern repeats.
 | 
			
		||||
+	 * Return pattern-size or an empty Size for an unsupported inputFormat.
 | 
			
		||||
+	 */
 | 
			
		||||
+	Size patternSize(PixelFormat inputFormat);
 | 
			
		||||
+
 | 
			
		||||
+	std::vector<PixelFormat> formats(PixelFormat input);
 | 
			
		||||
+	std::tuple<unsigned int, unsigned int>
 | 
			
		||||
+		strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
 | 
			
		||||
+
 | 
			
		||||
+	void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params);
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Get the file descriptor for the statistics.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return the file descriptor pointing to the statistics.
 | 
			
		||||
+	 */
 | 
			
		||||
+	const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
 | 
			
		||||
+
 | 
			
		||||
+	/**
 | 
			
		||||
+	 * \brief Get the output frame size.
 | 
			
		||||
+	 *
 | 
			
		||||
+	 * \return The output frame size.
 | 
			
		||||
+	 */
 | 
			
		||||
+	unsigned int frameSize() { return outputConfig_.frameSize; }
 | 
			
		||||
+private:
 | 
			
		||||
+	void initLinePointers(const uint8_t *linePointers[], const uint8_t *src);
 | 
			
		||||
+	void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
 | 
			
		||||
+	void process2(const uint8_t *src, uint8_t *dst);
 | 
			
		||||
+	void process4(const uint8_t *src, uint8_t *dst);
 | 
			
		||||
+	/* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
 | 
			
		||||
+	void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+
 | 
			
		||||
+	typedef void (DebayerCpu::*debayerFn)(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+
 | 
			
		||||
+	struct DebayerInputConfig {
 | 
			
		||||
+		Size patternSize;
 | 
			
		||||
+		unsigned int bpp; /* Memory used per pixel, not precision */
 | 
			
		||||
+		unsigned int stride;
 | 
			
		||||
+		std::vector<PixelFormat> outputFormats;
 | 
			
		||||
+	};
 | 
			
		||||
+
 | 
			
		||||
+	struct DebayerOutputConfig {
 | 
			
		||||
+		unsigned int bpp; /* Memory used per pixel, not precision */
 | 
			
		||||
+		unsigned int stride;
 | 
			
		||||
+		unsigned int frameSize;
 | 
			
		||||
+	};
 | 
			
		||||
+
 | 
			
		||||
+	int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
 | 
			
		||||
+	int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
 | 
			
		||||
+	int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
 | 
			
		||||
+
 | 
			
		||||
+	uint8_t gamma_[1024];
 | 
			
		||||
+	uint8_t red_[256];
 | 
			
		||||
+	uint8_t green_[256];
 | 
			
		||||
+	uint8_t blue_[256];
 | 
			
		||||
+	debayerFn debayer0_;
 | 
			
		||||
+	debayerFn debayer1_;
 | 
			
		||||
+	debayerFn debayer2_;
 | 
			
		||||
+	debayerFn debayer3_;
 | 
			
		||||
+	Rectangle window_;
 | 
			
		||||
+	DebayerInputConfig inputConfig_;
 | 
			
		||||
+	DebayerOutputConfig outputConfig_;
 | 
			
		||||
+	std::unique_ptr<SwStatsCpu> stats_;
 | 
			
		||||
+	uint8_t *lineBuffers_[5];
 | 
			
		||||
+	unsigned int lineBufferIndex_;
 | 
			
		||||
+	bool enableInputMemcpy_;
 | 
			
		||||
+	float gamma_correction_;
 | 
			
		||||
+	int measuredFrames_;
 | 
			
		||||
+	int64_t frameProcessTime_;
 | 
			
		||||
+	/* Skip 30 frames for things to stabilize then measure 30 frames */
 | 
			
		||||
+	static const int framesToSkip = 30;
 | 
			
		||||
+	static const int framesToMeasure = 60;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
index 7e40925e..b5a0d737 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/meson.build
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 
 | 
			
		||||
 libcamera_internal_headers += files([
 | 
			
		||||
     'debayer.h',
 | 
			
		||||
+    'debayer_cpu.h',
 | 
			
		||||
     'debayer_params.h',
 | 
			
		||||
     'swisp_stats.h',
 | 
			
		||||
     'swstats.h',
 | 
			
		||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..e0c3c658
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
@@ -0,0 +1,528 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ * Copyright (C) 2023, Red Hat Inc.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Authors:
 | 
			
		||||
+ * Hans de Goede <hdegoede@redhat.com> 
 | 
			
		||||
+ *
 | 
			
		||||
+ * debayer_cpu.cpp - CPU based debayering class
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/software_isp/debayer_cpu.h"
 | 
			
		||||
+
 | 
			
		||||
+#include <math.h>
 | 
			
		||||
+#include <stdlib.h>
 | 
			
		||||
+#include <time.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/formats.h>
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/bayer_format.h"
 | 
			
		||||
+#include "libcamera/internal/framebuffer.h"
 | 
			
		||||
+#include "libcamera/internal/mapped_framebuffer.h"
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
 | 
			
		||||
+	: stats_(std::move(stats)), gamma_correction_(1.0)
 | 
			
		||||
+{
 | 
			
		||||
+#ifdef __x86_64__
 | 
			
		||||
+	enableInputMemcpy_ = false;
 | 
			
		||||
+#else
 | 
			
		||||
+	enableInputMemcpy_ = true;
 | 
			
		||||
+#endif
 | 
			
		||||
+	/* Initialize gamma to 1.0 curve */
 | 
			
		||||
+	for (int i = 0; i < 1024; i++)
 | 
			
		||||
+		gamma_[i] = i / 4;
 | 
			
		||||
+
 | 
			
		||||
+	for (int i = 0; i < 5; i++)
 | 
			
		||||
+		lineBuffers_[i] = NULL;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+DebayerCpu::~DebayerCpu()
 | 
			
		||||
+{
 | 
			
		||||
+	for (int i = 0; i < 5; i++)
 | 
			
		||||
+		free(lineBuffers_[i]);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+// RGR
 | 
			
		||||
+// GBG
 | 
			
		||||
+// RGR
 | 
			
		||||
+#define BGGR_BGR888(p, n, div)                                                                \
 | 
			
		||||
+	*dst++ = blue_[curr[x] / (div)];                                                      \
 | 
			
		||||
+	*dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))];       \
 | 
			
		||||
+	*dst++ = red_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \
 | 
			
		||||
+	x++;
 | 
			
		||||
+
 | 
			
		||||
+// GBG
 | 
			
		||||
+// RGR
 | 
			
		||||
+// GBG
 | 
			
		||||
+#define GRBG_BGR888(p, n, div)                                    \
 | 
			
		||||
+	*dst++ = blue_[(prev[x] + next[x]) / (2 * (div))];        \
 | 
			
		||||
+	*dst++ = green_[curr[x] / (div)];                         \
 | 
			
		||||
+	*dst++ = red_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \
 | 
			
		||||
+	x++;
 | 
			
		||||
+
 | 
			
		||||
+// GRG
 | 
			
		||||
+// BGB
 | 
			
		||||
+// GRG
 | 
			
		||||
+#define GBRG_BGR888(p, n, div)                                     \
 | 
			
		||||
+	*dst++ = blue_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \
 | 
			
		||||
+	*dst++ = green_[curr[x] / (div)];                          \
 | 
			
		||||
+	*dst++ = red_[(prev[x] + next[x]) / (2 * (div))];          \
 | 
			
		||||
+	x++;
 | 
			
		||||
+
 | 
			
		||||
+// BGB
 | 
			
		||||
+// GRG
 | 
			
		||||
+// BGB
 | 
			
		||||
+#define RGGB_BGR888(p, n, div)                                                                 \
 | 
			
		||||
+	*dst++ = blue_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \
 | 
			
		||||
+	*dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))];        \
 | 
			
		||||
+	*dst++ = red_[curr[x] / (div)];                                                        \
 | 
			
		||||
+	x++;
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const int width_in_bytes = window_.width * 5 / 4;
 | 
			
		||||
+	const uint8_t *prev = (const uint8_t *)src[0];
 | 
			
		||||
+	const uint8_t *curr = (const uint8_t *)src[1];
 | 
			
		||||
+	const uint8_t *next = (const uint8_t *)src[2];
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * For the first pixel getting a pixel from the previous column uses
 | 
			
		||||
+	 * x - 2 to skip the 5th byte with least-significant bits for 4 pixels.
 | 
			
		||||
+	 * Same for last pixel (uses x + 2) and looking at the next column.
 | 
			
		||||
+	 * x++ in the for-loop skips the 5th byte with 4 x 2 lsb-s for 10bit packed.
 | 
			
		||||
+	 */
 | 
			
		||||
+	for (int x = 0; x < width_in_bytes; x++) {
 | 
			
		||||
+		/* Even pixel */
 | 
			
		||||
+		BGGR_BGR888(2, 1, 1)
 | 
			
		||||
+		/* Odd pixel BGGR -> GBRG */
 | 
			
		||||
+		GBRG_BGR888(1, 1, 1)
 | 
			
		||||
+		/* Same thing for next 2 pixels */
 | 
			
		||||
+		BGGR_BGR888(1, 1, 1)
 | 
			
		||||
+		GBRG_BGR888(1, 2, 1)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const int width_in_bytes = window_.width * 5 / 4;
 | 
			
		||||
+	const uint8_t *prev = (const uint8_t *)src[0];
 | 
			
		||||
+	const uint8_t *curr = (const uint8_t *)src[1];
 | 
			
		||||
+	const uint8_t *next = (const uint8_t *)src[2];
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < width_in_bytes; x++) {
 | 
			
		||||
+		/* Even pixel */
 | 
			
		||||
+		GRBG_BGR888(2, 1, 1)
 | 
			
		||||
+		/* Odd pixel GRBG -> RGGB */
 | 
			
		||||
+		RGGB_BGR888(1, 1, 1)
 | 
			
		||||
+		/* Same thing for next 2 pixels */
 | 
			
		||||
+		GRBG_BGR888(1, 1, 1)
 | 
			
		||||
+		RGGB_BGR888(1, 2, 1)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const int width_in_bytes = window_.width * 5 / 4;
 | 
			
		||||
+	const uint8_t *prev = (const uint8_t *)src[0];
 | 
			
		||||
+	const uint8_t *curr = (const uint8_t *)src[1];
 | 
			
		||||
+	const uint8_t *next = (const uint8_t *)src[2];
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < width_in_bytes; x++) {
 | 
			
		||||
+		/* Even pixel */
 | 
			
		||||
+		GBRG_BGR888(2, 1, 1)
 | 
			
		||||
+		/* Odd pixel GBGR -> BGGR */
 | 
			
		||||
+		BGGR_BGR888(1, 1, 1)
 | 
			
		||||
+		/* Same thing for next 2 pixels */
 | 
			
		||||
+		GBRG_BGR888(1, 1, 1)
 | 
			
		||||
+		BGGR_BGR888(1, 2, 1)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const int width_in_bytes = window_.width * 5 / 4;
 | 
			
		||||
+	const uint8_t *prev = (const uint8_t *)src[0];
 | 
			
		||||
+	const uint8_t *curr = (const uint8_t *)src[1];
 | 
			
		||||
+	const uint8_t *next = (const uint8_t *)src[2];
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < width_in_bytes; x++) {
 | 
			
		||||
+		/* Even pixel */
 | 
			
		||||
+		RGGB_BGR888(2, 1, 1)
 | 
			
		||||
+		/* Odd pixel RGGB -> GRBG*/
 | 
			
		||||
+		GRBG_BGR888(1, 1, 1)
 | 
			
		||||
+		/* Same thing for next 2 pixels */
 | 
			
		||||
+		RGGB_BGR888(1, 1, 1)
 | 
			
		||||
+		GRBG_BGR888(1, 2, 1)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static bool isStandardBayerOrder(BayerFormat::Order order)
 | 
			
		||||
+{
 | 
			
		||||
+	return order == BayerFormat::BGGR || order == BayerFormat::GBRG ||
 | 
			
		||||
+	       order == BayerFormat::GRBG || order == BayerFormat::RGGB;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)
 | 
			
		||||
+{
 | 
			
		||||
+	BayerFormat bayerFormat =
 | 
			
		||||
+		BayerFormat::fromPixelFormat(inputFormat);
 | 
			
		||||
+
 | 
			
		||||
+	if (bayerFormat.bitDepth == 10 &&
 | 
			
		||||
+	    bayerFormat.packing == BayerFormat::Packing::CSI2 &&
 | 
			
		||||
+	    isStandardBayerOrder(bayerFormat.order)) {
 | 
			
		||||
+		config.bpp = 10;
 | 
			
		||||
+		config.patternSize.width = 4; /* 5 bytes per *4* pixels */
 | 
			
		||||
+		config.patternSize.height = 2;
 | 
			
		||||
+		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
 | 
			
		||||
+		return 0;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	LOG(Debayer, Info)
 | 
			
		||||
+		<< "Unsupported input format " << inputFormat.toString();
 | 
			
		||||
+	return -EINVAL;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
 | 
			
		||||
+{
 | 
			
		||||
+	if (outputFormat == formats::RGB888) {
 | 
			
		||||
+		config.bpp = 24;
 | 
			
		||||
+		return 0;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	LOG(Debayer, Info)
 | 
			
		||||
+		<< "Unsupported output format " << outputFormat.toString();
 | 
			
		||||
+	return -EINVAL;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
 | 
			
		||||
+int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
 | 
			
		||||
+{
 | 
			
		||||
+	BayerFormat bayerFormat =
 | 
			
		||||
+		BayerFormat::fromPixelFormat(inputFormat);
 | 
			
		||||
+
 | 
			
		||||
+	if (bayerFormat.bitDepth == 10 &&
 | 
			
		||||
+	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
 | 
			
		||||
+		switch (bayerFormat.order) {
 | 
			
		||||
+		case BayerFormat::BGGR:
 | 
			
		||||
+			debayer0_ = &DebayerCpu::debayer10P_BGBG_BGR888;
 | 
			
		||||
+			debayer1_ = &DebayerCpu::debayer10P_GRGR_BGR888;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		case BayerFormat::GBRG:
 | 
			
		||||
+			debayer0_ = &DebayerCpu::debayer10P_GBGB_BGR888;
 | 
			
		||||
+			debayer1_ = &DebayerCpu::debayer10P_RGRG_BGR888;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		case BayerFormat::GRBG:
 | 
			
		||||
+			debayer0_ = &DebayerCpu::debayer10P_GRGR_BGR888;
 | 
			
		||||
+			debayer1_ = &DebayerCpu::debayer10P_BGBG_BGR888;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		case BayerFormat::RGGB:
 | 
			
		||||
+			debayer0_ = &DebayerCpu::debayer10P_RGRG_BGR888;
 | 
			
		||||
+			debayer1_ = &DebayerCpu::debayer10P_GBGB_BGR888;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		default:
 | 
			
		||||
+			break;
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	LOG(Debayer, Error) << "Unsupported input output format combination";
 | 
			
		||||
+	return -EINVAL;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int DebayerCpu::configure(const StreamConfiguration &inputCfg,
 | 
			
		||||
+			  const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)
 | 
			
		||||
+{
 | 
			
		||||
+	if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+
 | 
			
		||||
+	if (stats_->configure(inputCfg) != 0)
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+
 | 
			
		||||
+	const Size &stats_pattern_size = stats_->patternSize();
 | 
			
		||||
+	if (inputConfig_.patternSize.width != stats_pattern_size.width ||
 | 
			
		||||
+	    inputConfig_.patternSize.height != stats_pattern_size.height) {
 | 
			
		||||
+		LOG(Debayer, Error)
 | 
			
		||||
+			<< "mismatching stats and debayer pattern sizes for "
 | 
			
		||||
+			<< inputCfg.pixelFormat.toString();
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	inputConfig_.stride = inputCfg.stride;
 | 
			
		||||
+
 | 
			
		||||
+	if (outputCfgs.size() != 1) {
 | 
			
		||||
+		LOG(Debayer, Error)
 | 
			
		||||
+			<< "Unsupported number of output streams: "
 | 
			
		||||
+			<< outputCfgs.size();
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	const StreamConfiguration &outputCfg = outputCfgs[0];
 | 
			
		||||
+	SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
 | 
			
		||||
+	std::tie(outputConfig_.stride, outputConfig_.frameSize) =
 | 
			
		||||
+		strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
 | 
			
		||||
+
 | 
			
		||||
+	if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
 | 
			
		||||
+		LOG(Debayer, Error)
 | 
			
		||||
+			<< "Invalid output size/stride: "
 | 
			
		||||
+			<< "\n  " << outputCfg.size << " (" << outSizeRange << ")"
 | 
			
		||||
+			<< "\n  " << outputCfg.stride << " (" << outputConfig_.stride << ")";
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (setDebayerFunctions(inputCfg.pixelFormat, outputCfg.pixelFormat) != 0)
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+
 | 
			
		||||
+	window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
 | 
			
		||||
+		    ~(inputConfig_.patternSize.width - 1);
 | 
			
		||||
+	window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
 | 
			
		||||
+		    ~(inputConfig_.patternSize.height - 1);
 | 
			
		||||
+	window_.width = outputCfg.size.width;
 | 
			
		||||
+	window_.height = outputCfg.size.height;
 | 
			
		||||
+
 | 
			
		||||
+	/* Don't pass x,y since process() already adjusts src before passing it */
 | 
			
		||||
+	stats_->setWindow(Rectangle(window_.size()));
 | 
			
		||||
+
 | 
			
		||||
+	for (unsigned int i = 0;
 | 
			
		||||
+	     i < (inputConfig_.patternSize.height + 1) && enableInputMemcpy_;
 | 
			
		||||
+	     i++) {
 | 
			
		||||
+		/* pad with patternSize.Width on both left and right side */
 | 
			
		||||
+		size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) *
 | 
			
		||||
+				    inputConfig_.bpp / 8;
 | 
			
		||||
+
 | 
			
		||||
+		free(lineBuffers_[i]);
 | 
			
		||||
+		lineBuffers_[i] = (uint8_t *)malloc(lineLength);
 | 
			
		||||
+		if (!lineBuffers_[i])
 | 
			
		||||
+			return -ENOMEM;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	measuredFrames_ = 0;
 | 
			
		||||
+	frameProcessTime_ = 0;
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+Size DebayerCpu::patternSize(PixelFormat inputFormat)
 | 
			
		||||
+{
 | 
			
		||||
+	DebayerCpu::DebayerInputConfig config;
 | 
			
		||||
+
 | 
			
		||||
+	if (getInputConfig(inputFormat, config) != 0)
 | 
			
		||||
+		return {};
 | 
			
		||||
+
 | 
			
		||||
+	return config.patternSize;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+std::vector<PixelFormat> DebayerCpu::formats(PixelFormat inputFormat)
 | 
			
		||||
+{
 | 
			
		||||
+	DebayerCpu::DebayerInputConfig config;
 | 
			
		||||
+
 | 
			
		||||
+	if (getInputConfig(inputFormat, config) != 0)
 | 
			
		||||
+		return std::vector<PixelFormat>();
 | 
			
		||||
+
 | 
			
		||||
+	return config.outputFormats;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+std::tuple<unsigned int, unsigned int>
 | 
			
		||||
+DebayerCpu::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
 | 
			
		||||
+{
 | 
			
		||||
+	DebayerCpu::DebayerOutputConfig config;
 | 
			
		||||
+
 | 
			
		||||
+	if (getOutputConfig(outputFormat, config) != 0)
 | 
			
		||||
+		return std::make_tuple(0, 0);
 | 
			
		||||
+
 | 
			
		||||
+	/* round up to multiple of 8 for 64 bits alignment */
 | 
			
		||||
+	unsigned int stride = (size.width * config.bpp / 8 + 7) & ~7;
 | 
			
		||||
+
 | 
			
		||||
+	return std::make_tuple(stride, stride * size.height);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::initLinePointers(const uint8_t *linePointers[], const uint8_t *src)
 | 
			
		||||
+{
 | 
			
		||||
+	const int patternHeight = inputConfig_.patternSize.height;
 | 
			
		||||
+
 | 
			
		||||
+	for (int i = 0; i < patternHeight; i++)
 | 
			
		||||
+		linePointers[i + 1] = src +
 | 
			
		||||
+				      (-patternHeight / 2 + i) * (int)inputConfig_.stride;
 | 
			
		||||
+
 | 
			
		||||
+	if (!enableInputMemcpy_)
 | 
			
		||||
+		return;
 | 
			
		||||
+
 | 
			
		||||
+	for (int i = 0; i < patternHeight; i++) {
 | 
			
		||||
+		/* pad with patternSize.Width on both left and right side */
 | 
			
		||||
+		size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) *
 | 
			
		||||
+				    inputConfig_.bpp / 8;
 | 
			
		||||
+		int padding = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
 | 
			
		||||
+
 | 
			
		||||
+		memcpy(lineBuffers_[i], linePointers[i + 1] - padding, lineLength);
 | 
			
		||||
+		linePointers[i + 1] = lineBuffers_[i] + padding;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/* Point lineBufferIndex_ to first unused lineBuffer */
 | 
			
		||||
+	lineBufferIndex_ = patternHeight;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src)
 | 
			
		||||
+{
 | 
			
		||||
+	const int patternHeight = inputConfig_.patternSize.height;
 | 
			
		||||
+
 | 
			
		||||
+	for (int i = 0; i < patternHeight; i++)
 | 
			
		||||
+		linePointers[i] = linePointers[i + 1];
 | 
			
		||||
+
 | 
			
		||||
+	linePointers[patternHeight] = src +
 | 
			
		||||
+				      (patternHeight / 2) * (int)inputConfig_.stride;
 | 
			
		||||
+
 | 
			
		||||
+	if (!enableInputMemcpy_)
 | 
			
		||||
+		return;
 | 
			
		||||
+
 | 
			
		||||
+	size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) *
 | 
			
		||||
+			    inputConfig_.bpp / 8;
 | 
			
		||||
+	int padding = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
 | 
			
		||||
+	memcpy(lineBuffers_[lineBufferIndex_], linePointers[patternHeight] - padding, lineLength);
 | 
			
		||||
+	linePointers[patternHeight] = lineBuffers_[lineBufferIndex_] + padding;
 | 
			
		||||
+
 | 
			
		||||
+	lineBufferIndex_ = (lineBufferIndex_ + 1) % (patternHeight + 1);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::process2(const uint8_t *src, uint8_t *dst)
 | 
			
		||||
+{
 | 
			
		||||
+	const unsigned int y_end = window_.y + window_.height;
 | 
			
		||||
+	const uint8_t *linePointers[3];
 | 
			
		||||
+
 | 
			
		||||
+	/* Adjust src to top left corner of the window */
 | 
			
		||||
+	src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
 | 
			
		||||
+
 | 
			
		||||
+	initLinePointers(linePointers, src);
 | 
			
		||||
+
 | 
			
		||||
+	for (unsigned int y = window_.y; y < y_end; y += 2) {
 | 
			
		||||
+		shiftLinePointers(linePointers, src);
 | 
			
		||||
+		stats_->processLine0(y, linePointers);
 | 
			
		||||
+		(this->*debayer0_)(dst, linePointers);
 | 
			
		||||
+		src += inputConfig_.stride;
 | 
			
		||||
+		dst += outputConfig_.stride;
 | 
			
		||||
+
 | 
			
		||||
+		shiftLinePointers(linePointers, src);
 | 
			
		||||
+		(this->*debayer1_)(dst, linePointers);
 | 
			
		||||
+		src += inputConfig_.stride;
 | 
			
		||||
+		dst += outputConfig_.stride;
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::process4(const uint8_t *src, uint8_t *dst)
 | 
			
		||||
+{
 | 
			
		||||
+	const unsigned int y_end = window_.y + window_.height;
 | 
			
		||||
+	const uint8_t *linePointers[5];
 | 
			
		||||
+
 | 
			
		||||
+	/* Adjust src to top left corner of the window */
 | 
			
		||||
+	src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
 | 
			
		||||
+
 | 
			
		||||
+	initLinePointers(linePointers, src);
 | 
			
		||||
+
 | 
			
		||||
+	for (unsigned int y = window_.y; y < y_end; y += 4) {
 | 
			
		||||
+		shiftLinePointers(linePointers, src);
 | 
			
		||||
+		stats_->processLine0(y, linePointers);
 | 
			
		||||
+		(this->*debayer0_)(dst, linePointers);
 | 
			
		||||
+		src += inputConfig_.stride;
 | 
			
		||||
+		dst += outputConfig_.stride;
 | 
			
		||||
+
 | 
			
		||||
+		shiftLinePointers(linePointers, src);
 | 
			
		||||
+		(this->*debayer1_)(dst, linePointers);
 | 
			
		||||
+		src += inputConfig_.stride;
 | 
			
		||||
+		dst += outputConfig_.stride;
 | 
			
		||||
+
 | 
			
		||||
+		shiftLinePointers(linePointers, src);
 | 
			
		||||
+		stats_->processLine2(y, linePointers);
 | 
			
		||||
+		(this->*debayer2_)(dst, linePointers);
 | 
			
		||||
+		src += inputConfig_.stride;
 | 
			
		||||
+		dst += outputConfig_.stride;
 | 
			
		||||
+
 | 
			
		||||
+		shiftLinePointers(linePointers, src);
 | 
			
		||||
+		(this->*debayer3_)(dst, linePointers);
 | 
			
		||||
+		src += inputConfig_.stride;
 | 
			
		||||
+		dst += outputConfig_.stride;
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static inline int64_t timeDiff(timespec &after, timespec &before)
 | 
			
		||||
+{
 | 
			
		||||
+	return (after.tv_sec - before.tv_sec) * 1000000000LL +
 | 
			
		||||
+	       (int64_t)after.tv_nsec - (int64_t)before.tv_nsec;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams params)
 | 
			
		||||
+{
 | 
			
		||||
+	timespec frameStartTime;
 | 
			
		||||
+
 | 
			
		||||
+	if (measuredFrames_ < DebayerCpu::framesToMeasure) {
 | 
			
		||||
+		frameStartTime = {};
 | 
			
		||||
+		clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/* Apply DebayerParams */
 | 
			
		||||
+	if (params.gamma != gamma_correction_) {
 | 
			
		||||
+		for (int i = 0; i < 1024; i++)
 | 
			
		||||
+			gamma_[i] = 255 * powf(i / 1023.0, params.gamma);
 | 
			
		||||
+
 | 
			
		||||
+		gamma_correction_ = params.gamma;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	for (int i = 0; i < 256; i++) {
 | 
			
		||||
+		int idx;
 | 
			
		||||
+
 | 
			
		||||
+		/* Apply gamma after gain! */
 | 
			
		||||
+		idx = std::min({ i * params.gainR / 64U, 1023U });
 | 
			
		||||
+		red_[i] = gamma_[idx];
 | 
			
		||||
+
 | 
			
		||||
+		idx = std::min({ i * params.gainG / 64U, 1023U });
 | 
			
		||||
+		green_[i] = gamma_[idx];
 | 
			
		||||
+
 | 
			
		||||
+		idx = std::min({ i * params.gainB / 64U, 1023U });
 | 
			
		||||
+		blue_[i] = gamma_[idx];
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/* Copy metadata from the input buffer */
 | 
			
		||||
+	FrameMetadata &metadata = output->_d()->metadata();
 | 
			
		||||
+	metadata.status = input->metadata().status;
 | 
			
		||||
+	metadata.sequence = input->metadata().sequence;
 | 
			
		||||
+	metadata.timestamp = input->metadata().timestamp;
 | 
			
		||||
+
 | 
			
		||||
+	MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
 | 
			
		||||
+	MappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write);
 | 
			
		||||
+	if (!in.isValid() || !out.isValid()) {
 | 
			
		||||
+		LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
 | 
			
		||||
+		metadata.status = FrameMetadata::FrameError;
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	stats_->startFrame();
 | 
			
		||||
+
 | 
			
		||||
+	if (inputConfig_.patternSize.height == 2)
 | 
			
		||||
+		process2(in.planes()[0].data(), out.planes()[0].data());
 | 
			
		||||
+	else
 | 
			
		||||
+		process4(in.planes()[0].data(), out.planes()[0].data());
 | 
			
		||||
+
 | 
			
		||||
+	metadata.planes()[0].bytesused = out.planes()[0].size();
 | 
			
		||||
+
 | 
			
		||||
+	/* Measure before emitting signals */
 | 
			
		||||
+	if (measuredFrames_ < DebayerCpu::framesToMeasure &&
 | 
			
		||||
+	    ++measuredFrames_ > DebayerCpu::framesToSkip) {
 | 
			
		||||
+		timespec frameEndTime = {};
 | 
			
		||||
+		clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime);
 | 
			
		||||
+		frameProcessTime_ += timeDiff(frameEndTime, frameStartTime);
 | 
			
		||||
+		if (measuredFrames_ == DebayerCpu::framesToMeasure) {
 | 
			
		||||
+			const int measuredFrames = DebayerCpu::framesToMeasure -
 | 
			
		||||
+						   DebayerCpu::framesToSkip;
 | 
			
		||||
+			LOG(Debayer, Info)
 | 
			
		||||
+				<< "Processed " << measuredFrames
 | 
			
		||||
+				<< " frames in " << frameProcessTime_ / 1000 << "us, "
 | 
			
		||||
+				<< frameProcessTime_ / (1000 * measuredFrames)
 | 
			
		||||
+				<< " us/frame";
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	stats_->finishFrame();
 | 
			
		||||
+	outputBufferReady.emit(output);
 | 
			
		||||
+	inputBufferReady.emit(input);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
 | 
			
		||||
index d4ae5ac7..6d7a44d7 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/meson.build
 | 
			
		||||
+++ b/src/libcamera/software_isp/meson.build
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 
 | 
			
		||||
 libcamera_sources += files([
 | 
			
		||||
 	'debayer.cpp',
 | 
			
		||||
+	'debayer_cpu.cpp',
 | 
			
		||||
 	'swstats.cpp',
 | 
			
		||||
 	'swstats_cpu.cpp',
 | 
			
		||||
 ])
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,256 @@
 | 
			
		|||
From 05b353f1e45f2af0d0989261210b4bedef5144de Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Mon, 11 Dec 2023 23:41:58 +0300
 | 
			
		||||
Subject: [PATCH 11/25] libcamera: ipa: add Soft IPA common files
 | 
			
		||||
 | 
			
		||||
Define the Soft IPA main and event interfaces, add IPASoftBase
 | 
			
		||||
class the Soft IPA implementation inherit from.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 Documentation/Doxyfile.in           |  1 +
 | 
			
		||||
 include/libcamera/ipa/meson.build   |  1 +
 | 
			
		||||
 include/libcamera/ipa/soft.mojom    | 29 ++++++++++++
 | 
			
		||||
 src/ipa/simple/common/meson.build   | 17 +++++++
 | 
			
		||||
 src/ipa/simple/common/soft_base.cpp | 73 +++++++++++++++++++++++++++++
 | 
			
		||||
 src/ipa/simple/common/soft_base.h   | 50 ++++++++++++++++++++
 | 
			
		||||
 src/ipa/simple/meson.build          |  3 ++
 | 
			
		||||
 7 files changed, 174 insertions(+)
 | 
			
		||||
 create mode 100644 include/libcamera/ipa/soft.mojom
 | 
			
		||||
 create mode 100644 src/ipa/simple/common/meson.build
 | 
			
		||||
 create mode 100644 src/ipa/simple/common/soft_base.cpp
 | 
			
		||||
 create mode 100644 src/ipa/simple/common/soft_base.h
 | 
			
		||||
 create mode 100644 src/ipa/simple/meson.build
 | 
			
		||||
 | 
			
		||||
diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
 | 
			
		||||
index a86ea6c1..2be8d47b 100644
 | 
			
		||||
--- a/Documentation/Doxyfile.in
 | 
			
		||||
+++ b/Documentation/Doxyfile.in
 | 
			
		||||
@@ -44,6 +44,7 @@ EXCLUDE                = @TOP_SRCDIR@/include/libcamera/base/span.h \
 | 
			
		||||
                          @TOP_SRCDIR@/src/libcamera/pipeline/ \
 | 
			
		||||
                          @TOP_SRCDIR@/src/libcamera/tracepoints.cpp \
 | 
			
		||||
                          @TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \
 | 
			
		||||
+                         @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \
 | 
			
		||||
                          @TOP_BUILDDIR@/src/libcamera/proxy/
 | 
			
		||||
 
 | 
			
		||||
 EXCLUDE_PATTERNS       = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \
 | 
			
		||||
diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build
 | 
			
		||||
index f3b4881c..894e38a6 100644
 | 
			
		||||
--- a/include/libcamera/ipa/meson.build
 | 
			
		||||
+++ b/include/libcamera/ipa/meson.build
 | 
			
		||||
@@ -65,6 +65,7 @@ pipeline_ipa_mojom_mapping = {
 | 
			
		||||
     'ipu3': 'ipu3.mojom',
 | 
			
		||||
     'rkisp1': 'rkisp1.mojom',
 | 
			
		||||
     'rpi/vc4': 'raspberrypi.mojom',
 | 
			
		||||
+    'simple/simple': 'soft.mojom',
 | 
			
		||||
     'vimc': 'vimc.mojom',
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..2dae652b
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/include/libcamera/ipa/soft.mojom
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+
 | 
			
		||||
+/*
 | 
			
		||||
+ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry.
 | 
			
		||||
+ * \todo Add a way to tell SoftIPA the list of params SoftISP accepts?
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+module ipa.soft;
 | 
			
		||||
+
 | 
			
		||||
+import "include/libcamera/ipa/core.mojom";
 | 
			
		||||
+
 | 
			
		||||
+interface IPASoftInterface {
 | 
			
		||||
+	init(libcamera.IPASettings settings,
 | 
			
		||||
+	     libcamera.SharedFD fdStats,
 | 
			
		||||
+	     libcamera.SharedFD fdParams,
 | 
			
		||||
+	     libcamera.ControlInfoMap sensorCtrlInfoMap)
 | 
			
		||||
+		=> (int32 ret);
 | 
			
		||||
+	start() => (int32 ret);
 | 
			
		||||
+	stop();
 | 
			
		||||
+	configure(libcamera.ControlInfoMap sensorCtrlInfoMap)
 | 
			
		||||
+		=> (int32 ret);
 | 
			
		||||
+
 | 
			
		||||
+	[async] processStats(libcamera.ControlList sensorControls);
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+interface IPASoftEventInterface {
 | 
			
		||||
+	setSensorControls(libcamera.ControlList sensorControls);
 | 
			
		||||
+	setIspParams(int32 dummy);
 | 
			
		||||
+};
 | 
			
		||||
diff --git a/src/ipa/simple/common/meson.build b/src/ipa/simple/common/meson.build
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..023e617b
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/ipa/simple/common/meson.build
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
+# SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
+
 | 
			
		||||
+soft_ipa_common_sources = files([
 | 
			
		||||
+    'soft_base.cpp',
 | 
			
		||||
+])
 | 
			
		||||
+
 | 
			
		||||
+soft_ipa_common_includes = [
 | 
			
		||||
+    include_directories('..'),
 | 
			
		||||
+]
 | 
			
		||||
+
 | 
			
		||||
+soft_ipa_common_deps = [
 | 
			
		||||
+    libcamera_private,
 | 
			
		||||
+]
 | 
			
		||||
+
 | 
			
		||||
+soft_ipa_common_lib = static_library('soft_ipa_common', soft_ipa_common_sources,
 | 
			
		||||
+                                     include_directories : soft_ipa_common_includes,
 | 
			
		||||
+                                     dependencies : soft_ipa_common_deps)
 | 
			
		||||
diff --git a/src/ipa/simple/common/soft_base.cpp b/src/ipa/simple/common/soft_base.cpp
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..b4ed9023
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/ipa/simple/common/soft_base.cpp
 | 
			
		||||
@@ -0,0 +1,73 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ *
 | 
			
		||||
+ * soft-base.cpp - Software IPA base class
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#include "soft_base.h"
 | 
			
		||||
+
 | 
			
		||||
+#include <sys/mman.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/file.h>
 | 
			
		||||
+#include <libcamera/base/log.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/control_ids.h>
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+LOG_DEFINE_CATEGORY(IPASoft)
 | 
			
		||||
+
 | 
			
		||||
+namespace ipa::soft {
 | 
			
		||||
+
 | 
			
		||||
+IPASoftBase::IPASoftBase()
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+IPASoftBase::~IPASoftBase()
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int IPASoftBase::init([[maybe_unused]] const IPASettings &settings,
 | 
			
		||||
+		      const SharedFD &fdStats,
 | 
			
		||||
+		      const SharedFD &fdParams,
 | 
			
		||||
+		      const ControlInfoMap &sensorInfoMap)
 | 
			
		||||
+{
 | 
			
		||||
+	fdStats_ = std::move(fdStats);
 | 
			
		||||
+	if (!fdStats_.isValid()) {
 | 
			
		||||
+		LOG(IPASoft, Error) << "Invalid Statistics handle";
 | 
			
		||||
+		return -ENODEV;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	fdParams_ = std::move(fdParams);
 | 
			
		||||
+	if (!fdParams_.isValid()) {
 | 
			
		||||
+		LOG(IPASoft, Error) << "Invalid Parameters handle";
 | 
			
		||||
+		return -ENODEV;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	return platformInit(sensorInfoMap);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int IPASoftBase::configure(const ControlInfoMap &sensorInfoMap)
 | 
			
		||||
+{
 | 
			
		||||
+	return platformConfigure(sensorInfoMap);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int IPASoftBase::start()
 | 
			
		||||
+{
 | 
			
		||||
+	return platformStart();
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void IPASoftBase::stop()
 | 
			
		||||
+{
 | 
			
		||||
+	return platformStop();
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void IPASoftBase::processStats(const ControlList &sensorControls)
 | 
			
		||||
+{
 | 
			
		||||
+	return platformProcessStats(sensorControls);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace ipa::soft */
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/src/ipa/simple/common/soft_base.h b/src/ipa/simple/common/soft_base.h
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..85c98702
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/ipa/simple/common/soft_base.h
 | 
			
		||||
@@ -0,0 +1,50 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ *
 | 
			
		||||
+ * soft-base.h - Software IPA base class
 | 
			
		||||
+ */
 | 
			
		||||
+#pragma once
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/shared_fd.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/controls.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/ipa/soft_ipa_interface.h>
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+namespace ipa::soft {
 | 
			
		||||
+
 | 
			
		||||
+class IPASoftBase : public ipa::soft::IPASoftInterface
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	IPASoftBase();
 | 
			
		||||
+	~IPASoftBase();
 | 
			
		||||
+
 | 
			
		||||
+	int init(const IPASettings &settings,
 | 
			
		||||
+		 const SharedFD &fdStats,
 | 
			
		||||
+		 const SharedFD &fdParams,
 | 
			
		||||
+		 const ControlInfoMap &sensorInfoMap) override;
 | 
			
		||||
+	int configure(const ControlInfoMap &sensorInfoMap) override;
 | 
			
		||||
+
 | 
			
		||||
+	int start() override;
 | 
			
		||||
+	void stop() override;
 | 
			
		||||
+
 | 
			
		||||
+	void processStats(const ControlList &sensorControls) override;
 | 
			
		||||
+
 | 
			
		||||
+protected:
 | 
			
		||||
+	SharedFD fdStats_;
 | 
			
		||||
+	SharedFD fdParams_;
 | 
			
		||||
+
 | 
			
		||||
+private:
 | 
			
		||||
+	virtual int platformInit(const ControlInfoMap &sensorInfoMap) = 0;
 | 
			
		||||
+	virtual int platformConfigure(const ControlInfoMap &sensorInfoMap) = 0;
 | 
			
		||||
+	virtual int platformStart() = 0;
 | 
			
		||||
+	virtual void platformStop() = 0;
 | 
			
		||||
+	virtual void platformProcessStats(const ControlList &sensorControls) = 0;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace ipa::soft */
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..9688bbdb
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/ipa/simple/meson.build
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
+# SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
+
 | 
			
		||||
+subdir('common')
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,407 @@
 | 
			
		|||
From c0886381a2bbe494b900d699a3858573316059b2 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Mon, 11 Dec 2023 23:47:47 +0300
 | 
			
		||||
Subject: [PATCH 12/25] libcamera: ipa: Soft IPA: add a Simple Soft IPA
 | 
			
		||||
 implementation
 | 
			
		||||
 | 
			
		||||
Auto exposure/gain and AWB implementation by Dennis, Toon and Martti.
 | 
			
		||||
 | 
			
		||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Co-authored-by: Marttico <g.martti@gmail.com>
 | 
			
		||||
Signed-off-by: Marttico <g.martti@gmail.com>
 | 
			
		||||
Co-authored-by: Toon Langendam <t.langendam@gmail.com>
 | 
			
		||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 meson_options.txt                      |   3 +-
 | 
			
		||||
 src/ipa/simple/meson.build             |   9 +
 | 
			
		||||
 src/ipa/simple/simple/data/meson.build |   9 +
 | 
			
		||||
 src/ipa/simple/simple/data/soft.conf   |   3 +
 | 
			
		||||
 src/ipa/simple/simple/meson.build      |  26 +++
 | 
			
		||||
 src/ipa/simple/simple/soft_simple.cpp  | 273 +++++++++++++++++++++++++
 | 
			
		||||
 6 files changed, 322 insertions(+), 1 deletion(-)
 | 
			
		||||
 create mode 100644 src/ipa/simple/simple/data/meson.build
 | 
			
		||||
 create mode 100644 src/ipa/simple/simple/data/soft.conf
 | 
			
		||||
 create mode 100644 src/ipa/simple/simple/meson.build
 | 
			
		||||
 create mode 100644 src/ipa/simple/simple/soft_simple.cpp
 | 
			
		||||
 | 
			
		||||
diff --git a/meson_options.txt b/meson_options.txt
 | 
			
		||||
index 5fdc7be8..8ec08658 100644
 | 
			
		||||
--- a/meson_options.txt
 | 
			
		||||
+++ b/meson_options.txt
 | 
			
		||||
@@ -27,7 +27,7 @@ option('gstreamer',
 | 
			
		||||
 
 | 
			
		||||
 option('ipas',
 | 
			
		||||
         type : 'array',
 | 
			
		||||
-        choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'],
 | 
			
		||||
+        choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple/simple', 'vimc'],
 | 
			
		||||
         description : 'Select which IPA modules to build')
 | 
			
		||||
 
 | 
			
		||||
 option('lc-compliance',
 | 
			
		||||
@@ -46,6 +46,7 @@ option('pipelines',
 | 
			
		||||
             'rkisp1',
 | 
			
		||||
             'rpi/vc4',
 | 
			
		||||
             'simple',
 | 
			
		||||
+            'simple/simple',
 | 
			
		||||
             'uvcvideo',
 | 
			
		||||
             'vimc'
 | 
			
		||||
         ],
 | 
			
		||||
diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
 | 
			
		||||
index 9688bbdb..14be5dc2 100644
 | 
			
		||||
--- a/src/ipa/simple/meson.build
 | 
			
		||||
+++ b/src/ipa/simple/meson.build
 | 
			
		||||
@@ -1,3 +1,12 @@
 | 
			
		||||
 # SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
 
 | 
			
		||||
 subdir('common')
 | 
			
		||||
+
 | 
			
		||||
+foreach pipeline : pipelines
 | 
			
		||||
+    pipeline = pipeline.split('/')
 | 
			
		||||
+    if pipeline.length() < 2 or pipeline[0] != 'simple'
 | 
			
		||||
+        continue
 | 
			
		||||
+    endif
 | 
			
		||||
+
 | 
			
		||||
+    subdir(pipeline[1])
 | 
			
		||||
+endforeach
 | 
			
		||||
diff --git a/src/ipa/simple/simple/data/meson.build b/src/ipa/simple/simple/data/meson.build
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..33548cc6
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/ipa/simple/simple/data/meson.build
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
+# SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
+
 | 
			
		||||
+conf_files = files([
 | 
			
		||||
+    'soft.conf',
 | 
			
		||||
+])
 | 
			
		||||
+
 | 
			
		||||
+install_data(conf_files,
 | 
			
		||||
+             install_dir : ipa_data_dir / 'soft',
 | 
			
		||||
+             install_tag : 'runtime')
 | 
			
		||||
diff --git a/src/ipa/simple/simple/data/soft.conf b/src/ipa/simple/simple/data/soft.conf
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..0c70e7c0
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/ipa/simple/simple/data/soft.conf
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
+# SPDX-License-Identifier: LGPL-2.1-or-later
 | 
			
		||||
+#
 | 
			
		||||
+# Dummy configuration file for the soft IPA.
 | 
			
		||||
diff --git a/src/ipa/simple/simple/meson.build b/src/ipa/simple/simple/meson.build
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..8b5d76b5
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/ipa/simple/simple/meson.build
 | 
			
		||||
@@ -0,0 +1,26 @@
 | 
			
		||||
+# SPDX-License-Identifier: CC0-1.0
 | 
			
		||||
+
 | 
			
		||||
+ipa_name = 'ipa_soft_simple'
 | 
			
		||||
+
 | 
			
		||||
+mod = shared_module(ipa_name,
 | 
			
		||||
+                    ['soft_simple.cpp', libcamera_generated_ipa_headers],
 | 
			
		||||
+                    name_prefix : '',
 | 
			
		||||
+                    include_directories : [ipa_includes, libipa_includes, '..'],
 | 
			
		||||
+                    dependencies : libcamera_private,
 | 
			
		||||
+                    link_with : libipa,
 | 
			
		||||
+                    link_whole : soft_ipa_common_lib,
 | 
			
		||||
+                    install : true,
 | 
			
		||||
+                    install_dir : ipa_install_dir)
 | 
			
		||||
+
 | 
			
		||||
+if ipa_sign_module
 | 
			
		||||
+    custom_target(ipa_name + '.so.sign',
 | 
			
		||||
+                  input : mod,
 | 
			
		||||
+                  output : ipa_name + '.so.sign',
 | 
			
		||||
+                  command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],
 | 
			
		||||
+                  install : false,
 | 
			
		||||
+                  build_by_default : true)
 | 
			
		||||
+endif
 | 
			
		||||
+
 | 
			
		||||
+subdir('data')
 | 
			
		||||
+
 | 
			
		||||
+ipa_names += ipa_name
 | 
			
		||||
diff --git a/src/ipa/simple/simple/soft_simple.cpp b/src/ipa/simple/simple/soft_simple.cpp
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..93fc1545
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/ipa/simple/simple/soft_simple.cpp
 | 
			
		||||
@@ -0,0 +1,273 @@
 | 
			
		||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 | 
			
		||||
+/*
 | 
			
		||||
+ * Copyright (C) 2023, Linaro Ltd
 | 
			
		||||
+ *
 | 
			
		||||
+ * soft_simple.cpp - Simple Software Image Processing Algorithm module
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#include <sys/mman.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/base/file.h>
 | 
			
		||||
+#include <libcamera/base/log.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/control_ids.h>
 | 
			
		||||
+
 | 
			
		||||
+#include <libcamera/ipa/ipa_interface.h>
 | 
			
		||||
+#include <libcamera/ipa/ipa_module_info.h>
 | 
			
		||||
+
 | 
			
		||||
+#include "libcamera/internal/camera_sensor.h"
 | 
			
		||||
+#include "libcamera/internal/software_isp/debayer_params.h"
 | 
			
		||||
+#include "libcamera/internal/software_isp/swisp_stats.h"
 | 
			
		||||
+
 | 
			
		||||
+#include "common/soft_base.h"
 | 
			
		||||
+
 | 
			
		||||
+#define EXPOSURE_OPTIMAL_VALUE 2.5
 | 
			
		||||
+#define EXPOSURE_SATISFACTORY_OFFSET 0.2
 | 
			
		||||
+
 | 
			
		||||
+namespace libcamera {
 | 
			
		||||
+
 | 
			
		||||
+LOG_DECLARE_CATEGORY(IPASoft)
 | 
			
		||||
+
 | 
			
		||||
+namespace ipa::soft {
 | 
			
		||||
+
 | 
			
		||||
+class IPASoftSimple final : public IPASoftBase
 | 
			
		||||
+{
 | 
			
		||||
+public:
 | 
			
		||||
+	IPASoftSimple()
 | 
			
		||||
+		: IPASoftBase(), ignore_updates_(0)
 | 
			
		||||
+	{
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	~IPASoftSimple()
 | 
			
		||||
+	{
 | 
			
		||||
+		if (stats_)
 | 
			
		||||
+			munmap(stats_, sizeof(SwIspStats));
 | 
			
		||||
+		if (params_)
 | 
			
		||||
+			munmap(params_, sizeof(DebayerParams));
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	int platformInit(const ControlInfoMap &sensorInfoMap) override;
 | 
			
		||||
+	int platformConfigure(const ControlInfoMap &sensorInfoMap) override;
 | 
			
		||||
+	int platformStart() override;
 | 
			
		||||
+	void platformStop() override;
 | 
			
		||||
+	void platformProcessStats(const ControlList &sensorControls) override;
 | 
			
		||||
+
 | 
			
		||||
+private:
 | 
			
		||||
+	void update_exposure(double exposuremsv);
 | 
			
		||||
+
 | 
			
		||||
+	DebayerParams *params_;
 | 
			
		||||
+	SwIspStats *stats_;
 | 
			
		||||
+	int exposure_min_, exposure_max_;
 | 
			
		||||
+	int again_min_, again_max_;
 | 
			
		||||
+	int again_, exposure_;
 | 
			
		||||
+	int ignore_updates_;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+int IPASoftSimple::platformInit(const ControlInfoMap &sensorInfoMap)
 | 
			
		||||
+{
 | 
			
		||||
+	params_ = static_cast<DebayerParams *>(mmap(nullptr, sizeof(DebayerParams),
 | 
			
		||||
+						    PROT_WRITE, MAP_SHARED,
 | 
			
		||||
+						    fdParams_.get(), 0));
 | 
			
		||||
+	if (!params_) {
 | 
			
		||||
+		LOG(IPASoft, Error) << "Unable to map Parameters";
 | 
			
		||||
+		return -ENODEV;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	stats_ = static_cast<SwIspStats *>(mmap(nullptr, sizeof(SwIspStats),
 | 
			
		||||
+						PROT_READ, MAP_SHARED,
 | 
			
		||||
+						fdStats_.get(), 0));
 | 
			
		||||
+	if (!stats_) {
 | 
			
		||||
+		LOG(IPASoft, Error) << "Unable to map Statistics";
 | 
			
		||||
+		return -ENODEV;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (sensorInfoMap.find(V4L2_CID_EXPOSURE) == sensorInfoMap.end()) {
 | 
			
		||||
+		LOG(IPASoft, Error) << "Don't have exposure control";
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN) == sensorInfoMap.end()) {
 | 
			
		||||
+		LOG(IPASoft, Error) << "Don't have gain control";
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int IPASoftSimple::platformConfigure(const ControlInfoMap &sensorInfoMap)
 | 
			
		||||
+{
 | 
			
		||||
+	const ControlInfo &exposure_info = sensorInfoMap.find(V4L2_CID_EXPOSURE)->second;
 | 
			
		||||
+	const ControlInfo &gain_info = sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN)->second;
 | 
			
		||||
+
 | 
			
		||||
+	exposure_min_ = exposure_info.min().get<int>();
 | 
			
		||||
+	if (!exposure_min_) {
 | 
			
		||||
+		LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
 | 
			
		||||
+		exposure_min_ = 1;
 | 
			
		||||
+	}
 | 
			
		||||
+	exposure_max_ = exposure_info.max().get<int>();
 | 
			
		||||
+	again_min_ = gain_info.min().get<int>();
 | 
			
		||||
+	if (!again_min_) {
 | 
			
		||||
+		LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
 | 
			
		||||
+		again_min_ = 100;
 | 
			
		||||
+	}
 | 
			
		||||
+	again_max_ = gain_info.max().get<int>();
 | 
			
		||||
+
 | 
			
		||||
+	LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
 | 
			
		||||
+			   << ", gain " << again_min_ << "-" << again_max_;
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+int IPASoftSimple::platformStart()
 | 
			
		||||
+{
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void IPASoftSimple::platformStop()
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void IPASoftSimple::platformProcessStats(const ControlList &sensorControls)
 | 
			
		||||
+{
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * Calculate red and blue gains for AWB.
 | 
			
		||||
+	 * Clamp max gain at 4.0, this also avoids 0 division.
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (stats_->sumR_ <= stats_->sumG_ / 4)
 | 
			
		||||
+		params_->gainR = 1024;
 | 
			
		||||
+	else
 | 
			
		||||
+		params_->gainR = 256 * stats_->sumG_ / stats_->sumR_;
 | 
			
		||||
+
 | 
			
		||||
+	if (stats_->sumB_ <= stats_->sumG_ / 4)
 | 
			
		||||
+		params_->gainB = 1024;
 | 
			
		||||
+	else
 | 
			
		||||
+		params_->gainB = 256 * stats_->sumG_ / stats_->sumB_;
 | 
			
		||||
+
 | 
			
		||||
+	/* Green gain and gamma values are fixed */
 | 
			
		||||
+	params_->gainG = 256;
 | 
			
		||||
+	params_->gamma = 0.5;
 | 
			
		||||
+
 | 
			
		||||
+	setIspParams.emit(0);
 | 
			
		||||
+
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * AE / AGC, use 2 frames delay to make sure that the exposure and
 | 
			
		||||
+	 * the gain set have applied to the camera sensor.
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (ignore_updates_ > 0) {
 | 
			
		||||
+		--ignore_updates_;
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	unsigned int denom = 0;
 | 
			
		||||
+	unsigned int num = 0;
 | 
			
		||||
+	unsigned int y_histogramSmall[5] = {};
 | 
			
		||||
+
 | 
			
		||||
+	for (int i = 0; i < 16; i++)
 | 
			
		||||
+		y_histogramSmall[(i - i / 8) / 3] += stats_->y_histogram[i];
 | 
			
		||||
+
 | 
			
		||||
+	for (int i = 0; i < 5; i++) {
 | 
			
		||||
+		LOG(IPASoft, Debug) << i << ": " << y_histogramSmall[i];
 | 
			
		||||
+		denom += y_histogramSmall[i];
 | 
			
		||||
+		num += y_histogramSmall[i] * (i + 1);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	float exposuremsv = (float)num / denom;
 | 
			
		||||
+
 | 
			
		||||
+	/* sanity check */
 | 
			
		||||
+	if (!sensorControls.contains(V4L2_CID_EXPOSURE) ||
 | 
			
		||||
+	    !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) {
 | 
			
		||||
+		LOG(IPASoft, Error) << "Control(s) missing";
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	ControlList ctrls(sensorControls);
 | 
			
		||||
+
 | 
			
		||||
+	exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int>();
 | 
			
		||||
+	again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int>();
 | 
			
		||||
+
 | 
			
		||||
+	update_exposure(exposuremsv);
 | 
			
		||||
+
 | 
			
		||||
+	ctrls.set(V4L2_CID_EXPOSURE, exposure_);
 | 
			
		||||
+	ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
 | 
			
		||||
+
 | 
			
		||||
+	ignore_updates_ = 2;
 | 
			
		||||
+
 | 
			
		||||
+	setSensorControls.emit(ctrls);
 | 
			
		||||
+
 | 
			
		||||
+	LOG(IPASoft, Debug) << "exposuremsv " << exposuremsv
 | 
			
		||||
+			    << " exp " << exposure_ << " again " << again_
 | 
			
		||||
+			    << " gain R/B " << params_->gainR << "/" << params_->gainB;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/* DENOMINATOR of 10 gives ~10% increment/decrement; DENOMINATOR of 5 - about ~20% */
 | 
			
		||||
+#define DENOMINATOR 10
 | 
			
		||||
+#define UP_NUMERATOR (DENOMINATOR + 1)
 | 
			
		||||
+#define DOWN_NUMERATOR (DENOMINATOR - 1)
 | 
			
		||||
+
 | 
			
		||||
+void IPASoftSimple::update_exposure(double exposuremsv)
 | 
			
		||||
+{
 | 
			
		||||
+	int next;
 | 
			
		||||
+
 | 
			
		||||
+	if (exposuremsv < EXPOSURE_OPTIMAL_VALUE - EXPOSURE_SATISFACTORY_OFFSET) {
 | 
			
		||||
+		next = exposure_ * UP_NUMERATOR / DENOMINATOR;
 | 
			
		||||
+		if (next - exposure_ < 1)
 | 
			
		||||
+			exposure_ += 1;
 | 
			
		||||
+		else
 | 
			
		||||
+			exposure_ = next;
 | 
			
		||||
+		if (exposure_ >= exposure_max_) {
 | 
			
		||||
+			next = again_ * UP_NUMERATOR / DENOMINATOR;
 | 
			
		||||
+			if (next - again_ < 1)
 | 
			
		||||
+				again_ += 1;
 | 
			
		||||
+			else
 | 
			
		||||
+				again_ = next;
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (exposuremsv > EXPOSURE_OPTIMAL_VALUE + EXPOSURE_SATISFACTORY_OFFSET) {
 | 
			
		||||
+		if (exposure_ == exposure_max_ && again_ != again_min_) {
 | 
			
		||||
+			next = again_ * DOWN_NUMERATOR / DENOMINATOR;
 | 
			
		||||
+			if (again_ - next < 1)
 | 
			
		||||
+				again_ -= 1;
 | 
			
		||||
+			else
 | 
			
		||||
+				again_ = next;
 | 
			
		||||
+		} else {
 | 
			
		||||
+			next = exposure_ * DOWN_NUMERATOR / DENOMINATOR;
 | 
			
		||||
+			if (exposure_ - next < 1)
 | 
			
		||||
+				exposure_ -= 1;
 | 
			
		||||
+			else
 | 
			
		||||
+				exposure_ = next;
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (exposure_ > exposure_max_)
 | 
			
		||||
+		exposure_ = exposure_max_;
 | 
			
		||||
+	else if (exposure_ < exposure_min_)
 | 
			
		||||
+		exposure_ = exposure_min_;
 | 
			
		||||
+
 | 
			
		||||
+	if (again_ > again_max_)
 | 
			
		||||
+		again_ = again_max_;
 | 
			
		||||
+	else if (again_ < again_min_)
 | 
			
		||||
+		again_ = again_min_;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace ipa::soft */
 | 
			
		||||
+
 | 
			
		||||
+/*
 | 
			
		||||
+ * External IPA module interface
 | 
			
		||||
+ */
 | 
			
		||||
+extern "C" {
 | 
			
		||||
+const struct IPAModuleInfo ipaModuleInfo = {
 | 
			
		||||
+	IPA_MODULE_API_VERSION,
 | 
			
		||||
+	0,
 | 
			
		||||
+	"SimplePipelineHandler",
 | 
			
		||||
+	"soft/simple",
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+IPAInterface *ipaCreate()
 | 
			
		||||
+{
 | 
			
		||||
+	return new ipa::soft::IPASoftSimple();
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+} /* extern "C" */
 | 
			
		||||
+
 | 
			
		||||
+} /* namespace libcamera */
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,483 @@
 | 
			
		|||
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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,238 @@
 | 
			
		|||
From 155065b3d78c14173fd71c21fe0639f94c3da109 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Fri, 1 Dec 2023 15:45:14 +0300
 | 
			
		||||
Subject: [PATCH 14/25] libcamera: pipeline: simple: rename converterBuffers_
 | 
			
		||||
 and related vars
 | 
			
		||||
 | 
			
		||||
The converterBuffers_ and the converterQueue_ are not that specific
 | 
			
		||||
to the Converter, and could be used by another entity doing the format
 | 
			
		||||
conversion.
 | 
			
		||||
 | 
			
		||||
Rename converterBuffers_, converterQueue_, and useConverter_ to
 | 
			
		||||
conversionBuffers_, conversionQueue_ and useConversion_ to
 | 
			
		||||
disassociate them from the Converter.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 src/libcamera/pipeline/simple/simple.cpp | 63 ++++++++++++------------
 | 
			
		||||
 1 file changed, 32 insertions(+), 31 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
index 4d0e7255..fa298746 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
@@ -269,17 +269,18 @@ public:
 | 
			
		||||
 	std::vector<Configuration> configs_;
 | 
			
		||||
 	std::map<PixelFormat, std::vector<const Configuration *>> formats_;
 | 
			
		||||
 
 | 
			
		||||
+	std::vector<std::unique_ptr<FrameBuffer>> conversionBuffers_;
 | 
			
		||||
+	std::queue<std::map<unsigned int, FrameBuffer *>> conversionQueue_;
 | 
			
		||||
+	bool useConversion_;
 | 
			
		||||
+
 | 
			
		||||
 	std::unique_ptr<Converter> converter_;
 | 
			
		||||
-	std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;
 | 
			
		||||
-	bool useConverter_;
 | 
			
		||||
-	std::queue<std::map<unsigned int, FrameBuffer *>> converterQueue_;
 | 
			
		||||
 
 | 
			
		||||
 private:
 | 
			
		||||
 	void tryPipeline(unsigned int code, const Size &size);
 | 
			
		||||
 	static std::vector<const MediaPad *> routedSourcePads(MediaPad *sink);
 | 
			
		||||
 
 | 
			
		||||
-	void converterInputDone(FrameBuffer *buffer);
 | 
			
		||||
-	void converterOutputDone(FrameBuffer *buffer);
 | 
			
		||||
+	void conversionInputDone(FrameBuffer *buffer);
 | 
			
		||||
+	void conversionOutputDone(FrameBuffer *buffer);
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 class SimpleCameraConfiguration : public CameraConfiguration
 | 
			
		||||
@@ -503,8 +504,8 @@ int SimpleCameraData::init()
 | 
			
		||||
 				<< "Failed to create converter, disabling format conversion";
 | 
			
		||||
 			converter_.reset();
 | 
			
		||||
 		} else {
 | 
			
		||||
-			converter_->inputBufferReady.connect(this, &SimpleCameraData::converterInputDone);
 | 
			
		||||
-			converter_->outputBufferReady.connect(this, &SimpleCameraData::converterOutputDone);
 | 
			
		||||
+			converter_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone);
 | 
			
		||||
+			converter_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
 | 
			
		||||
 		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -740,7 +741,7 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
 | 
			
		||||
 	 * point converting an erroneous buffer.
 | 
			
		||||
 	 */
 | 
			
		||||
 	if (buffer->metadata().status != FrameMetadata::FrameSuccess) {
 | 
			
		||||
-		if (!useConverter_) {
 | 
			
		||||
+		if (!useConversion_) {
 | 
			
		||||
 			/* No conversion, just complete the request. */
 | 
			
		||||
 			Request *request = buffer->request();
 | 
			
		||||
 			pipe->completeBuffer(request, buffer);
 | 
			
		||||
@@ -756,16 +757,16 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
 | 
			
		||||
 		if (buffer->metadata().status != FrameMetadata::FrameCancelled)
 | 
			
		||||
 			video_->queueBuffer(buffer);
 | 
			
		||||
 
 | 
			
		||||
-		if (converterQueue_.empty())
 | 
			
		||||
+		if (conversionQueue_.empty())
 | 
			
		||||
 			return;
 | 
			
		||||
 
 | 
			
		||||
 		Request *request = nullptr;
 | 
			
		||||
-		for (auto &item : converterQueue_.front()) {
 | 
			
		||||
+		for (auto &item : conversionQueue_.front()) {
 | 
			
		||||
 			FrameBuffer *outputBuffer = item.second;
 | 
			
		||||
 			request = outputBuffer->request();
 | 
			
		||||
 			pipe->completeBuffer(request, outputBuffer);
 | 
			
		||||
 		}
 | 
			
		||||
-		converterQueue_.pop();
 | 
			
		||||
+		conversionQueue_.pop();
 | 
			
		||||
 
 | 
			
		||||
 		if (request)
 | 
			
		||||
 			pipe->completeRequest(request);
 | 
			
		||||
@@ -782,9 +783,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
 | 
			
		||||
 	 */
 | 
			
		||||
 	Request *request = buffer->request();
 | 
			
		||||
 
 | 
			
		||||
-	if (useConverter_ && !converterQueue_.empty()) {
 | 
			
		||||
+	if (useConversion_ && !conversionQueue_.empty()) {
 | 
			
		||||
 		const std::map<unsigned int, FrameBuffer *> &outputs =
 | 
			
		||||
-			converterQueue_.front();
 | 
			
		||||
+			conversionQueue_.front();
 | 
			
		||||
 		if (!outputs.empty()) {
 | 
			
		||||
 			FrameBuffer *outputBuffer = outputs.begin()->second;
 | 
			
		||||
 			if (outputBuffer)
 | 
			
		||||
@@ -801,14 +802,14 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
 | 
			
		||||
 	 * conversion is needed. If there's no queued request, just requeue the
 | 
			
		||||
 	 * captured buffer for capture.
 | 
			
		||||
 	 */
 | 
			
		||||
-	if (useConverter_) {
 | 
			
		||||
-		if (converterQueue_.empty()) {
 | 
			
		||||
+	if (useConversion_) {
 | 
			
		||||
+		if (conversionQueue_.empty()) {
 | 
			
		||||
 			video_->queueBuffer(buffer);
 | 
			
		||||
 			return;
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
-		converter_->queueBuffers(buffer, converterQueue_.front());
 | 
			
		||||
-		converterQueue_.pop();
 | 
			
		||||
+		converter_->queueBuffers(buffer, conversionQueue_.front());
 | 
			
		||||
+		conversionQueue_.pop();
 | 
			
		||||
 		return;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -817,13 +818,13 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
 | 
			
		||||
 	pipe->completeRequest(request);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-void SimpleCameraData::converterInputDone(FrameBuffer *buffer)
 | 
			
		||||
+void SimpleCameraData::conversionInputDone(FrameBuffer *buffer)
 | 
			
		||||
 {
 | 
			
		||||
 	/* Queue the input buffer back for capture. */
 | 
			
		||||
 	video_->queueBuffer(buffer);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-void SimpleCameraData::converterOutputDone(FrameBuffer *buffer)
 | 
			
		||||
+void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
 | 
			
		||||
 {
 | 
			
		||||
 	SimplePipelineHandler *pipe = SimpleCameraData::pipe();
 | 
			
		||||
 
 | 
			
		||||
@@ -1159,14 +1160,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
 | 
			
		||||
 
 | 
			
		||||
 	/* Configure the converter if needed. */
 | 
			
		||||
 	std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
 | 
			
		||||
-	data->useConverter_ = config->needConversion();
 | 
			
		||||
+	data->useConversion_ = config->needConversion();
 | 
			
		||||
 
 | 
			
		||||
 	for (unsigned int i = 0; i < config->size(); ++i) {
 | 
			
		||||
 		StreamConfiguration &cfg = config->at(i);
 | 
			
		||||
 
 | 
			
		||||
 		cfg.setStream(&data->streams_[i]);
 | 
			
		||||
 
 | 
			
		||||
-		if (data->useConverter_)
 | 
			
		||||
+		if (data->useConversion_)
 | 
			
		||||
 			outputCfgs.push_back(cfg);
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1192,7 +1193,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
 | 
			
		||||
 	 * Export buffers on the converter or capture video node, depending on
 | 
			
		||||
 	 * whether the converter is used or not.
 | 
			
		||||
 	 */
 | 
			
		||||
-	if (data->useConverter_)
 | 
			
		||||
+	if (data->useConversion_)
 | 
			
		||||
 		return data->converter_->exportBuffers(data->streamIndex(stream),
 | 
			
		||||
 						       count, buffers);
 | 
			
		||||
 	else
 | 
			
		||||
@@ -1213,13 +1214,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 | 
			
		||||
 		return -EBUSY;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	if (data->useConverter_) {
 | 
			
		||||
+	if (data->useConversion_) {
 | 
			
		||||
 		/*
 | 
			
		||||
 		 * When using the converter allocate a fixed number of internal
 | 
			
		||||
 		 * buffers.
 | 
			
		||||
 		 */
 | 
			
		||||
 		ret = video->allocateBuffers(kNumInternalBuffers,
 | 
			
		||||
-					     &data->converterBuffers_);
 | 
			
		||||
+					     &data->conversionBuffers_);
 | 
			
		||||
 	} else {
 | 
			
		||||
 		/* Otherwise, prepare for using buffers from the only stream. */
 | 
			
		||||
 		Stream *stream = &data->streams_[0];
 | 
			
		||||
@@ -1238,7 +1239,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 | 
			
		||||
 		return ret;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	if (data->useConverter_) {
 | 
			
		||||
+	if (data->useConversion_) {
 | 
			
		||||
 		ret = data->converter_->start();
 | 
			
		||||
 		if (ret < 0) {
 | 
			
		||||
 			stop(camera);
 | 
			
		||||
@@ -1246,7 +1247,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
 		/* Queue all internal buffers for capture. */
 | 
			
		||||
-		for (std::unique_ptr<FrameBuffer> &buffer : data->converterBuffers_)
 | 
			
		||||
+		for (std::unique_ptr<FrameBuffer> &buffer : data->conversionBuffers_)
 | 
			
		||||
 			video->queueBuffer(buffer.get());
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1258,7 +1259,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
 | 
			
		||||
 	SimpleCameraData *data = cameraData(camera);
 | 
			
		||||
 	V4L2VideoDevice *video = data->video_;
 | 
			
		||||
 
 | 
			
		||||
-	if (data->useConverter_)
 | 
			
		||||
+	if (data->useConversion_)
 | 
			
		||||
 		data->converter_->stop();
 | 
			
		||||
 
 | 
			
		||||
 	video->streamOff();
 | 
			
		||||
@@ -1266,7 +1267,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
 | 
			
		||||
 
 | 
			
		||||
 	video->bufferReady.disconnect(data, &SimpleCameraData::bufferReady);
 | 
			
		||||
 
 | 
			
		||||
-	data->converterBuffers_.clear();
 | 
			
		||||
+	data->conversionBuffers_.clear();
 | 
			
		||||
 
 | 
			
		||||
 	releasePipeline(data);
 | 
			
		||||
 }
 | 
			
		||||
@@ -1284,7 +1285,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
 | 
			
		||||
 		 * queue, it will be handed to the converter in the capture
 | 
			
		||||
 		 * completion handler.
 | 
			
		||||
 		 */
 | 
			
		||||
-		if (data->useConverter_) {
 | 
			
		||||
+		if (data->useConversion_) {
 | 
			
		||||
 			buffers.emplace(data->streamIndex(stream), buffer);
 | 
			
		||||
 		} else {
 | 
			
		||||
 			ret = data->video_->queueBuffer(buffer);
 | 
			
		||||
@@ -1293,8 +1294,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
 | 
			
		||||
 		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	if (data->useConverter_)
 | 
			
		||||
-		data->converterQueue_.push(std::move(buffers));
 | 
			
		||||
+	if (data->useConversion_)
 | 
			
		||||
+		data->conversionQueue_.push(std::move(buffers));
 | 
			
		||||
 
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,243 @@
 | 
			
		|||
From ad61052d9aea1f1af6aaef9ce7e5d447c595187c Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
Date: Tue, 12 Dec 2023 02:02:37 +0300
 | 
			
		||||
Subject: [PATCH 15/25] libcamera: pipeline: simple: enable use of Soft ISP and
 | 
			
		||||
 Soft IPA
 | 
			
		||||
 | 
			
		||||
To enable the Simple Soft ISP and Soft IPA for simple pipeline handler
 | 
			
		||||
configure the build with:
 | 
			
		||||
  -Dpipelines=simple/simple -Dipas=simple/simple
 | 
			
		||||
 | 
			
		||||
If the pipeline uses Converter, Soft ISP and Soft IPA aren't
 | 
			
		||||
available.
 | 
			
		||||
 | 
			
		||||
Configuring the build with just:
 | 
			
		||||
  -Dpipelines=simple
 | 
			
		||||
makes it to work like before - Soft ISP and Soft IPA aren't used.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
 | 
			
		||||
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>
 | 
			
		||||
---
 | 
			
		||||
 src/libcamera/pipeline/simple/simple.cpp | 111 ++++++++++++++++++-----
 | 
			
		||||
 1 file changed, 89 insertions(+), 22 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
index fa298746..c76510c2 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
@@ -34,6 +34,7 @@
 | 
			
		||||
 #include "libcamera/internal/device_enumerator.h"
 | 
			
		||||
 #include "libcamera/internal/media_device.h"
 | 
			
		||||
 #include "libcamera/internal/pipeline_handler.h"
 | 
			
		||||
+#include "libcamera/internal/software_isp.h"
 | 
			
		||||
 #include "libcamera/internal/v4l2_subdevice.h"
 | 
			
		||||
 #include "libcamera/internal/v4l2_videodevice.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -274,6 +275,7 @@ public:
 | 
			
		||||
 	bool useConversion_;
 | 
			
		||||
 
 | 
			
		||||
 	std::unique_ptr<Converter> converter_;
 | 
			
		||||
+	std::unique_ptr<SoftwareIsp> swIsp_;
 | 
			
		||||
 
 | 
			
		||||
 private:
 | 
			
		||||
 	void tryPipeline(unsigned int code, const Size &size);
 | 
			
		||||
@@ -281,6 +283,9 @@ private:
 | 
			
		||||
 
 | 
			
		||||
 	void conversionInputDone(FrameBuffer *buffer);
 | 
			
		||||
 	void conversionOutputDone(FrameBuffer *buffer);
 | 
			
		||||
+
 | 
			
		||||
+	void ispStatsReady(int dummy);
 | 
			
		||||
+	void setSensorControls(const ControlList &sensorControls);
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 class SimpleCameraConfiguration : public CameraConfiguration
 | 
			
		||||
@@ -509,6 +514,25 @@ int SimpleCameraData::init()
 | 
			
		||||
 		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	/*
 | 
			
		||||
+	 * Create SoftwareIsp unconditionally if no converter is used
 | 
			
		||||
+	 * - to be revisited
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (!converter_) {
 | 
			
		||||
+		swIsp_ = SoftwareIspFactoryBase::create(pipe, sensor_->controls());
 | 
			
		||||
+		if (!swIsp_) {
 | 
			
		||||
+			LOG(SimplePipeline, Warning)
 | 
			
		||||
+				<< "Failed to create software ISP, disabling software debayering";
 | 
			
		||||
+			swIsp_.reset();
 | 
			
		||||
+		} else {
 | 
			
		||||
+			swIsp_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone);
 | 
			
		||||
+			swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
 | 
			
		||||
+			swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady);
 | 
			
		||||
+
 | 
			
		||||
+			swIsp_->getSignalSetSensorControls().connect(this, &SimpleCameraData::setSensorControls);
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	video_ = pipe->video(entities_.back().entity);
 | 
			
		||||
 	ASSERT(video_);
 | 
			
		||||
 
 | 
			
		||||
@@ -599,12 +623,20 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
 | 
			
		||||
 		config.captureFormat = pixelFormat;
 | 
			
		||||
 		config.captureSize = format.size;
 | 
			
		||||
 
 | 
			
		||||
-		if (!converter_) {
 | 
			
		||||
-			config.outputFormats = { pixelFormat };
 | 
			
		||||
-			config.outputSizes = config.captureSize;
 | 
			
		||||
-		} else {
 | 
			
		||||
+		if (converter_) {
 | 
			
		||||
 			config.outputFormats = converter_->formats(pixelFormat);
 | 
			
		||||
 			config.outputSizes = converter_->sizes(format.size);
 | 
			
		||||
+		} else if (swIsp_) {
 | 
			
		||||
+			config.outputFormats = swIsp_->formats(pixelFormat);
 | 
			
		||||
+			config.outputSizes = swIsp_->sizes(pixelFormat, format.size);
 | 
			
		||||
+			if (config.outputFormats.empty()) {
 | 
			
		||||
+				/* Do not use swIsp for unsupported pixelFormat's */
 | 
			
		||||
+				config.outputFormats = { pixelFormat };
 | 
			
		||||
+				config.outputSizes = config.captureSize;
 | 
			
		||||
+			}
 | 
			
		||||
+		} else {
 | 
			
		||||
+			config.outputFormats = { pixelFormat };
 | 
			
		||||
+			config.outputSizes = config.captureSize;
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
 		configs_.push_back(config);
 | 
			
		||||
@@ -750,9 +782,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
 		/*
 | 
			
		||||
-		 * The converter is in use. Requeue the internal buffer for
 | 
			
		||||
-		 * capture (unless the stream is being stopped), and complete
 | 
			
		||||
-		 * the request with all the user-facing buffers.
 | 
			
		||||
+		 * The converter or Software ISP is in use. Requeue the internal
 | 
			
		||||
+		 * buffer for capture (unless the stream is being stopped), and
 | 
			
		||||
+		 * complete the request with all the user-facing buffers.
 | 
			
		||||
 		 */
 | 
			
		||||
 		if (buffer->metadata().status != FrameMetadata::FrameCancelled)
 | 
			
		||||
 			video_->queueBuffer(buffer);
 | 
			
		||||
@@ -798,9 +830,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
 | 
			
		||||
 					buffer->metadata().timestamp);
 | 
			
		||||
 
 | 
			
		||||
 	/*
 | 
			
		||||
-	 * Queue the captured and the request buffer to the converter if format
 | 
			
		||||
-	 * conversion is needed. If there's no queued request, just requeue the
 | 
			
		||||
-	 * captured buffer for capture.
 | 
			
		||||
+	 * Queue the captured and the request buffer to the converter or Software
 | 
			
		||||
+	 * ISP if format conversion is needed. If there's no queued request, just
 | 
			
		||||
+	 * requeue the captured buffer for capture.
 | 
			
		||||
 	 */
 | 
			
		||||
 	if (useConversion_) {
 | 
			
		||||
 		if (conversionQueue_.empty()) {
 | 
			
		||||
@@ -808,7 +840,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
 | 
			
		||||
 			return;
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
-		converter_->queueBuffers(buffer, conversionQueue_.front());
 | 
			
		||||
+		if (converter_)
 | 
			
		||||
+			converter_->queueBuffers(buffer, conversionQueue_.front());
 | 
			
		||||
+		else
 | 
			
		||||
+			swIsp_->queueBuffers(buffer, conversionQueue_.front());
 | 
			
		||||
+
 | 
			
		||||
 		conversionQueue_.pop();
 | 
			
		||||
 		return;
 | 
			
		||||
 	}
 | 
			
		||||
@@ -834,6 +870,18 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
 | 
			
		||||
 		pipe->completeRequest(request);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+void SimpleCameraData::ispStatsReady([[maybe_unused]] int dummy)
 | 
			
		||||
+{
 | 
			
		||||
+	swIsp_->processStats(sensor_->getControls({ V4L2_CID_ANALOGUE_GAIN,
 | 
			
		||||
+						    V4L2_CID_EXPOSURE }));
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
 | 
			
		||||
+{
 | 
			
		||||
+	ControlList ctrls(sensorControls);
 | 
			
		||||
+	sensor_->setControls(&ctrls);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 /* Retrieve all source pads connected to a sink pad through active routes. */
 | 
			
		||||
 std::vector<const MediaPad *> SimpleCameraData::routedSourcePads(MediaPad *sink)
 | 
			
		||||
 {
 | 
			
		||||
@@ -1016,8 +1064,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
 | 
			
		||||
 		/* Set the stride, frameSize and bufferCount. */
 | 
			
		||||
 		if (needConversion_) {
 | 
			
		||||
 			std::tie(cfg.stride, cfg.frameSize) =
 | 
			
		||||
-				data_->converter_->strideAndFrameSize(cfg.pixelFormat,
 | 
			
		||||
-								      cfg.size);
 | 
			
		||||
+				(data_->converter_) ? data_->converter_->strideAndFrameSize(cfg.pixelFormat,
 | 
			
		||||
+											    cfg.size)
 | 
			
		||||
+						    : data_->swIsp_->strideAndFrameSize(cfg.pixelFormat,
 | 
			
		||||
+											cfg.size);
 | 
			
		||||
 			if (cfg.stride == 0)
 | 
			
		||||
 				return Invalid;
 | 
			
		||||
 		} else {
 | 
			
		||||
@@ -1180,7 +1230,9 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
 | 
			
		||||
 	inputCfg.stride = captureFormat.planes[0].bpl;
 | 
			
		||||
 	inputCfg.bufferCount = kNumInternalBuffers;
 | 
			
		||||
 
 | 
			
		||||
-	return data->converter_->configure(inputCfg, outputCfgs);
 | 
			
		||||
+	return (data->converter_) ? data->converter_->configure(inputCfg, outputCfgs)
 | 
			
		||||
+				  : data->swIsp_->configure(inputCfg, outputCfgs,
 | 
			
		||||
+							    data->sensor_->controls());
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
 | 
			
		||||
@@ -1194,8 +1246,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
 | 
			
		||||
 	 * whether the converter is used or not.
 | 
			
		||||
 	 */
 | 
			
		||||
 	if (data->useConversion_)
 | 
			
		||||
-		return data->converter_->exportBuffers(data->streamIndex(stream),
 | 
			
		||||
-						       count, buffers);
 | 
			
		||||
+		return (data->converter_) ? data->converter_->exportBuffers(data->streamIndex(stream),
 | 
			
		||||
+									    count, buffers)
 | 
			
		||||
+					  : data->swIsp_->exportBuffers(data->streamIndex(stream),
 | 
			
		||||
+									count, buffers);
 | 
			
		||||
 	else
 | 
			
		||||
 		return data->video_->exportBuffers(count, buffers);
 | 
			
		||||
 }
 | 
			
		||||
@@ -1240,10 +1294,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	if (data->useConversion_) {
 | 
			
		||||
-		ret = data->converter_->start();
 | 
			
		||||
-		if (ret < 0) {
 | 
			
		||||
-			stop(camera);
 | 
			
		||||
-			return ret;
 | 
			
		||||
+		if (data->converter_) {
 | 
			
		||||
+			ret = data->converter_->start();
 | 
			
		||||
+			if (ret < 0) {
 | 
			
		||||
+				stop(camera);
 | 
			
		||||
+				return ret;
 | 
			
		||||
+			}
 | 
			
		||||
+		} else if (data->swIsp_) {
 | 
			
		||||
+			ret = data->swIsp_->start();
 | 
			
		||||
+			if (ret < 0) {
 | 
			
		||||
+				stop(camera);
 | 
			
		||||
+				return ret;
 | 
			
		||||
+			}
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
 		/* Queue all internal buffers for capture. */
 | 
			
		||||
@@ -1259,8 +1321,13 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
 | 
			
		||||
 	SimpleCameraData *data = cameraData(camera);
 | 
			
		||||
 	V4L2VideoDevice *video = data->video_;
 | 
			
		||||
 
 | 
			
		||||
-	if (data->useConversion_)
 | 
			
		||||
-		data->converter_->stop();
 | 
			
		||||
+	if (data->useConversion_) {
 | 
			
		||||
+		if (data->converter_)
 | 
			
		||||
+			data->converter_->stop();
 | 
			
		||||
+		else if (data->swIsp_) {
 | 
			
		||||
+			data->swIsp_->stop();
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
 
 | 
			
		||||
 	video->streamOff();
 | 
			
		||||
 	video->releaseBuffers();
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,199 @@
 | 
			
		|||
From 66ef9f32e67a96655d10ba38f7a830b3bbfe50f2 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Thu, 14 Dec 2023 19:09:44 +0100
 | 
			
		||||
Subject: [PATCH 16/25] libcamera: swstats_cpu: Add support for 8, 10 and 12
 | 
			
		||||
 bpp unpacked bayer input
 | 
			
		||||
 | 
			
		||||
Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard
 | 
			
		||||
bayer orders.
 | 
			
		||||
 | 
			
		||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
 | 
			
		||||
Tested-by: Pavel Machek <pavel@ucw.cz>
 | 
			
		||||
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 .../internal/software_isp/swstats_cpu.h       |   9 ++
 | 
			
		||||
 src/libcamera/software_isp/swstats_cpu.cpp    | 126 ++++++++++++++++++
 | 
			
		||||
 2 files changed, 135 insertions(+)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
index 8bb86e98..e7abc6bb 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
 
 | 
			
		||||
 #pragma once
 | 
			
		||||
 
 | 
			
		||||
+#include "libcamera/internal/bayer_format.h"
 | 
			
		||||
 #include "libcamera/internal/shared_mem_object.h"
 | 
			
		||||
 #include "libcamera/internal/software_isp/swisp_stats.h"
 | 
			
		||||
 #include "libcamera/internal/software_isp/swstats.h"
 | 
			
		||||
@@ -31,6 +32,14 @@ public:
 | 
			
		||||
 	const SharedFD &getStatsFD() { return sharedStats_.fd(); }
 | 
			
		||||
 	int configure(const StreamConfiguration &inputCfg);
 | 
			
		||||
 private:
 | 
			
		||||
+	int setupStandardBayerOrder(BayerFormat::Order order);
 | 
			
		||||
+	/* Bayer 8 bpp unpacked */
 | 
			
		||||
+	void statsBGGR8Line0(const uint8_t *src[]);
 | 
			
		||||
+	/* Bayer 10 bpp unpacked */
 | 
			
		||||
+	void statsBGGR10Line0(const uint8_t *src[]);
 | 
			
		||||
+	/* Bayer 12 bpp unpacked */
 | 
			
		||||
+	void statsBGGR12Line0(const uint8_t *src[]);
 | 
			
		||||
+	/* Bayer 10 bpp packed */
 | 
			
		||||
 	void statsBGGR10PLine0(const uint8_t *src[]);
 | 
			
		||||
 	void statsGBRG10PLine0(const uint8_t *src[]);
 | 
			
		||||
 	void resetStats(void);
 | 
			
		||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
index 59453d07..87550371 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
@@ -59,6 +59,83 @@ static const unsigned int BLUE_Y_MUL = 29; /* 0.11 * 256 */
 | 
			
		||||
 	stats_.sumG_ += sumG;            \
 | 
			
		||||
 	stats_.sumB_ += sumB;
 | 
			
		||||
 
 | 
			
		||||
+void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint8_t *src0 = src[1] + window_.x;
 | 
			
		||||
+	const uint8_t *src1 = src[2] + window_.x;
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_START_LINE_STATS(uint8_t)
 | 
			
		||||
+
 | 
			
		||||
+	if (swap_lines_)
 | 
			
		||||
+		std::swap(src0, src1);
 | 
			
		||||
+
 | 
			
		||||
+	/* x += 4 sample every other 2x2 block */
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width; x += 4) {
 | 
			
		||||
+		b = src0[x];
 | 
			
		||||
+		g = src0[x + 1];
 | 
			
		||||
+		g2 = src1[x];
 | 
			
		||||
+		r = src1[x + 1];
 | 
			
		||||
+
 | 
			
		||||
+		g = (g + g2) / 2;
 | 
			
		||||
+
 | 
			
		||||
+		SWISP_LINARO_ACCUMULATE_LINE_STATS(1)
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_FINISH_LINE_STATS()
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SwStatsCpu::statsBGGR10Line0(const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
 | 
			
		||||
+	const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_START_LINE_STATS(uint16_t)
 | 
			
		||||
+
 | 
			
		||||
+	if (swap_lines_)
 | 
			
		||||
+		std::swap(src0, src1);
 | 
			
		||||
+
 | 
			
		||||
+	/* x += 4 sample every other 2x2 block */
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width; x += 4) {
 | 
			
		||||
+		b = src0[x];
 | 
			
		||||
+		g = src0[x + 1];
 | 
			
		||||
+		g2 = src1[x];
 | 
			
		||||
+		r = src1[x + 1];
 | 
			
		||||
+
 | 
			
		||||
+		g = (g + g2) / 2;
 | 
			
		||||
+
 | 
			
		||||
+		/* divide Y by 4 for 10 -> 8 bpp value */
 | 
			
		||||
+		SWISP_LINARO_ACCUMULATE_LINE_STATS(4)
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_FINISH_LINE_STATS()
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SwStatsCpu::statsBGGR12Line0(const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
 | 
			
		||||
+	const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_START_LINE_STATS(uint16_t)
 | 
			
		||||
+
 | 
			
		||||
+	if (swap_lines_)
 | 
			
		||||
+		std::swap(src0, src1);
 | 
			
		||||
+
 | 
			
		||||
+	/* x += 4 sample every other 2x2 block */
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width; x += 4) {
 | 
			
		||||
+		b = src0[x];
 | 
			
		||||
+		g = src0[x + 1];
 | 
			
		||||
+		g2 = src1[x];
 | 
			
		||||
+		r = src1[x + 1];
 | 
			
		||||
+
 | 
			
		||||
+		g = (g + g2) / 2;
 | 
			
		||||
+
 | 
			
		||||
+		/* divide Y by 16 for 12 -> 8 bpp value */
 | 
			
		||||
+		SWISP_LINARO_ACCUMULATE_LINE_STATS(16)
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_FINISH_LINE_STATS()
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 static inline __attribute__((always_inline)) void
 | 
			
		||||
 statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bggr, SwIspStats &stats_)
 | 
			
		||||
 {
 | 
			
		||||
@@ -124,6 +201,39 @@ void SwStatsCpu::finishStats(void)
 | 
			
		||||
 	statsReady.emit(0);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+/*
 | 
			
		||||
+ * Check if order is a standard Bayer order and setup x_shift_ and swap_lines_
 | 
			
		||||
+ * so that a single BGGR stats function can be used for all 4 standard orders.
 | 
			
		||||
+ */
 | 
			
		||||
+int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order)
 | 
			
		||||
+{
 | 
			
		||||
+	switch (order) {
 | 
			
		||||
+	case BayerFormat::BGGR:
 | 
			
		||||
+		x_shift_ = 0;
 | 
			
		||||
+		swap_lines_ = false;
 | 
			
		||||
+		break;
 | 
			
		||||
+	case BayerFormat::GBRG:
 | 
			
		||||
+		x_shift_ = 1; /* BGGR -> GBRG */
 | 
			
		||||
+		swap_lines_ = false;
 | 
			
		||||
+		break;
 | 
			
		||||
+	case BayerFormat::GRBG:
 | 
			
		||||
+		x_shift_ = 0;
 | 
			
		||||
+		swap_lines_ = true; /* BGGR -> GRBG */
 | 
			
		||||
+		break;
 | 
			
		||||
+	case BayerFormat::RGGB:
 | 
			
		||||
+		x_shift_ = 1; /* BGGR -> GBRG */
 | 
			
		||||
+		swap_lines_ = true; /* GBRG -> RGGB */
 | 
			
		||||
+		break;
 | 
			
		||||
+	default:
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	patternSize_.height = 2;
 | 
			
		||||
+	patternSize_.width = 2;
 | 
			
		||||
+	y_skip_mask_ = 0x02; /* Skip every 3th and 4th line */
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
 | 
			
		||||
 {
 | 
			
		||||
 	BayerFormat bayerFormat =
 | 
			
		||||
@@ -132,6 +242,22 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
 | 
			
		||||
 	startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats;
 | 
			
		||||
 	finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats;
 | 
			
		||||
 
 | 
			
		||||
+	if (bayerFormat.packing == BayerFormat::Packing::None &&
 | 
			
		||||
+	    setupStandardBayerOrder(bayerFormat.order) == 0) {
 | 
			
		||||
+		bpp_ = (bayerFormat.bitDepth + 7) & ~7;
 | 
			
		||||
+		switch (bayerFormat.bitDepth) {
 | 
			
		||||
+		case 8:
 | 
			
		||||
+			stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR8Line0;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		case 10:
 | 
			
		||||
+			stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR10Line0;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		case 12:
 | 
			
		||||
+			stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR12Line0;
 | 
			
		||||
+			return 0;
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	if (bayerFormat.bitDepth == 10 &&
 | 
			
		||||
 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
 | 
			
		||||
 		bpp_ = 10;
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,237 @@
 | 
			
		|||
From b7b211eb56d98d5b170bd73a23b55aeb45bde8c5 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Thu, 14 Dec 2023 19:57:15 +0100
 | 
			
		||||
Subject: [PATCH 17/25] libcamera: debayer_cpu: Add support for 8, 10 and 12
 | 
			
		||||
 bpp unpacked bayer input
 | 
			
		||||
 | 
			
		||||
Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard
 | 
			
		||||
bayer orders.
 | 
			
		||||
 | 
			
		||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
 | 
			
		||||
Tested-by: Pavel Machek <pavel@ucw.cz>
 | 
			
		||||
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 .../internal/software_isp/debayer_cpu.h       |  13 ++
 | 
			
		||||
 src/libcamera/software_isp/debayer_cpu.cpp    | 128 ++++++++++++++++++
 | 
			
		||||
 2 files changed, 141 insertions(+)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
index 78573f44..1147b368 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
 
 | 
			
		||||
 #include <libcamera/base/object.h>
 | 
			
		||||
 
 | 
			
		||||
+#include "libcamera/internal/bayer_format.h"
 | 
			
		||||
 #include "libcamera/internal/software_isp/swstats_cpu.h"
 | 
			
		||||
 #include "libcamera/internal/software_isp/debayer.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -75,11 +76,21 @@ public:
 | 
			
		||||
 	 * \return The output frame size.
 | 
			
		||||
 	 */
 | 
			
		||||
 	unsigned int frameSize() { return outputConfig_.frameSize; }
 | 
			
		||||
+
 | 
			
		||||
 private:
 | 
			
		||||
 	void initLinePointers(const uint8_t *linePointers[], const uint8_t *src);
 | 
			
		||||
 	void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
 | 
			
		||||
 	void process2(const uint8_t *src, uint8_t *dst);
 | 
			
		||||
 	void process4(const uint8_t *src, uint8_t *dst);
 | 
			
		||||
+	/* 8-bit raw bayer format */
 | 
			
		||||
+	void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	/* unpacked 10-bit raw bayer format */
 | 
			
		||||
+	void debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	/* unpacked 12-bit raw bayer format */
 | 
			
		||||
+	void debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
 	/* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
 | 
			
		||||
 	void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
 	void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
@@ -103,6 +114,7 @@ private:
 | 
			
		||||
 
 | 
			
		||||
 	int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
 | 
			
		||||
 	int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
 | 
			
		||||
+	int setupStandardBayerOrder(BayerFormat::Order order);
 | 
			
		||||
 	int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
 | 
			
		||||
 
 | 
			
		||||
 	uint8_t gamma_[1024];
 | 
			
		||||
@@ -119,6 +131,7 @@ private:
 | 
			
		||||
 	std::unique_ptr<SwStatsCpu> stats_;
 | 
			
		||||
 	uint8_t *lineBuffers_[5];
 | 
			
		||||
 	unsigned int lineBufferIndex_;
 | 
			
		||||
+	unsigned int x_shift_; /* Offset of 0/1 applied to window_.x */
 | 
			
		||||
 	bool enableInputMemcpy_;
 | 
			
		||||
 	float gamma_correction_;
 | 
			
		||||
 	int measuredFrames_;
 | 
			
		||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
index e0c3c658..7b7623b7 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
@@ -45,6 +45,11 @@ DebayerCpu::~DebayerCpu()
 | 
			
		||||
 		free(lineBuffers_[i]);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+#define DECLARE_SRC_POINTERS(pixel_t)                             \
 | 
			
		||||
+	const pixel_t *prev = (const pixel_t *)src[0] + x_shift_; \
 | 
			
		||||
+	const pixel_t *curr = (const pixel_t *)src[1] + x_shift_; \
 | 
			
		||||
+	const pixel_t *next = (const pixel_t *)src[2] + x_shift_;
 | 
			
		||||
+
 | 
			
		||||
 // RGR
 | 
			
		||||
 // GBG
 | 
			
		||||
 // RGR
 | 
			
		||||
@@ -81,6 +86,70 @@ DebayerCpu::~DebayerCpu()
 | 
			
		||||
 	*dst++ = red_[curr[x] / (div)];                                                        \
 | 
			
		||||
 	x++;
 | 
			
		||||
 
 | 
			
		||||
+void DebayerCpu::debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	DECLARE_SRC_POINTERS(uint8_t)
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		BGGR_BGR888(1, 1, 1)
 | 
			
		||||
+		GBRG_BGR888(1, 1, 1)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	DECLARE_SRC_POINTERS(uint8_t)
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		GRBG_BGR888(1, 1, 1)
 | 
			
		||||
+		RGGB_BGR888(1, 1, 1)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	DECLARE_SRC_POINTERS(uint16_t)
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		/* divide values by 4 for 10 -> 8 bpp value */
 | 
			
		||||
+		BGGR_BGR888(1, 1, 4)
 | 
			
		||||
+		GBRG_BGR888(1, 1, 4)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	DECLARE_SRC_POINTERS(uint16_t)
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		/* divide values by 4 for 10 -> 8 bpp value */
 | 
			
		||||
+		GRBG_BGR888(1, 1, 4)
 | 
			
		||||
+		RGGB_BGR888(1, 1, 4)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	DECLARE_SRC_POINTERS(uint16_t)
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		/* divide values by 16 for 12 -> 8 bpp value */
 | 
			
		||||
+		BGGR_BGR888(1, 1, 16)
 | 
			
		||||
+		GBRG_BGR888(1, 1, 16)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	DECLARE_SRC_POINTERS(uint16_t)
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		/* divide values by 16 for 12 -> 8 bpp value */
 | 
			
		||||
+		GRBG_BGR888(1, 1, 16)
 | 
			
		||||
+		RGGB_BGR888(1, 1, 16)
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
 {
 | 
			
		||||
 	const int width_in_bytes = window_.width * 5 / 4;
 | 
			
		||||
@@ -170,6 +239,16 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 | 
			
		||||
 	BayerFormat bayerFormat =
 | 
			
		||||
 		BayerFormat::fromPixelFormat(inputFormat);
 | 
			
		||||
 
 | 
			
		||||
+	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
 | 
			
		||||
+	    bayerFormat.packing == BayerFormat::Packing::None &&
 | 
			
		||||
+	    isStandardBayerOrder(bayerFormat.order)) {
 | 
			
		||||
+		config.bpp = (bayerFormat.bitDepth + 7) & ~7;
 | 
			
		||||
+		config.patternSize.width = 2;
 | 
			
		||||
+		config.patternSize.height = 2;
 | 
			
		||||
+		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
 | 
			
		||||
+		return 0;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	if (bayerFormat.bitDepth == 10 &&
 | 
			
		||||
 	    bayerFormat.packing == BayerFormat::Packing::CSI2 &&
 | 
			
		||||
 	    isStandardBayerOrder(bayerFormat.order)) {
 | 
			
		||||
@@ -197,12 +276,61 @@ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &c
 | 
			
		||||
 	return -EINVAL;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+/*
 | 
			
		||||
+ * Check for standard Bayer orders and set x_shift_ and swap debayer0/1, so that
 | 
			
		||||
+ * a single pair of BGGR debayer functions can be used for all 4 standard orders.
 | 
			
		||||
+ */
 | 
			
		||||
+int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
 | 
			
		||||
+{
 | 
			
		||||
+	switch (order) {
 | 
			
		||||
+	case BayerFormat::BGGR:
 | 
			
		||||
+		break;
 | 
			
		||||
+	case BayerFormat::GBRG:
 | 
			
		||||
+		x_shift_ = 1; /* BGGR -> GBRG */
 | 
			
		||||
+		break;
 | 
			
		||||
+	case BayerFormat::GRBG:
 | 
			
		||||
+		std::swap(debayer0_, debayer1_); /* BGGR -> GRBG */
 | 
			
		||||
+		break;
 | 
			
		||||
+	case BayerFormat::RGGB:
 | 
			
		||||
+		x_shift_ = 1; /* BGGR -> GBRG */
 | 
			
		||||
+		std::swap(debayer0_, debayer1_); /* GBRG -> RGGB */
 | 
			
		||||
+		break;
 | 
			
		||||
+	default:
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 /* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
 | 
			
		||||
 int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
 | 
			
		||||
 {
 | 
			
		||||
 	BayerFormat bayerFormat =
 | 
			
		||||
 		BayerFormat::fromPixelFormat(inputFormat);
 | 
			
		||||
 
 | 
			
		||||
+	x_shift_ = 0;
 | 
			
		||||
+
 | 
			
		||||
+	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
 | 
			
		||||
+	    bayerFormat.packing == BayerFormat::Packing::None &&
 | 
			
		||||
+	    isStandardBayerOrder(bayerFormat.order)) {
 | 
			
		||||
+		switch (bayerFormat.bitDepth) {
 | 
			
		||||
+		case 8:
 | 
			
		||||
+			debayer0_ = &DebayerCpu::debayer8_BGBG_BGR888;
 | 
			
		||||
+			debayer1_ = &DebayerCpu::debayer8_GRGR_BGR888;
 | 
			
		||||
+			break;
 | 
			
		||||
+		case 10:
 | 
			
		||||
+			debayer0_ = &DebayerCpu::debayer10_BGBG_BGR888;
 | 
			
		||||
+			debayer1_ = &DebayerCpu::debayer10_GRGR_BGR888;
 | 
			
		||||
+			break;
 | 
			
		||||
+		case 12:
 | 
			
		||||
+			debayer0_ = &DebayerCpu::debayer12_BGBG_BGR888;
 | 
			
		||||
+			debayer1_ = &DebayerCpu::debayer12_GRGR_BGR888;
 | 
			
		||||
+			break;
 | 
			
		||||
+		}
 | 
			
		||||
+		setupStandardBayerOrder(bayerFormat.order);
 | 
			
		||||
+		return 0;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	if (bayerFormat.bitDepth == 10 &&
 | 
			
		||||
 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
 | 
			
		||||
 		switch (bayerFormat.order) {
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,125 @@
 | 
			
		|||
From b835b2c90785ee02bc98888bf165713d16c24cc4 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Mon, 18 Dec 2023 19:21:07 +0100
 | 
			
		||||
Subject: [PATCH 18/25] libcamera: debayer_cpu: Add BGR888 output support
 | 
			
		||||
 | 
			
		||||
BGR888 is RGB888 with the red and blue pixels swapped, adjust
 | 
			
		||||
the debayering to swap the red and blue pixels in the bayer pattern
 | 
			
		||||
to add support for writing formats::BGR888.
 | 
			
		||||
 | 
			
		||||
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/debayer_cpu.h       |  1 +
 | 
			
		||||
 src/libcamera/software_isp/debayer_cpu.cpp    | 43 ++++++++++++++++---
 | 
			
		||||
 2 files changed, 39 insertions(+), 5 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
index 1147b368..bdeab7c0 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
@@ -133,6 +133,7 @@ private:
 | 
			
		||||
 	unsigned int lineBufferIndex_;
 | 
			
		||||
 	unsigned int x_shift_; /* Offset of 0/1 applied to window_.x */
 | 
			
		||||
 	bool enableInputMemcpy_;
 | 
			
		||||
+	bool swapRedBlueGains_;
 | 
			
		||||
 	float gamma_correction_;
 | 
			
		||||
 	int measuredFrames_;
 | 
			
		||||
 	int64_t frameProcessTime_;
 | 
			
		||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
index 7b7623b7..0edea4d3 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
@@ -245,7 +245,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 | 
			
		||||
 		config.bpp = (bayerFormat.bitDepth + 7) & ~7;
 | 
			
		||||
 		config.patternSize.width = 2;
 | 
			
		||||
 		config.patternSize.height = 2;
 | 
			
		||||
-		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
 | 
			
		||||
+		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888, formats::BGR888 });
 | 
			
		||||
 		return 0;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -255,7 +255,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 | 
			
		||||
 		config.bpp = 10;
 | 
			
		||||
 		config.patternSize.width = 4; /* 5 bytes per *4* pixels */
 | 
			
		||||
 		config.patternSize.height = 2;
 | 
			
		||||
-		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
 | 
			
		||||
+		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888, formats::BGR888 });
 | 
			
		||||
 		return 0;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -266,7 +266,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 | 
			
		||||
 
 | 
			
		||||
 int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
 | 
			
		||||
 {
 | 
			
		||||
-	if (outputFormat == formats::RGB888) {
 | 
			
		||||
+	if (outputFormat == formats::RGB888 || outputFormat == formats::BGR888) {
 | 
			
		||||
 		config.bpp = 24;
 | 
			
		||||
 		return 0;
 | 
			
		||||
 	}
 | 
			
		||||
@@ -302,12 +302,41 @@ int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
 | 
			
		||||
-int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
 | 
			
		||||
+int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat)
 | 
			
		||||
 {
 | 
			
		||||
 	BayerFormat bayerFormat =
 | 
			
		||||
 		BayerFormat::fromPixelFormat(inputFormat);
 | 
			
		||||
 
 | 
			
		||||
+	swapRedBlueGains_ = false;
 | 
			
		||||
+
 | 
			
		||||
+	switch (outputFormat) {
 | 
			
		||||
+	case formats::RGB888:
 | 
			
		||||
+		break;
 | 
			
		||||
+	case formats::BGR888:
 | 
			
		||||
+		/* Swap R and B in bayer order to generate BGR888 instead of RGB888 */
 | 
			
		||||
+		swapRedBlueGains_ = true;
 | 
			
		||||
+
 | 
			
		||||
+		switch (bayerFormat.order) {
 | 
			
		||||
+		case BayerFormat::BGGR:
 | 
			
		||||
+			bayerFormat.order = BayerFormat::RGGB;
 | 
			
		||||
+			break;
 | 
			
		||||
+		case BayerFormat::GBRG:
 | 
			
		||||
+			bayerFormat.order = BayerFormat::GRBG;
 | 
			
		||||
+			break;
 | 
			
		||||
+		case BayerFormat::GRBG:
 | 
			
		||||
+			bayerFormat.order = BayerFormat::GBRG;
 | 
			
		||||
+			break;
 | 
			
		||||
+		case BayerFormat::RGGB:
 | 
			
		||||
+			bayerFormat.order = BayerFormat::BGGR;
 | 
			
		||||
+			break;
 | 
			
		||||
+		default:
 | 
			
		||||
+			goto invalid_fmt;
 | 
			
		||||
+		}
 | 
			
		||||
+		break;
 | 
			
		||||
+	default:
 | 
			
		||||
+		goto invalid_fmt;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	x_shift_ = 0;
 | 
			
		||||
 
 | 
			
		||||
 	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
 | 
			
		||||
@@ -355,6 +384,7 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi
 | 
			
		||||
 		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+invalid_fmt:
 | 
			
		||||
 	LOG(Debayer, Error) << "Unsupported input output format combination";
 | 
			
		||||
 	return -EINVAL;
 | 
			
		||||
 }
 | 
			
		||||
@@ -594,6 +624,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
 | 
			
		||||
 		gamma_correction_ = params.gamma;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	if (swapRedBlueGains_)
 | 
			
		||||
+		std::swap(params.gainR, params.gainB);
 | 
			
		||||
+
 | 
			
		||||
 	for (int i = 0; i < 256; i++) {
 | 
			
		||||
 		int idx;
 | 
			
		||||
 
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
From eb45bdfe66af7844a779bc6fcf923cd951336309 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Date: Fri, 6 Oct 2023 10:39:45 +0200
 | 
			
		||||
Subject: [PATCH 19/25] libcamera: pipeline: simple: Enable simplepipeline for
 | 
			
		||||
 intel-ipu6 DNU
 | 
			
		||||
 | 
			
		||||
Do Not Upstream, first the ipu6 CSI receiver code needs to land in
 | 
			
		||||
the kernel.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 src/libcamera/pipeline/simple/simple.cpp | 1 +
 | 
			
		||||
 1 file changed, 1 insertion(+)
 | 
			
		||||
 | 
			
		||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
index c76510c2..130843cd 100644
 | 
			
		||||
--- a/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
 | 
			
		||||
@@ -197,6 +197,7 @@ static const SimplePipelineInfo supportedDevices[] = {
 | 
			
		||||
 	{ "mxc-isi", {} },
 | 
			
		||||
 	{ "qcom-camss", {} },
 | 
			
		||||
 	{ "sun6i-csi", {} },
 | 
			
		||||
+	{ "intel-ipu6", {} },
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 } /* namespace */
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,237 @@
 | 
			
		|||
From e03beabbad83c4c283c7f1c2c4798b6c3e2eaf06 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Tue, 19 Dec 2023 11:16:26 +0100
 | 
			
		||||
Subject: [PATCH 20/25] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer
 | 
			
		||||
 order DNU
 | 
			
		||||
 | 
			
		||||
The ov01a1s sensor has the following bayer pattern (4x4 tile repeating):
 | 
			
		||||
 | 
			
		||||
IGIG
 | 
			
		||||
GBGR
 | 
			
		||||
IGIG
 | 
			
		||||
GRGB
 | 
			
		||||
 | 
			
		||||
Add support for this PixelFormat to libcamera.
 | 
			
		||||
 | 
			
		||||
Do Not Upstream, first the include/linux/media-bus-format.h and
 | 
			
		||||
include/linux/videodev2.h changes need to land in the upstream kernel.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 include/libcamera/internal/bayer_format.h |  3 ++-
 | 
			
		||||
 include/linux/drm_fourcc.h                |  2 ++
 | 
			
		||||
 include/linux/media-bus-format.h          |  4 +++-
 | 
			
		||||
 include/linux/videodev2.h                 |  3 +++
 | 
			
		||||
 src/libcamera/bayer_format.cpp            |  5 +++++
 | 
			
		||||
 src/libcamera/camera_sensor.cpp           |  3 +++
 | 
			
		||||
 src/libcamera/formats.cpp                 | 20 ++++++++++++++++++++
 | 
			
		||||
 src/libcamera/formats.yaml                |  5 +++++
 | 
			
		||||
 src/libcamera/v4l2_pixelformat.cpp        |  4 ++++
 | 
			
		||||
 src/libcamera/v4l2_subdevice.cpp          |  1 +
 | 
			
		||||
 10 files changed, 48 insertions(+), 2 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/bayer_format.h b/include/libcamera/internal/bayer_format.h
 | 
			
		||||
index 78ba3969..e77106c3 100644
 | 
			
		||||
--- a/include/libcamera/internal/bayer_format.h
 | 
			
		||||
+++ b/include/libcamera/internal/bayer_format.h
 | 
			
		||||
@@ -27,7 +27,8 @@ public:
 | 
			
		||||
 		GBRG = 1,
 | 
			
		||||
 		GRBG = 2,
 | 
			
		||||
 		RGGB = 3,
 | 
			
		||||
-		MONO = 4
 | 
			
		||||
+		MONO = 4,
 | 
			
		||||
+		IGIG_GBGR_IGIG_GRGB = 5,
 | 
			
		||||
 	};
 | 
			
		||||
 
 | 
			
		||||
 	enum class Packing : uint16_t {
 | 
			
		||||
diff --git a/include/linux/drm_fourcc.h b/include/linux/drm_fourcc.h
 | 
			
		||||
index 1496e097..750ae8c9 100644
 | 
			
		||||
--- a/include/linux/drm_fourcc.h
 | 
			
		||||
+++ b/include/linux/drm_fourcc.h
 | 
			
		||||
@@ -405,6 +405,8 @@ extern "C" {
 | 
			
		||||
 #define DRM_FORMAT_SGRBG10	fourcc_code('B', 'A', '1', '0')
 | 
			
		||||
 #define DRM_FORMAT_SGBRG10	fourcc_code('G', 'B', '1', '0')
 | 
			
		||||
 #define DRM_FORMAT_SBGGR10	fourcc_code('B', 'G', '1', '0')
 | 
			
		||||
+/* Mixed 10 bit bayer + ir pixel pattern found on Omnivision ov01a1s */
 | 
			
		||||
+#define DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10 fourcc_code('O', 'V', '1', 'S')
 | 
			
		||||
 
 | 
			
		||||
 /* 12-bit Bayer formats */
 | 
			
		||||
 #define DRM_FORMAT_SRGGB12	fourcc_code('R', 'G', '1', '2')
 | 
			
		||||
diff --git a/include/linux/media-bus-format.h b/include/linux/media-bus-format.h
 | 
			
		||||
index 0dfc11ee..c5fbda0e 100644
 | 
			
		||||
--- a/include/linux/media-bus-format.h
 | 
			
		||||
+++ b/include/linux/media-bus-format.h
 | 
			
		||||
@@ -112,7 +112,7 @@
 | 
			
		||||
 #define MEDIA_BUS_FMT_YUV16_1X48		0x202a
 | 
			
		||||
 #define MEDIA_BUS_FMT_UYYVYY16_0_5X48		0x202b
 | 
			
		||||
 
 | 
			
		||||
-/* Bayer - next is	0x3021 */
 | 
			
		||||
+/* Bayer - next is 0x3022 */
 | 
			
		||||
 #define MEDIA_BUS_FMT_SBGGR8_1X8		0x3001
 | 
			
		||||
 #define MEDIA_BUS_FMT_SGBRG8_1X8		0x3013
 | 
			
		||||
 #define MEDIA_BUS_FMT_SGRBG8_1X8		0x3002
 | 
			
		||||
@@ -145,6 +145,8 @@
 | 
			
		||||
 #define MEDIA_BUS_FMT_SGBRG16_1X16		0x301e
 | 
			
		||||
 #define MEDIA_BUS_FMT_SGRBG16_1X16		0x301f
 | 
			
		||||
 #define MEDIA_BUS_FMT_SRGGB16_1X16		0x3020
 | 
			
		||||
+/* Mixed bayer + ir pixel pattern found on ov01a1s */
 | 
			
		||||
+#define MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10 0x3021
 | 
			
		||||
 
 | 
			
		||||
 /* JPEG compressed formats - next is	0x4002 */
 | 
			
		||||
 #define MEDIA_BUS_FMT_JPEG_1X8			0x4001
 | 
			
		||||
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
 | 
			
		||||
index bfb315d6..13c6c9d3 100644
 | 
			
		||||
--- a/include/linux/videodev2.h
 | 
			
		||||
+++ b/include/linux/videodev2.h
 | 
			
		||||
@@ -678,6 +678,9 @@ struct v4l2_pix_format {
 | 
			
		||||
 #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16  GBGB.. RGRG.. */
 | 
			
		||||
 #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16  GRGR.. BGBG.. */
 | 
			
		||||
 #define V4L2_PIX_FMT_SRGGB16 v4l2_fourcc('R', 'G', '1', '6') /* 16  RGRG.. GBGB.. */
 | 
			
		||||
+	/* 10bit mixed bayer + ir pixel pattern found on ov01a1s */
 | 
			
		||||
+#define V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10  v4l2_fourcc('O', 'V', '1', 'S') /* unpacked */
 | 
			
		||||
+#define V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P v4l2_fourcc('O', 'V', '1', 'P') /* packed */
 | 
			
		||||
 
 | 
			
		||||
 /* HSV formats */
 | 
			
		||||
 #define V4L2_PIX_FMT_HSV24 v4l2_fourcc('H', 'S', 'V', '3')
 | 
			
		||||
diff --git a/src/libcamera/bayer_format.cpp b/src/libcamera/bayer_format.cpp
 | 
			
		||||
index 3bf15fb4..ae227540 100644
 | 
			
		||||
--- a/src/libcamera/bayer_format.cpp
 | 
			
		||||
+++ b/src/libcamera/bayer_format.cpp
 | 
			
		||||
@@ -108,6 +108,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{
 | 
			
		||||
 		{ formats::SGRBG10, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10) } },
 | 
			
		||||
 	{ { BayerFormat::RGGB, 10, BayerFormat::Packing::None },
 | 
			
		||||
 		{ formats::SRGGB10, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10) } },
 | 
			
		||||
+	{ { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::None },
 | 
			
		||||
+		{ formats::SIGIG_GBGR_IGIG_GRGB10, V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10) } },
 | 
			
		||||
 	{ { BayerFormat::BGGR, 10, BayerFormat::Packing::CSI2 },
 | 
			
		||||
 		{ formats::SBGGR10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P) } },
 | 
			
		||||
 	{ { BayerFormat::GBRG, 10, BayerFormat::Packing::CSI2 },
 | 
			
		||||
@@ -116,6 +118,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{
 | 
			
		||||
 		{ formats::SGRBG10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P) } },
 | 
			
		||||
 	{ { BayerFormat::RGGB, 10, BayerFormat::Packing::CSI2 },
 | 
			
		||||
 		{ formats::SRGGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P) } },
 | 
			
		||||
+	{ { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::CSI2 },
 | 
			
		||||
+		{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P) } },
 | 
			
		||||
 	{ { BayerFormat::BGGR, 10, BayerFormat::Packing::IPU3 },
 | 
			
		||||
 		{ formats::SBGGR10_IPU3, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10) } },
 | 
			
		||||
 	{ { BayerFormat::GBRG, 10, BayerFormat::Packing::IPU3 },
 | 
			
		||||
@@ -193,6 +197,7 @@ const std::unordered_map<unsigned int, BayerFormat> mbusCodeToBayer{
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SGBRG10_1X10, { BayerFormat::GBRG, 10, BayerFormat::Packing::None } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SGRBG10_1X10, { BayerFormat::GRBG, 10, BayerFormat::Packing::None } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SRGGB10_1X10, { BayerFormat::RGGB, 10, BayerFormat::Packing::None } },
 | 
			
		||||
+	{ MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::None } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SBGGR12_1X12, { BayerFormat::BGGR, 12, BayerFormat::Packing::None } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SGBRG12_1X12, { BayerFormat::GBRG, 12, BayerFormat::Packing::None } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SGRBG12_1X12, { BayerFormat::GRBG, 12, BayerFormat::Packing::None } },
 | 
			
		||||
diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
 | 
			
		||||
index 0ef78d9c..f19f72ea 100644
 | 
			
		||||
--- a/src/libcamera/camera_sensor.cpp
 | 
			
		||||
+++ b/src/libcamera/camera_sensor.cpp
 | 
			
		||||
@@ -510,6 +510,9 @@ int CameraSensor::initProperties()
 | 
			
		||||
 		case BayerFormat::MONO:
 | 
			
		||||
 			cfa = properties::draft::MONO;
 | 
			
		||||
 			break;
 | 
			
		||||
+		case BayerFormat::IGIG_GBGR_IGIG_GRGB:
 | 
			
		||||
+			cfa = properties::draft::RGB;
 | 
			
		||||
+			break;
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
 		properties_.set(properties::draft::ColorFilterArrangement, cfa);
 | 
			
		||||
diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp
 | 
			
		||||
index 447e6238..aef7d598 100644
 | 
			
		||||
--- a/src/libcamera/formats.cpp
 | 
			
		||||
+++ b/src/libcamera/formats.cpp
 | 
			
		||||
@@ -599,6 +599,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
 | 
			
		||||
 		.pixelsPerGroup = 2,
 | 
			
		||||
 		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
 | 
			
		||||
 	} },
 | 
			
		||||
+	{ formats::SIGIG_GBGR_IGIG_GRGB10, {
 | 
			
		||||
+		.name = "SIGIG_GBGR_IGIG_GRGB10",
 | 
			
		||||
+		.format = formats::SIGIG_GBGR_IGIG_GRGB10,
 | 
			
		||||
+		.v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10), },
 | 
			
		||||
+		.bitsPerPixel = 10,
 | 
			
		||||
+		.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
 | 
			
		||||
+		.packed = false,
 | 
			
		||||
+		.pixelsPerGroup = 4,
 | 
			
		||||
+		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
 | 
			
		||||
+	} },
 | 
			
		||||
 	{ formats::SBGGR10_CSI2P, {
 | 
			
		||||
 		.name = "SBGGR10_CSI2P",
 | 
			
		||||
 		.format = formats::SBGGR10_CSI2P,
 | 
			
		||||
@@ -639,6 +649,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
 | 
			
		||||
 		.pixelsPerGroup = 4,
 | 
			
		||||
 		.planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
 | 
			
		||||
 	} },
 | 
			
		||||
+	{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, {
 | 
			
		||||
+		.name = "SIGIG_GBGR_IGIG_GRGB10_CSI2P",
 | 
			
		||||
+		.format = formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P,
 | 
			
		||||
+		.v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P), },
 | 
			
		||||
+		.bitsPerPixel = 10,
 | 
			
		||||
+		.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
 | 
			
		||||
+		.packed = true,
 | 
			
		||||
+		.pixelsPerGroup = 4,
 | 
			
		||||
+		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
 | 
			
		||||
+	} },
 | 
			
		||||
 	{ formats::SBGGR12, {
 | 
			
		||||
 		.name = "SBGGR12",
 | 
			
		||||
 		.format = formats::SBGGR12,
 | 
			
		||||
diff --git a/src/libcamera/formats.yaml b/src/libcamera/formats.yaml
 | 
			
		||||
index 539ac0b3..0786a900 100644
 | 
			
		||||
--- a/src/libcamera/formats.yaml
 | 
			
		||||
+++ b/src/libcamera/formats.yaml
 | 
			
		||||
@@ -100,6 +100,8 @@ formats:
 | 
			
		||||
       fourcc: DRM_FORMAT_SGBRG10
 | 
			
		||||
   - SBGGR10:
 | 
			
		||||
       fourcc: DRM_FORMAT_SBGGR10
 | 
			
		||||
+  - SIGIG_GBGR_IGIG_GRGB10:
 | 
			
		||||
+      fourcc: DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10
 | 
			
		||||
 
 | 
			
		||||
   - SRGGB12:
 | 
			
		||||
       fourcc: DRM_FORMAT_SRGGB12
 | 
			
		||||
@@ -144,6 +146,9 @@ formats:
 | 
			
		||||
   - SBGGR10_CSI2P:
 | 
			
		||||
       fourcc: DRM_FORMAT_SBGGR10
 | 
			
		||||
       mod: MIPI_FORMAT_MOD_CSI2_PACKED
 | 
			
		||||
+  - SIGIG_GBGR_IGIG_GRGB10_CSI2P:
 | 
			
		||||
+      fourcc: DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10
 | 
			
		||||
+      mod: MIPI_FORMAT_MOD_CSI2_PACKED
 | 
			
		||||
 
 | 
			
		||||
   - SRGGB12_CSI2P:
 | 
			
		||||
       fourcc: DRM_FORMAT_SRGGB12
 | 
			
		||||
diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp
 | 
			
		||||
index 5551c62e..53078d99 100644
 | 
			
		||||
--- a/src/libcamera/v4l2_pixelformat.cpp
 | 
			
		||||
+++ b/src/libcamera/v4l2_pixelformat.cpp
 | 
			
		||||
@@ -153,6 +153,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
 | 
			
		||||
 		{ formats::SGRBG10, "10-bit Bayer GRGR/BGBG" } },
 | 
			
		||||
 	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),
 | 
			
		||||
 		{ formats::SRGGB10, "10-bit Bayer RGRG/GBGB" } },
 | 
			
		||||
+	{ V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10),
 | 
			
		||||
+		{ formats::SIGIG_GBGR_IGIG_GRGB10, "10-bit Bayer GRGB/IGIG/GBGR/IGIG" } },
 | 
			
		||||
 	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),
 | 
			
		||||
 		{ formats::SBGGR10_CSI2P, "10-bit Bayer BGBG/GRGR Packed" } },
 | 
			
		||||
 	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),
 | 
			
		||||
@@ -161,6 +163,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
 | 
			
		||||
 		{ formats::SGRBG10_CSI2P, "10-bit Bayer GRGR/BGBG Packed" } },
 | 
			
		||||
 	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),
 | 
			
		||||
 		{ formats::SRGGB10_CSI2P, "10-bit Bayer RGRG/GBGB Packed" } },
 | 
			
		||||
+	{ V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P),
 | 
			
		||||
+		{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, "10-bit Bayer GRGB/IGIG/GBGR/IGIG Packed" } },
 | 
			
		||||
 	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),
 | 
			
		||||
 		{ formats::SBGGR12, "12-bit Bayer BGBG/GRGR" } },
 | 
			
		||||
 	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),
 | 
			
		||||
diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
 | 
			
		||||
index 15e8206a..4ad37aaf 100644
 | 
			
		||||
--- a/src/libcamera/v4l2_subdevice.cpp
 | 
			
		||||
+++ b/src/libcamera/v4l2_subdevice.cpp
 | 
			
		||||
@@ -128,6 +128,7 @@ const std::map<uint32_t, V4L2SubdeviceFormatInfo> formatInfoMap = {
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SGBRG10_1X10, { 10, "SGBRG10_1X10", PixelFormatInfo::ColourEncodingRAW } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SGRBG10_1X10, { 10, "SGRBG10_1X10", PixelFormatInfo::ColourEncodingRAW } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SRGGB10_1X10, { 10, "SRGGB10_1X10", PixelFormatInfo::ColourEncodingRAW } },
 | 
			
		||||
+	{ MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { 10, "SIGIG_GBGR_IGIG_GRGB10_1X10", PixelFormatInfo::ColourEncodingRAW } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SBGGR12_1X12, { 12, "SBGGR12_1X12", PixelFormatInfo::ColourEncodingRAW } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
 | 
			
		||||
 	{ MEDIA_BUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,131 @@
 | 
			
		|||
From f939e68a3ef556e572f0140df6d7ef17d72f457e Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Marttico <g.martti@gmail.com>
 | 
			
		||||
Date: Wed, 20 Dec 2023 20:26:15 +0100
 | 
			
		||||
Subject: [PATCH 21/25] libcamera: swstats_cpu: Add support for 10bpp
 | 
			
		||||
 IGIG_GBGR_IGIG_GRGB input
 | 
			
		||||
 | 
			
		||||
Add support to SwStatsCpu for 10bpp IGIG_GBGR_IGIG_GRGB input
 | 
			
		||||
generated by the Omnivision ov01a1s sensor.
 | 
			
		||||
 | 
			
		||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Co-authored-by: Toon Langendam <t.langendam@gmail.com>
 | 
			
		||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
 | 
			
		||||
Signed-off-by: Marttico <g.martti@gmail.com>
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 .../internal/software_isp/swstats_cpu.h       |  3 +
 | 
			
		||||
 src/libcamera/software_isp/swstats_cpu.cpp    | 76 +++++++++++++++++++
 | 
			
		||||
 2 files changed, 79 insertions(+)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
index e7abc6bb..a47241e1 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/swstats_cpu.h
 | 
			
		||||
@@ -42,6 +42,9 @@ private:
 | 
			
		||||
 	/* Bayer 10 bpp packed */
 | 
			
		||||
 	void statsBGGR10PLine0(const uint8_t *src[]);
 | 
			
		||||
 	void statsGBRG10PLine0(const uint8_t *src[]);
 | 
			
		||||
+	/* IGIG_GBGR_IGIG_GRGB 10 bpp unpacked */
 | 
			
		||||
+	void statsRGBIR10Line0(const uint8_t *src[]);
 | 
			
		||||
+	void statsRGBIR10Line2(const uint8_t *src[]);
 | 
			
		||||
 	void resetStats(void);
 | 
			
		||||
 	void finishStats(void);
 | 
			
		||||
 
 | 
			
		||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
index 87550371..96e21be5 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
@@ -187,6 +187,68 @@ void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])
 | 
			
		||||
 	statsBayer10P(window_.width, src0, src1, false, stats_);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+void SwStatsCpu::statsRGBIR10Line0(const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint16_t *src0_16 = (const uint16_t *)src[2] + window_.x;
 | 
			
		||||
+	const uint16_t *src1_16 = (const uint16_t *)src[3] + window_.x;
 | 
			
		||||
+	uint16_t g3, g4;
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_START_LINE_STATS(uint16_t)
 | 
			
		||||
+
 | 
			
		||||
+	/* x += 8 sample every other 4x4 block */
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width; x += 8) {
 | 
			
		||||
+		/* IGIG */
 | 
			
		||||
+		//i = src0_16[x];
 | 
			
		||||
+		g2 = src0_16[x + 1];
 | 
			
		||||
+		//i = src0_16[x + 2];
 | 
			
		||||
+		g4 = src0_16[x + 3];
 | 
			
		||||
+
 | 
			
		||||
+		/* GBGR */
 | 
			
		||||
+		g = src1_16[x];
 | 
			
		||||
+		b = src1_16[x + 1];
 | 
			
		||||
+		g3 = src1_16[x + 2];
 | 
			
		||||
+		r = src1_16[x + 3];
 | 
			
		||||
+
 | 
			
		||||
+		g = (g + g2 + g3 + g4) / 4;
 | 
			
		||||
+
 | 
			
		||||
+		/* divide Y by 4 for 10 -> 8 bpp value */
 | 
			
		||||
+		SWISP_LINARO_ACCUMULATE_LINE_STATS(4)
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_FINISH_LINE_STATS()
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void SwStatsCpu::statsRGBIR10Line2(const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint16_t *src0_16 = (const uint16_t *)src[2] + window_.x;
 | 
			
		||||
+	const uint16_t *src1_16 = (const uint16_t *)src[3] + window_.x;
 | 
			
		||||
+	uint16_t g3, g4;
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_START_LINE_STATS(uint16_t)
 | 
			
		||||
+
 | 
			
		||||
+	/* x += 8 sample every other 4x4 block */
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width; x += 8) {
 | 
			
		||||
+		/* IGIG */
 | 
			
		||||
+		//i = src0_16[x];
 | 
			
		||||
+		g2 = src0_16[x + 1];
 | 
			
		||||
+		//i = src0_16[x + 2];
 | 
			
		||||
+		g4 = src0_16[x + 3];
 | 
			
		||||
+
 | 
			
		||||
+		/* GRGB */
 | 
			
		||||
+		g = src1_16[x];
 | 
			
		||||
+		r = src1_16[x + 1];
 | 
			
		||||
+		g3 = src1_16[x + 2];
 | 
			
		||||
+		b = src1_16[x + 3];
 | 
			
		||||
+
 | 
			
		||||
+		g = (g + g2 + g3 + g4) / 4;
 | 
			
		||||
+
 | 
			
		||||
+		/* divide Y by 4 for 10 -> 8 bpp value */
 | 
			
		||||
+		SWISP_LINARO_ACCUMULATE_LINE_STATS(4)
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	SWISP_LINARO_FINISH_LINE_STATS()
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 void SwStatsCpu::resetStats(void)
 | 
			
		||||
 {
 | 
			
		||||
 	stats_.sumR_ = 0;
 | 
			
		||||
@@ -282,6 +344,20 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
 | 
			
		||||
 		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	if (bayerFormat.bitDepth == 10 &&
 | 
			
		||||
+	    bayerFormat.packing == BayerFormat::Packing::None &&
 | 
			
		||||
+	    bayerFormat.order == BayerFormat::IGIG_GBGR_IGIG_GRGB) {
 | 
			
		||||
+		bpp_ = 16;
 | 
			
		||||
+		patternSize_.height = 4;
 | 
			
		||||
+		patternSize_.width = 4;
 | 
			
		||||
+		y_skip_mask_ = 0x04;
 | 
			
		||||
+		x_shift_ = 0;
 | 
			
		||||
+		swap_lines_ = false;
 | 
			
		||||
+		stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsRGBIR10Line0;
 | 
			
		||||
+		stats2_ = (SwStats::statsProcessFn)&SwStatsCpu::statsRGBIR10Line2;
 | 
			
		||||
+		return 0;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	LOG(SwStats, Info)
 | 
			
		||||
 		<< "Unsupported input format " << inputCfg.pixelFormat.toString();
 | 
			
		||||
 	return -EINVAL;
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,315 @@
 | 
			
		|||
From e3638943a8bd3f93b8d81c3996035c60755b97f6 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Marttico <g.martti@gmail.com>
 | 
			
		||||
Date: Wed, 20 Dec 2023 20:28:12 +0100
 | 
			
		||||
Subject: [PATCH 22/25] libcamera: debayer_cpu: Add support for 10bpp
 | 
			
		||||
 IGIG_GBGR_IGIG_GRGB input
 | 
			
		||||
 | 
			
		||||
Add support to DebayerCpu for 10bpp IGIG_GBGR_IGIG_GRGB input
 | 
			
		||||
generated by the Omnivision ov01a1s sensor.
 | 
			
		||||
 | 
			
		||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
 | 
			
		||||
Co-authored-by: Toon Langendam <t.langendam@gmail.com>
 | 
			
		||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
 | 
			
		||||
Signed-off-by: Marttico <g.martti@gmail.com>
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 .../internal/software_isp/debayer_cpu.h       |   5 +
 | 
			
		||||
 src/libcamera/software_isp/debayer_cpu.cpp    | 251 ++++++++++++++++++
 | 
			
		||||
 2 files changed, 256 insertions(+)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
index bdeab7c0..52af117f 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
 | 
			
		||||
@@ -96,6 +96,11 @@ private:
 | 
			
		||||
 	void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
 	void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
 	void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	/* IGIG_GBGR_IGIG_GRGB  unpacked 10-bit raw bayer format */
 | 
			
		||||
+	void debayerIGIG10Line0(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayerGBGR10Line1(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayerIGIG10Line2(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
+	void debayerGRGB10Line3(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
 
 | 
			
		||||
 	typedef void (DebayerCpu::*debayerFn)(uint8_t *dst, const uint8_t *src[]);
 | 
			
		||||
 
 | 
			
		||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
index 0edea4d3..41c8805f 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
@@ -228,6 +228,238 @@ void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
 	}
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+void DebayerCpu::debayerIGIG10Line0(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint16_t *prev = (const uint16_t *)src[1];
 | 
			
		||||
+	const uint16_t *curr = (const uint16_t *)src[2];
 | 
			
		||||
+	const uint16_t *next = (const uint16_t *)src[3];
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * IGIG line pixel 0: IGIGI
 | 
			
		||||
+		 *                    GBGRG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GRGBG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[(prev[x - 1] + next[x + 1]) / 8];
 | 
			
		||||
+		*dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
 | 
			
		||||
+		*dst++ = red_[(prev[x + 1] + next[x - 1]) / 8];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * IGIG line pixel 1: GIGIG
 | 
			
		||||
+		 *                    BGRGB
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    RGBGR
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[next[x] / 4];
 | 
			
		||||
+		*dst++ = green_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = red_[prev[x] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * IGIG line pixel 2: IGIGI
 | 
			
		||||
+		 *                    GRGBG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GBGRG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[(prev[x + 1] + next[x - 1]) / 8];
 | 
			
		||||
+		*dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
 | 
			
		||||
+		*dst++ = red_[(prev[x - 1] + next[x + 1]) / 8];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * IGIG line pixel 3: GIGIG
 | 
			
		||||
+		 *                    RGBGR
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    BGRGB
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[prev[x] / 4];
 | 
			
		||||
+		*dst++ = green_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = red_[next[x] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayerGBGR10Line1(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint16_t *prev2 = (const uint16_t *)src[0];
 | 
			
		||||
+	const uint16_t *prev = (const uint16_t *)src[1];
 | 
			
		||||
+	const uint16_t *curr = (const uint16_t *)src[2];
 | 
			
		||||
+	const uint16_t *next = (const uint16_t *)src[3];
 | 
			
		||||
+	const uint16_t *next2 = (const uint16_t *)src[4];
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * GBGR line pixel 0: GBGRG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GRGBG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GBGRG
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[curr[x + 1] / 4];
 | 
			
		||||
+		*dst++ = green_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = red_[curr[x - 1] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * GBGR line pixel 1: BGRGB
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    RGBGR
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    BGRGB
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
 | 
			
		||||
+		*dst++ = red_[(curr[x - 2] + curr[x + 2] + prev2[x] + next2[x]) / 16];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * GBGR line pixel 2: GRGBG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GBGRG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GRGBG
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[curr[x - 1] / 4];
 | 
			
		||||
+		*dst++ = green_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = red_[curr[x + 1] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * GBGR line pixel 3: RGBGR
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    BGRGB
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    RGBGR
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[(curr[x - 2] + curr[x + 2] + prev2[x] + next2[x]) / 16];
 | 
			
		||||
+		*dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
 | 
			
		||||
+		*dst++ = red_[curr[x] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayerIGIG10Line2(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint16_t *prev = (const uint16_t *)src[1];
 | 
			
		||||
+	const uint16_t *curr = (const uint16_t *)src[2];
 | 
			
		||||
+	const uint16_t *next = (const uint16_t *)src[3];
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * IGIG line pixel 0: IGIGI
 | 
			
		||||
+		 *                    GRGBG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GBGRG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[(prev[x + 1] + next[x - 1]) / 8];
 | 
			
		||||
+		*dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
 | 
			
		||||
+		*dst++ = red_[(prev[x - 1] + next[x + 1]) / 8];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * IGIG line pixel 1: GIGIG
 | 
			
		||||
+		 *                    RGBGR
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    BGRGB
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[prev[x] / 4];
 | 
			
		||||
+		*dst++ = green_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = red_[next[x] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * IGIG line pixel 2: IGIGI
 | 
			
		||||
+		 *                    GBGRG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GRGBG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[(prev[x - 1] + next[x + 1]) / 8];
 | 
			
		||||
+		*dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
 | 
			
		||||
+		*dst++ = red_[(prev[x + 1] + next[x - 1]) / 8];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * IGIG line pixel 3: GIGIG
 | 
			
		||||
+		 *                    BGRGB
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    RGBGR
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[next[x] / 4];
 | 
			
		||||
+		*dst++ = green_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = red_[prev[x] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+void DebayerCpu::debayerGRGB10Line3(uint8_t *dst, const uint8_t *src[])
 | 
			
		||||
+{
 | 
			
		||||
+	const uint16_t *prev2 = (const uint16_t *)src[0];
 | 
			
		||||
+	const uint16_t *prev = (const uint16_t *)src[1];
 | 
			
		||||
+	const uint16_t *curr = (const uint16_t *)src[2];
 | 
			
		||||
+	const uint16_t *next = (const uint16_t *)src[3];
 | 
			
		||||
+	const uint16_t *next2 = (const uint16_t *)src[4];
 | 
			
		||||
+
 | 
			
		||||
+	for (int x = 0; x < (int)window_.width;) {
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * GRGB line pixel 0: GRGBG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GBGRG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GRGBG
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[curr[x - 1] / 4];
 | 
			
		||||
+		*dst++ = green_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = red_[curr[x + 1] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * GRGB line pixel 1: RGBGR
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    BGRGB
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    RGBGR
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[(curr[x - 2] + curr[x + 2] + prev2[x] + next2[x]) / 16];
 | 
			
		||||
+		*dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
 | 
			
		||||
+		*dst++ = red_[curr[x] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * GRGB line pixel 2: GBGRG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GRGBG
 | 
			
		||||
+		 *                    IGIGI
 | 
			
		||||
+		 *                    GBGRG
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[curr[x + 1] / 4];
 | 
			
		||||
+		*dst++ = green_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = red_[curr[x - 1] / 4];
 | 
			
		||||
+		x++;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * GRGB line pixel 3: BGRGB
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    RGBGR
 | 
			
		||||
+		 *                    GIGIG
 | 
			
		||||
+		 *                    BGRGB
 | 
			
		||||
+		 */
 | 
			
		||||
+		*dst++ = blue_[curr[x] / 4];
 | 
			
		||||
+		*dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
 | 
			
		||||
+		*dst++ = red_[(curr[x - 2] + curr[x + 2] + prev2[x] + next2[x]) / 16];
 | 
			
		||||
+		x++;
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 static bool isStandardBayerOrder(BayerFormat::Order order)
 | 
			
		||||
 {
 | 
			
		||||
 	return order == BayerFormat::BGGR || order == BayerFormat::GBRG ||
 | 
			
		||||
@@ -259,6 +491,15 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 | 
			
		||||
 		return 0;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	if (bayerFormat.bitDepth == 10 && bayerFormat.packing == BayerFormat::Packing::None &&
 | 
			
		||||
+	    bayerFormat.order == BayerFormat::IGIG_GBGR_IGIG_GRGB) {
 | 
			
		||||
+		config.bpp = 16;
 | 
			
		||||
+		config.patternSize.height = 4;
 | 
			
		||||
+		config.patternSize.width = 4;
 | 
			
		||||
+		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
 | 
			
		||||
+		return 0;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	LOG(Debayer, Info)
 | 
			
		||||
 		<< "Unsupported input format " << inputFormat.toString();
 | 
			
		||||
 	return -EINVAL;
 | 
			
		||||
@@ -384,6 +625,16 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF
 | 
			
		||||
 		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	if (bayerFormat.bitDepth == 10 &&
 | 
			
		||||
+	    bayerFormat.packing == BayerFormat::Packing::None &&
 | 
			
		||||
+	    bayerFormat.order == BayerFormat::IGIG_GBGR_IGIG_GRGB) {
 | 
			
		||||
+		debayer0_ = &DebayerCpu::debayerIGIG10Line0;
 | 
			
		||||
+		debayer1_ = &DebayerCpu::debayerGBGR10Line1;
 | 
			
		||||
+		debayer2_ = &DebayerCpu::debayerIGIG10Line2;
 | 
			
		||||
+		debayer3_ = &DebayerCpu::debayerGRGB10Line3;
 | 
			
		||||
+		return 0;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 invalid_fmt:
 | 
			
		||||
 	LOG(Debayer, Error) << "Unsupported input output format combination";
 | 
			
		||||
 	return -EINVAL;
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,130 @@
 | 
			
		|||
From 26e96232c314f9d34f6ee3be365c04918967084e Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Mon, 22 Jan 2024 17:18:00 +0100
 | 
			
		||||
Subject: [PATCH 23/25] libcamera: Add "Software ISP benchmarking"
 | 
			
		||||
 documentation
 | 
			
		||||
 | 
			
		||||
Add a "Software ISP benchmarking" documentation section which describes
 | 
			
		||||
the performance/power consumption measurements used during
 | 
			
		||||
the Software ISP's development.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 Documentation/index.rst                     |  1 +
 | 
			
		||||
 Documentation/meson.build                   |  1 +
 | 
			
		||||
 Documentation/software-isp-benchmarking.rst | 82 +++++++++++++++++++++
 | 
			
		||||
 3 files changed, 84 insertions(+)
 | 
			
		||||
 create mode 100644 Documentation/software-isp-benchmarking.rst
 | 
			
		||||
 | 
			
		||||
diff --git a/Documentation/index.rst b/Documentation/index.rst
 | 
			
		||||
index 63fac72d..5442ae75 100644
 | 
			
		||||
--- a/Documentation/index.rst
 | 
			
		||||
+++ b/Documentation/index.rst
 | 
			
		||||
@@ -24,3 +24,4 @@
 | 
			
		||||
    Lens driver requirements <lens_driver_requirements>
 | 
			
		||||
    Python Bindings <python-bindings>
 | 
			
		||||
    Camera Sensor Model <camera-sensor-model>
 | 
			
		||||
+   SoftwareISP Benchmarking <software-isp-benchmarking>
 | 
			
		||||
diff --git a/Documentation/meson.build b/Documentation/meson.build
 | 
			
		||||
index 7a58fec8..3872e0a8 100644
 | 
			
		||||
--- a/Documentation/meson.build
 | 
			
		||||
+++ b/Documentation/meson.build
 | 
			
		||||
@@ -80,6 +80,7 @@ if sphinx.found()
 | 
			
		||||
         'lens_driver_requirements.rst',
 | 
			
		||||
         'python-bindings.rst',
 | 
			
		||||
         'sensor_driver_requirements.rst',
 | 
			
		||||
+        'software-isp-benchmarking.rst',
 | 
			
		||||
        '../README.rst',
 | 
			
		||||
     ]
 | 
			
		||||
 
 | 
			
		||||
diff --git a/Documentation/software-isp-benchmarking.rst b/Documentation/software-isp-benchmarking.rst
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 00000000..738c8c65
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/Documentation/software-isp-benchmarking.rst
 | 
			
		||||
@@ -0,0 +1,82 @@
 | 
			
		||||
+.. SPDX-License-Identifier: CC-BY-SA-4.0
 | 
			
		||||
+
 | 
			
		||||
+.. _software-isp-benchmarking:
 | 
			
		||||
+
 | 
			
		||||
+Software ISP benchmarking
 | 
			
		||||
+=========================
 | 
			
		||||
+
 | 
			
		||||
+The Software ISP is paricular sensitive to performance regressions
 | 
			
		||||
+therefor it is a good idea to always benchmark the Software ISP
 | 
			
		||||
+before and after making changes to it and ensure that there are
 | 
			
		||||
+no performance regressions.
 | 
			
		||||
+
 | 
			
		||||
+DebayerCpu class builtin benchmark
 | 
			
		||||
+----------------------------------
 | 
			
		||||
+
 | 
			
		||||
+The DebayerCpu class has a builtin benchmark. This benchmark
 | 
			
		||||
+measures the time spend on processing (collecting statistics
 | 
			
		||||
+and debayering) only, it does not measure the time spend on
 | 
			
		||||
+capturing or outputting the frames.
 | 
			
		||||
+
 | 
			
		||||
+The builtin benchmark always runs. So this can be used by simply
 | 
			
		||||
+running "cam" or "qcam" with a pipeline using the Software ISP.
 | 
			
		||||
+
 | 
			
		||||
+When it runs it will skip measuring the first 30 frames to
 | 
			
		||||
+allow the caches and the CPU temperature (turbo-ing) to warm-up
 | 
			
		||||
+and then it measures 30 fps and shows the total and per frame
 | 
			
		||||
+processing time using an info level log message:
 | 
			
		||||
+
 | 
			
		||||
+.. code-block::
 | 
			
		||||
+
 | 
			
		||||
+   INFO Debayer debayer_cpu.cpp:907 Processed 30 frames in 244317us, 8143 us/frame
 | 
			
		||||
+
 | 
			
		||||
+To get stable measurements it is advised to disable any other processes which
 | 
			
		||||
+may cause significant CPU usage (e.g. disable wifi, bluetooth and browsers).
 | 
			
		||||
+When possible it is also advisable to disable CPU turbo-ing and
 | 
			
		||||
+frequency-scaling.
 | 
			
		||||
+
 | 
			
		||||
+For example when benchmarking on a Lenovo ThinkPad X1 Yoga Gen 8, with
 | 
			
		||||
+the charger plugged in, the CPU can be fixed to run at 2 GHz using:
 | 
			
		||||
+
 | 
			
		||||
+.. code-block::
 | 
			
		||||
+
 | 
			
		||||
+   sudo x86_energy_perf_policy --turbo-enable 0
 | 
			
		||||
+   sudo cpupower frequency-set -d 2GHz -u 2GHz
 | 
			
		||||
+
 | 
			
		||||
+with these settings the builtin bench reports a processing time of ~7.8ms/frame
 | 
			
		||||
+on this laptop for FHD SGRBG10 (unpacked) bayer data.
 | 
			
		||||
+
 | 
			
		||||
+Measuring power consumption
 | 
			
		||||
+---------------------------
 | 
			
		||||
+
 | 
			
		||||
+Since the Software ISP is often used on mobile devices it is also
 | 
			
		||||
+important to measure power consumption and ensure that that does
 | 
			
		||||
+not regress.
 | 
			
		||||
+
 | 
			
		||||
+For example to measure power consumption on a Lenovo ThinkPad X1 Yoga Gen 8
 | 
			
		||||
+it needs to be running on battery and it should be configured with its
 | 
			
		||||
+platform-profile (/sys/firmware/acpi/platform_profile) set to balanced and
 | 
			
		||||
+with its default turbo and frequency-scaling behavior to match real world usage.
 | 
			
		||||
+
 | 
			
		||||
+Then start qcam to capture a FHD picture at 30 fps and position the qcam window
 | 
			
		||||
+so that it is fully visible. After this run the following command to monitor
 | 
			
		||||
+the power consumption:
 | 
			
		||||
+
 | 
			
		||||
+.. code-block::
 | 
			
		||||
+
 | 
			
		||||
+   watch -n 10 cat /sys/class/power_supply/BAT0/power_now /sys/class/hwmon/hwmon6/fan?_input
 | 
			
		||||
+
 | 
			
		||||
+Note this not only measures the power consumption in ųW it also monitors
 | 
			
		||||
+the speed of this laptop's 2 fans. This is important because depending on
 | 
			
		||||
+the ambient temperature the 2 fans may spin up while testing and this
 | 
			
		||||
+will cause an additional power consumption of approx. 0.5W messing up
 | 
			
		||||
+the measurement.
 | 
			
		||||
+
 | 
			
		||||
+After starting qcam + the watch command let the laptop sit without using
 | 
			
		||||
+it for 2 minutes for the readings to stabilize. Then check that the fans
 | 
			
		||||
+have not turned on and manually take a couple of consecutive power readings
 | 
			
		||||
+and avarage these.
 | 
			
		||||
+
 | 
			
		||||
+On the example Lenovo ThinkPad X1 Yoga Gen 8 laptop this results in
 | 
			
		||||
+a measured power consumption of approx. 13W while running qcam versus
 | 
			
		||||
+approx. 4-5W while setting idle with its OLED panel on.
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										95
									
								
								modules/nixos/ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								modules/nixos/ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
From 9bec33e5c7e6765734eeef2d22d7f7f65dee2264 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Tue, 19 Dec 2023 15:45:51 +0100
 | 
			
		||||
Subject: [PATCH 24/25] ov01a1s HACK
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 src/libcamera/camera_sensor.cpp            | 6 ++++++
 | 
			
		||||
 src/libcamera/software_isp/debayer_cpu.cpp | 8 ++++++++
 | 
			
		||||
 src/libcamera/software_isp/swstats_cpu.cpp | 5 +++++
 | 
			
		||||
 3 files changed, 19 insertions(+)
 | 
			
		||||
 | 
			
		||||
diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
 | 
			
		||||
index f19f72ea..7ad4b9ef 100644
 | 
			
		||||
--- a/src/libcamera/camera_sensor.cpp
 | 
			
		||||
+++ b/src/libcamera/camera_sensor.cpp
 | 
			
		||||
@@ -34,6 +34,9 @@
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
 
 | 
			
		||||
+// HACK HACK
 | 
			
		||||
+bool is_ov01a1s = false;
 | 
			
		||||
+
 | 
			
		||||
 LOG_DEFINE_CATEGORY(CameraSensor)
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
@@ -426,6 +429,9 @@ int CameraSensor::initProperties()
 | 
			
		||||
 	model_ = subdev_->model();
 | 
			
		||||
 	properties_.set(properties::Model, utils::toAscii(model_));
 | 
			
		||||
 
 | 
			
		||||
+	if (model_ == "ov01a1s")
 | 
			
		||||
+		is_ov01a1s = true;
 | 
			
		||||
+
 | 
			
		||||
 	/* Generate a unique ID for the sensor. */
 | 
			
		||||
 	int ret = generateId();
 | 
			
		||||
 	if (ret)
 | 
			
		||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
index 41c8805f..b6393925 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
 | 
			
		||||
@@ -23,6 +23,8 @@
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
 
 | 
			
		||||
+extern bool is_ov01a1s;
 | 
			
		||||
+
 | 
			
		||||
 DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
 | 
			
		||||
 	: stats_(std::move(stats)), gamma_correction_(1.0)
 | 
			
		||||
 {
 | 
			
		||||
@@ -471,6 +473,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 | 
			
		||||
 	BayerFormat bayerFormat =
 | 
			
		||||
 		BayerFormat::fromPixelFormat(inputFormat);
 | 
			
		||||
 
 | 
			
		||||
+	if (is_ov01a1s)
 | 
			
		||||
+		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
 | 
			
		||||
+
 | 
			
		||||
 	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
 | 
			
		||||
 	    bayerFormat.packing == BayerFormat::Packing::None &&
 | 
			
		||||
 	    isStandardBayerOrder(bayerFormat.order)) {
 | 
			
		||||
@@ -548,6 +553,9 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF
 | 
			
		||||
 	BayerFormat bayerFormat =
 | 
			
		||||
 		BayerFormat::fromPixelFormat(inputFormat);
 | 
			
		||||
 
 | 
			
		||||
+	if (is_ov01a1s)
 | 
			
		||||
+		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
 | 
			
		||||
+
 | 
			
		||||
 	swapRedBlueGains_ = false;
 | 
			
		||||
 
 | 
			
		||||
 	switch (outputFormat) {
 | 
			
		||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
index 96e21be5..503ce799 100644
 | 
			
		||||
--- a/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
 | 
			
		||||
@@ -19,6 +19,8 @@
 | 
			
		||||
 
 | 
			
		||||
 namespace libcamera {
 | 
			
		||||
 
 | 
			
		||||
+extern bool is_ov01a1s;
 | 
			
		||||
+
 | 
			
		||||
 SwStatsCpu::SwStatsCpu()
 | 
			
		||||
 	: SwStats()
 | 
			
		||||
 {
 | 
			
		||||
@@ -301,6 +303,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
 | 
			
		||||
 	BayerFormat bayerFormat =
 | 
			
		||||
 		BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
 | 
			
		||||
 
 | 
			
		||||
+	if (is_ov01a1s)
 | 
			
		||||
+		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
 | 
			
		||||
+
 | 
			
		||||
 	startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats;
 | 
			
		||||
 	finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats;
 | 
			
		||||
 
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
From 4f2c94ba8b7f9f4d85a1d7e03f4c5272d92c3361 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
Date: Wed, 24 Jan 2024 20:44:29 +0100
 | 
			
		||||
Subject: [PATCH 25/25] libcamera: debayer_cpu: Make the minimum size 1280x720
 | 
			
		||||
 | 
			
		||||
pipewire + firefox default to what looks like 640x480 if we export
 | 
			
		||||
the entire supported cropping range. Hardcode 720p as minsize for now.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | 
			
		||||
---
 | 
			
		||||
 include/libcamera/internal/software_isp/debayer.h | 13 ++++++++++---
 | 
			
		||||
 1 file changed, 10 insertions(+), 3 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/include/libcamera/internal/software_isp/debayer.h b/include/libcamera/internal/software_isp/debayer.h
 | 
			
		||||
index 39e6f393..4348173d 100644
 | 
			
		||||
--- a/include/libcamera/internal/software_isp/debayer.h
 | 
			
		||||
+++ b/include/libcamera/internal/software_isp/debayer.h
 | 
			
		||||
@@ -112,9 +112,16 @@ public:
 | 
			
		||||
 			return {};
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
-		return SizeRange(Size(pattern_size.width, pattern_size.height),
 | 
			
		||||
-				 Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
 | 
			
		||||
-				      (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1)),
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * pipewire + firefox default to what looks like 640x480
 | 
			
		||||
+		 * if we export the entire supported cropping range.
 | 
			
		||||
+		 * Hardcode 720p as minsize for now. Minsize should be
 | 
			
		||||
+		 * Size(pattern_size.width, pattern_size.height)
 | 
			
		||||
+		 */
 | 
			
		||||
+		unsigned int w = (inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1);
 | 
			
		||||
+		unsigned int h = (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1);
 | 
			
		||||
+		return SizeRange(Size(std::min(w, 1280u), std::min(h, 720u)),
 | 
			
		||||
+				 Size(w, h),
 | 
			
		||||
 				 pattern_size.width, pattern_size.height);
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-- 
 | 
			
		||||
2.43.0
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue