chore(users/flokli/ipu6-softisp): refresh libcamera patches

Refresh them with the patches from
https://patchwork.libcamera.org/cover/19663/.

This is still based off v0.2.0.

Change-Id: I875fd64e3bb71a95c92af1108a23d27c0f3494e0
Reviewed-on: https://cl.tvl.fyi/c/depot/+/11179
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
Autosubmit: flokli <flokli@flokli.de>
This commit is contained in:
Florian Klink 2024-03-17 16:11:22 +02:00 committed by clbot
parent 622efa86fa
commit 9948eb64d1
33 changed files with 3861 additions and 3655 deletions

View file

@ -3,39 +3,36 @@
let
libcamera = pkgs.libcamera.overrideAttrs (old: {
mesonFlags = old.mesonFlags or [ ] ++ [
"-Dpipelines=simple/simple,ipu3,uvcvideo"
"-Dipas=simple/simple,ipu3"
"-Dpipelines=simple,ipu3,uvcvideo"
"-Dipas=simple,ipu3"
];
# This is
# https://copr-dist-git.fedorainfracloud.org/cgit/jwrdegoede/ipu6-softisp/libcamera.git/plain/libcamera-0.2.0-softisp.patch?h=f39&id=60e6b3d5e366a360a75942073dc0d642e4900982,
# but manually piped to git and back, as some renames were not processed properly.
# It was later refreshed with https://patchwork.libcamera.org/cover/19663/
patches = old.patches or [ ] ++ [
./libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch
./libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch
./libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch
./libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch
./libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch
./libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch
./libcamera/0007-libcamera-software_isp-Add-SwStats-base-class.patch
./libcamera/0008-libcamera-software_isp-Add-SwStatsCpu-class.patch
./libcamera/0009-libcamera-software_isp-Add-Debayer-base-class.patch
./libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch
./libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch
./libcamera/0012-libcamera-ipa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch
./libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch
./libcamera/0014-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
./libcamera/0015-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
./libcamera/0016-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
./libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
./libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch
./libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch
./libcamera/0020-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
./libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch
./libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch
./libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
./libcamera/0024-ov01a1s-HACK.patch
./libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
./libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch
./libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch
./libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch
./libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch
./libcamera/0009-libcamera-ipa-add-Soft-IPA.patch
./libcamera/0010-libcamera-introduce-SoftwareIsp.patch
./libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
./libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
./libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
./libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
./libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch
./libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
./libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
./libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch
./libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
./libcamera/0020-ov01a1s-HACK.patch
./libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
];
});

View file

@ -1,34 +1,73 @@
From aa818f7b749122f916be1ced48d1a3a2b3aeb47e Mon Sep 17 00:00:00 2001
From d86746fc1739f678e4bafe43f5047cba9b6b053e 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
Date: Mon, 11 Mar 2024 15:15:05 +0100
Subject: [PATCH 01/21] 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.
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>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
src/libcamera/pipeline/simple/simple.cpp | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
src/libcamera/pipeline/simple/simple.cpp | 37 ++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 911051b2..4d0e7255 100644
index 911051b2..a84f6760 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -997,10 +997,13 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
@@ -881,6 +881,30 @@ SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera,
{
}
+namespace {
+
+static Size adjustSize(const Size &requestedSize, const SizeRange &supportedSizes)
+{
+ ASSERT(supportedSizes.min <= supportedSizes.max);
+
+ if (supportedSizes.min == supportedSizes.max)
+ return supportedSizes.max;
+
+ unsigned int hStep = supportedSizes.hStep;
+ unsigned int vStep = supportedSizes.vStep;
+
+ if (hStep == 0)
+ hStep = supportedSizes.max.width - supportedSizes.min.width;
+ if (vStep == 0)
+ vStep = supportedSizes.max.height - supportedSizes.min.height;
+
+ Size adjusted = requestedSize.boundedTo(supportedSizes.max).expandedTo(supportedSizes.min);
+
+ return adjusted.shrunkBy(supportedSizes.min).alignedDownTo(hStep, vStep).grownBy(supportedSizes.min);
+}
+
+} /* namespace */
+
CameraConfiguration::Status SimpleCameraConfiguration::validate()
{
const CameraSensor *sensor = data_->sensor_.get();
@@ -997,10 +1021,19 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
}
if (!pipeConfig_->outputSizes.contains(cfg.size)) {
+ Size adjustedSize = pipeConfig_->captureSize;
+ /*
+ * The converter (when present) may not be able to output
+ * a size identical to its input size. The capture size is thus
+ * not guaranteed to be a valid output size. In such cases, use
+ * the smaller valid output size closest to the requested.
+ */
+ if (!pipeConfig_->outputSizes.contains(adjustedSize))
+ adjustedSize = pipeConfig_->outputSizes.max;
+ adjustedSize = adjustSize(cfg.size, pipeConfig_->outputSizes);
LOG(SimplePipeline, Debug)
<< "Adjusting size from " << cfg.size
- << " to " << pipeConfig_->captureSize;
@ -39,5 +78,5 @@ index 911051b2..4d0e7255 100644
}
--
2.43.0
2.43.2

View file

@ -1,7 +1,7 @@
From ca3bcfde49f069a85f7860f61d8c3bd196f97139 Mon Sep 17 00:00:00 2001
From 96e50c6a43352a9cb81d558fea27e580f2b26585 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
Date: Mon, 11 Mar 2024 15:15:06 +0100
Subject: [PATCH 02/21] libcamera: internal: Move dma_heaps.[h, cpp] to common
directories
DmaHeap class is useful outside the RPi pipeline handler too.
@ -9,20 +9,26 @@ 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>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
---
.../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(-)
.../libcamera/internal}/dma_heaps.h | 4 -
include/libcamera/internal/meson.build | 1 +
src/libcamera/dma_heaps.cpp | 127 ++++++++++++++++++
src/libcamera/meson.build | 1 +
src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp | 90 -------------
src/libcamera/pipeline/rpi/vc4/meson.build | 1 -
src/libcamera/pipeline/rpi/vc4/vc4.cpp | 5 +-
7 files changed, 131 insertions(+), 98 deletions(-)
rename {src/libcamera/pipeline/rpi/vc4 => include/libcamera/internal}/dma_heaps.h (92%)
rename src/libcamera/{pipeline/rpi/vc4 => }/dma_heaps.cpp (83%)
create mode 100644 src/libcamera/dma_heaps.cpp
delete mode 100644 src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
similarity index 92%
@ -59,85 +65,139 @@ index 7f1f3440..33eb0fb3 100644
'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
diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
new file mode 100644
index 00000000..38ef175a
--- /dev/null
+++ 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>
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Raspberry Pi Ltd
+ *
+ * dma_heaps.h - Helper class for dma-heap allocations.
+ */
+
+#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 {
+#include <array>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <linux/dma-buf.h>
+#include <linux/dma-heap.h>
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file dma_heaps.cpp
+ * \brief CMA dma-heap allocator
+ */
+
+/*
+ * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
+ * to only have to worry about importing.
+ *
+ * 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"
+};
+
+namespace libcamera {
+
+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";
+
+/**
+ * \class DmaHeap
+ * \brief Helper class for CMA dma-heap allocations
+ */
+
+/**
+ * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor
+ *
+ * Goes through the internal list of possible names of the CMA dma-heap devices
+ * until a CMA dma-heap device is successfully opened. If it fails to open any
+ * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap
+ * object owns a wrapped dma-heap file descriptor.
+ *
+ * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it.
+ */
+DmaHeap::DmaHeap()
+{
+ 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;
+ }
+
+ dmaHeapHandle_ = UniqueFD(ret);
+ break;
+ }
+
+ if (!dmaHeapHandle_.isValid())
+ 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 */
+}
+
+/**
+ * \brief Destroy the DmaHeap instance
+ *
+ * Destroying a DmaHeap instance which owns a wrapped dma-heap file descriptor
+ * closes the descriptor automatically.
+ */
+DmaHeap::~DmaHeap() = default;
+
+/**
+ * \fn DmaHeap::isValid()
+ * \brief Check if the DmaHeap instance is valid
+ * \return True if the DmaHeap is valid, false otherwise
+ */
+
+/**
+ * \brief Allocate a dma-buf from the DmaHeap
+ * \param [in] name The name to set for the allocated buffer
+ * \param [in] size The size of the buffer to allocate
+ * \return The \ref UniqueFD of the allocated buffer
+ *
+ * Allocates a dma-buf with read/write access.
+ * If the allocation fails returns invalid UniqueFD.
+ */
+UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
+{
+ int ret;
+
+ if (!name)
+ return {};
+
+ struct dma_heap_allocation_data alloc = {};
+
+ alloc.len = size;
+ alloc.fd_flags = O_CLOEXEC | O_RDWR;
+
+ ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
+ if (ret < 0) {
+ LOG(DmaHeap, Error) << "dmaHeap allocation failure for " << name;
+ return {};
+ }
+
+ UniqueFD allocFd(alloc.fd);
+ ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
+ if (ret < 0) {
+ LOG(DmaHeap, Error) << "dmaHeap naming failure for " << name;
+ return {};
+ }
+
+ return allocFd;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 45f63e93..3c5e43df 100644
--- a/src/libcamera/meson.build
@ -150,6 +210,102 @@ index 45f63e93..3c5e43df 100644
'fence.cpp',
'formats.cpp',
'framebuffer.cpp',
diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
deleted file mode 100644
index 317b1fc1..00000000
--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2020, Raspberry Pi Ltd
- *
- * dma_heaps.h - Helper class for dma-heap allocations.
- */
-
-#include "dma_heaps.h"
-
-#include <array>
-#include <fcntl.h>
-#include <linux/dma-buf.h>
-#include <linux/dma-heap.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-#include <libcamera/base/log.h>
-
-/*
- * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
- * to only have to worry about importing.
- *
- * 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"
-};
-
-namespace libcamera {
-
-LOG_DECLARE_CATEGORY(RPI)
-
-namespace RPi {
-
-DmaHeap::DmaHeap()
-{
- for (const char *name : heapNames) {
- int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
- if (ret < 0) {
- ret = errno;
- LOG(RPI, Debug) << "Failed to open " << name << ": "
- << strerror(ret);
- continue;
- }
-
- dmaHeapHandle_ = UniqueFD(ret);
- break;
- }
-
- if (!dmaHeapHandle_.isValid())
- LOG(RPI, Error) << "Could not open any dmaHeap device";
-}
-
-DmaHeap::~DmaHeap() = default;
-
-UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
-{
- int ret;
-
- if (!name)
- return {};
-
- struct dma_heap_allocation_data alloc = {};
-
- alloc.len = size;
- alloc.fd_flags = O_CLOEXEC | O_RDWR;
-
- ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
- if (ret < 0) {
- LOG(RPI, Error) << "dmaHeap allocation failure for "
- << name;
- return {};
- }
-
- UniqueFD allocFd(alloc.fd);
- ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
- if (ret < 0) {
- LOG(RPI, Error) << "dmaHeap naming failure for "
- << name;
- return {};
- }
-
- return allocFd;
-}
-
-} /* namespace RPi */
-
-} /* namespace libcamera */
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
@ -190,5 +346,5 @@ index 26102ea7..3a42e75e 100644
struct Config {
--
2.43.0
2.43.2

View file

@ -1,7 +1,7 @@
From 6d5f3b0b54df4ff66079675a4c1f0f0b76778e22 Mon Sep 17 00:00:00 2001
From 5df9bc3b2a3d86bcc8504896cc87d7fcb5aea3a4 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
Date: Mon, 11 Mar 2024 15:15:07 +0100
Subject: [PATCH 03/21] libcamera: dma_heaps: extend DmaHeap class to support
system heap
Add an argument to the constructor to specify dma heaps type(s)
@ -9,17 +9,19 @@ 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>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
include/libcamera/internal/dma_heaps.h | 12 +++++++-
src/libcamera/dma_heaps.cpp | 39 +++++++++++++++-----------
2 files changed, 34 insertions(+), 17 deletions(-)
include/libcamera/internal/dma_heaps.h | 12 ++++-
src/libcamera/dma_heaps.cpp | 67 ++++++++++++++++++++------
2 files changed, 63 insertions(+), 16 deletions(-)
diff --git a/include/libcamera/internal/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
index cff8f140..22aa1007 100644
index cff8f140..80bf29e7 100644
--- a/include/libcamera/internal/dma_heaps.h
+++ b/include/libcamera/internal/dma_heaps.h
@@ -9,6 +9,7 @@
@ -36,8 +38,8 @@ index cff8f140..22aa1007 100644
public:
- DmaHeap();
+ enum class DmaHeapFlag {
+ Cma = (1 << 0),
+ System = (1 << 1),
+ Cma = 1 << 0,
+ System = 1 << 1,
+ };
+
+ using DmaHeapFlags = Flags<DmaHeapFlag>;
@ -54,68 +56,114 @@ index cff8f140..22aa1007 100644
+
} /* namespace libcamera */
diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
index 7444d9c2..177de31b 100644
index 38ef175a..d0e33ce6 100644
--- a/src/libcamera/dma_heaps.cpp
+++ b/src/libcamera/dma_heaps.cpp
@@ -16,6 +16,8 @@
@@ -19,9 +19,11 @@
#include "libcamera/internal/dma_heaps.h"
/**
* \file dma_heaps.cpp
- * \brief CMA dma-heap allocator
+ * \brief dma-heap allocator
*/
+namespace libcamera {
+
/*
* /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
* /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 @@
@@ -29,42 +31,77 @@
* 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")
+
+/**
+ * \struct DmaHeapInfo
+ * \brief Tells what type of dma-heap the dma-heap represented by the device node name is
+ * \var DmaHeapInfo::flag
+ * \brief The type of the dma-heap
+ * \var DmaHeapInfo::name
+ * \brief The dma-heap's device node name
+ */
+struct DmaHeapInfo {
+ DmaHeap::DmaHeapFlag flag;
+ const char *name;
};
-namespace libcamera {
-
+static constexpr std::array<DmaHeapInfo, 3> heapInfos = {
+ { /* CMA heap names first */
+ { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma" },
+ { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved" },
+ { DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system" } }
+};
LOG_DEFINE_CATEGORY(DmaHeap)
/**
* \class DmaHeap
- * \brief Helper class for CMA dma-heap allocations
+ * \brief Helper class for dma-heap allocations
*/
/**
- * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor
+ * \enum DmaHeap::DmaHeapFlag
+ * \brief Type of the dma-heap
+ * \var DmaHeap::Cma
+ * \brief Allocate from a CMA dma-heap
+ * \var DmaHeap::System
+ * \brief Allocate from the system dma-heap
+ */
+
+/**
+ * \typedef DmaHeap::DmaHeapFlags
+ * \brief A bitwise combination of DmaHeap::DmaHeapFlag values
+ */
+
+/**
+ * \brief Construct a DmaHeap that owns a CMA or system dma-heap file descriptor
+ * \param [in] flags The type(s) of the dma-heap(s) to allocate from
*
- * Goes through the internal list of possible names of the CMA dma-heap devices
- * until a CMA dma-heap device is successfully opened. If it fails to open any
- * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap
- * object owns a wrapped dma-heap file descriptor.
+ * By default \a flags are set to DmaHeap::DmaHeapFlag::Cma. The constructor goes
+ * through the internal list of possible names of the CMA and system dma-heap devices
+ * until the dma-heap device of the requested type is successfully opened. If more
+ * than one dma-heap type is specified in flags the CMA heap is tried first. If it
+ * fails to open any dma-heap device an invalid DmaHeap object is constructed.
+ * A valid DmaHeap object owns a wrapped dma-heap file descriptor.
*
* Please check the new DmaHeap object with \ref DmaHeap::isValid before using it.
*/
-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;
+ }
+ for (const auto &info : heapInfos) {
+ if (!(flags & info.flag))
+ continue;
+
+ LOG(DmaHeap, Debug) << "Using " << name.second;
+ dmaHeapHandle_ = UniqueFD(ret);
+ break;
+ }
}
+ int ret = ::open(info.name, O_RDWR | O_CLOEXEC, 0);
if (ret < 0) {
ret = errno;
LOG(DmaHeap, Debug)
- << "Failed to open " << name << ": "
+ << "Failed to open " << info.name << ": "
<< strerror(ret);
continue;
}
if (!dmaHeapHandle_.isValid())
+ LOG(DmaHeap, Debug) << "Using " << info.name;
dmaHeapHandle_ = UniqueFD(ret);
break;
}
--
2.43.0
2.43.2

View file

@ -1,21 +1,23 @@
From 006a4a31a6803e92ec67f48b66da2cdff8b2f6ab Mon Sep 17 00:00:00 2001
From a6777760a2121f02808baecea504ac0e242f860b 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
Date: Mon, 11 Mar 2024 15:15:08 +0100
Subject: [PATCH 04/21] 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>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
---
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%)
include/libcamera/internal/meson.build | 1 +
.../libcamera/internal}/shared_mem_object.h | 6 +-----
2 files changed, 2 insertions(+), 5 deletions(-)
rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (97%)
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index 33eb0fb3..5807dfd9 100644
@ -30,12 +32,22 @@ index 33eb0fb3..5807dfd9 100644
'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%
similarity index 97%
rename from src/libcamera/pipeline/rpi/common/shared_mem_object.h
rename to include/libcamera/internal/shared_mem_object.h
index aa56c220..bfb639ee 100644
index aa56c220..98636b44 100644
--- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h
+++ b/include/libcamera/internal/shared_mem_object.h
@@ -6,8 +6,8 @@
*/
#pragma once
-#include <cstddef>
#include <fcntl.h>
+#include <stddef.h>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
@@ -19,8 +19,6 @@
namespace libcamera {
@ -53,5 +65,5 @@ index aa56c220..bfb639ee 100644
-
} /* namespace libcamera */
--
2.43.0
2.43.2

View file

@ -1,139 +0,0 @@
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

View file

@ -0,0 +1,403 @@
From f94af21adc1889706127d07c5425f44c9cec9a95 Mon Sep 17 00:00:00 2001
From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
Date: Mon, 11 Mar 2024 15:15:09 +0100
Subject: [PATCH 05/21] libcamera: shared_mem_object: reorganize the code and
document the SharedMemObject class
Split the parts which doesn't otherwise depend on the type T or
arguments Args out of the SharedMemObject class into a new
SharedMem class.
Doxygen documentation by Dennis Bonke and Andrei Konovalov.
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
---
.../libcamera/internal/shared_mem_object.h | 101 ++++++----
src/libcamera/meson.build | 1 +
src/libcamera/shared_mem_object.cpp | 190 ++++++++++++++++++
3 files changed, 253 insertions(+), 39 deletions(-)
create mode 100644 src/libcamera/shared_mem_object.cpp
diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
index 98636b44..43b07c9d 100644
--- a/include/libcamera/internal/shared_mem_object.h
+++ b/include/libcamera/internal/shared_mem_object.h
@@ -6,12 +6,9 @@
*/
#pragma once
-#include <fcntl.h>
#include <stddef.h>
#include <string>
#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include <utility>
#include <libcamera/base/class.h>
@@ -19,58 +16,92 @@
namespace libcamera {
+class SharedMem
+{
+public:
+ SharedMem()
+ : mem_(nullptr)
+ {
+ }
+
+ SharedMem(const std::string &name, std::size_t size);
+
+ SharedMem(SharedMem &&rhs)
+ {
+ this->name_ = std::move(rhs.name_);
+ this->fd_ = std::move(rhs.fd_);
+ this->mem_ = rhs.mem_;
+ rhs.mem_ = nullptr;
+ }
+
+ virtual ~SharedMem()
+ {
+ if (mem_)
+ munmap(mem_, size_);
+ }
+
+ /* Make SharedMem non-copyable for now. */
+ LIBCAMERA_DISABLE_COPY(SharedMem)
+
+ SharedMem &operator=(SharedMem &&rhs)
+ {
+ this->name_ = std::move(rhs.name_);
+ this->fd_ = std::move(rhs.fd_);
+ this->mem_ = rhs.mem_;
+ rhs.mem_ = nullptr;
+ return *this;
+ }
+
+ const SharedFD &fd() const
+ {
+ return fd_;
+ }
+
+ void *mem() const
+ {
+ return mem_;
+ }
+
+private:
+ std::string name_;
+ SharedFD fd_;
+ size_t size_;
+protected:
+ void *mem_;
+};
+
template<class T>
-class SharedMemObject
+class SharedMemObject : public SharedMem
{
public:
static constexpr std::size_t SIZE = sizeof(T);
SharedMemObject()
- : obj_(nullptr)
+ : SharedMem(), obj_(nullptr)
{
}
template<class... Args>
SharedMemObject(const std::string &name, Args &&...args)
- : name_(name), obj_(nullptr)
+ : SharedMem(name, SIZE), obj_(nullptr)
{
- void *mem;
- int ret;
-
- ret = memfd_create(name_.c_str(), MFD_CLOEXEC);
- if (ret < 0)
- return;
-
- fd_ = SharedFD(std::move(ret));
- if (!fd_.isValid())
- return;
-
- ret = ftruncate(fd_.get(), SIZE);
- if (ret < 0)
+ if (mem_ == nullptr)
return;
- mem = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
- fd_.get(), 0);
- if (mem == MAP_FAILED)
- return;
-
- obj_ = new (mem) T(std::forward<Args>(args)...);
+ obj_ = new (mem_) T(std::forward<Args>(args)...);
}
SharedMemObject(SharedMemObject<T> &&rhs)
+ : SharedMem(std::move(rhs))
{
- this->name_ = std::move(rhs.name_);
- this->fd_ = std::move(rhs.fd_);
this->obj_ = rhs.obj_;
rhs.obj_ = nullptr;
}
~SharedMemObject()
{
- if (obj_) {
+ if (obj_)
obj_->~T();
- munmap(obj_, SIZE);
- }
}
/* Make SharedMemObject non-copyable for now. */
@@ -78,8 +109,7 @@ public:
SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
{
- this->name_ = std::move(rhs.name_);
- this->fd_ = std::move(rhs.fd_);
+ SharedMem::operator=(std::move(rhs));
this->obj_ = rhs.obj_;
rhs.obj_ = nullptr;
return *this;
@@ -105,19 +135,12 @@ public:
return *obj_;
}
- const SharedFD &fd() const
- {
- return fd_;
- }
-
explicit operator bool() const
{
return !!obj_;
}
private:
- std::string name_;
- SharedFD fd_;
T *obj_;
};
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 3c5e43df..94a95ae3 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',
+ 'shared_mem_object.cpp',
'source_paths.cpp',
'stream.cpp',
'sysfs.cpp',
diff --git a/src/libcamera/shared_mem_object.cpp b/src/libcamera/shared_mem_object.cpp
new file mode 100644
index 00000000..44fe74c2
--- /dev/null
+++ b/src/libcamera/shared_mem_object.cpp
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Raspberry Pi Ltd
+ *
+ * shared_mem_object.cpp - Helper class for shared memory allocations
+ */
+
+#include "libcamera/internal/shared_mem_object.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+/**
+ * \file shared_mem_object.cpp
+ * \brief Helper class for shared memory allocations
+ */
+
+namespace libcamera {
+
+/**
+ * \class SharedMem
+ * \brief Helper class for allocating shared memory
+ *
+ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries.
+ *
+ * SharedMem allocates the shared memory of the given size and maps it.
+ * To check that the shared memory was allocated and mapped successfully, one
+ * needs to verify that the pointer to the shared memory returned by SharedMem::mem()
+ * is not nullptr.
+ *
+ * To access the shared memory from another process the SharedFD should be passed
+ * to that process, and then the shared memory should be mapped into that process
+ * address space by calling mmap().
+ *
+ * A single memfd is created for every SharedMem. If there is a need to allocate
+ * a large number of objects in shared memory, these objects should be grouped
+ * together and use the shared memory allocated by a single SharedMem object if
+ * possible. This will help to minimize the number of created memfd's.
+ */
+
+/**
+ * \fn SharedMem::SharedMem(const std::string &name, std::size_t size)
+ * \brief Constructor for the SharedMem
+ * \param[in] name Name of the SharedMem
+ * \param[in] size Size of the shared memory to allocate and map
+ */
+
+/**
+ * \fn SharedMem::SharedMem(SharedMem &&rhs)
+ * \brief Move constructor for SharedMem
+ * \param[in] rhs The object to move
+ */
+
+/**
+ * \fn SharedMem::~SharedMem()
+ * \brief SharedMem destructor
+ *
+ * Unmaps the allocated shared memory. Decrements the shared memory descriptor use
+ * count.
+ */
+
+/**
+ * \fn SharedMem &SharedMem::operator=(SharedMem &&rhs)
+ * \brief Move constructor for SharedMem
+ * \param[in] rhs The object to move
+ */
+
+/**
+ * \fn const SharedFD &SharedMem::fd() const
+ * \brief Gets the file descriptor for the underlying shared memory
+ * \return The file descriptor
+ */
+
+/**
+ * \fn void *SharedMem::mem() const
+ * \brief Gets the pointer to the underlying shared memory
+ * \return The pointer to the shared memory
+ */
+
+SharedMem::SharedMem(const std::string &name, std::size_t size)
+ : name_(name), size_(size), mem_(nullptr)
+{
+ int fd = memfd_create(name_.c_str(), MFD_CLOEXEC);
+ if (fd < 0)
+ return;
+
+ fd_ = SharedFD(std::move(fd));
+ if (!fd_.isValid())
+ return;
+
+ if (ftruncate(fd_.get(), size_) < 0)
+ return;
+
+ mem_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd_.get(), 0);
+ if (mem_ == MAP_FAILED)
+ mem_ = nullptr;
+}
+
+/**
+ * \var SharedMem::mem_
+ * \brief Pointer to the shared memory allocated
+ */
+
+/**
+ * \class SharedMemObject
+ * \brief Helper class for allocating objects in shared memory
+ *
+ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries.
+ *
+ * Given the type of the object to be created in shared memory and the arguments
+ * to pass to this object's constructor, SharedMemObject allocates the shared memory
+ * of the size of the object and constructs the object in this memory. To ensure
+ * that the SharedMemObject was created successfully, one needs to verify that the
+ * overloaded bool() operator returns true. The object created in the shared memory
+ * can be accessed using the SharedMemObject::operator*() indirection operator. Its
+ * members can be accessed with the SharedMemObject::operator->() member of pointer
+ * operator.
+ *
+ * To access the object from another process the SharedFD should be passed to that
+ * process, and the shared memory should be mapped by calling mmap().
+ *
+ * A single memfd is created for every SharedMemObject. If there is a need to allocate
+ * a large number of objects in shared memory, these objects should be grouped into a
+ * single large object to keep the number of created memfd's reasonably small.
+ */
+
+/**
+ * \var SharedMemObject::SIZE
+ * \brief The size of the object that is going to be stored here
+ */
+
+/**
+ * \fn SharedMemObject< T >::SharedMemObject(const std::string &name, Args &&...args)
+ * \brief Constructor for the SharedMemObject
+ * \param[in] name Name of the SharedMemObject
+ * \param[in] args Args to pass to the constructor of the object in shared memory
+ */
+
+/**
+ * \fn SharedMemObject::SharedMemObject(SharedMemObject<T> &&rhs)
+ * \brief Move constructor for SharedMemObject
+ * \param[in] rhs The object to move
+ */
+
+/**
+ * \fn SharedMemObject::~SharedMemObject()
+ * \brief SharedMemObject destructor
+ *
+ * Destroys the object created in the shared memory and then unmaps the shared memory.
+ * Decrements the shared memory descriptor use count.
+ */
+
+/**
+ * \fn SharedMemObject::operator=(SharedMemObject<T> &&rhs)
+ * \brief Operator= for SharedMemObject
+ * \param[in] rhs The SharedMemObject object to take the data from
+ */
+
+/**
+ * \fn SharedMemObject::operator->()
+ * \brief Operator-> for SharedMemObject
+ * \return The pointer to the object
+ */
+
+/**
+ * \fn const T *SharedMemObject::operator->() const
+ * \brief Operator-> for SharedMemObject
+ * \return The pointer to the const object
+ */
+
+/**
+ * \fn SharedMemObject::operator*()
+ * \brief Operator* for SharedMemObject
+ * \return The reference to the object
+ */
+
+/**
+ * \fn const T &SharedMemObject::operator*() const
+ * \brief Operator* for SharedMemObject
+ * \return Const reference to the object
+ */
+
+/**
+ * \fn SharedMemObject::operator bool()
+ * \brief Operator bool() for SharedMemObject
+ * \return True if the object was created OK in the shared memory, false otherwise
+ */
+
+} // namespace libcamera
--
2.43.2

View file

@ -1,354 +0,0 @@
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 *&registered =
+ 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

View file

@ -0,0 +1,523 @@
From 4259b01930333c6666a185d923e6e68ec915a4fd Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Mon, 11 Mar 2024 15:15:10 +0100
Subject: [PATCH 06/21] libcamera: software_isp: Add SwStatsCpu class
Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.
This implementation offers 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 implementation also allows specifying a window over which to gather
statistics instead of processing the whole frame.
Doxygen documentation by Dennis Bonke.
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
Tested-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Co-developed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Pavel Machek <pavel@ucw.cz>
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
Co-developed-by: Marttico <g.martti@gmail.com>
Signed-off-by: Marttico <g.martti@gmail.com>
Co-developed-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>
---
include/libcamera/internal/meson.build | 1 +
.../internal/software_isp/meson.build | 5 +
.../internal/software_isp/swisp_stats.h | 38 ++++
src/libcamera/meson.build | 1 +
src/libcamera/software_isp/meson.build | 12 +
src/libcamera/software_isp/swstats_cpu.cpp | 208 ++++++++++++++++++
src/libcamera/software_isp/swstats_cpu.h | 159 +++++++++++++
7 files changed, 424 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 src/libcamera/software_isp/meson.build
create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp
create mode 100644 src/libcamera/software_isp/swstats_cpu.h
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index 5807dfd9..160fdc37 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -50,3 +50,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..66c9c3fb
--- /dev/null
+++ b/include/libcamera/internal/software_isp/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libcamera_internal_headers += files([
+ 'swisp_stats.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..afe42c9a
--- /dev/null
+++ b/include/libcamera/internal/software_isp/swisp_stats.h
@@ -0,0 +1,38 @@
+/* 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 Number of bins in the yHistogram.
+ */
+ static constexpr unsigned int kYHistogramSize = 16;
+ /**
+ * \brief A histogram of luminance values.
+ */
+ std::array<unsigned int, kYHistogramSize> yHistogram;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 94a95ae3..91e4cc60 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..fcfff74a
--- /dev/null
+++ b/src/libcamera/software_isp/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: CC0-1.0
+
+softisp_enabled = pipelines.contains('simple')
+summary({'SoftISP support' : softisp_enabled}, section : 'Configuration')
+
+if not (softisp_enabled)
+ subdir_done()
+endif
+
+libcamera_sources += files([
+ '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..448d0e4c
--- /dev/null
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
@@ -0,0 +1,208 @@
+/* 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 "swstats_cpu.h"
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/bayer_format.h"
+
+namespace libcamera {
+
+/**
+ * \class SwStatsCpu
+ * \brief Class for gathering statistics on the CPU
+ *
+ * CPU based software ISP statistics implementation.
+ *
+ * This class offers a configure function + functions to gather statistics
+ * on a line by line basis. This allows CPU based software debayering to
+ * interlace debayering and statistics gathering on a line by line basis
+ * while the input data is still hot in the cache.
+ *
+ * It is also possible to specify a window over which to gather
+ * statistics instead of processing the whole frame.
+ */
+
+LOG_DEFINE_CATEGORY(SwStatsCpu)
+
+SwStatsCpu::SwStatsCpu()
+{
+ sharedStats_ = SharedMemObject<SwIspStats>("softIsp_stats");
+ if (!sharedStats_.fd().isValid())
+ LOG(SwStatsCpu, Error)
+ << "Failed to create shared memory for statistics";
+}
+
+static const unsigned int kRedYMul = 77; /* 0.299 * 256 */
+static const unsigned int kGreenYMul = 150; /* 0.587 * 256 */
+static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */
+
+#define SWSTATS_START_LINE_STATS(pixel_t) \
+ pixel_t r, g, g2, b; \
+ unsigned int yVal; \
+ \
+ unsigned int sumR = 0; \
+ unsigned int sumG = 0; \
+ unsigned int sumB = 0;
+
+#define SWSTATS_ACCUMULATE_LINE_STATS(div) \
+ sumR += r; \
+ sumG += g; \
+ sumB += b; \
+ \
+ yVal = r * kRedYMul; \
+ yVal += g * kGreenYMul; \
+ yVal += b * kBlueYMul; \
+ stats_.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++;
+
+#define SWSTATS_FINISH_LINE_STATS() \
+ stats_.sumR_ += sumR; \
+ stats_.sumG_ += sumG; \
+ stats_.sumB_ += sumB;
+
+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;
+ const int widthInBytes = window_.width * 5 / 4;
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ SWSTATS_START_LINE_STATS(uint8_t)
+
+ /* x += 5 sample every other 2x2 block */
+ for (int x = 0; x < widthInBytes; x += 5) {
+ /* BGGR */
+ b = src0[x];
+ g = src0[x + 1];
+ g2 = src1[x];
+ r = src1[x + 1];
+ g = (g + g2) / 2;
+ /* Data is already 8 bits, divide by 1 */
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
+ }
+
+ SWSTATS_FINISH_LINE_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;
+ const int widthInBytes = window_.width * 5 / 4;
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ SWSTATS_START_LINE_STATS(uint8_t)
+
+ /* x += 5 sample every other 2x2 block */
+ for (int x = 0; x < widthInBytes; x += 5) {
+ /* GBRG */
+ g = src0[x];
+ b = src0[x + 1];
+ r = src1[x];
+ g2 = src1[x + 1];
+ g = (g + g2) / 2;
+ /* Data is already 8 bits, divide by 1 */
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
+ }
+
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+/**
+ * \brief Reset state to start statistics gathering for a new frame.
+ *
+ * This may only be called after a successful setWindow() call.
+ */
+void SwStatsCpu::startFrame(void)
+{
+ stats_.sumR_ = 0;
+ stats_.sumB_ = 0;
+ stats_.sumG_ = 0;
+ stats_.yHistogram.fill(0);
+}
+
+/**
+ * \brief Finish statistics calculation for the current frame.
+ *
+ * This may only be called after a successful setWindow() call.
+ */
+void SwStatsCpu::finishFrame(void)
+{
+ *sharedStats_ = stats_;
+ statsReady.emit(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
+ */
+int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
+{
+ BayerFormat bayerFormat =
+ BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+
+ if (bayerFormat.bitDepth == 10 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ patternSize_.height = 2;
+ patternSize_.width = 4; /* 5 bytes per *4* pixels */
+ /* Skip every 3th and 4th line, sample every other 2x2 block */
+ ySkipMask_ = 0x02;
+ xShift_ = 0;
+
+ switch (bayerFormat.order) {
+ case BayerFormat::BGGR:
+ case BayerFormat::GRBG:
+ stats0_ = &SwStatsCpu::statsBGGR10PLine0;
+ swapLines_ = bayerFormat.order == BayerFormat::GRBG;
+ return 0;
+ case BayerFormat::GBRG:
+ case BayerFormat::RGGB:
+ stats0_ = &SwStatsCpu::statsGBRG10PLine0;
+ swapLines_ = bayerFormat.order == BayerFormat::RGGB;
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ LOG(SwStatsCpu, Info)
+ << "Unsupported input format " << inputCfg.pixelFormat.toString();
+ return -EINVAL;
+}
+
+/**
+ * \brief Specify window coordinates over which to gather statistics.
+ * \param[in] window The window object.
+ */
+void SwStatsCpu::setWindow(Rectangle window)
+{
+ window_ = window;
+
+ window_.x &= ~(patternSize_.width - 1);
+ window_.x += xShift_;
+ window_.y &= ~(patternSize_.height - 1);
+
+ /* width_ - xShift_ to make sure the window fits */
+ window_.width -= xShift_;
+ window_.width &= ~(patternSize_.width - 1);
+ window_.height &= ~(patternSize_.height - 1);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
new file mode 100644
index 00000000..0ac9ae71
--- /dev/null
+++ b/src/libcamera/software_isp/swstats_cpu.h
@@ -0,0 +1,159 @@
+/* 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 <stdint.h>
+
+#include <libcamera/base/signal.h>
+
+#include <libcamera/geometry.h>
+
+#include "libcamera/internal/shared_mem_object.h"
+#include "libcamera/internal/software_isp/swisp_stats.h"
+
+namespace libcamera {
+
+class PixelFormat;
+struct StreamConfiguration;
+
+class SwStatsCpu
+{
+public:
+ SwStatsCpu();
+ ~SwStatsCpu() = default;
+
+ /**
+ * \brief Gets whether the statistics object is valid.
+ *
+ * \return true if it's valid, false otherwise
+ */
+ bool isValid() const { return sharedStats_.fd().isValid(); }
+
+ /**
+ * \brief Get the file descriptor for the statistics.
+ *
+ * \return the file descriptor
+ */
+ const SharedFD &getStatsFD() { return sharedStats_.fd(); }
+
+ /**
+ * \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_; }
+
+ int configure(const StreamConfiguration &inputCfg);
+ void setWindow(Rectangle window);
+ void startFrame();
+ void finishFrame();
+
+ /**
+ * \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 & ySkipMask_) || 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 & ySkipMask_) || y < (unsigned int)window_.y ||
+ y >= (window_.y + window_.height))
+ return;
+
+ (this->*stats2_)(src);
+ }
+
+ /**
+ * \brief Signals that the statistics are ready.
+ *
+ * The int parameter isn't actually used.
+ */
+ Signal<int> statsReady;
+
+private:
+ /**
+ * \brief Called when there is data to get statistics from.
+ * \param[in] src The input data
+ *
+ * These functions take an array of (patternSize_.height + 1) src
+ * pointers each pointing to a line in the source image. The middle
+ * element of the array will point to the actual line being processed.
+ * Earlier element(s) will point to the previous line(s) and later
+ * element(s) to the next line(s).
+ *
+ * See the documentation of DebayerCpu::debayerFn for more details.
+ */
+ using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
+
+ void statsBGGR10PLine0(const uint8_t *src[]);
+ void statsGBRG10PLine0(const uint8_t *src[]);
+
+ /* Variables set by configure(), used every line */
+ statsProcessFn stats0_;
+ statsProcessFn stats2_;
+ bool swapLines_;
+
+ /**
+ * \brief Skip lines where this bitmask is set in y.
+ */
+ unsigned int ySkipMask_;
+
+ /**
+ * \brief Statistics window, set by setWindow(), used ever line.
+ */
+ Rectangle window_;
+
+ /**
+ * \brief The size of the bayer pattern.
+ *
+ * Valid sizes are: 2x2, 4x2 or 4x4.
+ */
+ Size patternSize_;
+
+ /**
+ * \brief The offset of x, applied to window_.x for bayer variants.
+ *
+ * This can either be 0 or 1.
+ */
+ unsigned int xShift_;
+
+ SharedMemObject<SwIspStats> sharedStats_;
+ SwIspStats stats_;
+};
+
+} /* namespace libcamera */
--
2.43.2

View file

@ -1,7 +1,7 @@
From 8fc77447c0d76b0b52b19d23674049181c6cf8d2 Mon Sep 17 00:00:00 2001
From 25e6893e46bd2174f6913eea79817988d9280706 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
Date: Mon, 11 Mar 2024 15:15:11 +0100
Subject: [PATCH 07/21] 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
@ -9,37 +9,139 @@ 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>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
Co-developed-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>
---
.../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 +++
.../internal/software_isp/debayer_params.h | 48 ++++++++
.../internal/software_isp/meson.build | 1 +
src/libcamera/software_isp/debayer.cpp | 29 +++++
src/libcamera/software_isp/debayer.h | 104 ++++++++++++++++++
src/libcamera/software_isp/meson.build | 1 +
5 files changed, 200 insertions(+)
create mode 100644 include/libcamera/internal/software_isp/debayer.h
5 files changed, 183 insertions(+)
create mode 100644 include/libcamera/internal/software_isp/debayer_params.h
create mode 100644 src/libcamera/software_isp/debayer.cpp
create mode 100644 src/libcamera/software_isp/debayer.h
diff --git a/include/libcamera/internal/software_isp/debayer.h b/include/libcamera/internal/software_isp/debayer.h
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..39e6f393
index 00000000..98965fa1
--- /dev/null
+++ b/include/libcamera/internal/software_isp/debayer.h
@@ -0,0 +1,132 @@
+++ b/include/libcamera/internal/software_isp/debayer_params.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Red Hat Inc.
+ *
+ * Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * debayer_params.h - DebayerParams header
+ */
+
+#pragma once
+
+namespace libcamera {
+
+/**
+ * \brief Struct to hold the debayer parameters.
+ */
+struct DebayerParams {
+ /**
+ * \brief const value for 1.0 gain
+ */
+ static constexpr unsigned int kGain10 = 256;
+
+ /**
+ * \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 66c9c3fb..a620e16d 100644
--- a/include/libcamera/internal/software_isp/meson.build
+++ b/include/libcamera/internal/software_isp/meson.build
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: CC0-1.0
libcamera_internal_headers += files([
+ 'debayer_params.h',
'swisp_stats.h',
])
diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
new file mode 100644
index 00000000..64f0b5a0
--- /dev/null
+++ b/src/libcamera/software_isp/debayer.cpp
@@ -0,0 +1,29 @@
+/* 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>
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * debayer.cpp - debayer base class
+ */
+
+#include "debayer.h"
+
+namespace libcamera {
+
+/**
+ * \class Debayer
+ * \brief Base debayering class
+ *
+ * Base class that provides functions for setting up the debayering process.
+ */
+
+LOG_DEFINE_CATEGORY(Debayer)
+
+Debayer::~Debayer()
+{
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h
new file mode 100644
index 00000000..8880ff99
--- /dev/null
+++ b/src/libcamera/software_isp/debayer.h
@@ -0,0 +1,104 @@
+/* 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
+ */
@ -62,12 +164,6 @@ index 00000000..39e6f393
+
+LOG_DECLARE_CATEGORY(Debayer)
+
+/**
+ * \class Debayer
+ * \brief Base debayering class
+ *
+ * Base class that provides functions for setting up the debayering process.
+ */
+class Debayer
+{
+public:
@ -87,6 +183,8 @@ index 00000000..39e6f393
+ * \brief Get the width and height at which the bayer pattern repeats.
+ * \param[in] inputFormat The input format.
+ *
+ * Valid sizes are: 2x2, 4x2 or 4x4.
+ *
+ * \return pattern size or an empty size for unsupported inputFormats.
+ */
+ virtual Size patternSize(PixelFormat inputFormat) = 0;
@ -107,7 +205,7 @@ index 00000000..39e6f393
+ * \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;
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
+
+ /**
+ * \brief Process the bayer data into the requested format.
@ -127,31 +225,7 @@ index 00000000..39e6f393
+ *
+ * \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);
+ }
+ virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0;
+
+ /**
+ * \brief Signals when the input buffer is ready.
@ -165,108 +239,17 @@ index 00000000..39e6f393
+};
+
+} /* 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
index fcfff74a..62095f61 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
@@ -8,5 +8,6 @@ if not (softisp_enabled)
endif
libcamera_sources += files([
+ 'debayer.cpp',
'swstats.cpp',
'swstats_cpu.cpp',
+ 'debayer.cpp',
'swstats_cpu.cpp',
])
--
2.43.0
2.43.2

View file

@ -1,382 +0,0 @@
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

View file

@ -1,7 +1,7 @@
From 7eb7164ed7d90ea4cf9ec7e4f35fa8efa25f35e9 Mon Sep 17 00:00:00 2001
From 5f57a52ea1054cac73344d83ff605cba0df0d279 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
Date: Mon, 11 Mar 2024 15:15:12 +0100
Subject: [PATCH 08/21] 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
@ -9,191 +9,43 @@ 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>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Co-developed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
---
.../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
src/libcamera/software_isp/debayer_cpu.cpp | 626 +++++++++++++++++++++
src/libcamera/software_isp/debayer_cpu.h | 143 +++++
src/libcamera/software_isp/meson.build | 1 +
3 files changed, 770 insertions(+)
create mode 100644 src/libcamera/software_isp/debayer_cpu.cpp
create mode 100644 src/libcamera/software_isp/debayer_cpu.h
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
index 00000000..f932362c
--- /dev/null
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -0,0 +1,528 @@
@@ -0,0 +1,626 @@
+/* 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>
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * debayer_cpu.cpp - CPU based debayering class
+ */
+
+#include "libcamera/internal/software_isp/debayer_cpu.h"
+#include "debayer_cpu.h"
+
+#include <math.h>
+#include <stdlib.h>
@ -207,6 +59,17 @@ index 00000000..e0c3c658
+
+namespace libcamera {
+
+/**
+ * \class DebayerCpu
+ * \brief Class for debayering on the CPU
+ *
+ * Implementation for CPU based debayering
+ */
+
+/**
+ * \brief Constructs a DebayerCpu object.
+ * \param[in] stats Pointer to the stats object to use.
+ */
+DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
+ : stats_(std::move(stats)), gamma_correction_(1.0)
+{
@ -216,16 +79,16 @@ index 00000000..e0c3c658
+ enableInputMemcpy_ = true;
+#endif
+ /* Initialize gamma to 1.0 curve */
+ for (int i = 0; i < 1024; i++)
+ gamma_[i] = i / 4;
+ for (unsigned int i = 0; i < kGammaLookupSize; i++)
+ gamma_[i] = i / (kGammaLookupSize / kRGBLookupSize);
+
+ for (int i = 0; i < 5; i++)
+ lineBuffers_[i] = NULL;
+ for (unsigned int i = 0; i < kMaxLineBuffers; i++)
+ lineBuffers_[i] = nullptr;
+}
+
+DebayerCpu::~DebayerCpu()
+{
+ for (int i = 0; i < 5; i++)
+ for (unsigned int i = 0; i < kMaxLineBuffers; i++)
+ free(lineBuffers_[i]);
+}
+
@ -276,16 +139,17 @@ index 00000000..e0c3c658
+ * 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 */
+ for (int x = 0; x < width_in_bytes;) {
+ /* First pixel */
+ BGGR_BGR888(2, 1, 1)
+ /* Odd pixel BGGR -> GBRG */
+ /* Second pixel BGGR -> GBRG */
+ GBRG_BGR888(1, 1, 1)
+ /* Same thing for next 2 pixels */
+ /* Same thing for third and fourth pixels */
+ BGGR_BGR888(1, 1, 1)
+ GBRG_BGR888(1, 2, 1)
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
+ x++;
+ }
+}
+
@ -296,14 +160,16 @@ index 00000000..e0c3c658
+ 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 */
+ for (int x = 0; x < width_in_bytes;) {
+ /* First pixel */
+ GRBG_BGR888(2, 1, 1)
+ /* Odd pixel GRBG -> RGGB */
+ /* Second pixel GRBG -> RGGB */
+ RGGB_BGR888(1, 1, 1)
+ /* Same thing for next 2 pixels */
+ /* Same thing for third and fourth pixels */
+ GRBG_BGR888(1, 1, 1)
+ RGGB_BGR888(1, 2, 1)
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
+ x++;
+ }
+}
+
@ -314,7 +180,7 @@ index 00000000..e0c3c658
+ 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++) {
+ for (int x = 0; x < width_in_bytes;) {
+ /* Even pixel */
+ GBRG_BGR888(2, 1, 1)
+ /* Odd pixel GBGR -> BGGR */
@ -322,6 +188,8 @@ index 00000000..e0c3c658
+ /* Same thing for next 2 pixels */
+ GBRG_BGR888(1, 1, 1)
+ BGGR_BGR888(1, 2, 1)
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
+ x++;
+ }
+}
+
@ -332,14 +200,16 @@ index 00000000..e0c3c658
+ 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++) {
+ for (int x = 0; x < width_in_bytes;) {
+ /* Even pixel */
+ RGGB_BGR888(2, 1, 1)
+ /* Odd pixel RGGB -> GRBG*/
+ /* 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)
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
+ x++;
+ }
+}
+
@ -349,6 +219,11 @@ index 00000000..e0c3c658
+ order == BayerFormat::GRBG || order == BayerFormat::RGGB;
+}
+
+/*
+ * Setup the Debayer object according to the passed in parameters.
+ * Return 0 on success, a negative errno value on failure
+ * (unsupported parameters).
+ */
+int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)
+{
+ BayerFormat bayerFormat =
@ -468,15 +343,15 @@ index 00000000..e0c3c658
+ /* Don't pass x,y since process() already adjusts src before passing it */
+ stats_->setWindow(Rectangle(window_.size()));
+
+ /* pad with patternSize.Width on both left and right side */
+ lineBufferPadding_ = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
+ lineBufferLength_ = window_.width * inputConfig_.bpp / 8 +
+ 2 * lineBufferPadding_;
+ 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);
+ lineBuffers_[i] = (uint8_t *)malloc(lineBufferLength_);
+ if (!lineBuffers_[i])
+ return -ENOMEM;
+ }
@ -487,6 +362,10 @@ index 00000000..e0c3c658
+ return 0;
+}
+
+/*
+ * Get width and height at which the bayer-pattern repeats.
+ * Return pattern-size or an empty Size for an unsupported inputFormat.
+ */
+Size DebayerCpu::patternSize(PixelFormat inputFormat)
+{
+ DebayerCpu::DebayerInputConfig config;
@ -521,25 +400,17 @@ index 00000000..e0c3c658
+ return std::make_tuple(stride, stride * size.height);
+}
+
+void DebayerCpu::initLinePointers(const uint8_t *linePointers[], const uint8_t *src)
+void DebayerCpu::setupInputMemcpy(const uint8_t *linePointers[])
+{
+ const int patternHeight = inputConfig_.patternSize.height;
+
+ for (int i = 0; i < patternHeight; i++)
+ linePointers[i + 1] = src +
+ (-patternHeight / 2 + i) * (int)inputConfig_.stride;
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
+
+ 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;
+ for (unsigned int i = 0; i < patternHeight; i++) {
+ memcpy(lineBuffers_[i], linePointers[i + 1] - lineBufferPadding_,
+ lineBufferLength_);
+ linePointers[i + 1] = lineBuffers_[i] + lineBufferPadding_;
+ }
+
+ /* Point lineBufferIndex_ to first unused lineBuffer */
@ -548,44 +419,78 @@ index 00000000..e0c3c658
+
+void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src)
+{
+ const int patternHeight = inputConfig_.patternSize.height;
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
+
+ for (int i = 0; i < patternHeight; i++)
+ for (unsigned int i = 0; i < patternHeight; i++)
+ linePointers[i] = linePointers[i + 1];
+
+ linePointers[patternHeight] = src +
+ (patternHeight / 2) * (int)inputConfig_.stride;
+}
+
+void DebayerCpu::memcpyNextLine(const uint8_t *linePointers[])
+{
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
+
+ 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;
+ memcpy(lineBuffers_[lineBufferIndex_], linePointers[patternHeight] - lineBufferPadding_,
+ lineBufferLength_);
+ linePointers[patternHeight] = lineBuffers_[lineBufferIndex_] + lineBufferPadding_;
+
+ lineBufferIndex_ = (lineBufferIndex_ + 1) % (patternHeight + 1);
+}
+
+void DebayerCpu::process2(const uint8_t *src, uint8_t *dst)
+{
+ const unsigned int y_end = window_.y + window_.height;
+ unsigned int y_end = window_.y + window_.height;
+ /* Holds [0] previous- [1] current- [2] next-line */
+ 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);
+ /* [x] becomes [x - 1] after initial shiftLinePointers() call */
+ if (window_.y) {
+ linePointers[1] = src - inputConfig_.stride; /* previous-line */
+ linePointers[2] = src;
+ } else {
+ /* window_.y == 0, use the next line as prev line */
+ linePointers[1] = src + inputConfig_.stride;
+ linePointers[2] = src;
+ /* Last 2 lines also need special handling */
+ y_end -= 2;
+ }
+
+ setupInputMemcpy(linePointers);
+
+ for (unsigned int y = window_.y; y < y_end; y += 2) {
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ stats_->processLine0(y, linePointers);
+ (this->*debayer0_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ (this->*debayer1_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+ }
+
+ if (window_.y == 0) {
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ stats_->processLine0(y_end, linePointers);
+ (this->*debayer0_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ /* next line may point outside of src, use prev. */
+ linePointers[2] = linePointers[0];
+ (this->*debayer1_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
@ -595,32 +500,46 @@ index 00000000..e0c3c658
+void DebayerCpu::process4(const uint8_t *src, uint8_t *dst)
+{
+ const unsigned int y_end = window_.y + window_.height;
+ /*
+ * This holds pointers to [0] 2-lines-up [1] 1-line-up [2] current-line
+ * [3] 1-line-down [4] 2-lines-down.
+ */
+ 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);
+ /* [x] becomes [x - 1] after initial shiftLinePointers() call */
+ linePointers[1] = src - 2 * inputConfig_.stride;
+ linePointers[2] = src - inputConfig_.stride;
+ linePointers[3] = src;
+ linePointers[4] = src + inputConfig_.stride;
+
+ setupInputMemcpy(linePointers);
+
+ for (unsigned int y = window_.y; y < y_end; y += 4) {
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ stats_->processLine0(y, linePointers);
+ (this->*debayer0_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ (this->*debayer1_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ stats_->processLine2(y, linePointers);
+ (this->*debayer2_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ (this->*debayer3_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
@ -637,30 +556,32 @@ index 00000000..e0c3c658
+{
+ timespec frameStartTime;
+
+ if (measuredFrames_ < DebayerCpu::framesToMeasure) {
+ if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure) {
+ 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);
+ for (unsigned int i = 0; i < kGammaLookupSize; i++)
+ gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma);
+
+ gamma_correction_ = params.gamma;
+ }
+
+ for (int i = 0; i < 256; i++) {
+ int idx;
+ for (unsigned int i = 0; i < kRGBLookupSize; i++) {
+ constexpr unsigned int div =
+ kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
+ unsigned int idx;
+
+ /* Apply gamma after gain! */
+ idx = std::min({ i * params.gainR / 64U, 1023U });
+ idx = std::min({ i * params.gainR / div, (kGammaLookupSize - 1) });
+ red_[i] = gamma_[idx];
+
+ idx = std::min({ i * params.gainG / 64U, 1023U });
+ idx = std::min({ i * params.gainG / div, (kGammaLookupSize - 1) });
+ green_[i] = gamma_[idx];
+
+ idx = std::min({ i * params.gainB / 64U, 1023U });
+ idx = std::min({ i * params.gainB / div, (kGammaLookupSize - 1) });
+ blue_[i] = gamma_[idx];
+ }
+
@ -688,14 +609,14 @@ index 00000000..e0c3c658
+ metadata.planes()[0].bytesused = out.planes()[0].size();
+
+ /* Measure before emitting signals */
+ if (measuredFrames_ < DebayerCpu::framesToMeasure &&
+ ++measuredFrames_ > DebayerCpu::framesToSkip) {
+ if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure &&
+ ++measuredFrames_ > DebayerCpu::kFramesToSkip) {
+ timespec frameEndTime = {};
+ clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime);
+ frameProcessTime_ += timeDiff(frameEndTime, frameStartTime);
+ if (measuredFrames_ == DebayerCpu::framesToMeasure) {
+ const int measuredFrames = DebayerCpu::framesToMeasure -
+ DebayerCpu::framesToSkip;
+ if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) {
+ const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure -
+ DebayerCpu::kFramesToSkip;
+ LOG(Debayer, Info)
+ << "Processed " << measuredFrames
+ << " frames in " << frameProcessTime_ / 1000 << "us, "
@ -709,19 +630,196 @@ index 00000000..e0c3c658
+ inputBufferReady.emit(input);
+}
+
+SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
+{
+ Size pattern_size = patternSize(inputFormat);
+ unsigned int border_height = pattern_size.height;
+
+ if (pattern_size.isNull())
+ return {};
+
+ /* No need for top/bottom border with a pattern height of 2 */
+ if (pattern_size.height == 2)
+ border_height = 0;
+
+ /*
+ * For debayer interpolation a border is kept around the entire image
+ * and the minimum output size is pattern-height x pattern-width.
+ */
+ if (inputSize.width < (3 * pattern_size.width) ||
+ inputSize.height < (2 * border_height + 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 * border_height) & ~(pattern_size.height - 1)),
+ pattern_size.width, pattern_size.height);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
new file mode 100644
index 00000000..8a51ed85
--- /dev/null
+++ b/src/libcamera/software_isp/debayer_cpu.h
@@ -0,0 +1,143 @@
+/* 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 "debayer.h"
+#include "swstats_cpu.h"
+
+namespace libcamera {
+
+class DebayerCpu : public Debayer, public Object
+{
+public:
+ DebayerCpu(std::unique_ptr<SwStatsCpu> stats);
+ ~DebayerCpu();
+
+ int configure(const StreamConfiguration &inputCfg,
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs);
+ 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);
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
+
+ /**
+ * \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:
+ /**
+ * \brief Called to debayer 1 line of Bayer input data to output format
+ * \param[out] dst Pointer to the start of the output line to write
+ * \param[in] src The input data
+ *
+ * Input data is an array of (patternSize_.height + 1) src
+ * pointers each pointing to a line in the Bayer source. The middle
+ * element of the array will point to the actual line being processed.
+ * Earlier element(s) will point to the previous line(s) and later
+ * element(s) to the next line(s).
+ *
+ * These functions take an array of src pointers, rather than
+ * a single src pointer + a stride for the source, so that when the src
+ * is slow uncached memory it can be copied to faster memory before
+ * debayering. Debayering a standard 2x2 Bayer pattern requires access
+ * to the previous and next src lines for interpolating the missing
+ * colors. To allow copying the src lines only once 3 temporary buffers
+ * each holding a single line are used, re-using the oldest buffer for
+ * the next line and the pointers are swizzled so that:
+ * src[0] = previous-line, src[1] = currrent-line, src[2] = next-line.
+ * This way the 3 pointers passed to the debayer functions form
+ * a sliding window over the src avoiding the need to copy each
+ * line more than once.
+ *
+ * Similarly for bayer patterns which repeat every 4 lines, 5 src
+ * pointers are passed holding: src[0] = 2-lines-up, src[1] = 1-line-up
+ * src[2] = current-line, src[3] = 1-line-down, src[4] = 2-lines-down.
+ */
+ using debayerFn = void (DebayerCpu::*)(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[]);
+ void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
+ void debayer10P_RGRG_BGR888(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);
+ void setupInputMemcpy(const uint8_t *linePointers[]);
+ void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
+ void memcpyNextLine(const uint8_t *linePointers[]);
+ void process2(const uint8_t *src, uint8_t *dst);
+ void process4(const uint8_t *src, uint8_t *dst);
+
+ static constexpr unsigned int kGammaLookupSize = 1024;
+ static constexpr unsigned int kRGBLookupSize = 256;
+ /* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */
+ static constexpr unsigned int kMaxLineBuffers = 5;
+
+ std::array<uint8_t, kGammaLookupSize> gamma_;
+ std::array<uint8_t, kRGBLookupSize> red_;
+ std::array<uint8_t, kRGBLookupSize> green_;
+ std::array<uint8_t, kRGBLookupSize> blue_;
+ debayerFn debayer0_;
+ debayerFn debayer1_;
+ debayerFn debayer2_;
+ debayerFn debayer3_;
+ Rectangle window_;
+ DebayerInputConfig inputConfig_;
+ DebayerOutputConfig outputConfig_;
+ std::unique_ptr<SwStatsCpu> stats_;
+ uint8_t *lineBuffers_[kMaxLineBuffers];
+ unsigned int lineBufferLength_;
+ unsigned int lineBufferPadding_;
+ unsigned int lineBufferIndex_;
+ bool enableInputMemcpy_;
+ float gamma_correction_;
+ unsigned int measuredFrames_;
+ int64_t frameProcessTime_;
+ /* Skip 30 frames for things to stabilize then measure 30 frames */
+ static constexpr unsigned int kFramesToSkip = 30;
+ static constexpr unsigned int kLastFrameToMeasure = 60;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
index d4ae5ac7..6d7a44d7 100644
index 62095f61..71b46539 100644
--- a/src/libcamera/software_isp/meson.build
+++ b/src/libcamera/software_isp/meson.build
@@ -2,6 +2,7 @@
@@ -9,5 +9,6 @@ endif
libcamera_sources += files([
'debayer.cpp',
+ 'debayer_cpu.cpp',
'swstats.cpp',
'swstats_cpu.cpp',
'debayer.cpp',
+ 'debayer_cpu.cpp',
'swstats_cpu.cpp',
])
--
2.43.0
2.43.2

View file

@ -1,272 +0,0 @@
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

View file

@ -0,0 +1,506 @@
From 5261c801d8425fa82bcbd3da0199d06153eb5bd7 Mon Sep 17 00:00:00 2001
From: Andrey Konovalov <andrey.konovalov@linaro.org>
Date: Mon, 11 Mar 2024 15:15:13 +0100
Subject: [PATCH 09/21] libcamera: ipa: add Soft IPA
Define the Soft IPA main and event interfaces, add the Soft IPA
implementation.
The current src/ipa/meson.build assumes the IPA name to match the
pipeline name. For this reason "-Dipas=simple" is used for the
Soft IPA module.
Auto exposure/gain and AWB implementation by Dennis, Toon and Martti.
Auto exposure/gain targets a Mean Sample Value of 2.5 following
the MSV calculation algorithm from:
https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
Tested-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
Co-developed-by: Marttico <g.martti@gmail.com>
Signed-off-by: Marttico <g.martti@gmail.com>
Co-developed-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>
---
Documentation/Doxyfile.in | 1 +
include/libcamera/ipa/meson.build | 1 +
include/libcamera/ipa/soft.mojom | 28 +++
meson_options.txt | 2 +-
src/ipa/simple/data/meson.build | 9 +
src/ipa/simple/data/soft.conf | 3 +
src/ipa/simple/meson.build | 25 +++
src/ipa/simple/soft_simple.cpp | 326 ++++++++++++++++++++++++++++++
8 files changed, 394 insertions(+), 1 deletion(-)
create mode 100644 include/libcamera/ipa/soft.mojom
create mode 100644 src/ipa/simple/data/meson.build
create mode 100644 src/ipa/simple/data/soft.conf
create mode 100644 src/ipa/simple/meson.build
create mode 100644 src/ipa/simple/soft_simple.cpp
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..3352d08f 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': '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..c249bd75
--- /dev/null
+++ b/include/libcamera/ipa/soft.mojom
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/*
+ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry.
+ */
+
+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/meson_options.txt b/meson_options.txt
index 5fdc7be8..94372e47 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', 'vimc'],
description : 'Select which IPA modules to build')
option('lc-compliance',
diff --git a/src/ipa/simple/data/meson.build b/src/ipa/simple/data/meson.build
new file mode 100644
index 00000000..33548cc6
--- /dev/null
+++ b/src/ipa/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/data/soft.conf b/src/ipa/simple/data/soft.conf
new file mode 100644
index 00000000..0c70e7c0
--- /dev/null
+++ b/src/ipa/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/meson.build b/src/ipa/simple/meson.build
new file mode 100644
index 00000000..3e863db7
--- /dev/null
+++ b/src/ipa/simple/meson.build
@@ -0,0 +1,25 @@
+# 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,
+ 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/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
new file mode 100644
index 00000000..312df4ba
--- /dev/null
+++ b/src/ipa/simple/soft_simple.cpp
@@ -0,0 +1,326 @@
+/* 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/base/shared_fd.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libcamera/ipa/soft_ipa_interface.h>
+
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/software_isp/debayer_params.h"
+#include "libcamera/internal/software_isp/swisp_stats.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPASoft)
+
+namespace ipa::soft {
+
+class IPASoftSimple : public ipa::soft::IPASoftInterface
+{
+public:
+ IPASoftSimple()
+ : params_(static_cast<DebayerParams *>(MAP_FAILED)),
+ stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0)
+ {
+ }
+
+ ~IPASoftSimple()
+ {
+ if (stats_ != MAP_FAILED)
+ munmap(stats_, sizeof(SwIspStats));
+ if (params_ != MAP_FAILED)
+ munmap(params_, sizeof(DebayerParams));
+ }
+
+ 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;
+
+private:
+ void updateExposure(double exposureMSV);
+
+ SharedFD fdStats_;
+ SharedFD fdParams_;
+ DebayerParams *params_;
+ SwIspStats *stats_;
+
+ int32_t exposure_min_, exposure_max_;
+ int32_t again_min_, again_max_;
+ int32_t again_, exposure_;
+ unsigned int ignore_updates_;
+};
+
+int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
+ const SharedFD &fdStats,
+ const SharedFD &fdParams,
+ const ControlInfoMap &sensorInfoMap)
+{
+ fdStats_ = fdStats;
+ if (!fdStats_.isValid()) {
+ LOG(IPASoft, Error) << "Invalid Statistics handle";
+ return -ENODEV;
+ }
+
+ fdParams_ = fdParams;
+ if (!fdParams_.isValid()) {
+ LOG(IPASoft, Error) << "Invalid Parameters handle";
+ return -ENODEV;
+ }
+
+ params_ = static_cast<DebayerParams *>(mmap(nullptr, sizeof(DebayerParams),
+ PROT_WRITE, MAP_SHARED,
+ fdParams_.get(), 0));
+ if (params_ == MAP_FAILED) {
+ LOG(IPASoft, Error) << "Unable to map Parameters";
+ return -errno;
+ }
+
+ stats_ = static_cast<SwIspStats *>(mmap(nullptr, sizeof(SwIspStats),
+ PROT_READ, MAP_SHARED,
+ fdStats_.get(), 0));
+ if (stats_ == MAP_FAILED) {
+ LOG(IPASoft, Error) << "Unable to map Statistics";
+ return -errno;
+ }
+
+ 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::configure(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<int32_t>();
+ exposure_max_ = exposure_info.max().get<int32_t>();
+ if (!exposure_min_) {
+ LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
+ exposure_min_ = 1;
+ }
+
+ again_min_ = gain_info.min().get<int32_t>();
+ again_max_ = gain_info.max().get<int32_t>();
+ /*
+ * The camera sensor gain (g) is usually not equal to the value written
+ * into the gain register (x). But the way how the AGC algorithm changes
+ * the gain value to make the total exposure closer to the optimum assumes
+ * that g(x) is not too far from linear function. If the minimal gain is 0,
+ * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
+ * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
+ * one edge, and very small near the other) we limit the range of the gain
+ * values used.
+ */
+ if (!again_min_) {
+ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
+ again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
+ }
+
+ LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
+ << ", gain " << again_min_ << "-" << again_max_;
+
+ return 0;
+}
+
+int IPASoftSimple::start()
+{
+ return 0;
+}
+
+void IPASoftSimple::stop()
+{
+}
+
+/*
+ * The number of bins to use for the optimal exposure calculations.
+ */
+static constexpr unsigned int kExposureBinsCount = 5;
+/*
+ * The exposure is optimal when the mean sample value of the histogram is
+ * in the middle of the range.
+ */
+static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;
+/*
+ * The below value implements the hysteresis for the exposure adjustment.
+ * It is small enough to have the exposure close to the optimal, and is big
+ * enough to prevent the exposure from wobbling around the optimal value.
+ */
+static constexpr float kExposureSatisfactory = 0.2;
+
+void IPASoftSimple::processStats(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;
+ }
+
+ /*
+ * Calculate Mean Sample Value (MSV) according to formula from:
+ * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
+ */
+ constexpr unsigned int yHistValsPerBin =
+ SwIspStats::kYHistogramSize / kExposureBinsCount;
+ constexpr unsigned int yHistValsPerBinMod =
+ SwIspStats::kYHistogramSize /
+ (SwIspStats::kYHistogramSize % kExposureBinsCount + 1);
+ int ExposureBins[kExposureBinsCount] = {};
+ unsigned int denom = 0;
+ unsigned int num = 0;
+
+ for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) {
+ unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
+ ExposureBins[idx] += stats_->yHistogram[i];
+ }
+
+ for (unsigned int i = 0; i < kExposureBinsCount; i++) {
+ LOG(IPASoft, Debug) << i << ": " << ExposureBins[i];
+ denom += ExposureBins[i];
+ num += ExposureBins[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<int32_t>();
+ again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
+
+ updateExposure(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;
+}
+
+void IPASoftSimple::updateExposure(double exposureMSV)
+{
+ /* DENOMINATOR of 10 gives ~10% increment/decrement; DENOMINATOR of 5 - about ~20% */
+ static constexpr uint8_t kExpDenominator = 10;
+ static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
+ static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
+
+ int next;
+
+ if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
+ next = exposure_ * kExpNumeratorUp / kExpDenominator;
+ if (next - exposure_ < 1)
+ exposure_ += 1;
+ else
+ exposure_ = next;
+ if (exposure_ >= exposure_max_) {
+ next = again_ * kExpNumeratorUp / kExpDenominator;
+ if (next - again_ < 1)
+ again_ += 1;
+ else
+ again_ = next;
+ }
+ }
+
+ if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
+ if (exposure_ == exposure_max_ && again_ != again_min_) {
+ next = again_ * kExpNumeratorDown / kExpDenominator;
+ if (again_ - next < 1)
+ again_ -= 1;
+ else
+ again_ = next;
+ } else {
+ next = exposure_ * kExpNumeratorDown / kExpDenominator;
+ if (exposure_ - next < 1)
+ exposure_ -= 1;
+ else
+ exposure_ = next;
+ }
+ }
+
+ exposure_ = std::clamp(exposure_, exposure_min_, exposure_max_);
+ again_ = std::clamp(again_, again_min_, again_max_);
+}
+
+} /* namespace ipa::soft */
+
+/*
+ * External IPA module interface
+ */
+extern "C" {
+const struct IPAModuleInfo ipaModuleInfo = {
+ IPA_MODULE_API_VERSION,
+ 0,
+ "SimplePipelineHandler",
+ "simple",
+};
+
+IPAInterface *ipaCreate()
+{
+ return new ipa::soft::IPASoftSimple();
+}
+
+} /* extern "C" */
+
+} /* namespace libcamera */
--
2.43.2

View file

@ -0,0 +1,507 @@
From ad41ea12fe4b8ca0ace20781c775a63ed0d66f4c Mon Sep 17 00:00:00 2001
From: Andrey Konovalov <andrey.konovalov@linaro.org>
Date: Mon, 11 Mar 2024 15:15:14 +0100
Subject: [PATCH 10/21] libcamera: introduce SoftwareIsp
Doxygen documentation by Dennis Bonke.
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
Tested-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Co-developed-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>
---
.../internal/software_isp/meson.build | 1 +
.../internal/software_isp/software_isp.h | 98 +++++
src/libcamera/software_isp/meson.build | 1 +
src/libcamera/software_isp/software_isp.cpp | 349 ++++++++++++++++++
4 files changed, 449 insertions(+)
create mode 100644 include/libcamera/internal/software_isp/software_isp.h
create mode 100644 src/libcamera/software_isp/software_isp.cpp
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
index a620e16d..508ddddc 100644
--- a/include/libcamera/internal/software_isp/meson.build
+++ b/include/libcamera/internal/software_isp/meson.build
@@ -2,5 +2,6 @@
libcamera_internal_headers += files([
'debayer_params.h',
+ 'software_isp.h',
'swisp_stats.h',
])
diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
new file mode 100644
index 00000000..8d25e979
--- /dev/null
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ *
+ * software_isp.h - Simple software ISP implementation
+ */
+
+#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/base/thread.h>
+
+#include <libcamera/geometry.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/pipeline_handler.h"
+#include "libcamera/internal/shared_mem_object.h"
+#include "libcamera/internal/software_isp/debayer_params.h"
+
+namespace libcamera {
+
+class DebayerCpu;
+class FrameBuffer;
+class PixelFormat;
+struct StreamConfiguration;
+
+LOG_DECLARE_CATEGORY(SoftwareIsp)
+
+class SoftwareIsp
+{
+public:
+ SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
+ ~SoftwareIsp();
+
+ int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
+
+ bool isValid() const;
+
+ std::vector<PixelFormat> formats(PixelFormat input);
+
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
+
+ std::tuple<unsigned int, unsigned int>
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
+
+ int configure(const StreamConfiguration &inputCfg,
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
+ const ControlInfoMap &sensorControls);
+
+ int exportBuffers(unsigned int output, unsigned int count,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers);
+
+ void processStats(const ControlList &sensorControls);
+
+ int start();
+ void stop();
+
+ int queueBuffers(FrameBuffer *input,
+ const std::map<unsigned int, FrameBuffer *> &outputs);
+
+ void process(FrameBuffer *input, FrameBuffer *output);
+
+ Signal<FrameBuffer *> inputBufferReady;
+ Signal<FrameBuffer *> outputBufferReady;
+ Signal<int> ispStatsReady;
+ Signal<const ControlList &> setSensorControls;
+
+private:
+ void saveIspParams(int dummy);
+ void setSensorCtrls(const ControlList &sensorControls);
+ 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 71b46539..e9266e54 100644
--- a/src/libcamera/software_isp/meson.build
+++ b/src/libcamera/software_isp/meson.build
@@ -10,5 +10,6 @@ endif
libcamera_sources += files([
'debayer.cpp',
'debayer_cpu.cpp',
+ 'software_isp.cpp',
'swstats_cpu.cpp',
])
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
new file mode 100644
index 00000000..388b4496
--- /dev/null
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -0,0 +1,349 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ *
+ * software_isp.cpp - Simple software ISP implementation
+ */
+
+#include "libcamera/internal/software_isp/software_isp.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"
+
+#include "debayer_cpu.h"
+
+/**
+ * \file software_isp.cpp
+ * \brief Simple software ISP implementation
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(SoftwareIsp)
+
+/**
+ * \class SoftwareIsp
+ * \brief Class for the Software ISP
+ */
+
+/**
+ * \var SoftwareIsp::inputBufferReady
+ * \brief A signal emitted when the input frame buffer completes
+ */
+
+/**
+ * \var SoftwareIsp::outputBufferReady
+ * \brief A signal emitted when the output frame buffer completes
+ */
+
+/**
+ * \var SoftwareIsp::ispStatsReady
+ * \brief A signal emitted when the statistics for IPA are ready
+ *
+ * The int parameter isn't actually used.
+ */
+
+/**
+ * \var SoftwareIsp::setSensorControls
+ * \brief A signal emitted when the values to write to the sensor controls are ready
+ */
+
+/**
+ * \brief Constructs SoftwareIsp object
+ * \param[in] pipe The pipeline handler in use
+ * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
+ */
+SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
+ : debayer_(nullptr),
+ debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 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_) {
+ LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
+ return;
+ }
+
+ auto stats = std::make_unique<SwStatsCpu>();
+ if (!stats->isValid()) {
+ LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
+ return;
+ }
+ stats->statsReady.connect(this, &SoftwareIsp::statsReady);
+
+ debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
+ debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
+ debayer_->outputBufferReady.connect(this, &SoftwareIsp::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, &SoftwareIsp::saveIspParams);
+ ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls);
+
+ debayer_->moveToThread(&ispWorkerThread_);
+}
+
+SoftwareIsp::~SoftwareIsp()
+{
+ /* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */
+ debayer_.reset();
+}
+
+/**
+ * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename)
+ * \brief Load a configuration from a file
+ * \param[in] filename The file to load the configuration data from
+ *
+ * Currently is a stub doing nothing and always returning "success".
+ *
+ * \return 0 on success
+ */
+
+/**
+ * \brief Process the statistics gathered
+ * \param[in] sensorControls The sensor controls
+ *
+ * Requests the IPA to calculate new parameters for ISP and new control
+ * values for the sensor.
+ */
+void SoftwareIsp::processStats(const ControlList &sensorControls)
+{
+ ASSERT(ipa_);
+ ipa_->processStats(sensorControls);
+}
+
+/**
+ * \brief Check the validity of Software Isp object
+ * \return True if Software Isp is valid, false otherwise
+ */
+bool SoftwareIsp::isValid() const
+{
+ return !!debayer_;
+}
+
+/**
+ * \brief Get the output formats supported for the given input format
+ * \param[in] inputFormat The input format
+ * \return All the supported output formats or an empty vector if there are none
+ */
+std::vector<PixelFormat> SoftwareIsp::formats(PixelFormat inputFormat)
+{
+ ASSERT(debayer_ != nullptr);
+
+ return debayer_->formats(inputFormat);
+}
+
+/**
+ * \brief Get the supported output sizes for the given input format and size
+ * \param[in] inputFormat The input format
+ * \param[in] inputSize The input frame size
+ * \return The valid size range or an empty range if there are none
+ */
+SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
+{
+ ASSERT(debayer_ != nullptr);
+
+ return debayer_->sizes(inputFormat, inputSize);
+}
+
+/**
+ * Get the output stride and the frame size in bytes for the given output format and size
+ * \param[in] outputFormat The output format
+ * \param[in] size The output size (width and height in pixels)
+ * \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0
+ * if there is no valid output config
+ */
+std::tuple<unsigned int, unsigned int>
+SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
+{
+ ASSERT(debayer_ != nullptr);
+
+ return debayer_->strideAndFrameSize(outputFormat, size);
+}
+
+/**
+ * \brief Configure the SoftwareIsp object according to the passed in parameters
+ * \param[in] inputCfg The input configuration
+ * \param[in] outputCfgs The output configurations
+ * \param[in] sensorControls ControlInfoMap of the controls supported by the sensor
+ * \return 0 on success, a negative errno on failure
+ */
+int SoftwareIsp::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);
+}
+
+/**
+ * \brief Export the buffers from the Software ISP
+ * \param[in] output Output stream index exporting the buffers
+ * \param[in] count Number of buffers to allocate
+ * \param[out] buffers Vector to store the allocated buffers
+ * \return The number of allocated buffers on success or a negative error code
+ * otherwise
+ */
+int SoftwareIsp::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;
+}
+
+/**
+ * \brief Queue buffers to Software ISP
+ * \param[in] input The input framebuffer
+ * \param[in] outputs The container holding the output stream indexes and
+ * their respective frame buffer outputs
+ * \return 0 on success, a negative errno on failure
+ */
+int SoftwareIsp::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;
+}
+
+/**
+ * \brief Starts the Software ISP streaming operation
+ * \return 0 on success, any other value indicates an error
+ */
+int SoftwareIsp::start()
+{
+ int ret = ipa_->start();
+ if (ret)
+ return ret;
+
+ ispWorkerThread_.start();
+ return 0;
+}
+
+/**
+ * \brief Stops the Software ISP streaming operation
+ */
+void SoftwareIsp::stop()
+{
+ ispWorkerThread_.exit();
+ ispWorkerThread_.wait();
+
+ ipa_->stop();
+}
+
+/**
+ * \brief Passes the input framebuffer to the ISP worker to process
+ * \param[in] input The input framebuffer
+ * \param[out] output The framebuffer to write the processed frame to
+ */
+void SoftwareIsp::process(FrameBuffer *input, FrameBuffer *output)
+{
+ debayer_->invokeMethod(&DebayerCpu::process,
+ ConnectionTypeQueued, input, output, debayerParams_);
+}
+
+void SoftwareIsp::saveIspParams([[maybe_unused]] int dummy)
+{
+ debayerParams_ = *sharedParams_;
+}
+
+void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
+{
+ setSensorControls.emit(sensorControls);
+}
+
+void SoftwareIsp::statsReady(int dummy)
+{
+ ispStatsReady.emit(dummy);
+}
+
+void SoftwareIsp::inputReady(FrameBuffer *input)
+{
+ inputBufferReady.emit(input);
+}
+
+void SoftwareIsp::outputReady(FrameBuffer *output)
+{
+ outputBufferReady.emit(output);
+}
+
+} /* namespace libcamera */
--
2.43.2

View file

@ -1,256 +0,0 @@
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

View file

@ -1,7 +1,7 @@
From 155065b3d78c14173fd71c21fe0639f94c3da109 Mon Sep 17 00:00:00 2001
From 050440eed6ab90686df217f5ff7dea0b241e3898 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_
Date: Mon, 11 Mar 2024 15:15:15 +0100
Subject: [PATCH 11/21] libcamera: pipeline: simple: rename converterBuffers_
and related vars
The converterBuffers_ and the converterQueue_ are not that specific
@ -12,16 +12,18 @@ 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>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
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
index a84f6760..78854ef8 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -269,17 +269,18 @@ public:
@ -135,7 +137,7 @@ index 4d0e7255..fa298746 100644
{
SimplePipelineHandler *pipe = SimpleCameraData::pipe();
@@ -1159,14 +1160,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
@@ -1189,14 +1190,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
/* Configure the converter if needed. */
std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
@ -152,7 +154,7 @@ index 4d0e7255..fa298746 100644
outputCfgs.push_back(cfg);
}
@@ -1192,7 +1193,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
@@ -1222,7 +1223,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.
*/
@ -161,7 +163,7 @@ index 4d0e7255..fa298746 100644
return data->converter_->exportBuffers(data->streamIndex(stream),
count, buffers);
else
@@ -1213,13 +1214,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
@@ -1243,13 +1244,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
return -EBUSY;
}
@ -177,7 +179,7 @@ index 4d0e7255..fa298746 100644
} 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
@@ -1268,7 +1269,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
return ret;
}
@ -186,7 +188,7 @@ index 4d0e7255..fa298746 100644
ret = data->converter_->start();
if (ret < 0) {
stop(camera);
@@ -1246,7 +1247,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
@@ -1276,7 +1277,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
}
/* Queue all internal buffers for capture. */
@ -195,7 +197,7 @@ index 4d0e7255..fa298746 100644
video->queueBuffer(buffer.get());
}
@@ -1258,7 +1259,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
@@ -1288,7 +1289,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
SimpleCameraData *data = cameraData(camera);
V4L2VideoDevice *video = data->video_;
@ -204,7 +206,7 @@ index 4d0e7255..fa298746 100644
data->converter_->stop();
video->streamOff();
@@ -1266,7 +1267,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
@@ -1296,7 +1297,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
video->bufferReady.disconnect(data, &SimpleCameraData::bufferReady);
@ -213,7 +215,7 @@ index 4d0e7255..fa298746 100644
releasePipeline(data);
}
@@ -1284,7 +1285,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
@@ -1314,7 +1315,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
* queue, it will be handed to the converter in the capture
* completion handler.
*/
@ -222,7 +224,7 @@ index 4d0e7255..fa298746 100644
buffers.emplace(data->streamIndex(stream), buffer);
} else {
ret = data->video_->queueBuffer(buffer);
@@ -1293,8 +1294,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
@@ -1323,8 +1324,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
}
}
@ -234,5 +236,5 @@ index 4d0e7255..fa298746 100644
return 0;
}
--
2.43.0
2.43.2

View file

@ -1,407 +0,0 @@
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

View file

@ -1,41 +1,70 @@
From ad61052d9aea1f1af6aaef9ce7e5d447c595187c Mon Sep 17 00:00:00 2001
From d64b0fca22ef25b8a14d7fc97dfab64eb1c4f21a 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
Date: Mon, 11 Mar 2024 15:15:16 +0100
Subject: [PATCH 12/21] 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
-Dpipelines=simple -Dipas=simple
Also using the Soft ISP for the particular hardware platform must
be enabled in the supportedDevices[] table.
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>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
src/libcamera/pipeline/simple/simple.cpp | 111 ++++++++++++++++++-----
1 file changed, 89 insertions(+), 22 deletions(-)
src/libcamera/pipeline/simple/simple.cpp | 137 ++++++++++++++++++-----
1 file changed, 109 insertions(+), 28 deletions(-)
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index fa298746..c76510c2 100644
index 78854ef8..c3ebb7b7 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/software_isp/software_isp.h"
#include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h"
@@ -274,6 +275,7 @@ public:
@@ -185,17 +186,22 @@ struct SimplePipelineInfo {
* and the number of streams it supports.
*/
std::vector<std::pair<const char *, unsigned int>> converters;
+ /*
+ * Using Software ISP is to be enabled per driver.
+ * The Software ISP can't be used together with the converters.
+ */
+ bool swIspEnabled;
};
namespace {
static const SimplePipelineInfo supportedDevices[] = {
- { "dcmipp", {} },
- { "imx7-csi", { { "pxp", 1 } } },
- { "j721e-csi2rx", {} },
- { "mxc-isi", {} },
- { "qcom-camss", {} },
- { "sun6i-csi", {} },
+ { "dcmipp", {}, false },
+ { "imx7-csi", { { "pxp", 1 } }, false },
+ { "j721e-csi2rx", {}, false },
+ { "mxc-isi", {}, false },
+ { "qcom-camss", {}, true },
+ { "sun6i-csi", {}, false },
};
} /* namespace */
@@ -274,6 +280,7 @@ public:
bool useConversion_;
std::unique_ptr<Converter> converter_;
@ -43,7 +72,7 @@ index fa298746..c76510c2 100644
private:
void tryPipeline(unsigned int code, const Size &size);
@@ -281,6 +283,9 @@ private:
@@ -281,6 +288,9 @@ private:
void conversionInputDone(FrameBuffer *buffer);
void conversionOutputDone(FrameBuffer *buffer);
@ -53,58 +82,79 @@ index fa298746..c76510c2 100644
};
class SimpleCameraConfiguration : public CameraConfiguration
@@ -509,6 +514,25 @@ int SimpleCameraData::init()
@@ -332,6 +342,7 @@ public:
V4L2VideoDevice *video(const MediaEntity *entity);
V4L2Subdevice *subdev(const MediaEntity *entity);
MediaDevice *converter() { return converter_; }
+ bool swIspEnabled() { return swIspEnabled_; }
protected:
int queueRequestDevice(Camera *camera, Request *request) override;
@@ -360,6 +371,7 @@ private:
std::map<const MediaEntity *, EntityData> entities_;
MediaDevice *converter_;
+ bool swIspEnabled_;
};
/* -----------------------------------------------------------------------------
@@ -509,6 +521,29 @@ int SimpleCameraData::init()
}
}
+ /*
+ * Create SoftwareIsp unconditionally if no converter is used
+ * - to be revisited
+ * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
+ */
+ if (!converter_) {
+ swIsp_ = SoftwareIspFactoryBase::create(pipe, sensor_->controls());
+ if (!swIsp_) {
+ if (!converter_ && pipe->swIspEnabled()) {
+ swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
+ if (!swIsp_->isValid()) {
+ LOG(SimplePipeline, Warning)
+ << "Failed to create software ISP, disabling software debayering";
+ swIsp_.reset();
+ } else {
+ swIsp_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone);
+ /*
+ * \todo explain why SimpleCameraData::conversionInputDone() can't be directly
+ * connected to inputBufferReady signal.
+ */
+ swIsp_->inputBufferReady.connect(pipe, [this](FrameBuffer *buffer) {
+ this->conversionInputDone(buffer);
+ });
+ swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
+ swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady);
+
+ swIsp_->getSignalSetSensorControls().connect(this, &SimpleCameraData::setSensorControls);
+ swIsp_->setSensorControls.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)
@@ -599,12 +634,21 @@ 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);
+ 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 */
+ /* Do not use swIsp for unsupported pixelFormat's: */
+ config.outputFormats = { pixelFormat };
+ config.outputSizes = config.captureSize;
+ }
+ } else {
+ config.outputFormats = { pixelFormat };
+ config.outputSizes = config.captureSize;
config.outputFormats = { pixelFormat };
config.outputSizes = config.captureSize;
- } else {
- config.outputFormats = converter_->formats(pixelFormat);
- config.outputSizes = converter_->sizes(format.size);
}
configs_.push_back(config);
@@ -750,9 +782,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
@@ -750,9 +794,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
}
/*
@ -117,7 +167,7 @@ index fa298746..c76510c2 100644
*/
if (buffer->metadata().status != FrameMetadata::FrameCancelled)
video_->queueBuffer(buffer);
@@ -798,9 +830,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
@@ -798,9 +842,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
buffer->metadata().timestamp);
/*
@ -130,7 +180,7 @@ index fa298746..c76510c2 100644
*/
if (useConversion_) {
if (conversionQueue_.empty()) {
@@ -808,7 +840,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
@@ -808,7 +852,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
return;
}
@ -143,7 +193,7 @@ index fa298746..c76510c2 100644
conversionQueue_.pop();
return;
}
@@ -834,6 +870,18 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
@@ -834,6 +882,18 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
pipe->completeRequest(request);
}
@ -162,7 +212,7 @@ index fa298746..c76510c2 100644
/* 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()
@@ -1046,8 +1106,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
/* Set the stride, frameSize and bufferCount. */
if (needConversion_) {
std::tie(cfg.stride, cfg.frameSize) =
@ -175,7 +225,7 @@ index fa298746..c76510c2 100644
if (cfg.stride == 0)
return Invalid;
} else {
@@ -1180,7 +1230,9 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
@@ -1210,7 +1272,9 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
inputCfg.stride = captureFormat.planes[0].bpl;
inputCfg.bufferCount = kNumInternalBuffers;
@ -186,7 +236,7 @@ index fa298746..c76510c2 100644
}
int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
@@ -1194,8 +1246,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
@@ -1224,8 +1288,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
* whether the converter is used or not.
*/
if (data->useConversion_)
@ -199,7 +249,7 @@ index fa298746..c76510c2 100644
else
return data->video_->exportBuffers(count, buffers);
}
@@ -1240,10 +1294,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
@@ -1270,10 +1336,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
}
if (data->useConversion_) {
@ -222,7 +272,7 @@ index fa298746..c76510c2 100644
}
/* Queue all internal buffers for capture. */
@@ -1259,8 +1321,13 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
@@ -1289,8 +1363,13 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
SimpleCameraData *data = cameraData(camera);
V4L2VideoDevice *video = data->video_;
@ -238,6 +288,15 @@ index fa298746..c76510c2 100644
video->streamOff();
video->releaseBuffers();
@@ -1452,6 +1531,8 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
}
}
+ swIspEnabled_ = info->swIspEnabled;
+
/* Locate the sensors. */
std::vector<MediaEntity *> sensors = locateSensors();
if (sensors.empty()) {
--
2.43.0
2.43.2

View file

@ -1,483 +0,0 @@
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

View file

@ -1,7 +1,7 @@
From 66ef9f32e67a96655d10ba38f7a830b3bbfe50f2 Mon Sep 17 00:00:00 2001
From aabc53453d542495d9da25411f57308c01f2bc28 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
Date: Mon, 11 Mar 2024 15:15:17 +0100
Subject: [PATCH 13/21] 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
@ -9,46 +9,21 @@ bayer orders.
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
Tested-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
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(+)
src/libcamera/software_isp/swstats_cpu.cpp | 128 +++++++++++++++++++++
src/libcamera/software_isp/swstats_cpu.h | 9 ++
2 files changed, 137 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
index 448d0e4c..be310f56 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; \
@@ -71,6 +71,83 @@ static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */
stats_.sumG_ += sumG; \
stats_.sumB_ += sumB;
+void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[])
@ -56,9 +31,9 @@ index 59453d07..87550371 100644
+ const uint8_t *src0 = src[1] + window_.x;
+ const uint8_t *src1 = src[2] + window_.x;
+
+ SWISP_LINARO_START_LINE_STATS(uint8_t)
+ SWSTATS_START_LINE_STATS(uint8_t)
+
+ if (swap_lines_)
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ /* x += 4 sample every other 2x2 block */
@ -70,10 +45,10 @@ index 59453d07..87550371 100644
+
+ g = (g + g2) / 2;
+
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(1)
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
+ }
+
+ SWISP_LINARO_FINISH_LINE_STATS()
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+void SwStatsCpu::statsBGGR10Line0(const uint8_t *src[])
@ -81,9 +56,9 @@ index 59453d07..87550371 100644
+ 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)
+ SWSTATS_START_LINE_STATS(uint16_t)
+
+ if (swap_lines_)
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ /* x += 4 sample every other 2x2 block */
@ -96,10 +71,10 @@ index 59453d07..87550371 100644
+ g = (g + g2) / 2;
+
+ /* divide Y by 4 for 10 -> 8 bpp value */
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(4)
+ SWSTATS_ACCUMULATE_LINE_STATS(4)
+ }
+
+ SWISP_LINARO_FINISH_LINE_STATS()
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+void SwStatsCpu::statsBGGR12Line0(const uint8_t *src[])
@ -107,9 +82,9 @@ index 59453d07..87550371 100644
+ 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)
+ SWSTATS_START_LINE_STATS(uint16_t)
+
+ if (swap_lines_)
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ /* x += 4 sample every other 2x2 block */
@ -122,41 +97,44 @@ index 59453d07..87550371 100644
+ g = (g + g2) / 2;
+
+ /* divide Y by 16 for 12 -> 8 bpp value */
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(16)
+ SWSTATS_ACCUMULATE_LINE_STATS(16)
+ }
+
+ SWISP_LINARO_FINISH_LINE_STATS()
+ SWSTATS_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_)
void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
{
@@ -124,6 +201,39 @@ void SwStatsCpu::finishStats(void)
const uint8_t *src0 = src[1] + window_.x * 5 / 4;
@@ -147,6 +224,42 @@ void SwStatsCpu::finishFrame(void)
statsReady.emit(0);
}
+/*
+ * Check if order is a standard Bayer order and setup x_shift_ and swap_lines_
+/**
+ * \brief Setup SwStatsCpu object for standard Bayer orders
+ * \param[in] order The Bayer order
+ *
+ * Check if order is a standard Bayer order and setup xShift_ and swapLines_
+ * 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;
+ xShift_ = 0;
+ swapLines_ = false;
+ break;
+ case BayerFormat::GBRG:
+ x_shift_ = 1; /* BGGR -> GBRG */
+ swap_lines_ = false;
+ xShift_ = 1; /* BGGR -> GBRG */
+ swapLines_ = false;
+ break;
+ case BayerFormat::GRBG:
+ x_shift_ = 0;
+ swap_lines_ = true; /* BGGR -> GRBG */
+ xShift_ = 0;
+ swapLines_ = true; /* BGGR -> GRBG */
+ break;
+ case BayerFormat::RGGB:
+ x_shift_ = 1; /* BGGR -> GBRG */
+ swap_lines_ = true; /* GBRG -> RGGB */
+ xShift_ = 1; /* BGGR -> GBRG */
+ swapLines_ = true; /* GBRG -> RGGB */
+ break;
+ default:
+ return -EINVAL;
@ -164,36 +142,62 @@ index 59453d07..87550371 100644
+
+ patternSize_.height = 2;
+ patternSize_.width = 2;
+ y_skip_mask_ = 0x02; /* Skip every 3th and 4th line */
+ ySkipMask_ = 0x02; /* Skip every 3th and 4th line */
+ return 0;
+}
+
int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
{
/**
* \brief Configure the statistics object for the passed in input format.
* \param[in] inputCfg The input format
@@ -158,6 +271,21 @@ 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;
BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+ 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;
+ stats0_ = &SwStatsCpu::statsBGGR8Line0;
+ return 0;
+ case 10:
+ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR10Line0;
+ stats0_ = &SwStatsCpu::statsBGGR10Line0;
+ return 0;
+ case 12:
+ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR12Line0;
+ stats0_ = &SwStatsCpu::statsBGGR12Line0;
+ return 0;
+ }
+ }
+
if (bayerFormat.bitDepth == 10 &&
bayerFormat.packing == BayerFormat::Packing::CSI2) {
bpp_ = 10;
patternSize_.height = 2;
diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
index 0ac9ae71..bbbcf69b 100644
--- a/src/libcamera/software_isp/swstats_cpu.h
+++ b/src/libcamera/software_isp/swstats_cpu.h
@@ -17,6 +17,7 @@
#include <libcamera/geometry.h>
+#include "libcamera/internal/bayer_format.h"
#include "libcamera/internal/shared_mem_object.h"
#include "libcamera/internal/software_isp/swisp_stats.h"
@@ -120,6 +121,14 @@ private:
*/
using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
+ 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[]);
--
2.43.0
2.43.2

View file

@ -1,7 +1,7 @@
From b7b211eb56d98d5b170bd73a23b55aeb45bde8c5 Mon Sep 17 00:00:00 2001
From 5f3647bd4f12dd62256a425c49fd18a0f5990930 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
Date: Mon, 11 Mar 2024 15:15:18 +0100
Subject: [PATCH 14/21] 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
@ -9,80 +9,32 @@ bayer orders.
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
Tested-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
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 ++++++++++++++++++
src/libcamera/software_isp/debayer_cpu.cpp | 128 +++++++++++++++++++++
src/libcamera/software_isp/debayer_cpu.h | 13 +++
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
index f932362c..eb1c2718 100644
--- a/src/libcamera/software_isp/debayer_cpu.cpp
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -45,6 +45,11 @@ DebayerCpu::~DebayerCpu()
@@ -56,6 +56,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_;
+#define DECLARE_SRC_POINTERS(pixel_t) \
+ const pixel_t *prev = (const pixel_t *)src[0] + xShift_; \
+ const pixel_t *curr = (const pixel_t *)src[1] + xShift_; \
+ const pixel_t *next = (const pixel_t *)src[2] + xShift_;
+
// RGR
// GBG
// RGR
@@ -81,6 +86,70 @@ DebayerCpu::~DebayerCpu()
@@ -92,6 +97,70 @@ DebayerCpu::~DebayerCpu()
*dst++ = red_[curr[x] / (div)]; \
x++;
@ -153,7 +105,7 @@ index e0c3c658..7b7623b7 100644
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
@@ -193,6 +262,16 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
BayerFormat bayerFormat =
BayerFormat::fromPixelFormat(inputFormat);
@ -170,12 +122,12 @@ index e0c3c658..7b7623b7 100644
if (bayerFormat.bitDepth == 10 &&
bayerFormat.packing == BayerFormat::Packing::CSI2 &&
isStandardBayerOrder(bayerFormat.order)) {
@@ -197,12 +276,61 @@ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &c
@@ -220,12 +299,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
+ * Check for standard Bayer orders and set xShift_ 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)
@ -184,13 +136,13 @@ index e0c3c658..7b7623b7 100644
+ case BayerFormat::BGGR:
+ break;
+ case BayerFormat::GBRG:
+ x_shift_ = 1; /* BGGR -> GBRG */
+ xShift_ = 1; /* BGGR -> GBRG */
+ break;
+ case BayerFormat::GRBG:
+ std::swap(debayer0_, debayer1_); /* BGGR -> GRBG */
+ break;
+ case BayerFormat::RGGB:
+ x_shift_ = 1; /* BGGR -> GBRG */
+ xShift_ = 1; /* BGGR -> GBRG */
+ std::swap(debayer0_, debayer1_); /* GBRG -> RGGB */
+ break;
+ default:
@ -206,7 +158,7 @@ index e0c3c658..7b7623b7 100644
BayerFormat bayerFormat =
BayerFormat::fromPixelFormat(inputFormat);
+ x_shift_ = 0;
+ xShift_ = 0;
+
+ if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
+ bayerFormat.packing == BayerFormat::Packing::None &&
@ -232,6 +184,51 @@ index e0c3c658..7b7623b7 100644
if (bayerFormat.bitDepth == 10 &&
bayerFormat.packing == BayerFormat::Packing::CSI2) {
switch (bayerFormat.order) {
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
index 8a51ed85..fd1fa180 100644
--- a/src/libcamera/software_isp/debayer_cpu.h
+++ b/src/libcamera/software_isp/debayer_cpu.h
@@ -17,6 +17,8 @@
#include <libcamera/base/object.h>
+#include "libcamera/internal/bayer_format.h"
+
#include "debayer.h"
#include "swstats_cpu.h"
@@ -82,6 +84,15 @@ private:
*/
using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]);
+ /* 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);
void setupInputMemcpy(const uint8_t *linePointers[]);
void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
@@ -131,6 +143,7 @@ private:
unsigned int lineBufferLength_;
unsigned int lineBufferPadding_;
unsigned int lineBufferIndex_;
+ unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
bool enableInputMemcpy_;
float gamma_correction_;
unsigned int measuredFrames_;
--
2.43.0
2.43.2

View file

@ -1,37 +1,27 @@
From b835b2c90785ee02bc98888bf165713d16c24cc4 Mon Sep 17 00:00:00 2001
From 186db51d54bcbd4d5096bea1e4396966c2dad001 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
Date: Mon, 11 Mar 2024 15:15:19 +0100
Subject: [PATCH 15/21] 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>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
---
.../internal/software_isp/debayer_cpu.h | 1 +
src/libcamera/software_isp/debayer_cpu.cpp | 43 ++++++++++++++++---
2 files changed, 39 insertions(+), 5 deletions(-)
src/libcamera/software_isp/debayer_cpu.cpp | 42 +++++++++++++++++++---
src/libcamera/software_isp/debayer_cpu.h | 1 +
2 files changed, 38 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
index eb1c2718..a1692693 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
@@ -268,7 +268,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
config.bpp = (bayerFormat.bitDepth + 7) & ~7;
config.patternSize.width = 2;
config.patternSize.height = 2;
@ -40,7 +30,7 @@ index 7b7623b7..0edea4d3 100644
return 0;
}
@@ -255,7 +255,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
@@ -278,7 +278,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
config.bpp = 10;
config.patternSize.width = 4; /* 5 bytes per *4* pixels */
config.patternSize.height = 2;
@ -49,7 +39,7 @@ index 7b7623b7..0edea4d3 100644
return 0;
}
@@ -266,7 +266,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
@@ -289,7 +289,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
{
@ -58,7 +48,7 @@ index 7b7623b7..0edea4d3 100644
config.bpp = 24;
return 0;
}
@@ -302,12 +302,41 @@ int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
@@ -325,13 +325,41 @@ int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
return 0;
}
@ -69,6 +59,7 @@ index 7b7623b7..0edea4d3 100644
BayerFormat bayerFormat =
BayerFormat::fromPixelFormat(inputFormat);
xShift_ = 0;
+ swapRedBlueGains_ = false;
+
+ switch (outputFormat) {
@ -98,11 +89,10 @@ index 7b7623b7..0edea4d3 100644
+ 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
bayerFormat.packing == BayerFormat::Packing::None &&
@@ -378,6 +406,7 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi
}
}
@ -110,16 +100,28 @@ index 7b7623b7..0edea4d3 100644
LOG(Debayer, Error) << "Unsupported input output format combination";
return -EINVAL;
}
@@ -594,6 +624,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
@@ -661,6 +690,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;
for (unsigned int i = 0; i < kRGBLookupSize; i++) {
constexpr unsigned int div =
kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
index fd1fa180..5f44fc65 100644
--- a/src/libcamera/software_isp/debayer_cpu.h
+++ b/src/libcamera/software_isp/debayer_cpu.h
@@ -145,6 +145,7 @@ private:
unsigned int lineBufferIndex_;
unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
bool enableInputMemcpy_;
+ bool swapRedBlueGains_;
float gamma_correction_;
unsigned int measuredFrames_;
int64_t frameProcessTime_;
--
2.43.0
2.43.2

View file

@ -1,7 +1,7 @@
From e03beabbad83c4c283c7f1c2c4798b6c3e2eaf06 Mon Sep 17 00:00:00 2001
From e9580d30a1a79fce1ebd72ae74ceb4a3d1cf8fbb 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
Subject: [PATCH 16/21] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer
order DNU
The ov01a1s sensor has the following bayer pattern (4x4 tile repeating):
@ -233,5 +233,5 @@ index 15e8206a..4ad37aaf 100644
{ MEDIA_BUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
{ MEDIA_BUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
--
2.43.0
2.43.2

View file

@ -1,14 +1,16 @@
From 26e96232c314f9d34f6ee3be365c04918967084e Mon Sep 17 00:00:00 2001
From 6c509a3d144d46a11454d32d128d16e16602b50f 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"
Date: Mon, 11 Mar 2024 15:15:20 +0100
Subject: [PATCH 17/21] 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.
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
---
Documentation/index.rst | 1 +
Documentation/meson.build | 1 +
@ -39,7 +41,7 @@ index 7a58fec8..3872e0a8 100644
diff --git a/Documentation/software-isp-benchmarking.rst b/Documentation/software-isp-benchmarking.rst
new file mode 100644
index 00000000..738c8c65
index 00000000..b2803953
--- /dev/null
+++ b/Documentation/software-isp-benchmarking.rst
@@ -0,0 +1,82 @@
@ -50,8 +52,8 @@ index 00000000..738c8c65
+Software ISP benchmarking
+=========================
+
+The Software ISP is paricular sensitive to performance regressions
+therefor it is a good idea to always benchmark the Software ISP
+The Software ISP is particularly sensitive to performance regressions
+therefore 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.
+
@ -59,8 +61,8 @@ index 00000000..738c8c65
+----------------------------------
+
+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
+measures the time spent on processing (collecting statistics
+and debayering) only, it does not measure the time spent on
+capturing or outputting the frames.
+
+The builtin benchmark always runs. So this can be used by simply
@ -71,7 +73,7 @@ index 00000000..738c8c65
+and then it measures 30 fps and shows the total and per frame
+processing time using an info level log message:
+
+.. code-block::
+.. code-block:: text
+
+ INFO Debayer debayer_cpu.cpp:907 Processed 30 frames in 244317us, 8143 us/frame
+
@ -83,7 +85,7 @@ index 00000000..738c8c65
+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::
+.. code-block:: shell
+
+ sudo x86_energy_perf_policy --turbo-enable 0
+ sudo cpupower frequency-set -d 2GHz -u 2GHz
@ -107,14 +109,14 @@ index 00000000..738c8c65
+so that it is fully visible. After this run the following command to monitor
+the power consumption:
+
+.. code-block::
+.. code-block:: shell
+
+ 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
+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
+will cause an additional power consumption of approx. 0.5 W messing up
+the measurement.
+
+After starting qcam + the watch command let the laptop sit without using
@ -123,8 +125,8 @@ index 00000000..738c8c65
+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.
+a measured power consumption of approx. 13 W while running qcam versus
+approx. 4-5 W while setting idle with its OLED panel on.
--
2.43.0
2.43.2

View file

@ -0,0 +1,396 @@
From bb608d177135d74e3c98b8a61fb459ebe254bca5 Mon Sep 17 00:00:00 2001
From: Milan Zamazal <mzamazal@redhat.com>
Date: Mon, 11 Mar 2024 15:15:21 +0100
Subject: [PATCH 18/21] libcamera: software_isp: Apply black level compensation
Black may not be represented as 0 pixel value for given hardware, it may be
higher. If this is not compensated then various problems may occur such as low
contrast or suboptimal exposure.
The black pixel value can be either retrieved from a tuning file for the given
hardware, or automatically on fly. The former is the right and correct method,
while the latter can be used when a tuning file is not available for the given
hardware. Since there is currently no support for tuning files in software ISP,
the automatic, hardware independent way, is always used. Support for tuning
files should be added in future but it will require more work than this patch.
The patch looks at the image histogram and assumes that black starts when pixel
values start occurring on the left. A certain amount of the darkest pixels is
ignored; it doesn't matter whether they represent various kinds of noise or are
real, they are better to omit in any case to make the image looking better. It
also doesn't matter whether the darkest pixels occur around the supposed black
level or are spread between 0 and the black level, the difference is not
important.
An arbitrary threshold of 2% darkest pixels is applied; there is no magic about
that value.
The patch assumes that the black values for different colors are the same and
doesn't attempt any other non-primitive enhancements. It cannot completely
replace tuning files and simplicity, while providing visible benefit, is its
goal. Anything more sophisticated is left for future patches.
A possible cheap enhancement, if needed, could be setting exposure + gain to
minimum values temporarily, before setting the black level. In theory, the
black level should be fixed but it may not be reached in all images. For this
reason, the patch updates black level only if the observed value is lower than
the current one; it should be never increased.
The purpose of the patch is to compensate for hardware properties. General
image contrast enhancements are out of scope of this patch.
Stats are still gathered as an uncorrected histogram, to avoid any confusion and
to represent the raw image data. Exposure must be determined after the black
level correction -- it has no influence on the sub-black area and must be
correct after applying the black level correction. The granularity of the
histogram is increased from 16 to 64 to provide a better precision (there is no
theory behind either of those numbers).
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
.../internal/software_isp/debayer_params.h | 4 +
.../internal/software_isp/swisp_stats.h | 10 ++-
src/ipa/simple/black_level.cpp | 86 +++++++++++++++++++
src/ipa/simple/black_level.h | 28 ++++++
src/ipa/simple/meson.build | 7 +-
src/ipa/simple/soft_simple.cpp | 28 ++++--
src/libcamera/software_isp/debayer_cpu.cpp | 13 ++-
src/libcamera/software_isp/debayer_cpu.h | 1 +
src/libcamera/software_isp/software_isp.cpp | 2 +-
9 files changed, 162 insertions(+), 17 deletions(-)
create mode 100644 src/ipa/simple/black_level.cpp
create mode 100644 src/ipa/simple/black_level.h
diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
index 98965fa1..5e38e08b 100644
--- a/include/libcamera/internal/software_isp/debayer_params.h
+++ b/include/libcamera/internal/software_isp/debayer_params.h
@@ -43,6 +43,10 @@ struct DebayerParams {
* \brief Gamma correction, 1.0 is no correction
*/
float gamma;
+ /**
+ * \brief Level of the black point, 0..255, 0 is no correction.
+ */
+ unsigned int blackLevel;
};
} /* namespace libcamera */
diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
index afe42c9a..25cd5abd 100644
--- a/include/libcamera/internal/software_isp/swisp_stats.h
+++ b/include/libcamera/internal/software_isp/swisp_stats.h
@@ -7,6 +7,8 @@
#pragma once
+#include <array>
+
namespace libcamera {
/**
@@ -28,11 +30,15 @@ struct SwIspStats {
/**
* \brief Number of bins in the yHistogram.
*/
- static constexpr unsigned int kYHistogramSize = 16;
+ static constexpr unsigned int kYHistogramSize = 64;
+ /**
+ * \brief Type of the histogram.
+ */
+ using histogram = std::array<unsigned int, kYHistogramSize>;
/**
* \brief A histogram of luminance values.
*/
- std::array<unsigned int, kYHistogramSize> yHistogram;
+ histogram yHistogram;
};
} /* namespace libcamera */
diff --git a/src/ipa/simple/black_level.cpp b/src/ipa/simple/black_level.cpp
new file mode 100644
index 00000000..8d52201b
--- /dev/null
+++ b/src/ipa/simple/black_level.cpp
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * black_level.cpp - black level handling
+ */
+
+#include "black_level.h"
+
+#include <numeric>
+
+#include <libcamera/base/log.h>
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPASoftBL)
+
+/**
+ * \class BlackLevel
+ * \brief Object providing black point level for software ISP
+ *
+ * Black level can be provided in hardware tuning files or, if no tuning file is
+ * available for the given hardware, guessed automatically, with less accuracy.
+ * As tuning files are not yet implemented for software ISP, BlackLevel
+ * currently provides only guessed black levels.
+ *
+ * This class serves for tracking black level as a property of the underlying
+ * hardware, not as means of enhancing a particular scene or image.
+ *
+ * The class is supposed to be instantiated for the given camera stream.
+ * The black level can be retrieved using BlackLevel::get() method. It is
+ * initially 0 and may change when updated using BlackLevel::update() method.
+ */
+
+BlackLevel::BlackLevel()
+ : blackLevel_(255), blackLevelSet_(false)
+{
+}
+
+/**
+ * \brief Return the current black level
+ *
+ * \return The black level, in the range from 0 (minimum) to 255 (maximum).
+ * If the black level couldn't be determined yet, return 0.
+ */
+unsigned int BlackLevel::get() const
+{
+ return blackLevelSet_ ? blackLevel_ : 0;
+}
+
+/**
+ * \brief Update black level from the provided histogram
+ * \param[in] yHistogram The histogram to be used for updating black level
+ *
+ * The black level is property of the given hardware, not image. It is updated
+ * only if it has not been yet set or if it is lower than the lowest value seen
+ * so far.
+ */
+void BlackLevel::update(SwIspStats::histogram &yHistogram)
+{
+ // The constant is selected to be "good enough", not overly conservative or
+ // aggressive. There is no magic about the given value.
+ constexpr float ignoredPercentage_ = 0.02;
+ const unsigned int total =
+ std::accumulate(begin(yHistogram), end(yHistogram), 0);
+ const unsigned int pixelThreshold = ignoredPercentage_ * total;
+ const unsigned int currentBlackIdx =
+ blackLevel_ / (256 / SwIspStats::kYHistogramSize);
+
+ for (unsigned int i = 0, seen = 0;
+ i < currentBlackIdx && i < SwIspStats::kYHistogramSize;
+ i++) {
+ seen += yHistogram[i];
+ if (seen >= pixelThreshold) {
+ blackLevel_ = i * (256 / SwIspStats::kYHistogramSize);
+ blackLevelSet_ = true;
+ LOG(IPASoftBL, Debug)
+ << "Auto-set black level: "
+ << i << "/" << SwIspStats::kYHistogramSize
+ << " (" << 100 * (seen - yHistogram[i]) / total << "% below, "
+ << 100 * seen / total << "% at or below)";
+ break;
+ }
+ };
+}
+} // namespace libcamera
diff --git a/src/ipa/simple/black_level.h b/src/ipa/simple/black_level.h
new file mode 100644
index 00000000..b3785db0
--- /dev/null
+++ b/src/ipa/simple/black_level.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * black_level.h - black level handling
+ */
+
+#pragma once
+
+#include <array>
+
+#include "libcamera/internal/software_isp/swisp_stats.h"
+
+namespace libcamera {
+
+class BlackLevel
+{
+public:
+ BlackLevel();
+ unsigned int get() const;
+ void update(std::array<unsigned int, SwIspStats::kYHistogramSize> &yHistogram);
+
+private:
+ unsigned int blackLevel_;
+ bool blackLevelSet_;
+};
+
+} // namespace libcamera
diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
index 3e863db7..44b5f1d7 100644
--- a/src/ipa/simple/meson.build
+++ b/src/ipa/simple/meson.build
@@ -2,8 +2,13 @@
ipa_name = 'ipa_soft_simple'
+soft_simple_sources = files([
+ 'soft_simple.cpp',
+ 'black_level.cpp',
+])
+
mod = shared_module(ipa_name,
- ['soft_simple.cpp', libcamera_generated_ipa_headers],
+ [soft_simple_sources, libcamera_generated_ipa_headers],
name_prefix : '',
include_directories : [ipa_includes, libipa_includes],
dependencies : libcamera_private,
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
index 312df4ba..ac027568 100644
--- a/src/ipa/simple/soft_simple.cpp
+++ b/src/ipa/simple/soft_simple.cpp
@@ -22,6 +22,8 @@
#include "libcamera/internal/software_isp/debayer_params.h"
#include "libcamera/internal/software_isp/swisp_stats.h"
+#include "black_level.h"
+
namespace libcamera {
LOG_DEFINE_CATEGORY(IPASoft)
@@ -33,7 +35,8 @@ class IPASoftSimple : public ipa::soft::IPASoftInterface
public:
IPASoftSimple()
: params_(static_cast<DebayerParams *>(MAP_FAILED)),
- stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0)
+ stats_(static_cast<SwIspStats *>(MAP_FAILED)),
+ blackLevel_(BlackLevel()), ignore_updates_(0)
{
}
@@ -63,6 +66,7 @@ private:
SharedFD fdParams_;
DebayerParams *params_;
SwIspStats *stats_;
+ BlackLevel blackLevel_;
int32_t exposure_min_, exposure_max_;
int32_t again_min_, again_max_;
@@ -196,6 +200,10 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
params_->gainG = 256;
params_->gamma = 0.5;
+ if (ignore_updates_ > 0)
+ blackLevel_.update(stats_->yHistogram);
+ params_->blackLevel = blackLevel_.get();
+
setIspParams.emit(0);
/*
@@ -211,18 +219,19 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
* Calculate Mean Sample Value (MSV) according to formula from:
* https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
*/
- constexpr unsigned int yHistValsPerBin =
- SwIspStats::kYHistogramSize / kExposureBinsCount;
- constexpr unsigned int yHistValsPerBinMod =
- SwIspStats::kYHistogramSize /
- (SwIspStats::kYHistogramSize % kExposureBinsCount + 1);
+ const unsigned int blackLevelHistIdx =
+ params_->blackLevel / (256 / SwIspStats::kYHistogramSize);
+ const unsigned int histogramSize = SwIspStats::kYHistogramSize - blackLevelHistIdx;
+ const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount;
+ const unsigned int yHistValsPerBinMod =
+ histogramSize / (histogramSize % kExposureBinsCount + 1);
int ExposureBins[kExposureBinsCount] = {};
unsigned int denom = 0;
unsigned int num = 0;
- for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) {
+ for (unsigned int i = 0; i < histogramSize; i++) {
unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
- ExposureBins[idx] += stats_->yHistogram[i];
+ ExposureBins[idx] += stats_->yHistogram[blackLevelHistIdx + i];
}
for (unsigned int i = 0; i < kExposureBinsCount; i++) {
@@ -256,7 +265,8 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV
<< " exp " << exposure_ << " again " << again_
- << " gain R/B " << params_->gainR << "/" << params_->gainB;
+ << " gain R/B " << params_->gainR << "/" << params_->gainB
+ << " black level " << params_->blackLevel;
}
void IPASoftSimple::updateExposure(double exposureMSV)
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
index a1692693..3be3cdfe 100644
--- a/src/libcamera/software_isp/debayer_cpu.cpp
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -35,7 +35,7 @@ namespace libcamera {
* \param[in] stats Pointer to the stats object to use.
*/
DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
- : stats_(std::move(stats)), gamma_correction_(1.0)
+ : stats_(std::move(stats)), gamma_correction_(1.0), blackLevel_(0)
{
#ifdef __x86_64__
enableInputMemcpy_ = false;
@@ -683,11 +683,16 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
}
/* Apply DebayerParams */
- if (params.gamma != gamma_correction_) {
- for (unsigned int i = 0; i < kGammaLookupSize; i++)
- gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma);
+ if (params.gamma != gamma_correction_ || params.blackLevel != blackLevel_) {
+ const unsigned int blackIndex =
+ params.blackLevel * kGammaLookupSize / 256;
+ std::fill(gamma_.begin(), gamma_.begin() + blackIndex, 0);
+ const float divisor = kGammaLookupSize - blackIndex - 1.0;
+ for (unsigned int i = blackIndex; i < kGammaLookupSize; i++)
+ gamma_[i] = UINT8_MAX * powf((i - blackIndex) / divisor, params.gamma);
gamma_correction_ = params.gamma;
+ blackLevel_ = params.blackLevel;
}
if (swapRedBlueGains_)
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
index 5f44fc65..ea02f909 100644
--- a/src/libcamera/software_isp/debayer_cpu.h
+++ b/src/libcamera/software_isp/debayer_cpu.h
@@ -147,6 +147,7 @@ private:
bool enableInputMemcpy_;
bool swapRedBlueGains_;
float gamma_correction_;
+ unsigned int blackLevel_;
unsigned int measuredFrames_;
int64_t frameProcessTime_;
/* Skip 30 frames for things to stabilize then measure 30 frames */
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index 388b4496..9b49be41 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -64,7 +64,7 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
*/
SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
: debayer_(nullptr),
- debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f },
+ debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
{
if (!dmaHeap_.isValid()) {
--
2.43.2

View file

@ -0,0 +1,239 @@
From b0c07674abecb05dc0af93a4b749971f057bc3c6 Mon Sep 17 00:00:00 2001
From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
Date: Mon, 11 Mar 2024 15:15:22 +0100
Subject: [PATCH 19/21] libcamera: Soft IPA: use CameraSensorHelper for
analogue gain
Use CameraSensorHelper to convert the analogue gain code read from the
camera sensor into real analogue gain value. In the future this makes
it possible to use faster AE/AGC algorithm. For now the same AE/AGC
algorithm is used, but even then the CameraSensorHelper lets us use the
full range of analogue gain values.
If there is no CameraSensorHelper for the camera sensor in use, a
warning log message is printed, and the AE/AGC works exactly as before
this change.
Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
---
.../internal/software_isp/software_isp.h | 3 +-
src/ipa/simple/soft_simple.cpp | 77 ++++++++++++-------
src/libcamera/pipeline/simple/simple.cpp | 2 +-
src/libcamera/software_isp/software_isp.cpp | 8 +-
4 files changed, 57 insertions(+), 33 deletions(-)
diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index 8d25e979..2a6db7ba 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -26,6 +26,7 @@
#include <libcamera/ipa/soft_ipa_interface.h>
#include <libcamera/ipa/soft_ipa_proxy.h>
+#include "libcamera/internal/camera_sensor.h"
#include "libcamera/internal/dma_heaps.h"
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/shared_mem_object.h"
@@ -43,7 +44,7 @@ LOG_DECLARE_CATEGORY(SoftwareIsp)
class SoftwareIsp
{
public:
- SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
+ SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor);
~SoftwareIsp();
int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
index ac027568..e4d64762 100644
--- a/src/ipa/simple/soft_simple.cpp
+++ b/src/ipa/simple/soft_simple.cpp
@@ -22,6 +22,8 @@
#include "libcamera/internal/software_isp/debayer_params.h"
#include "libcamera/internal/software_isp/swisp_stats.h"
+#include "libipa/camera_sensor_helper.h"
+
#include "black_level.h"
namespace libcamera {
@@ -67,18 +69,27 @@ private:
DebayerParams *params_;
SwIspStats *stats_;
BlackLevel blackLevel_;
+ std::unique_ptr<CameraSensorHelper> camHelper_;
int32_t exposure_min_, exposure_max_;
- int32_t again_min_, again_max_;
- int32_t again_, exposure_;
+ int32_t exposure_;
+ double again_min_, again_max_, againMinStep_;
+ double again_;
unsigned int ignore_updates_;
};
-int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
+int IPASoftSimple::init(const IPASettings &settings,
const SharedFD &fdStats,
const SharedFD &fdParams,
const ControlInfoMap &sensorInfoMap)
{
+ camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
+ if (camHelper_ == nullptr) {
+ LOG(IPASoft, Warning)
+ << "Failed to create camera sensor helper for "
+ << settings.sensorModel;
+ }
+
fdStats_ = fdStats;
if (!fdStats_.isValid()) {
LOG(IPASoft, Error) << "Invalid Statistics handle";
@@ -132,25 +143,35 @@ int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)
exposure_min_ = 1;
}
- again_min_ = gain_info.min().get<int32_t>();
- again_max_ = gain_info.max().get<int32_t>();
- /*
- * The camera sensor gain (g) is usually not equal to the value written
- * into the gain register (x). But the way how the AGC algorithm changes
- * the gain value to make the total exposure closer to the optimum assumes
- * that g(x) is not too far from linear function. If the minimal gain is 0,
- * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
- * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
- * one edge, and very small near the other) we limit the range of the gain
- * values used.
- */
- if (!again_min_) {
- LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
- again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
+ int32_t again_min = gain_info.min().get<int32_t>();
+ int32_t again_max = gain_info.max().get<int32_t>();
+
+ if (camHelper_) {
+ again_min_ = camHelper_->gain(again_min);
+ again_max_ = camHelper_->gain(again_max);
+ againMinStep_ = (again_max_ - again_min_) / 100.0;
+ } else {
+ /*
+ * The camera sensor gain (g) is usually not equal to the value written
+ * into the gain register (x). But the way how the AGC algorithm changes
+ * the gain value to make the total exposure closer to the optimum assumes
+ * that g(x) is not too far from linear function. If the minimal gain is 0,
+ * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
+ * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
+ * one edge, and very small near the other) we limit the range of the gain
+ * values used.
+ */
+ again_max_ = again_max;
+ if (!again_min) {
+ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
+ again_min_ = std::min(100, again_min / 2 + again_max / 2);
+ }
+ againMinStep_ = 1.0;
}
LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
- << ", gain " << again_min_ << "-" << again_max_;
+ << ", gain " << again_min_ << "-" << again_max_
+ << " (" << againMinStep_ << ")";
return 0;
}
@@ -252,12 +273,14 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
ControlList ctrls(sensorControls);
exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();
- again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
+ int32_t again = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
+ again_ = camHelper_ ? camHelper_->gain(again) : again;
updateExposure(exposureMSV);
ctrls.set(V4L2_CID_EXPOSURE, exposure_);
- ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN,
+ static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(again_) : again_));
ignore_updates_ = 2;
@@ -276,7 +299,7 @@ void IPASoftSimple::updateExposure(double exposureMSV)
static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
- int next;
+ double next;
if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
next = exposure_ * kExpNumeratorUp / kExpDenominator;
@@ -286,18 +309,18 @@ void IPASoftSimple::updateExposure(double exposureMSV)
exposure_ = next;
if (exposure_ >= exposure_max_) {
next = again_ * kExpNumeratorUp / kExpDenominator;
- if (next - again_ < 1)
- again_ += 1;
+ if (next - again_ < againMinStep_)
+ again_ += againMinStep_;
else
again_ = next;
}
}
if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
- if (exposure_ == exposure_max_ && again_ != again_min_) {
+ if (exposure_ == exposure_max_ && again_ > again_min_) {
next = again_ * kExpNumeratorDown / kExpDenominator;
- if (again_ - next < 1)
- again_ -= 1;
+ if (again_ - next < againMinStep_)
+ again_ -= againMinStep_;
else
again_ = next;
} else {
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index c3ebb7b7..7e932a14 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -525,7 +525,7 @@ int SimpleCameraData::init()
* Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
*/
if (!converter_ && pipe->swIspEnabled()) {
- swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
+ swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get());
if (!swIsp_->isValid()) {
LOG(SimplePipeline, Warning)
<< "Failed to create software ISP, disabling software debayering";
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index 9b49be41..ea4d96e4 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -60,9 +60,9 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
/**
* \brief Constructs SoftwareIsp object
* \param[in] pipe The pipeline handler in use
- * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
+ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline handler
*/
-SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
+SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
: debayer_(nullptr),
debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
@@ -97,10 +97,10 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorCont
return;
}
- int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
+ int ret = ipa_->init(IPASettings{ "No cfg file", sensor->model() },
debayer_->getStatsFD(),
sharedParams_.fd(),
- sensorControls);
+ sensor->controls());
if (ret) {
LOG(SoftwareIsp, Error) << "IPA init failed";
debayer_.reset();
--
2.43.2

View file

@ -1,30 +0,0 @@
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

View file

@ -1,14 +1,14 @@
From 9bec33e5c7e6765734eeef2d22d7f7f65dee2264 Mon Sep 17 00:00:00 2001
From 2bde6e420571c6dc0ff25246620b4c987987f6be 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
Subject: [PATCH 20/21] 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(+)
src/libcamera/software_isp/swstats_cpu.cpp | 4 ++++
3 files changed, 18 insertions(+)
diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
index f19f72ea..7ad4b9ef 100644
@ -35,19 +35,18 @@ index f19f72ea..7ad4b9ef 100644
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
index 3be3cdfe..d6599805 100644
--- a/src/libcamera/software_isp/debayer_cpu.cpp
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -23,6 +23,8 @@
@@ -23,6 +23,7 @@
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
/**
* \class DebayerCpu
* \brief Class for debayering on the CPU
@@ -262,6 +263,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
BayerFormat bayerFormat =
BayerFormat::fromPixelFormat(inputFormat);
@ -57,39 +56,40 @@ index 41c8805f..b6393925 100644
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
@@ -330,7 +334,11 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF
BayerFormat bayerFormat =
BayerFormat::fromPixelFormat(inputFormat);
+ if (is_ov01a1s)
+ bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
+
xShift_ = 0;
+
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
index be310f56..cda1894a 100644
--- a/src/libcamera/software_isp/swstats_cpu.cpp
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
@@ -19,6 +19,8 @@
@@ -19,6 +19,7 @@
namespace libcamera {
+extern bool is_ov01a1s;
+
SwStatsCpu::SwStatsCpu()
: SwStats()
{
@@ -301,6 +303,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
/**
* \class SwStatsCpu
* \brief Class for gathering statistics on the CPU
@@ -271,6 +272,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;
if (bayerFormat.packing == BayerFormat::Packing::None &&
setupStandardBayerOrder(bayerFormat.order) == 0) {
switch (bayerFormat.bitDepth) {
--
2.43.0
2.43.2

View file

@ -0,0 +1,42 @@
From a21bb26dcfcc00425f031421b87576f9c81e4824 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 21/21] 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>
---
src/libcamera/software_isp/debayer_cpu.cpp | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
index d6599805..5a06b191 100644
--- a/src/libcamera/software_isp/debayer_cpu.cpp
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -790,10 +790,17 @@ SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
return {};
}
- return SizeRange(Size(pattern_size.width, pattern_size.height),
- Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
- (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)),
- pattern_size.width, pattern_size.height);
+ /*
+ * 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);
}
} /* namespace libcamera */
--
2.43.2

View file

@ -1,131 +0,0 @@
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

View file

@ -1,315 +0,0 @@
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

View file

@ -1,40 +0,0 @@
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