From af9a8d372b24710bf7fc27c8e81244e1ca6d1658 Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Tue, 30 Jan 2024 11:43:15 +0200 Subject: feat(users/flokli/ipu6-softisp): init This code adds support for the ipu6 webcams via libcamera, based on the work in https://copr.fedorainfracloud.org/coprs/jwrdegoede/ipu6-softisp/. It's supposed to be included in your NixOS configuration imports. Change-Id: Ifb71999ad61161fa23506b97cb449f73fb1270e3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/10709 Tested-by: BuildkiteCI Reviewed-by: flokli Autosubmit: flokli --- users/flokli/ipu6-softisp/README.md | 26 + users/flokli/ipu6-softisp/config.nix | 94 +++ ...ipeline-simple-fix-size-adjustment-in-val.patch | 43 ++ ...nternal-Move-dma_heaps.-h-cpp-to-common-d.patch | 194 ++++++ ...ma_heaps-extend-DmaHeap-class-to-support-.patch | 121 ++++ ...nternal-Move-SharedMemObject-class-to-a-c.patch | 57 ++ ...nternal-Document-the-SharedMemObject-clas.patch | 139 ++++ ...006-libcamera-introduce-SoftwareIsp-class.patch | 354 ++++++++++ ...amera-software_isp-Add-SwStats-base-class.patch | 382 +++++++++++ ...bcamera-software_isp-Add-SwStatsCpu-class.patch | 272 ++++++++ ...amera-software_isp-Add-Debayer-base-class.patch | 272 ++++++++ ...bcamera-software_isp-Add-DebayerCpu-class.patch | 727 +++++++++++++++++++++ ...1-libcamera-ipa-add-Soft-IPA-common-files.patch | 256 ++++++++ ...pa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch | 407 ++++++++++++ ...oftware_isp-add-Simple-SoftwareIsp-implem.patch | 483 ++++++++++++++ ...ipeline-simple-rename-converterBuffers_-a.patch | 238 +++++++ ...ipeline-simple-enable-use-of-Soft-ISP-and.patch | 243 +++++++ ...wstats_cpu-Add-support-for-8-10-and-12-bp.patch | 199 ++++++ ...ebayer_cpu-Add-support-for-8-10-and-12-bp.patch | 237 +++++++ ...era-debayer_cpu-Add-BGR888-output-support.patch | 125 ++++ ...ipeline-simple-Enable-simplepipeline-for-.patch | 30 + ...dd-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch | 237 +++++++ ...wstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch | 131 ++++ ...ebayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch | 315 +++++++++ ...dd-Software-ISP-benchmarking-documentatio.patch | 130 ++++ .../ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch | 95 +++ ...ebayer_cpu-Make-the-minimum-size-1280x720.patch | 40 ++ 27 files changed, 5847 insertions(+) create mode 100644 users/flokli/ipu6-softisp/README.md create mode 100644 users/flokli/ipu6-softisp/config.nix create mode 100644 users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-SwStats-base-class.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-SwStatsCpu-class.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0009-libcamera-software_isp-Add-Debayer-base-class.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0012-libcamera-ipa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0014-libcamera-pipeline-simple-rename-converterBuffers_-a.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0015-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0016-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0020-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch (limited to 'users') diff --git a/users/flokli/ipu6-softisp/README.md b/users/flokli/ipu6-softisp/README.md new file mode 100644 index 000000000000..9c09e9a158e4 --- /dev/null +++ b/users/flokli/ipu6-softisp/README.md @@ -0,0 +1,26 @@ +# ipu6-softisp + +This code adds support for the ipu6 webcams via libcamera, based on the work in +https://copr.fedorainfracloud.org/coprs/jwrdegoede/ipu6-softisp/. + +It's supposed to be included in your NixOS configuration imports, and will: + + - Add some patches to your kernel, which should apply on 6.7.x + - Add the `ipu6-camera-bins` firmware (still needed) + - Enable some kernel config options + - Add an udev rule so libcamera can do DMABUF things + - Override `services.pipewire.package` and + `services.pipewire.wireplumber.package` to use a pipewire built with a libcamera + with support for this webcam. + +Please make sure you don't have any of the `hardware.ipu6` options still +enabled, as they use the closed-source userspace stack and will conflict. + +The testing instructions from +https://copr.fedorainfracloud.org/coprs/jwrdegoede/ipu6-softisp/ still apply. + +`qcam` can be found in `libcamera-qcam` (pending on +https://github.com/NixOS/nixpkgs/pull/284964 to trickle into master). + +Thanks to Hans de Goede for helping me bringing this up, as well as to +puckipedia for sorting out some pipewire-related confusion. diff --git a/users/flokli/ipu6-softisp/config.nix b/users/flokli/ipu6-softisp/config.nix new file mode 100644 index 000000000000..9232d5bc9ee5 --- /dev/null +++ b/users/flokli/ipu6-softisp/config.nix @@ -0,0 +1,94 @@ +{ pkgs, lib, ... }: + +let + libcamera = pkgs.libcamera.overrideAttrs (old: { + # This is a mix of #281755 (bump pipewire to 0.2.0), + # and the additional ipu6-softisp patches. + version = "0.2.0"; + src = pkgs.fetchgit { + url = "https://git.libcamera.org/libcamera/libcamera.git"; + rev = "v0.2.0"; + hash = "sha256-x0Im9m9MoACJhQKorMI34YQ+/bd62NdAPc2nWwaJAvM="; + }; + + mesonFlags = old.mesonFlags or [ ] ++ [ + "-Dpipelines=simple/simple,ipu3,uvcvideo" + "-Dipas=simple/simple,ipu3" + ]; + + # Explicitly clear list of patches, as #281755 did. + # 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. + patches = [ + ./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 + ]; + }); + + # compat with libcamera 0.2 + pipewire' = (pkgs.pipewire.overrideAttrs (old: { + patches = old.patches or [ ] ++ [ + (pkgs.fetchpatch { + # https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/1750 + name = "pipewire-spa-libcamera-use-cameraconfiguration-orientation-pr1750.patch"; + url = "https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/1750.patch "; + hash = "sha256-Ugg913KZDKELnYLwpDEgYh92YPxccw61l6kAJulBbIA="; + }) + ]; + })).override { + inherit libcamera; + }; + + wireplumber' = (pkgs.wireplumber.override { + pipewire = pipewire'; + }); +in +{ + hardware.firmware = [ pkgs.ipu6-camera-bins ]; + + boot.kernelPatches = [{ + name = "linux-kernel-test.patch"; + patch = pkgs.fetchurl { + url = "https://copr-dist-git.fedorainfracloud.org/cgit/jwrdegoede/ipu6-softisp/kernel.git/plain/linux-kernel-test.patch?h=f39&id=0ed76891b2fc08579d08bedb9294a41840007299"; + hash = "sha256-8hKP4nltGlkzr8iOgsIUT9Tt5i+x4kdLmw/+lFeNoGQ="; + }; + extraStructuredConfig = { + # needed for /dev/dma_heap + DMABUF_HEAPS_CMA = lib.kernel.yes; + DMABUF_HEAPS_SYSTEM = lib.kernel.yes; + DMABUF_HEAPS = lib.kernel.yes; + }; + }]; + + + services.udev.extraRules = '' + KERNEL=="system", SUBSYSTEM=="dma_heap", TAG+="uaccess" + ''; + + services.pipewire.package = pipewire'; + services.pipewire.wireplumber.package = wireplumber'; +} diff --git a/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch b/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch new file mode 100644 index 000000000000..7a71ed1db7a4 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch @@ -0,0 +1,43 @@ +From aa818f7b749122f916be1ced48d1a3a2b3aeb47e Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Tue, 2 Jan 2024 23:47:20 +0300 +Subject: [PATCH 01/25] libcamera: pipeline: simple: fix size adjustment in + validate() + +SimpleCameraConfiguration::validate() adjusts the configuration +of its streams (if the size is not in the outputSizes) to +the captureSize. But the captureSize itself can be not in the +outputSizes, and then the adjusted configuration won't be +valid resulting in camera configuration failure. + +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + src/libcamera/pipeline/simple/simple.cpp | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp +index 911051b2..4d0e7255 100644 +--- a/src/libcamera/pipeline/simple/simple.cpp ++++ b/src/libcamera/pipeline/simple/simple.cpp +@@ -997,10 +997,13 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate() + } + + if (!pipeConfig_->outputSizes.contains(cfg.size)) { ++ Size adjustedSize = pipeConfig_->captureSize; ++ if (!pipeConfig_->outputSizes.contains(adjustedSize)) ++ adjustedSize = pipeConfig_->outputSizes.max; + LOG(SimplePipeline, Debug) + << "Adjusting size from " << cfg.size +- << " to " << pipeConfig_->captureSize; +- cfg.size = pipeConfig_->captureSize; ++ << " to " << adjustedSize; ++ cfg.size = adjustedSize; + status = Adjusted; + } + +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch b/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch new file mode 100644 index 000000000000..85a27ba8e226 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch @@ -0,0 +1,194 @@ +From ca3bcfde49f069a85f7860f61d8c3bd196f97139 Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Tue, 26 Dec 2023 16:55:08 +0300 +Subject: [PATCH 02/25] libcamera: internal: Move dma_heaps.[h,cpp] to common + directories + +DmaHeap class is useful outside the RPi pipeline handler too. + +Move dma_heaps.h and dma_heaps.cpp to common directories. Update +the build files and RPi vc4 pipeline handler accordingly. + +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + .../libcamera/internal}/dma_heaps.h | 4 ---- + include/libcamera/internal/meson.build | 1 + + .../{pipeline/rpi/vc4 => }/dma_heaps.cpp | 18 +++++++----------- + src/libcamera/meson.build | 1 + + src/libcamera/pipeline/rpi/vc4/meson.build | 1 - + src/libcamera/pipeline/rpi/vc4/vc4.cpp | 5 ++--- + 6 files changed, 11 insertions(+), 19 deletions(-) + rename {src/libcamera/pipeline/rpi/vc4 => include/libcamera/internal}/dma_heaps.h (92%) + rename src/libcamera/{pipeline/rpi/vc4 => }/dma_heaps.cpp (83%) + +diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h b/include/libcamera/internal/dma_heaps.h +similarity index 92% +rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.h +rename to include/libcamera/internal/dma_heaps.h +index 0a4a8d86..cff8f140 100644 +--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h ++++ b/include/libcamera/internal/dma_heaps.h +@@ -13,8 +13,6 @@ + + namespace libcamera { + +-namespace RPi { +- + class DmaHeap + { + public: +@@ -27,6 +25,4 @@ private: + UniqueFD dmaHeapHandle_; + }; + +-} /* namespace RPi */ +- + } /* namespace libcamera */ +diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build +index 7f1f3440..33eb0fb3 100644 +--- a/include/libcamera/internal/meson.build ++++ b/include/libcamera/internal/meson.build +@@ -25,6 +25,7 @@ libcamera_internal_headers = files([ + 'device_enumerator.h', + 'device_enumerator_sysfs.h', + 'device_enumerator_udev.h', ++ 'dma_heaps.h', + 'formats.h', + 'framebuffer.h', + 'ipa_manager.h', +diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp +similarity index 83% +rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp +rename to src/libcamera/dma_heaps.cpp +index 317b1fc1..7444d9c2 100644 +--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp ++++ b/src/libcamera/dma_heaps.cpp +@@ -5,8 +5,6 @@ + * dma_heaps.h - Helper class for dma-heap allocations. + */ + +-#include "dma_heaps.h" +- + #include + #include + #include +@@ -16,6 +14,8 @@ + + #include + ++#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 heapNames = { + + namespace libcamera { + +-LOG_DECLARE_CATEGORY(RPI) +- +-namespace RPi { ++LOG_DEFINE_CATEGORY(DmaHeap) + + DmaHeap::DmaHeap() + { +@@ -40,7 +38,7 @@ DmaHeap::DmaHeap() + int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); + if (ret < 0) { + ret = errno; +- LOG(RPI, Debug) << "Failed to open " << name << ": " ++ LOG(DmaHeap, Debug) << "Failed to open " << name << ": " + << strerror(ret); + continue; + } +@@ -50,7 +48,7 @@ DmaHeap::DmaHeap() + } + + if (!dmaHeapHandle_.isValid()) +- LOG(RPI, Error) << "Could not open any dmaHeap device"; ++ LOG(DmaHeap, Error) << "Could not open any dmaHeap device"; + } + + DmaHeap::~DmaHeap() = default; +@@ -69,7 +67,7 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size) + + ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc); + if (ret < 0) { +- LOG(RPI, Error) << "dmaHeap allocation failure for " ++ LOG(DmaHeap, Error) << "dmaHeap allocation failure for " + << name; + return {}; + } +@@ -77,7 +75,7 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size) + UniqueFD allocFd(alloc.fd); + ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name); + if (ret < 0) { +- LOG(RPI, Error) << "dmaHeap naming failure for " ++ LOG(DmaHeap, Error) << "dmaHeap naming failure for " + << name; + return {}; + } +@@ -85,6 +83,4 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size) + return allocFd; + } + +-} /* namespace RPi */ +- + } /* namespace libcamera */ +diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build +index 45f63e93..3c5e43df 100644 +--- a/src/libcamera/meson.build ++++ b/src/libcamera/meson.build +@@ -17,6 +17,7 @@ libcamera_sources = files([ + 'delayed_controls.cpp', + 'device_enumerator.cpp', + 'device_enumerator_sysfs.cpp', ++ 'dma_heaps.cpp', + 'fence.cpp', + 'formats.cpp', + 'framebuffer.cpp', +diff --git a/src/libcamera/pipeline/rpi/vc4/meson.build b/src/libcamera/pipeline/rpi/vc4/meson.build +index cdb049c5..386e2296 100644 +--- a/src/libcamera/pipeline/rpi/vc4/meson.build ++++ b/src/libcamera/pipeline/rpi/vc4/meson.build +@@ -1,7 +1,6 @@ + # SPDX-License-Identifier: CC0-1.0 + + libcamera_sources += files([ +- 'dma_heaps.cpp', + 'vc4.cpp', + ]) + +diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp +index 26102ea7..3a42e75e 100644 +--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp ++++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp +@@ -12,12 +12,11 @@ + #include + + #include "libcamera/internal/device_enumerator.h" ++#include "libcamera/internal/dma_heaps.h" + + #include "../common/pipeline_base.h" + #include "../common/rpi_stream.h" + +-#include "dma_heaps.h" +- + using namespace std::chrono_literals; + + namespace libcamera { +@@ -87,7 +86,7 @@ public: + RPi::Device isp_; + + /* DMAHEAP allocation helper. */ +- RPi::DmaHeap dmaHeap_; ++ DmaHeap dmaHeap_; + SharedFD lsTable_; + + struct Config { +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch b/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch new file mode 100644 index 000000000000..de819244871a --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch @@ -0,0 +1,121 @@ +From 6d5f3b0b54df4ff66079675a4c1f0f0b76778e22 Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Wed, 10 Jan 2024 23:51:25 +0300 +Subject: [PATCH 03/25] libcamera: dma_heaps: extend DmaHeap class to support + system heap + +Add an argument to the constructor to specify dma heaps type(s) +to use. Can be DmaHeapFlag::Cma and/or DmaHeapFlag::System. +By default DmaHeapFlag::Cma is used. If both DmaHeapFlag::Cma and +DmaHeapFlag::System are set, CMA heap is tried first. + +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + include/libcamera/internal/dma_heaps.h | 12 +++++++- + src/libcamera/dma_heaps.cpp | 39 +++++++++++++++----------- + 2 files changed, 34 insertions(+), 17 deletions(-) + +diff --git a/include/libcamera/internal/dma_heaps.h b/include/libcamera/internal/dma_heaps.h +index cff8f140..22aa1007 100644 +--- a/include/libcamera/internal/dma_heaps.h ++++ b/include/libcamera/internal/dma_heaps.h +@@ -9,6 +9,7 @@ + + #include + ++#include + #include + + namespace libcamera { +@@ -16,7 +17,14 @@ namespace libcamera { + class DmaHeap + { + public: +- DmaHeap(); ++ enum class DmaHeapFlag { ++ Cma = (1 << 0), ++ System = (1 << 1), ++ }; ++ ++ using DmaHeapFlags = Flags; ++ ++ DmaHeap(DmaHeapFlags flags = DmaHeapFlag::Cma); + ~DmaHeap(); + bool isValid() const { return dmaHeapHandle_.isValid(); } + UniqueFD alloc(const char *name, std::size_t size); +@@ -25,4 +33,6 @@ private: + UniqueFD dmaHeapHandle_; + }; + ++LIBCAMERA_FLAGS_ENABLE_OPERATORS(DmaHeap::DmaHeapFlag) ++ + } /* namespace libcamera */ +diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp +index 7444d9c2..177de31b 100644 +--- a/src/libcamera/dma_heaps.cpp ++++ b/src/libcamera/dma_heaps.cpp +@@ -16,6 +16,8 @@ + + #include "libcamera/internal/dma_heaps.h" + ++namespace libcamera { ++ + /* + * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma + * to only have to worry about importing. +@@ -23,28 +25,33 @@ + * Annoyingly, should the cma heap size be specified on the kernel command line + * instead of DT, the heap gets named "reserved" instead. + */ +-static constexpr std::array heapNames = { +- "/dev/dma_heap/linux,cma", +- "/dev/dma_heap/reserved" ++static constexpr std::array, 3> heapNames = { ++ /* CMA heap names first */ ++ std::make_pair(DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma"), ++ std::make_pair(DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved"), ++ std::make_pair(DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system") + }; + +-namespace libcamera { +- + LOG_DEFINE_CATEGORY(DmaHeap) + +-DmaHeap::DmaHeap() ++DmaHeap::DmaHeap(DmaHeapFlags flags) + { +- for (const char *name : heapNames) { +- int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); +- if (ret < 0) { +- ret = errno; +- LOG(DmaHeap, Debug) << "Failed to open " << name << ": " +- << strerror(ret); +- continue; +- } ++ int ret; + +- dmaHeapHandle_ = UniqueFD(ret); +- break; ++ for (const auto &name : heapNames) { ++ if (flags & name.first) { ++ ret = ::open(name.second, O_RDWR | O_CLOEXEC, 0); ++ if (ret < 0) { ++ ret = errno; ++ LOG(DmaHeap, Debug) << "Failed to open " << name.second << ": " ++ << strerror(ret); ++ continue; ++ } ++ ++ LOG(DmaHeap, Debug) << "Using " << name.second; ++ dmaHeapHandle_ = UniqueFD(ret); ++ break; ++ } + } + + if (!dmaHeapHandle_.isValid()) +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch b/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch new file mode 100644 index 000000000000..022270723a4e --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch @@ -0,0 +1,57 @@ +From 006a4a31a6803e92ec67f48b66da2cdff8b2f6ab Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Sun, 29 Oct 2023 15:56:48 +0300 +Subject: [PATCH 04/25] libcamera: internal: Move SharedMemObject class to a + common directory + +Move SharedMemObject class out of RPi namespace and put it into +include/libcamera/internal so that everyone could use it. + +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + include/libcamera/internal/meson.build | 1 + + .../common => include/libcamera/internal}/shared_mem_object.h | 4 ---- + 2 files changed, 1 insertion(+), 4 deletions(-) + rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (98%) + +diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build +index 33eb0fb3..5807dfd9 100644 +--- a/include/libcamera/internal/meson.build ++++ b/include/libcamera/internal/meson.build +@@ -39,6 +39,7 @@ libcamera_internal_headers = files([ + 'process.h', + 'pub_key.h', + 'request.h', ++ 'shared_mem_object.h', + 'source_paths.h', + 'sysfs.h', + 'v4l2_device.h', +diff --git a/src/libcamera/pipeline/rpi/common/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h +similarity index 98% +rename from src/libcamera/pipeline/rpi/common/shared_mem_object.h +rename to include/libcamera/internal/shared_mem_object.h +index aa56c220..bfb639ee 100644 +--- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h ++++ b/include/libcamera/internal/shared_mem_object.h +@@ -19,8 +19,6 @@ + + namespace libcamera { + +-namespace RPi { +- + template + class SharedMemObject + { +@@ -123,6 +121,4 @@ private: + T *obj_; + }; + +-} /* namespace RPi */ +- + } /* namespace libcamera */ +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch b/users/flokli/ipu6-softisp/libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch new file mode 100644 index 000000000000..a20d27059db0 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch @@ -0,0 +1,139 @@ +From cb9ff82efd82af8ae26b2aca4183928c74f7ef34 Mon Sep 17 00:00:00 2001 +From: Dennis Bonke +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 +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + .../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 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 + SharedMemObject(const std::string &name, Args &&...args) + : name_(name), obj_(nullptr) +@@ -57,6 +72,10 @@ public: + obj_ = new (mem) T(std::forward(args)...); + } + ++ /** ++ * \brief Move constructor for SharedMemObject. ++ * \param[in] rhs The object to move. ++ */ + SharedMemObject(SharedMemObject &&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 &operator=(SharedMemObject &&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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch b/users/flokli/ipu6-softisp/libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch new file mode 100644 index 000000000000..ebda98d2672c --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch @@ -0,0 +1,354 @@ +From 3fa62a8e2f34c9794ba67e2565db8fef22938fa4 Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +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 +Signed-off-by: Dennis Bonke +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#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 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 ++ 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> &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> *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 &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 &getSignalSetSensorControls() = 0; ++ ++ /** ++ * \brief Signals that the input buffer is ready. ++ */ ++ Signal inputBufferReady; ++ /** ++ * \brief Signals that the output buffer is ready. ++ */ ++ Signal outputBufferReady; ++ ++ /** ++ * \brief Signals that the ISP stats are ready. ++ * ++ * The int parameter isn't actually used. ++ */ ++ Signal 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 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 createInstance(PipelineHandler *pipe, ++ const ControlInfoMap &sensorControls) const = 0; ++}; ++ ++/** ++ * \brief Implementation for the Software ISP Factory. ++ */ ++template ++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 createInstance(PipelineHandler *pipe, ++ const ControlInfoMap &sensorControls) const override ++ { ++ return std::make_unique<_SoftwareIsp>(pipe, sensorControls); ++ } ++}; ++ ++#define REGISTER_SOFTWAREISP(softwareIsp) \ ++ static SoftwareIspFactory 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 ++ ++namespace libcamera { ++ ++LOG_DEFINE_CATEGORY(SoftwareIsp) ++ ++SoftwareIsp::SoftwareIsp([[maybe_unused]] PipelineHandler *pipe, ++ [[maybe_unused]] const ControlInfoMap &sensorControls) ++{ ++} ++ ++SoftwareIsp::~SoftwareIsp() ++{ ++} ++ ++/* SoftwareIspFactoryBase */ ++ ++SoftwareIspFactoryBase::SoftwareIspFactoryBase() ++{ ++ registerType(this); ++} ++ ++void SoftwareIspFactoryBase::registerType(SoftwareIspFactoryBase *factory) ++{ ++ SoftwareIspFactoryBase *®istered = ++ SoftwareIspFactoryBase::factory(); ++ ++ ASSERT(!registered && factory); ++ registered = factory; ++} ++ ++SoftwareIspFactoryBase *&SoftwareIspFactoryBase::factory() ++{ ++ static SoftwareIspFactoryBase *factory; ++ return factory; ++} ++ ++std::unique_ptr ++SoftwareIspFactoryBase::create(PipelineHandler *pipe, ++ const ControlInfoMap &sensorControls) ++{ ++ SoftwareIspFactoryBase *factory = SoftwareIspFactoryBase::factory(); ++ if (!factory) ++ return nullptr; ++ ++ std::unique_ptr swIsp = factory->createInstance(pipe, sensorControls); ++ if (swIsp->isValid()) ++ return swIsp; ++ ++ return nullptr; ++} ++ ++} /* namespace libcamera */ +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-SwStats-base-class.patch b/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-SwStats-base-class.patch new file mode 100644 index 000000000000..9d6f2ac07fd8 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-SwStats-base-class.patch @@ -0,0 +1,382 @@ +From ca3bb6ddf5307537aa05e43d3ec1ff7ffdc0efed Mon Sep 17 00:00:00 2001 +From: Hans de Goede +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 +Signed-off-by: Dennis Bonke +Co-authored-by: Andrey Konovalov +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + 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 ++ * ++ * swstats.h - software statistics base class ++ */ ++ ++#pragma once ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++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 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 ++ * ++ * 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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-SwStatsCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-SwStatsCpu-class.patch new file mode 100644 index 000000000000..d48eadd9949f --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-SwStatsCpu-class.patch @@ -0,0 +1,272 @@ +From c1c43445cd4408010e500fe9d6b6424c77bcf75d Mon Sep 17 00:00:00 2001 +From: Hans de Goede +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 +Signed-off-by: Andrey Konovalov +Co-authored-by: Pavel Machek +Signed-off-by: Pavel Machek +Co-authored-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Co-authored-by: Marttico +Signed-off-by: Marttico +Co-authored-by: Toon Langendam +Signed-off-by: Toon Langendam +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + .../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 ++ * ++ * 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 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 ++ * ++ * swstats_cpu.cpp - CPU based software statistics implementation ++ */ ++ ++#include "libcamera/internal/software_isp/swstats_cpu.h" ++ ++#include ++ ++#include ++ ++#include "libcamera/internal/bayer_format.h" ++ ++namespace libcamera { ++ ++SwStatsCpu::SwStatsCpu() ++ : SwStats() ++{ ++ sharedStats_ = SharedMemObject("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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0009-libcamera-software_isp-Add-Debayer-base-class.patch b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-software_isp-Add-Debayer-base-class.patch new file mode 100644 index 000000000000..f43b3368e616 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-software_isp-Add-Debayer-base-class.patch @@ -0,0 +1,272 @@ +From 8fc77447c0d76b0b52b19d23674049181c6cf8d2 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Dec 2023 14:46:53 +0100 +Subject: [PATCH 09/25] libcamera: software_isp: Add Debayer base class + +Add a base class for debayer implementations. This is intended to be +suitable for both GPU (or otherwise) accelerated debayer implementations +as well as CPU based debayering. + +Doxygen documentation by Dennis Bonke. + +Co-authored-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Co-authored-by: Andrey Konovalov +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + .../libcamera/internal/software_isp/debayer.h | 132 ++++++++++++++++++ + .../internal/software_isp/debayer_params.h | 43 ++++++ + .../internal/software_isp/meson.build | 2 + + src/libcamera/software_isp/debayer.cpp | 22 +++ + src/libcamera/software_isp/meson.build | 1 + + 5 files changed, 200 insertions(+) + create mode 100644 include/libcamera/internal/software_isp/debayer.h + create mode 100644 include/libcamera/internal/software_isp/debayer_params.h + create mode 100644 src/libcamera/software_isp/debayer.cpp + +diff --git a/include/libcamera/internal/software_isp/debayer.h b/include/libcamera/internal/software_isp/debayer.h +new file mode 100644 +index 00000000..39e6f393 +--- /dev/null ++++ b/include/libcamera/internal/software_isp/debayer.h +@@ -0,0 +1,132 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * debayer.h - debayering base class ++ */ ++ ++#pragma once ++ ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "libcamera/internal/software_isp/debayer_params.h" ++ ++namespace libcamera { ++ ++class FrameBuffer; ++ ++LOG_DECLARE_CATEGORY(Debayer) ++ ++/** ++ * \class Debayer ++ * \brief Base debayering class ++ * ++ * Base class that provides functions for setting up the debayering process. ++ */ ++class Debayer ++{ ++public: ++ virtual ~Debayer() = 0; ++ ++ /** ++ * \brief Configure the debayer object according to the passed in parameters. ++ * \param[in] inputCfg The input configuration. ++ * \param[in] outputCfgs The output configurations. ++ * ++ * \return 0 on success, a negative errno on failure. ++ */ ++ virtual int configure(const StreamConfiguration &inputCfg, ++ const std::vector> &outputCfgs) = 0; ++ ++ /** ++ * \brief Get the width and height at which the bayer pattern repeats. ++ * \param[in] inputFormat The input format. ++ * ++ * \return pattern size or an empty size for unsupported inputFormats. ++ */ ++ virtual Size patternSize(PixelFormat inputFormat) = 0; ++ ++ /** ++ * \brief Get the supported output formats. ++ * \param[in] inputFormat The input format. ++ * ++ * \return all supported output formats or an empty vector if there are none. ++ */ ++ virtual std::vector formats(PixelFormat inputFormat) = 0; ++ ++ /** ++ * \brief Get the stride and the frame size. ++ * \param[in] outputFormat The output format. ++ * \param[in] size The output size. ++ * ++ * \return a tuple of the stride and the frame size, or a tuple with 0,0 if there is no valid output config. ++ */ ++ virtual std::tuple ++ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0; ++ ++ /** ++ * \brief Process the bayer data into the requested format. ++ * \param[in] input The input buffer. ++ * \param[in] output The output buffer. ++ * \param[in] params The parameters to be used in debayering. ++ * ++ * \note DebayerParams is passed by value deliberately so that a copy is passed ++ * when this is run in another thread by invokeMethod(). ++ */ ++ virtual void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params) = 0; ++ ++ /** ++ * \brief Get the supported output sizes for the given input format and size. ++ * \param[in] inputFormat The input format. ++ * \param[in] inputSize The input size. ++ * ++ * \return The valid size ranges or an empty range if there are none. ++ */ ++ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) ++ { ++ Size pattern_size = patternSize(inputFormat); ++ ++ if (pattern_size.isNull()) ++ return {}; ++ ++ /* ++ * For debayer interpolation a border of pattern-height x pattern-width ++ * is kept around the entire image. Combined with a minimum-size of ++ * pattern-height x pattern-width this means the input-size needs to be ++ * at least (3 * pattern-height) x (3 * pattern-width). ++ */ ++ if (inputSize.width < (3 * pattern_size.width) || ++ inputSize.height < (3 * pattern_size.height)) { ++ LOG(Debayer, Warning) ++ << "Input format size too small: " << inputSize.toString(); ++ return {}; ++ } ++ ++ return SizeRange(Size(pattern_size.width, pattern_size.height), ++ Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1), ++ (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1)), ++ pattern_size.width, pattern_size.height); ++ } ++ ++ /** ++ * \brief Signals when the input buffer is ready. ++ */ ++ Signal inputBufferReady; ++ ++ /** ++ * \brief Signals when the output buffer is ready. ++ */ ++ Signal outputBufferReady; ++}; ++ ++} /* namespace libcamera */ +diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h +new file mode 100644 +index 00000000..8f515304 +--- /dev/null ++++ b/include/libcamera/internal/software_isp/debayer_params.h +@@ -0,0 +1,43 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * 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 ++ * ++ * debayer.cpp - debayer base class ++ */ ++ ++#include "libcamera/internal/software_isp/debayer.h" ++ ++namespace libcamera { ++ ++LOG_DEFINE_CATEGORY(Debayer) ++ ++Debayer::~Debayer() ++{ ++} ++ ++} /* namespace libcamera */ +diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build +index d31c6217..d4ae5ac7 100644 +--- a/src/libcamera/software_isp/meson.build ++++ b/src/libcamera/software_isp/meson.build +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: CC0-1.0 + + libcamera_sources += files([ ++ 'debayer.cpp', + 'swstats.cpp', + 'swstats_cpu.cpp', + ]) +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch new file mode 100644 index 000000000000..56bc87336c2e --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch @@ -0,0 +1,727 @@ +From 7eb7164ed7d90ea4cf9ec7e4f35fa8efa25f35e9 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Dec 2023 17:00:17 +0100 +Subject: [PATCH 10/25] libcamera: software_isp: Add DebayerCpu class + +Add CPU based debayering implementation. This initial implementation +only supports debayering packed 10 bits per pixel bayer data in +the 4 standard bayer orders. + +Doxygen documentation by Dennis Bonke. + +Co-authored-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Co-authored-by: Andrey Konovalov +Signed-off-by: Andrey Konovalov +Co-authored-by: Pavel Machek +Signed-off-by: Pavel Machek +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + .../internal/software_isp/debayer_cpu.h | 131 +++++ + .../internal/software_isp/meson.build | 1 + + src/libcamera/software_isp/debayer_cpu.cpp | 528 ++++++++++++++++++ + src/libcamera/software_isp/meson.build | 1 + + 4 files changed, 661 insertions(+) + create mode 100644 include/libcamera/internal/software_isp/debayer_cpu.h + create mode 100644 src/libcamera/software_isp/debayer_cpu.cpp + +diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h +new file mode 100644 +index 00000000..78573f44 +--- /dev/null ++++ b/include/libcamera/internal/software_isp/debayer_cpu.h +@@ -0,0 +1,131 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * debayer_cpu.h - CPU based debayering header ++ */ ++ ++#pragma once ++ ++#include ++#include ++#include ++ ++#include ++ ++#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 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> &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 formats(PixelFormat input); ++ std::tuple ++ 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 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 stats_; ++ uint8_t *lineBuffers_[5]; ++ unsigned int lineBufferIndex_; ++ bool enableInputMemcpy_; ++ float gamma_correction_; ++ int measuredFrames_; ++ int64_t frameProcessTime_; ++ /* Skip 30 frames for things to stabilize then measure 30 frames */ ++ static const int framesToSkip = 30; ++ static const int framesToMeasure = 60; ++}; ++ ++} /* namespace libcamera */ +diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build +index 7e40925e..b5a0d737 100644 +--- a/include/libcamera/internal/software_isp/meson.build ++++ b/include/libcamera/internal/software_isp/meson.build +@@ -2,6 +2,7 @@ + + libcamera_internal_headers += files([ + 'debayer.h', ++ 'debayer_cpu.h', + 'debayer_params.h', + 'swisp_stats.h', + 'swstats.h', +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +new file mode 100644 +index 00000000..e0c3c658 +--- /dev/null ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -0,0 +1,528 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * debayer_cpu.cpp - CPU based debayering class ++ */ ++ ++#include "libcamera/internal/software_isp/debayer_cpu.h" ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "libcamera/internal/bayer_format.h" ++#include "libcamera/internal/framebuffer.h" ++#include "libcamera/internal/mapped_framebuffer.h" ++ ++namespace libcamera { ++ ++DebayerCpu::DebayerCpu(std::unique_ptr stats) ++ : stats_(std::move(stats)), gamma_correction_(1.0) ++{ ++#ifdef __x86_64__ ++ enableInputMemcpy_ = false; ++#else ++ enableInputMemcpy_ = true; ++#endif ++ /* Initialize gamma to 1.0 curve */ ++ for (int i = 0; i < 1024; i++) ++ gamma_[i] = i / 4; ++ ++ for (int i = 0; i < 5; i++) ++ lineBuffers_[i] = NULL; ++} ++ ++DebayerCpu::~DebayerCpu() ++{ ++ for (int i = 0; i < 5; i++) ++ free(lineBuffers_[i]); ++} ++ ++// RGR ++// GBG ++// RGR ++#define BGGR_BGR888(p, n, div) \ ++ *dst++ = blue_[curr[x] / (div)]; \ ++ *dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))]; \ ++ *dst++ = red_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \ ++ x++; ++ ++// GBG ++// RGR ++// GBG ++#define GRBG_BGR888(p, n, div) \ ++ *dst++ = blue_[(prev[x] + next[x]) / (2 * (div))]; \ ++ *dst++ = green_[curr[x] / (div)]; \ ++ *dst++ = red_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \ ++ x++; ++ ++// GRG ++// BGB ++// GRG ++#define GBRG_BGR888(p, n, div) \ ++ *dst++ = blue_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \ ++ *dst++ = green_[curr[x] / (div)]; \ ++ *dst++ = red_[(prev[x] + next[x]) / (2 * (div))]; \ ++ x++; ++ ++// BGB ++// GRG ++// BGB ++#define RGGB_BGR888(p, n, div) \ ++ *dst++ = blue_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \ ++ *dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))]; \ ++ *dst++ = red_[curr[x] / (div)]; \ ++ x++; ++ ++void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ const int width_in_bytes = window_.width * 5 / 4; ++ const uint8_t *prev = (const uint8_t *)src[0]; ++ const uint8_t *curr = (const uint8_t *)src[1]; ++ const uint8_t *next = (const uint8_t *)src[2]; ++ ++ /* ++ * For the first pixel getting a pixel from the previous column uses ++ * x - 2 to skip the 5th byte with least-significant bits for 4 pixels. ++ * Same for last pixel (uses x + 2) and looking at the next column. ++ * x++ in the for-loop skips the 5th byte with 4 x 2 lsb-s for 10bit packed. ++ */ ++ for (int x = 0; x < width_in_bytes; x++) { ++ /* Even pixel */ ++ BGGR_BGR888(2, 1, 1) ++ /* Odd pixel BGGR -> GBRG */ ++ GBRG_BGR888(1, 1, 1) ++ /* Same thing for next 2 pixels */ ++ BGGR_BGR888(1, 1, 1) ++ GBRG_BGR888(1, 2, 1) ++ } ++} ++ ++void DebayerCpu::debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ const int width_in_bytes = window_.width * 5 / 4; ++ const uint8_t *prev = (const uint8_t *)src[0]; ++ const uint8_t *curr = (const uint8_t *)src[1]; ++ const uint8_t *next = (const uint8_t *)src[2]; ++ ++ for (int x = 0; x < width_in_bytes; x++) { ++ /* Even pixel */ ++ GRBG_BGR888(2, 1, 1) ++ /* Odd pixel GRBG -> RGGB */ ++ RGGB_BGR888(1, 1, 1) ++ /* Same thing for next 2 pixels */ ++ GRBG_BGR888(1, 1, 1) ++ RGGB_BGR888(1, 2, 1) ++ } ++} ++ ++void DebayerCpu::debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ const int width_in_bytes = window_.width * 5 / 4; ++ const uint8_t *prev = (const uint8_t *)src[0]; ++ const uint8_t *curr = (const uint8_t *)src[1]; ++ const uint8_t *next = (const uint8_t *)src[2]; ++ ++ for (int x = 0; x < width_in_bytes; x++) { ++ /* Even pixel */ ++ GBRG_BGR888(2, 1, 1) ++ /* Odd pixel GBGR -> BGGR */ ++ BGGR_BGR888(1, 1, 1) ++ /* Same thing for next 2 pixels */ ++ GBRG_BGR888(1, 1, 1) ++ BGGR_BGR888(1, 2, 1) ++ } ++} ++ ++void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ const int width_in_bytes = window_.width * 5 / 4; ++ const uint8_t *prev = (const uint8_t *)src[0]; ++ const uint8_t *curr = (const uint8_t *)src[1]; ++ const uint8_t *next = (const uint8_t *)src[2]; ++ ++ for (int x = 0; x < width_in_bytes; x++) { ++ /* Even pixel */ ++ RGGB_BGR888(2, 1, 1) ++ /* Odd pixel RGGB -> GRBG*/ ++ GRBG_BGR888(1, 1, 1) ++ /* Same thing for next 2 pixels */ ++ RGGB_BGR888(1, 1, 1) ++ GRBG_BGR888(1, 2, 1) ++ } ++} ++ ++static bool isStandardBayerOrder(BayerFormat::Order order) ++{ ++ return order == BayerFormat::BGGR || order == BayerFormat::GBRG || ++ order == BayerFormat::GRBG || order == BayerFormat::RGGB; ++} ++ ++int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config) ++{ ++ BayerFormat bayerFormat = ++ BayerFormat::fromPixelFormat(inputFormat); ++ ++ if (bayerFormat.bitDepth == 10 && ++ bayerFormat.packing == BayerFormat::Packing::CSI2 && ++ isStandardBayerOrder(bayerFormat.order)) { ++ config.bpp = 10; ++ config.patternSize.width = 4; /* 5 bytes per *4* pixels */ ++ config.patternSize.height = 2; ++ config.outputFormats = std::vector({ formats::RGB888 }); ++ return 0; ++ } ++ ++ LOG(Debayer, Info) ++ << "Unsupported input format " << inputFormat.toString(); ++ return -EINVAL; ++} ++ ++int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config) ++{ ++ if (outputFormat == formats::RGB888) { ++ config.bpp = 24; ++ return 0; ++ } ++ ++ LOG(Debayer, Info) ++ << "Unsupported output format " << outputFormat.toString(); ++ return -EINVAL; ++} ++ ++/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */ ++int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat) ++{ ++ BayerFormat bayerFormat = ++ BayerFormat::fromPixelFormat(inputFormat); ++ ++ if (bayerFormat.bitDepth == 10 && ++ bayerFormat.packing == BayerFormat::Packing::CSI2) { ++ switch (bayerFormat.order) { ++ case BayerFormat::BGGR: ++ debayer0_ = &DebayerCpu::debayer10P_BGBG_BGR888; ++ debayer1_ = &DebayerCpu::debayer10P_GRGR_BGR888; ++ return 0; ++ case BayerFormat::GBRG: ++ debayer0_ = &DebayerCpu::debayer10P_GBGB_BGR888; ++ debayer1_ = &DebayerCpu::debayer10P_RGRG_BGR888; ++ return 0; ++ case BayerFormat::GRBG: ++ debayer0_ = &DebayerCpu::debayer10P_GRGR_BGR888; ++ debayer1_ = &DebayerCpu::debayer10P_BGBG_BGR888; ++ return 0; ++ case BayerFormat::RGGB: ++ debayer0_ = &DebayerCpu::debayer10P_RGRG_BGR888; ++ debayer1_ = &DebayerCpu::debayer10P_GBGB_BGR888; ++ return 0; ++ default: ++ break; ++ } ++ } ++ ++ LOG(Debayer, Error) << "Unsupported input output format combination"; ++ return -EINVAL; ++} ++ ++int DebayerCpu::configure(const StreamConfiguration &inputCfg, ++ const std::vector> &outputCfgs) ++{ ++ if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0) ++ return -EINVAL; ++ ++ if (stats_->configure(inputCfg) != 0) ++ return -EINVAL; ++ ++ const Size &stats_pattern_size = stats_->patternSize(); ++ if (inputConfig_.patternSize.width != stats_pattern_size.width || ++ inputConfig_.patternSize.height != stats_pattern_size.height) { ++ LOG(Debayer, Error) ++ << "mismatching stats and debayer pattern sizes for " ++ << inputCfg.pixelFormat.toString(); ++ return -EINVAL; ++ } ++ ++ inputConfig_.stride = inputCfg.stride; ++ ++ if (outputCfgs.size() != 1) { ++ LOG(Debayer, Error) ++ << "Unsupported number of output streams: " ++ << outputCfgs.size(); ++ return -EINVAL; ++ } ++ ++ const StreamConfiguration &outputCfg = outputCfgs[0]; ++ SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size); ++ std::tie(outputConfig_.stride, outputConfig_.frameSize) = ++ strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size); ++ ++ if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) { ++ LOG(Debayer, Error) ++ << "Invalid output size/stride: " ++ << "\n " << outputCfg.size << " (" << outSizeRange << ")" ++ << "\n " << outputCfg.stride << " (" << outputConfig_.stride << ")"; ++ return -EINVAL; ++ } ++ ++ if (setDebayerFunctions(inputCfg.pixelFormat, outputCfg.pixelFormat) != 0) ++ return -EINVAL; ++ ++ window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) & ++ ~(inputConfig_.patternSize.width - 1); ++ window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) & ++ ~(inputConfig_.patternSize.height - 1); ++ window_.width = outputCfg.size.width; ++ window_.height = outputCfg.size.height; ++ ++ /* Don't pass x,y since process() already adjusts src before passing it */ ++ stats_->setWindow(Rectangle(window_.size())); ++ ++ for (unsigned int i = 0; ++ i < (inputConfig_.patternSize.height + 1) && enableInputMemcpy_; ++ i++) { ++ /* pad with patternSize.Width on both left and right side */ ++ size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) * ++ inputConfig_.bpp / 8; ++ ++ free(lineBuffers_[i]); ++ lineBuffers_[i] = (uint8_t *)malloc(lineLength); ++ if (!lineBuffers_[i]) ++ return -ENOMEM; ++ } ++ ++ measuredFrames_ = 0; ++ frameProcessTime_ = 0; ++ ++ return 0; ++} ++ ++Size DebayerCpu::patternSize(PixelFormat inputFormat) ++{ ++ DebayerCpu::DebayerInputConfig config; ++ ++ if (getInputConfig(inputFormat, config) != 0) ++ return {}; ++ ++ return config.patternSize; ++} ++ ++std::vector DebayerCpu::formats(PixelFormat inputFormat) ++{ ++ DebayerCpu::DebayerInputConfig config; ++ ++ if (getInputConfig(inputFormat, config) != 0) ++ return std::vector(); ++ ++ return config.outputFormats; ++} ++ ++std::tuple ++DebayerCpu::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) ++{ ++ DebayerCpu::DebayerOutputConfig config; ++ ++ if (getOutputConfig(outputFormat, config) != 0) ++ return std::make_tuple(0, 0); ++ ++ /* round up to multiple of 8 for 64 bits alignment */ ++ unsigned int stride = (size.width * config.bpp / 8 + 7) & ~7; ++ ++ return std::make_tuple(stride, stride * size.height); ++} ++ ++void DebayerCpu::initLinePointers(const uint8_t *linePointers[], const uint8_t *src) ++{ ++ const int patternHeight = inputConfig_.patternSize.height; ++ ++ for (int i = 0; i < patternHeight; i++) ++ linePointers[i + 1] = src + ++ (-patternHeight / 2 + i) * (int)inputConfig_.stride; ++ ++ if (!enableInputMemcpy_) ++ return; ++ ++ for (int i = 0; i < patternHeight; i++) { ++ /* pad with patternSize.Width on both left and right side */ ++ size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) * ++ inputConfig_.bpp / 8; ++ int padding = inputConfig_.patternSize.width * inputConfig_.bpp / 8; ++ ++ memcpy(lineBuffers_[i], linePointers[i + 1] - padding, lineLength); ++ linePointers[i + 1] = lineBuffers_[i] + padding; ++ } ++ ++ /* Point lineBufferIndex_ to first unused lineBuffer */ ++ lineBufferIndex_ = patternHeight; ++} ++ ++void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src) ++{ ++ const int patternHeight = inputConfig_.patternSize.height; ++ ++ for (int i = 0; i < patternHeight; i++) ++ linePointers[i] = linePointers[i + 1]; ++ ++ linePointers[patternHeight] = src + ++ (patternHeight / 2) * (int)inputConfig_.stride; ++ ++ if (!enableInputMemcpy_) ++ return; ++ ++ size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) * ++ inputConfig_.bpp / 8; ++ int padding = inputConfig_.patternSize.width * inputConfig_.bpp / 8; ++ memcpy(lineBuffers_[lineBufferIndex_], linePointers[patternHeight] - padding, lineLength); ++ linePointers[patternHeight] = lineBuffers_[lineBufferIndex_] + padding; ++ ++ lineBufferIndex_ = (lineBufferIndex_ + 1) % (patternHeight + 1); ++} ++ ++void DebayerCpu::process2(const uint8_t *src, uint8_t *dst) ++{ ++ const unsigned int y_end = window_.y + window_.height; ++ const uint8_t *linePointers[3]; ++ ++ /* Adjust src to top left corner of the window */ ++ src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8; ++ ++ initLinePointers(linePointers, src); ++ ++ for (unsigned int y = window_.y; y < y_end; y += 2) { ++ shiftLinePointers(linePointers, src); ++ stats_->processLine0(y, linePointers); ++ (this->*debayer0_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ (this->*debayer1_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ } ++} ++ ++void DebayerCpu::process4(const uint8_t *src, uint8_t *dst) ++{ ++ const unsigned int y_end = window_.y + window_.height; ++ const uint8_t *linePointers[5]; ++ ++ /* Adjust src to top left corner of the window */ ++ src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8; ++ ++ initLinePointers(linePointers, src); ++ ++ for (unsigned int y = window_.y; y < y_end; y += 4) { ++ shiftLinePointers(linePointers, src); ++ stats_->processLine0(y, linePointers); ++ (this->*debayer0_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ (this->*debayer1_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ stats_->processLine2(y, linePointers); ++ (this->*debayer2_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ (this->*debayer3_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ } ++} ++ ++static inline int64_t timeDiff(timespec &after, timespec &before) ++{ ++ return (after.tv_sec - before.tv_sec) * 1000000000LL + ++ (int64_t)after.tv_nsec - (int64_t)before.tv_nsec; ++} ++ ++void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams params) ++{ ++ timespec frameStartTime; ++ ++ if (measuredFrames_ < DebayerCpu::framesToMeasure) { ++ frameStartTime = {}; ++ clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime); ++ } ++ ++ /* Apply DebayerParams */ ++ if (params.gamma != gamma_correction_) { ++ for (int i = 0; i < 1024; i++) ++ gamma_[i] = 255 * powf(i / 1023.0, params.gamma); ++ ++ gamma_correction_ = params.gamma; ++ } ++ ++ for (int i = 0; i < 256; i++) { ++ int idx; ++ ++ /* Apply gamma after gain! */ ++ idx = std::min({ i * params.gainR / 64U, 1023U }); ++ red_[i] = gamma_[idx]; ++ ++ idx = std::min({ i * params.gainG / 64U, 1023U }); ++ green_[i] = gamma_[idx]; ++ ++ idx = std::min({ i * params.gainB / 64U, 1023U }); ++ blue_[i] = gamma_[idx]; ++ } ++ ++ /* Copy metadata from the input buffer */ ++ FrameMetadata &metadata = output->_d()->metadata(); ++ metadata.status = input->metadata().status; ++ metadata.sequence = input->metadata().sequence; ++ metadata.timestamp = input->metadata().timestamp; ++ ++ MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read); ++ MappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write); ++ if (!in.isValid() || !out.isValid()) { ++ LOG(Debayer, Error) << "mmap-ing buffer(s) failed"; ++ metadata.status = FrameMetadata::FrameError; ++ return; ++ } ++ ++ stats_->startFrame(); ++ ++ if (inputConfig_.patternSize.height == 2) ++ process2(in.planes()[0].data(), out.planes()[0].data()); ++ else ++ process4(in.planes()[0].data(), out.planes()[0].data()); ++ ++ metadata.planes()[0].bytesused = out.planes()[0].size(); ++ ++ /* Measure before emitting signals */ ++ if (measuredFrames_ < DebayerCpu::framesToMeasure && ++ ++measuredFrames_ > DebayerCpu::framesToSkip) { ++ timespec frameEndTime = {}; ++ clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime); ++ frameProcessTime_ += timeDiff(frameEndTime, frameStartTime); ++ if (measuredFrames_ == DebayerCpu::framesToMeasure) { ++ const int measuredFrames = DebayerCpu::framesToMeasure - ++ DebayerCpu::framesToSkip; ++ LOG(Debayer, Info) ++ << "Processed " << measuredFrames ++ << " frames in " << frameProcessTime_ / 1000 << "us, " ++ << frameProcessTime_ / (1000 * measuredFrames) ++ << " us/frame"; ++ } ++ } ++ ++ stats_->finishFrame(); ++ outputBufferReady.emit(output); ++ inputBufferReady.emit(input); ++} ++ ++} /* namespace libcamera */ +diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build +index d4ae5ac7..6d7a44d7 100644 +--- a/src/libcamera/software_isp/meson.build ++++ b/src/libcamera/software_isp/meson.build +@@ -2,6 +2,7 @@ + + libcamera_sources += files([ + 'debayer.cpp', ++ 'debayer_cpu.cpp', + 'swstats.cpp', + 'swstats_cpu.cpp', + ]) +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch b/users/flokli/ipu6-softisp/libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch new file mode 100644 index 000000000000..d5fd94f6af37 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch @@ -0,0 +1,256 @@ +From 05b353f1e45f2af0d0989261210b4bedef5144de Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +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 +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + 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 ++ ++#include ++#include ++ ++#include ++ ++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 ++ ++#include ++ ++#include ++ ++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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0012-libcamera-ipa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch b/users/flokli/ipu6-softisp/libcamera/0012-libcamera-ipa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch new file mode 100644 index 000000000000..b7cb27455b87 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0012-libcamera-ipa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch @@ -0,0 +1,407 @@ +From c0886381a2bbe494b900d699a3858573316059b2 Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +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 +Signed-off-by: Dennis Bonke +Co-authored-by: Marttico +Signed-off-by: Marttico +Co-authored-by: Toon Langendam +Signed-off-by: Toon Langendam +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + 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 ++ ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#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(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(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(); ++ if (!exposure_min_) { ++ LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear"; ++ exposure_min_ = 1; ++ } ++ exposure_max_ = exposure_info.max().get(); ++ again_min_ = gain_info.min().get(); ++ if (!again_min_) { ++ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear"; ++ again_min_ = 100; ++ } ++ again_max_ = gain_info.max().get(); ++ ++ 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(); ++ again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get(); ++ ++ 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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch b/users/flokli/ipu6-softisp/libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch new file mode 100644 index 000000000000..24636ed7569a --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch @@ -0,0 +1,483 @@ +From 21f1dd954a44b4e8f81abbfea276e4b60f8a8297 Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +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 +Co-authored-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Co-authored-by: Hans de Goede +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + .../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 ++#include ++ ++#include ++ ++#include ++#include ++ ++#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 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 ++ 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> &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> *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 &outputs); ++ ++ /** ++ * \brief Get the signal for when the sensor controls are set. ++ * ++ * \return The control list of the sensor controls. ++ */ ++ Signal &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 debayer_; ++ Thread ispWorkerThread_; ++ SharedMemObject sharedParams_; ++ DebayerParams debayerParams_; ++ DmaHeap dmaHeap_; ++ ++ std::unique_ptr 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/' e.g.: ++# -Dpipelines=simple/simple ++# The source file should be named swisp_.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 ++#include ++#include ++ ++#include ++#include ++ ++#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("softIsp_params"); ++ if (!sharedParams_.fd().isValid()) { ++ LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters"; ++ return; ++ } ++ ++ std::unique_ptr stats; ++ ++ stats = std::make_unique(); ++ if (!stats) { ++ LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object"; ++ return; ++ } ++ ++ stats->statsReady.connect(this, &SwIspSimple::statsReady); ++ ++ debayer_ = std::make_unique(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(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 &SwIspSimple::getSignalSetSensorControls() ++{ ++ ASSERT(ipa_); ++ return ipa_->setSensorControls; ++} ++ ++bool SwIspSimple::isValid() const ++{ ++ return !!debayer_; ++} ++ ++std::vector 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 ++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> &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> *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 planes{ outPlane }; ++ buffers->emplace_back(std::make_unique(std::move(planes))); ++ } ++ ++ return count; ++} ++ ++int SwIspSimple::queueBuffers(FrameBuffer *input, ++ const std::map &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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0014-libcamera-pipeline-simple-rename-converterBuffers_-a.patch b/users/flokli/ipu6-softisp/libcamera/0014-libcamera-pipeline-simple-rename-converterBuffers_-a.patch new file mode 100644 index 000000000000..fc603fc9c8eb --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0014-libcamera-pipeline-simple-rename-converterBuffers_-a.patch @@ -0,0 +1,238 @@ +From 155065b3d78c14173fd71c21fe0639f94c3da109 Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Fri, 1 Dec 2023 15:45:14 +0300 +Subject: [PATCH 14/25] libcamera: pipeline: simple: rename converterBuffers_ + and related vars + +The converterBuffers_ and the converterQueue_ are not that specific +to the Converter, and could be used by another entity doing the format +conversion. + +Rename converterBuffers_, converterQueue_, and useConverter_ to +conversionBuffers_, conversionQueue_ and useConversion_ to +disassociate them from the Converter. + +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + src/libcamera/pipeline/simple/simple.cpp | 63 ++++++++++++------------ + 1 file changed, 32 insertions(+), 31 deletions(-) + +diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp +index 4d0e7255..fa298746 100644 +--- a/src/libcamera/pipeline/simple/simple.cpp ++++ b/src/libcamera/pipeline/simple/simple.cpp +@@ -269,17 +269,18 @@ public: + std::vector configs_; + std::map> formats_; + ++ std::vector> conversionBuffers_; ++ std::queue> conversionQueue_; ++ bool useConversion_; ++ + std::unique_ptr converter_; +- std::vector> converterBuffers_; +- bool useConverter_; +- std::queue> converterQueue_; + + private: + void tryPipeline(unsigned int code, const Size &size); + static std::vector routedSourcePads(MediaPad *sink); + +- void converterInputDone(FrameBuffer *buffer); +- void converterOutputDone(FrameBuffer *buffer); ++ void conversionInputDone(FrameBuffer *buffer); ++ void conversionOutputDone(FrameBuffer *buffer); + }; + + class SimpleCameraConfiguration : public CameraConfiguration +@@ -503,8 +504,8 @@ int SimpleCameraData::init() + << "Failed to create converter, disabling format conversion"; + converter_.reset(); + } else { +- converter_->inputBufferReady.connect(this, &SimpleCameraData::converterInputDone); +- converter_->outputBufferReady.connect(this, &SimpleCameraData::converterOutputDone); ++ converter_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone); ++ converter_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone); + } + } + +@@ -740,7 +741,7 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) + * point converting an erroneous buffer. + */ + if (buffer->metadata().status != FrameMetadata::FrameSuccess) { +- if (!useConverter_) { ++ if (!useConversion_) { + /* No conversion, just complete the request. */ + Request *request = buffer->request(); + pipe->completeBuffer(request, buffer); +@@ -756,16 +757,16 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) + if (buffer->metadata().status != FrameMetadata::FrameCancelled) + video_->queueBuffer(buffer); + +- if (converterQueue_.empty()) ++ if (conversionQueue_.empty()) + return; + + Request *request = nullptr; +- for (auto &item : converterQueue_.front()) { ++ for (auto &item : conversionQueue_.front()) { + FrameBuffer *outputBuffer = item.second; + request = outputBuffer->request(); + pipe->completeBuffer(request, outputBuffer); + } +- converterQueue_.pop(); ++ conversionQueue_.pop(); + + if (request) + pipe->completeRequest(request); +@@ -782,9 +783,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) + */ + Request *request = buffer->request(); + +- if (useConverter_ && !converterQueue_.empty()) { ++ if (useConversion_ && !conversionQueue_.empty()) { + const std::map &outputs = +- converterQueue_.front(); ++ conversionQueue_.front(); + if (!outputs.empty()) { + FrameBuffer *outputBuffer = outputs.begin()->second; + if (outputBuffer) +@@ -801,14 +802,14 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) + * conversion is needed. If there's no queued request, just requeue the + * captured buffer for capture. + */ +- if (useConverter_) { +- if (converterQueue_.empty()) { ++ if (useConversion_) { ++ if (conversionQueue_.empty()) { + video_->queueBuffer(buffer); + return; + } + +- converter_->queueBuffers(buffer, converterQueue_.front()); +- converterQueue_.pop(); ++ converter_->queueBuffers(buffer, conversionQueue_.front()); ++ conversionQueue_.pop(); + return; + } + +@@ -817,13 +818,13 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) + pipe->completeRequest(request); + } + +-void SimpleCameraData::converterInputDone(FrameBuffer *buffer) ++void SimpleCameraData::conversionInputDone(FrameBuffer *buffer) + { + /* Queue the input buffer back for capture. */ + video_->queueBuffer(buffer); + } + +-void SimpleCameraData::converterOutputDone(FrameBuffer *buffer) ++void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer) + { + SimplePipelineHandler *pipe = SimpleCameraData::pipe(); + +@@ -1159,14 +1160,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c) + + /* Configure the converter if needed. */ + std::vector> outputCfgs; +- data->useConverter_ = config->needConversion(); ++ data->useConversion_ = config->needConversion(); + + for (unsigned int i = 0; i < config->size(); ++i) { + StreamConfiguration &cfg = config->at(i); + + cfg.setStream(&data->streams_[i]); + +- if (data->useConverter_) ++ if (data->useConversion_) + outputCfgs.push_back(cfg); + } + +@@ -1192,7 +1193,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream, + * Export buffers on the converter or capture video node, depending on + * whether the converter is used or not. + */ +- if (data->useConverter_) ++ if (data->useConversion_) + return data->converter_->exportBuffers(data->streamIndex(stream), + count, buffers); + else +@@ -1213,13 +1214,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL + return -EBUSY; + } + +- if (data->useConverter_) { ++ if (data->useConversion_) { + /* + * When using the converter allocate a fixed number of internal + * buffers. + */ + ret = video->allocateBuffers(kNumInternalBuffers, +- &data->converterBuffers_); ++ &data->conversionBuffers_); + } else { + /* Otherwise, prepare for using buffers from the only stream. */ + Stream *stream = &data->streams_[0]; +@@ -1238,7 +1239,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL + return ret; + } + +- if (data->useConverter_) { ++ if (data->useConversion_) { + ret = data->converter_->start(); + if (ret < 0) { + stop(camera); +@@ -1246,7 +1247,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL + } + + /* Queue all internal buffers for capture. */ +- for (std::unique_ptr &buffer : data->converterBuffers_) ++ for (std::unique_ptr &buffer : data->conversionBuffers_) + video->queueBuffer(buffer.get()); + } + +@@ -1258,7 +1259,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera) + SimpleCameraData *data = cameraData(camera); + V4L2VideoDevice *video = data->video_; + +- if (data->useConverter_) ++ if (data->useConversion_) + data->converter_->stop(); + + video->streamOff(); +@@ -1266,7 +1267,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera) + + video->bufferReady.disconnect(data, &SimpleCameraData::bufferReady); + +- data->converterBuffers_.clear(); ++ data->conversionBuffers_.clear(); + + releasePipeline(data); + } +@@ -1284,7 +1285,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request) + * queue, it will be handed to the converter in the capture + * completion handler. + */ +- if (data->useConverter_) { ++ if (data->useConversion_) { + buffers.emplace(data->streamIndex(stream), buffer); + } else { + ret = data->video_->queueBuffer(buffer); +@@ -1293,8 +1294,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request) + } + } + +- if (data->useConverter_) +- data->converterQueue_.push(std::move(buffers)); ++ if (data->useConversion_) ++ data->conversionQueue_.push(std::move(buffers)); + + return 0; + } +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0015-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch b/users/flokli/ipu6-softisp/libcamera/0015-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch new file mode 100644 index 000000000000..f639a3b74c93 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0015-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch @@ -0,0 +1,243 @@ +From ad61052d9aea1f1af6aaef9ce7e5d447c595187c Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Tue, 12 Dec 2023 02:02:37 +0300 +Subject: [PATCH 15/25] libcamera: pipeline: simple: enable use of Soft ISP and + Soft IPA + +To enable the Simple Soft ISP and Soft IPA for simple pipeline handler +configure the build with: + -Dpipelines=simple/simple -Dipas=simple/simple + +If the pipeline uses Converter, Soft ISP and Soft IPA aren't +available. + +Configuring the build with just: + -Dpipelines=simple +makes it to work like before - Soft ISP and Soft IPA aren't used. + +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + src/libcamera/pipeline/simple/simple.cpp | 111 ++++++++++++++++++----- + 1 file changed, 89 insertions(+), 22 deletions(-) + +diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp +index fa298746..c76510c2 100644 +--- a/src/libcamera/pipeline/simple/simple.cpp ++++ b/src/libcamera/pipeline/simple/simple.cpp +@@ -34,6 +34,7 @@ + #include "libcamera/internal/device_enumerator.h" + #include "libcamera/internal/media_device.h" + #include "libcamera/internal/pipeline_handler.h" ++#include "libcamera/internal/software_isp.h" + #include "libcamera/internal/v4l2_subdevice.h" + #include "libcamera/internal/v4l2_videodevice.h" + +@@ -274,6 +275,7 @@ public: + bool useConversion_; + + std::unique_ptr converter_; ++ std::unique_ptr swIsp_; + + private: + void tryPipeline(unsigned int code, const Size &size); +@@ -281,6 +283,9 @@ private: + + void conversionInputDone(FrameBuffer *buffer); + void conversionOutputDone(FrameBuffer *buffer); ++ ++ void ispStatsReady(int dummy); ++ void setSensorControls(const ControlList &sensorControls); + }; + + class SimpleCameraConfiguration : public CameraConfiguration +@@ -509,6 +514,25 @@ int SimpleCameraData::init() + } + } + ++ /* ++ * Create SoftwareIsp unconditionally if no converter is used ++ * - to be revisited ++ */ ++ if (!converter_) { ++ swIsp_ = SoftwareIspFactoryBase::create(pipe, sensor_->controls()); ++ if (!swIsp_) { ++ LOG(SimplePipeline, Warning) ++ << "Failed to create software ISP, disabling software debayering"; ++ swIsp_.reset(); ++ } else { ++ swIsp_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone); ++ swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone); ++ swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady); ++ ++ swIsp_->getSignalSetSensorControls().connect(this, &SimpleCameraData::setSensorControls); ++ } ++ } ++ + video_ = pipe->video(entities_.back().entity); + ASSERT(video_); + +@@ -599,12 +623,20 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size) + config.captureFormat = pixelFormat; + config.captureSize = format.size; + +- if (!converter_) { +- config.outputFormats = { pixelFormat }; +- config.outputSizes = config.captureSize; +- } else { ++ if (converter_) { + config.outputFormats = converter_->formats(pixelFormat); + config.outputSizes = converter_->sizes(format.size); ++ } else if (swIsp_) { ++ config.outputFormats = swIsp_->formats(pixelFormat); ++ config.outputSizes = swIsp_->sizes(pixelFormat, format.size); ++ if (config.outputFormats.empty()) { ++ /* Do not use swIsp for unsupported pixelFormat's */ ++ config.outputFormats = { pixelFormat }; ++ config.outputSizes = config.captureSize; ++ } ++ } else { ++ config.outputFormats = { pixelFormat }; ++ config.outputSizes = config.captureSize; + } + + configs_.push_back(config); +@@ -750,9 +782,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) + } + + /* +- * The converter is in use. Requeue the internal buffer for +- * capture (unless the stream is being stopped), and complete +- * the request with all the user-facing buffers. ++ * The converter or Software ISP is in use. Requeue the internal ++ * buffer for capture (unless the stream is being stopped), and ++ * complete the request with all the user-facing buffers. + */ + if (buffer->metadata().status != FrameMetadata::FrameCancelled) + video_->queueBuffer(buffer); +@@ -798,9 +830,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) + buffer->metadata().timestamp); + + /* +- * Queue the captured and the request buffer to the converter if format +- * conversion is needed. If there's no queued request, just requeue the +- * captured buffer for capture. ++ * Queue the captured and the request buffer to the converter or Software ++ * ISP if format conversion is needed. If there's no queued request, just ++ * requeue the captured buffer for capture. + */ + if (useConversion_) { + if (conversionQueue_.empty()) { +@@ -808,7 +840,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) + return; + } + +- converter_->queueBuffers(buffer, conversionQueue_.front()); ++ if (converter_) ++ converter_->queueBuffers(buffer, conversionQueue_.front()); ++ else ++ swIsp_->queueBuffers(buffer, conversionQueue_.front()); ++ + conversionQueue_.pop(); + return; + } +@@ -834,6 +870,18 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer) + pipe->completeRequest(request); + } + ++void SimpleCameraData::ispStatsReady([[maybe_unused]] int dummy) ++{ ++ swIsp_->processStats(sensor_->getControls({ V4L2_CID_ANALOGUE_GAIN, ++ V4L2_CID_EXPOSURE })); ++} ++ ++void SimpleCameraData::setSensorControls(const ControlList &sensorControls) ++{ ++ ControlList ctrls(sensorControls); ++ sensor_->setControls(&ctrls); ++} ++ + /* Retrieve all source pads connected to a sink pad through active routes. */ + std::vector SimpleCameraData::routedSourcePads(MediaPad *sink) + { +@@ -1016,8 +1064,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate() + /* Set the stride, frameSize and bufferCount. */ + if (needConversion_) { + std::tie(cfg.stride, cfg.frameSize) = +- data_->converter_->strideAndFrameSize(cfg.pixelFormat, +- cfg.size); ++ (data_->converter_) ? data_->converter_->strideAndFrameSize(cfg.pixelFormat, ++ cfg.size) ++ : data_->swIsp_->strideAndFrameSize(cfg.pixelFormat, ++ cfg.size); + if (cfg.stride == 0) + return Invalid; + } else { +@@ -1180,7 +1230,9 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c) + inputCfg.stride = captureFormat.planes[0].bpl; + inputCfg.bufferCount = kNumInternalBuffers; + +- return data->converter_->configure(inputCfg, outputCfgs); ++ return (data->converter_) ? data->converter_->configure(inputCfg, outputCfgs) ++ : data->swIsp_->configure(inputCfg, outputCfgs, ++ data->sensor_->controls()); + } + + int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream, +@@ -1194,8 +1246,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream, + * whether the converter is used or not. + */ + if (data->useConversion_) +- return data->converter_->exportBuffers(data->streamIndex(stream), +- count, buffers); ++ return (data->converter_) ? data->converter_->exportBuffers(data->streamIndex(stream), ++ count, buffers) ++ : data->swIsp_->exportBuffers(data->streamIndex(stream), ++ count, buffers); + else + return data->video_->exportBuffers(count, buffers); + } +@@ -1240,10 +1294,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL + } + + if (data->useConversion_) { +- ret = data->converter_->start(); +- if (ret < 0) { +- stop(camera); +- return ret; ++ if (data->converter_) { ++ ret = data->converter_->start(); ++ if (ret < 0) { ++ stop(camera); ++ return ret; ++ } ++ } else if (data->swIsp_) { ++ ret = data->swIsp_->start(); ++ if (ret < 0) { ++ stop(camera); ++ return ret; ++ } + } + + /* Queue all internal buffers for capture. */ +@@ -1259,8 +1321,13 @@ void SimplePipelineHandler::stopDevice(Camera *camera) + SimpleCameraData *data = cameraData(camera); + V4L2VideoDevice *video = data->video_; + +- if (data->useConversion_) +- data->converter_->stop(); ++ if (data->useConversion_) { ++ if (data->converter_) ++ data->converter_->stop(); ++ else if (data->swIsp_) { ++ data->swIsp_->stop(); ++ } ++ } + + video->streamOff(); + video->releaseBuffers(); +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0016-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch b/users/flokli/ipu6-softisp/libcamera/0016-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch new file mode 100644 index 000000000000..04388859a543 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0016-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch @@ -0,0 +1,199 @@ +From 66ef9f32e67a96655d10ba38f7a830b3bbfe50f2 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 14 Dec 2023 19:09:44 +0100 +Subject: [PATCH 16/25] libcamera: swstats_cpu: Add support for 8, 10 and 12 + bpp unpacked bayer input + +Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard +bayer orders. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Signed-off-by: Kieran Bingham +Signed-off-by: Hans de Goede +--- + .../internal/software_isp/swstats_cpu.h | 9 ++ + src/libcamera/software_isp/swstats_cpu.cpp | 126 ++++++++++++++++++ + 2 files changed, 135 insertions(+) + +diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h +index 8bb86e98..e7abc6bb 100644 +--- a/include/libcamera/internal/software_isp/swstats_cpu.h ++++ b/include/libcamera/internal/software_isp/swstats_cpu.h +@@ -11,6 +11,7 @@ + + #pragma once + ++#include "libcamera/internal/bayer_format.h" + #include "libcamera/internal/shared_mem_object.h" + #include "libcamera/internal/software_isp/swisp_stats.h" + #include "libcamera/internal/software_isp/swstats.h" +@@ -31,6 +32,14 @@ public: + const SharedFD &getStatsFD() { return sharedStats_.fd(); } + int configure(const StreamConfiguration &inputCfg); + private: ++ int setupStandardBayerOrder(BayerFormat::Order order); ++ /* Bayer 8 bpp unpacked */ ++ void statsBGGR8Line0(const uint8_t *src[]); ++ /* Bayer 10 bpp unpacked */ ++ void statsBGGR10Line0(const uint8_t *src[]); ++ /* Bayer 12 bpp unpacked */ ++ void statsBGGR12Line0(const uint8_t *src[]); ++ /* Bayer 10 bpp packed */ + void statsBGGR10PLine0(const uint8_t *src[]); + void statsGBRG10PLine0(const uint8_t *src[]); + void resetStats(void); +diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp +index 59453d07..87550371 100644 +--- a/src/libcamera/software_isp/swstats_cpu.cpp ++++ b/src/libcamera/software_isp/swstats_cpu.cpp +@@ -59,6 +59,83 @@ static const unsigned int BLUE_Y_MUL = 29; /* 0.11 * 256 */ + stats_.sumG_ += sumG; \ + stats_.sumB_ += sumB; + ++void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[]) ++{ ++ const uint8_t *src0 = src[1] + window_.x; ++ const uint8_t *src1 = src[2] + window_.x; ++ ++ SWISP_LINARO_START_LINE_STATS(uint8_t) ++ ++ if (swap_lines_) ++ std::swap(src0, src1); ++ ++ /* x += 4 sample every other 2x2 block */ ++ for (int x = 0; x < (int)window_.width; x += 4) { ++ b = src0[x]; ++ g = src0[x + 1]; ++ g2 = src1[x]; ++ r = src1[x + 1]; ++ ++ g = (g + g2) / 2; ++ ++ SWISP_LINARO_ACCUMULATE_LINE_STATS(1) ++ } ++ ++ SWISP_LINARO_FINISH_LINE_STATS() ++} ++ ++void SwStatsCpu::statsBGGR10Line0(const uint8_t *src[]) ++{ ++ const uint16_t *src0 = (const uint16_t *)src[1] + window_.x; ++ const uint16_t *src1 = (const uint16_t *)src[2] + window_.x; ++ ++ SWISP_LINARO_START_LINE_STATS(uint16_t) ++ ++ if (swap_lines_) ++ std::swap(src0, src1); ++ ++ /* x += 4 sample every other 2x2 block */ ++ for (int x = 0; x < (int)window_.width; x += 4) { ++ b = src0[x]; ++ g = src0[x + 1]; ++ g2 = src1[x]; ++ r = src1[x + 1]; ++ ++ g = (g + g2) / 2; ++ ++ /* divide Y by 4 for 10 -> 8 bpp value */ ++ SWISP_LINARO_ACCUMULATE_LINE_STATS(4) ++ } ++ ++ SWISP_LINARO_FINISH_LINE_STATS() ++} ++ ++void SwStatsCpu::statsBGGR12Line0(const uint8_t *src[]) ++{ ++ const uint16_t *src0 = (const uint16_t *)src[1] + window_.x; ++ const uint16_t *src1 = (const uint16_t *)src[2] + window_.x; ++ ++ SWISP_LINARO_START_LINE_STATS(uint16_t) ++ ++ if (swap_lines_) ++ std::swap(src0, src1); ++ ++ /* x += 4 sample every other 2x2 block */ ++ for (int x = 0; x < (int)window_.width; x += 4) { ++ b = src0[x]; ++ g = src0[x + 1]; ++ g2 = src1[x]; ++ r = src1[x + 1]; ++ ++ g = (g + g2) / 2; ++ ++ /* divide Y by 16 for 12 -> 8 bpp value */ ++ SWISP_LINARO_ACCUMULATE_LINE_STATS(16) ++ } ++ ++ SWISP_LINARO_FINISH_LINE_STATS() ++} ++ + static inline __attribute__((always_inline)) void + statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bggr, SwIspStats &stats_) + { +@@ -124,6 +201,39 @@ void SwStatsCpu::finishStats(void) + statsReady.emit(0); + } + ++/* ++ * Check if order is a standard Bayer order and setup x_shift_ and swap_lines_ ++ * so that a single BGGR stats function can be used for all 4 standard orders. ++ */ ++int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order) ++{ ++ switch (order) { ++ case BayerFormat::BGGR: ++ x_shift_ = 0; ++ swap_lines_ = false; ++ break; ++ case BayerFormat::GBRG: ++ x_shift_ = 1; /* BGGR -> GBRG */ ++ swap_lines_ = false; ++ break; ++ case BayerFormat::GRBG: ++ x_shift_ = 0; ++ swap_lines_ = true; /* BGGR -> GRBG */ ++ break; ++ case BayerFormat::RGGB: ++ x_shift_ = 1; /* BGGR -> GBRG */ ++ swap_lines_ = true; /* GBRG -> RGGB */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ patternSize_.height = 2; ++ patternSize_.width = 2; ++ y_skip_mask_ = 0x02; /* Skip every 3th and 4th line */ ++ return 0; ++} ++ + int SwStatsCpu::configure(const StreamConfiguration &inputCfg) + { + BayerFormat bayerFormat = +@@ -132,6 +242,22 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg) + startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats; + finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats; + ++ if (bayerFormat.packing == BayerFormat::Packing::None && ++ setupStandardBayerOrder(bayerFormat.order) == 0) { ++ bpp_ = (bayerFormat.bitDepth + 7) & ~7; ++ switch (bayerFormat.bitDepth) { ++ case 8: ++ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR8Line0; ++ return 0; ++ case 10: ++ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR10Line0; ++ return 0; ++ case 12: ++ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR12Line0; ++ return 0; ++ } ++ } ++ + if (bayerFormat.bitDepth == 10 && + bayerFormat.packing == BayerFormat::Packing::CSI2) { + bpp_ = 10; +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch new file mode 100644 index 000000000000..a5a992f8764d --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch @@ -0,0 +1,237 @@ +From b7b211eb56d98d5b170bd73a23b55aeb45bde8c5 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 14 Dec 2023 19:57:15 +0100 +Subject: [PATCH 17/25] libcamera: debayer_cpu: Add support for 8, 10 and 12 + bpp unpacked bayer input + +Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard +bayer orders. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Signed-off-by: Kieran Bingham +Signed-off-by: Hans de Goede +--- + .../internal/software_isp/debayer_cpu.h | 13 ++ + src/libcamera/software_isp/debayer_cpu.cpp | 128 ++++++++++++++++++ + 2 files changed, 141 insertions(+) + +diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h +index 78573f44..1147b368 100644 +--- a/include/libcamera/internal/software_isp/debayer_cpu.h ++++ b/include/libcamera/internal/software_isp/debayer_cpu.h +@@ -17,6 +17,7 @@ + + #include + ++#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 stats_; + uint8_t *lineBuffers_[5]; + unsigned int lineBufferIndex_; ++ unsigned int x_shift_; /* Offset of 0/1 applied to window_.x */ + bool enableInputMemcpy_; + float gamma_correction_; + int measuredFrames_; +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +index e0c3c658..7b7623b7 100644 +--- a/src/libcamera/software_isp/debayer_cpu.cpp ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -45,6 +45,11 @@ DebayerCpu::~DebayerCpu() + free(lineBuffers_[i]); + } + ++#define DECLARE_SRC_POINTERS(pixel_t) \ ++ const pixel_t *prev = (const pixel_t *)src[0] + x_shift_; \ ++ const pixel_t *curr = (const pixel_t *)src[1] + x_shift_; \ ++ const pixel_t *next = (const pixel_t *)src[2] + x_shift_; ++ + // RGR + // GBG + // RGR +@@ -81,6 +86,70 @@ DebayerCpu::~DebayerCpu() + *dst++ = red_[curr[x] / (div)]; \ + x++; + ++void DebayerCpu::debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ DECLARE_SRC_POINTERS(uint8_t) ++ ++ for (int x = 0; x < (int)window_.width;) { ++ BGGR_BGR888(1, 1, 1) ++ GBRG_BGR888(1, 1, 1) ++ } ++} ++ ++void DebayerCpu::debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ DECLARE_SRC_POINTERS(uint8_t) ++ ++ for (int x = 0; x < (int)window_.width;) { ++ GRBG_BGR888(1, 1, 1) ++ RGGB_BGR888(1, 1, 1) ++ } ++} ++ ++void DebayerCpu::debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ DECLARE_SRC_POINTERS(uint16_t) ++ ++ for (int x = 0; x < (int)window_.width;) { ++ /* divide values by 4 for 10 -> 8 bpp value */ ++ BGGR_BGR888(1, 1, 4) ++ GBRG_BGR888(1, 1, 4) ++ } ++} ++ ++void DebayerCpu::debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ DECLARE_SRC_POINTERS(uint16_t) ++ ++ for (int x = 0; x < (int)window_.width;) { ++ /* divide values by 4 for 10 -> 8 bpp value */ ++ GRBG_BGR888(1, 1, 4) ++ RGGB_BGR888(1, 1, 4) ++ } ++} ++ ++void DebayerCpu::debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ DECLARE_SRC_POINTERS(uint16_t) ++ ++ for (int x = 0; x < (int)window_.width;) { ++ /* divide values by 16 for 12 -> 8 bpp value */ ++ BGGR_BGR888(1, 1, 16) ++ GBRG_BGR888(1, 1, 16) ++ } ++} ++ ++void DebayerCpu::debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]) ++{ ++ DECLARE_SRC_POINTERS(uint16_t) ++ ++ for (int x = 0; x < (int)window_.width;) { ++ /* divide values by 16 for 12 -> 8 bpp value */ ++ GRBG_BGR888(1, 1, 16) ++ RGGB_BGR888(1, 1, 16) ++ } ++} ++ + void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]) + { + const int width_in_bytes = window_.width * 5 / 4; +@@ -170,6 +239,16 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputFormat); + ++ if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) && ++ bayerFormat.packing == BayerFormat::Packing::None && ++ isStandardBayerOrder(bayerFormat.order)) { ++ config.bpp = (bayerFormat.bitDepth + 7) & ~7; ++ config.patternSize.width = 2; ++ config.patternSize.height = 2; ++ config.outputFormats = std::vector({ formats::RGB888 }); ++ return 0; ++ } ++ + if (bayerFormat.bitDepth == 10 && + bayerFormat.packing == BayerFormat::Packing::CSI2 && + isStandardBayerOrder(bayerFormat.order)) { +@@ -197,12 +276,61 @@ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &c + return -EINVAL; + } + ++/* ++ * Check for standard Bayer orders and set x_shift_ and swap debayer0/1, so that ++ * a single pair of BGGR debayer functions can be used for all 4 standard orders. ++ */ ++int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order) ++{ ++ switch (order) { ++ case BayerFormat::BGGR: ++ break; ++ case BayerFormat::GBRG: ++ x_shift_ = 1; /* BGGR -> GBRG */ ++ break; ++ case BayerFormat::GRBG: ++ std::swap(debayer0_, debayer1_); /* BGGR -> GRBG */ ++ break; ++ case BayerFormat::RGGB: ++ x_shift_ = 1; /* BGGR -> GBRG */ ++ std::swap(debayer0_, debayer1_); /* GBRG -> RGGB */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + /* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */ + int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat) + { + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputFormat); + ++ x_shift_ = 0; ++ ++ if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) && ++ bayerFormat.packing == BayerFormat::Packing::None && ++ isStandardBayerOrder(bayerFormat.order)) { ++ switch (bayerFormat.bitDepth) { ++ case 8: ++ debayer0_ = &DebayerCpu::debayer8_BGBG_BGR888; ++ debayer1_ = &DebayerCpu::debayer8_GRGR_BGR888; ++ break; ++ case 10: ++ debayer0_ = &DebayerCpu::debayer10_BGBG_BGR888; ++ debayer1_ = &DebayerCpu::debayer10_GRGR_BGR888; ++ break; ++ case 12: ++ debayer0_ = &DebayerCpu::debayer12_BGBG_BGR888; ++ debayer1_ = &DebayerCpu::debayer12_GRGR_BGR888; ++ break; ++ } ++ setupStandardBayerOrder(bayerFormat.order); ++ return 0; ++ } ++ + if (bayerFormat.bitDepth == 10 && + bayerFormat.packing == BayerFormat::Packing::CSI2) { + switch (bayerFormat.order) { +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch new file mode 100644 index 000000000000..50d826be7a48 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch @@ -0,0 +1,125 @@ +From b835b2c90785ee02bc98888bf165713d16c24cc4 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 18 Dec 2023 19:21:07 +0100 +Subject: [PATCH 18/25] libcamera: debayer_cpu: Add BGR888 output support + +BGR888 is RGB888 with the red and blue pixels swapped, adjust +the debayering to swap the red and blue pixels in the bayer pattern +to add support for writing formats::BGR888. + +Signed-off-by: Hans de Goede +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +--- + .../internal/software_isp/debayer_cpu.h | 1 + + src/libcamera/software_isp/debayer_cpu.cpp | 43 ++++++++++++++++--- + 2 files changed, 39 insertions(+), 5 deletions(-) + +diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h +index 1147b368..bdeab7c0 100644 +--- a/include/libcamera/internal/software_isp/debayer_cpu.h ++++ b/include/libcamera/internal/software_isp/debayer_cpu.h +@@ -133,6 +133,7 @@ private: + unsigned int lineBufferIndex_; + unsigned int x_shift_; /* Offset of 0/1 applied to window_.x */ + bool enableInputMemcpy_; ++ bool swapRedBlueGains_; + float gamma_correction_; + int measuredFrames_; + int64_t frameProcessTime_; +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +index 7b7623b7..0edea4d3 100644 +--- a/src/libcamera/software_isp/debayer_cpu.cpp ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -245,7 +245,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf + config.bpp = (bayerFormat.bitDepth + 7) & ~7; + config.patternSize.width = 2; + config.patternSize.height = 2; +- config.outputFormats = std::vector({ formats::RGB888 }); ++ config.outputFormats = std::vector({ formats::RGB888, formats::BGR888 }); + return 0; + } + +@@ -255,7 +255,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf + config.bpp = 10; + config.patternSize.width = 4; /* 5 bytes per *4* pixels */ + config.patternSize.height = 2; +- config.outputFormats = std::vector({ formats::RGB888 }); ++ config.outputFormats = std::vector({ formats::RGB888, formats::BGR888 }); + return 0; + } + +@@ -266,7 +266,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf + + int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config) + { +- if (outputFormat == formats::RGB888) { ++ if (outputFormat == formats::RGB888 || outputFormat == formats::BGR888) { + config.bpp = 24; + return 0; + } +@@ -302,12 +302,41 @@ int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order) + return 0; + } + +-/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */ +-int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat) ++int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat) + { + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputFormat); + ++ swapRedBlueGains_ = false; ++ ++ switch (outputFormat) { ++ case formats::RGB888: ++ break; ++ case formats::BGR888: ++ /* Swap R and B in bayer order to generate BGR888 instead of RGB888 */ ++ swapRedBlueGains_ = true; ++ ++ switch (bayerFormat.order) { ++ case BayerFormat::BGGR: ++ bayerFormat.order = BayerFormat::RGGB; ++ break; ++ case BayerFormat::GBRG: ++ bayerFormat.order = BayerFormat::GRBG; ++ break; ++ case BayerFormat::GRBG: ++ bayerFormat.order = BayerFormat::GBRG; ++ break; ++ case BayerFormat::RGGB: ++ bayerFormat.order = BayerFormat::BGGR; ++ break; ++ default: ++ goto invalid_fmt; ++ } ++ break; ++ default: ++ goto invalid_fmt; ++ } ++ + x_shift_ = 0; + + if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) && +@@ -355,6 +384,7 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi + } + } + ++invalid_fmt: + LOG(Debayer, Error) << "Unsupported input output format combination"; + return -EINVAL; + } +@@ -594,6 +624,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams + gamma_correction_ = params.gamma; + } + ++ if (swapRedBlueGains_) ++ std::swap(params.gainR, params.gainB); ++ + for (int i = 0; i < 256; i++) { + int idx; + +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch b/users/flokli/ipu6-softisp/libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch new file mode 100644 index 000000000000..cdbd0b992250 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch @@ -0,0 +1,30 @@ +From eb45bdfe66af7844a779bc6fcf923cd951336309 Mon Sep 17 00:00:00 2001 +From: Dennis Bonke +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 +Signed-off-by: Hans de Goede +--- + 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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0020-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch b/users/flokli/ipu6-softisp/libcamera/0020-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch new file mode 100644 index 000000000000..768ec07119e8 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0020-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch @@ -0,0 +1,237 @@ +From e03beabbad83c4c283c7f1c2c4798b6c3e2eaf06 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 19 Dec 2023 11:16:26 +0100 +Subject: [PATCH 20/25] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer + order DNU + +The ov01a1s sensor has the following bayer pattern (4x4 tile repeating): + +IGIG +GBGR +IGIG +GRGB + +Add support for this PixelFormat to libcamera. + +Do Not Upstream, first the include/linux/media-bus-format.h and +include/linux/videodev2.h changes need to land in the upstream kernel. + +Signed-off-by: Hans de Goede +--- + include/libcamera/internal/bayer_format.h | 3 ++- + include/linux/drm_fourcc.h | 2 ++ + include/linux/media-bus-format.h | 4 +++- + include/linux/videodev2.h | 3 +++ + src/libcamera/bayer_format.cpp | 5 +++++ + src/libcamera/camera_sensor.cpp | 3 +++ + src/libcamera/formats.cpp | 20 ++++++++++++++++++++ + src/libcamera/formats.yaml | 5 +++++ + src/libcamera/v4l2_pixelformat.cpp | 4 ++++ + src/libcamera/v4l2_subdevice.cpp | 1 + + 10 files changed, 48 insertions(+), 2 deletions(-) + +diff --git a/include/libcamera/internal/bayer_format.h b/include/libcamera/internal/bayer_format.h +index 78ba3969..e77106c3 100644 +--- a/include/libcamera/internal/bayer_format.h ++++ b/include/libcamera/internal/bayer_format.h +@@ -27,7 +27,8 @@ public: + GBRG = 1, + GRBG = 2, + RGGB = 3, +- MONO = 4 ++ MONO = 4, ++ IGIG_GBGR_IGIG_GRGB = 5, + }; + + enum class Packing : uint16_t { +diff --git a/include/linux/drm_fourcc.h b/include/linux/drm_fourcc.h +index 1496e097..750ae8c9 100644 +--- a/include/linux/drm_fourcc.h ++++ b/include/linux/drm_fourcc.h +@@ -405,6 +405,8 @@ extern "C" { + #define DRM_FORMAT_SGRBG10 fourcc_code('B', 'A', '1', '0') + #define DRM_FORMAT_SGBRG10 fourcc_code('G', 'B', '1', '0') + #define DRM_FORMAT_SBGGR10 fourcc_code('B', 'G', '1', '0') ++/* Mixed 10 bit bayer + ir pixel pattern found on Omnivision ov01a1s */ ++#define DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10 fourcc_code('O', 'V', '1', 'S') + + /* 12-bit Bayer formats */ + #define DRM_FORMAT_SRGGB12 fourcc_code('R', 'G', '1', '2') +diff --git a/include/linux/media-bus-format.h b/include/linux/media-bus-format.h +index 0dfc11ee..c5fbda0e 100644 +--- a/include/linux/media-bus-format.h ++++ b/include/linux/media-bus-format.h +@@ -112,7 +112,7 @@ + #define MEDIA_BUS_FMT_YUV16_1X48 0x202a + #define MEDIA_BUS_FMT_UYYVYY16_0_5X48 0x202b + +-/* Bayer - next is 0x3021 */ ++/* Bayer - next is 0x3022 */ + #define MEDIA_BUS_FMT_SBGGR8_1X8 0x3001 + #define MEDIA_BUS_FMT_SGBRG8_1X8 0x3013 + #define MEDIA_BUS_FMT_SGRBG8_1X8 0x3002 +@@ -145,6 +145,8 @@ + #define MEDIA_BUS_FMT_SGBRG16_1X16 0x301e + #define MEDIA_BUS_FMT_SGRBG16_1X16 0x301f + #define MEDIA_BUS_FMT_SRGGB16_1X16 0x3020 ++/* Mixed bayer + ir pixel pattern found on ov01a1s */ ++#define MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10 0x3021 + + /* JPEG compressed formats - next is 0x4002 */ + #define MEDIA_BUS_FMT_JPEG_1X8 0x4001 +diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h +index bfb315d6..13c6c9d3 100644 +--- a/include/linux/videodev2.h ++++ b/include/linux/videodev2.h +@@ -678,6 +678,9 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16 GBGB.. RGRG.. */ + #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16 GRGR.. BGBG.. */ + #define V4L2_PIX_FMT_SRGGB16 v4l2_fourcc('R', 'G', '1', '6') /* 16 RGRG.. GBGB.. */ ++ /* 10bit mixed bayer + ir pixel pattern found on ov01a1s */ ++#define V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10 v4l2_fourcc('O', 'V', '1', 'S') /* unpacked */ ++#define V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P v4l2_fourcc('O', 'V', '1', 'P') /* packed */ + + /* HSV formats */ + #define V4L2_PIX_FMT_HSV24 v4l2_fourcc('H', 'S', 'V', '3') +diff --git a/src/libcamera/bayer_format.cpp b/src/libcamera/bayer_format.cpp +index 3bf15fb4..ae227540 100644 +--- a/src/libcamera/bayer_format.cpp ++++ b/src/libcamera/bayer_format.cpp +@@ -108,6 +108,8 @@ const std::map bayerToFormat{ + { formats::SGRBG10, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10) } }, + { { BayerFormat::RGGB, 10, BayerFormat::Packing::None }, + { formats::SRGGB10, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10) } }, ++ { { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::None }, ++ { formats::SIGIG_GBGR_IGIG_GRGB10, V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10) } }, + { { BayerFormat::BGGR, 10, BayerFormat::Packing::CSI2 }, + { formats::SBGGR10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P) } }, + { { BayerFormat::GBRG, 10, BayerFormat::Packing::CSI2 }, +@@ -116,6 +118,8 @@ const std::map bayerToFormat{ + { formats::SGRBG10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P) } }, + { { BayerFormat::RGGB, 10, BayerFormat::Packing::CSI2 }, + { formats::SRGGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P) } }, ++ { { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::CSI2 }, ++ { formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P) } }, + { { BayerFormat::BGGR, 10, BayerFormat::Packing::IPU3 }, + { formats::SBGGR10_IPU3, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10) } }, + { { BayerFormat::GBRG, 10, BayerFormat::Packing::IPU3 }, +@@ -193,6 +197,7 @@ const std::unordered_map mbusCodeToBayer{ + { MEDIA_BUS_FMT_SGBRG10_1X10, { BayerFormat::GBRG, 10, BayerFormat::Packing::None } }, + { MEDIA_BUS_FMT_SGRBG10_1X10, { BayerFormat::GRBG, 10, BayerFormat::Packing::None } }, + { MEDIA_BUS_FMT_SRGGB10_1X10, { BayerFormat::RGGB, 10, BayerFormat::Packing::None } }, ++ { MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::None } }, + { MEDIA_BUS_FMT_SBGGR12_1X12, { BayerFormat::BGGR, 12, BayerFormat::Packing::None } }, + { MEDIA_BUS_FMT_SGBRG12_1X12, { BayerFormat::GBRG, 12, BayerFormat::Packing::None } }, + { MEDIA_BUS_FMT_SGRBG12_1X12, { BayerFormat::GRBG, 12, BayerFormat::Packing::None } }, +diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp +index 0ef78d9c..f19f72ea 100644 +--- a/src/libcamera/camera_sensor.cpp ++++ b/src/libcamera/camera_sensor.cpp +@@ -510,6 +510,9 @@ int CameraSensor::initProperties() + case BayerFormat::MONO: + cfa = properties::draft::MONO; + break; ++ case BayerFormat::IGIG_GBGR_IGIG_GRGB: ++ cfa = properties::draft::RGB; ++ break; + } + + properties_.set(properties::draft::ColorFilterArrangement, cfa); +diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp +index 447e6238..aef7d598 100644 +--- a/src/libcamera/formats.cpp ++++ b/src/libcamera/formats.cpp +@@ -599,6 +599,16 @@ const std::map pixelFormatInfo{ + .pixelsPerGroup = 2, + .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, + } }, ++ { formats::SIGIG_GBGR_IGIG_GRGB10, { ++ .name = "SIGIG_GBGR_IGIG_GRGB10", ++ .format = formats::SIGIG_GBGR_IGIG_GRGB10, ++ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10), }, ++ .bitsPerPixel = 10, ++ .colourEncoding = PixelFormatInfo::ColourEncodingRAW, ++ .packed = false, ++ .pixelsPerGroup = 4, ++ .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, ++ } }, + { formats::SBGGR10_CSI2P, { + .name = "SBGGR10_CSI2P", + .format = formats::SBGGR10_CSI2P, +@@ -639,6 +649,16 @@ const std::map pixelFormatInfo{ + .pixelsPerGroup = 4, + .planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }}, + } }, ++ { formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, { ++ .name = "SIGIG_GBGR_IGIG_GRGB10_CSI2P", ++ .format = formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, ++ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P), }, ++ .bitsPerPixel = 10, ++ .colourEncoding = PixelFormatInfo::ColourEncodingRAW, ++ .packed = true, ++ .pixelsPerGroup = 4, ++ .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, ++ } }, + { formats::SBGGR12, { + .name = "SBGGR12", + .format = formats::SBGGR12, +diff --git a/src/libcamera/formats.yaml b/src/libcamera/formats.yaml +index 539ac0b3..0786a900 100644 +--- a/src/libcamera/formats.yaml ++++ b/src/libcamera/formats.yaml +@@ -100,6 +100,8 @@ formats: + fourcc: DRM_FORMAT_SGBRG10 + - SBGGR10: + fourcc: DRM_FORMAT_SBGGR10 ++ - SIGIG_GBGR_IGIG_GRGB10: ++ fourcc: DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10 + + - SRGGB12: + fourcc: DRM_FORMAT_SRGGB12 +@@ -144,6 +146,9 @@ formats: + - SBGGR10_CSI2P: + fourcc: DRM_FORMAT_SBGGR10 + mod: MIPI_FORMAT_MOD_CSI2_PACKED ++ - SIGIG_GBGR_IGIG_GRGB10_CSI2P: ++ fourcc: DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10 ++ mod: MIPI_FORMAT_MOD_CSI2_PACKED + + - SRGGB12_CSI2P: + fourcc: DRM_FORMAT_SRGGB12 +diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp +index 5551c62e..53078d99 100644 +--- a/src/libcamera/v4l2_pixelformat.cpp ++++ b/src/libcamera/v4l2_pixelformat.cpp +@@ -153,6 +153,8 @@ const std::map vpf2pf{ + { formats::SGRBG10, "10-bit Bayer GRGR/BGBG" } }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), + { formats::SRGGB10, "10-bit Bayer RGRG/GBGB" } }, ++ { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10), ++ { formats::SIGIG_GBGR_IGIG_GRGB10, "10-bit Bayer GRGB/IGIG/GBGR/IGIG" } }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), + { formats::SBGGR10_CSI2P, "10-bit Bayer BGBG/GRGR Packed" } }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), +@@ -161,6 +163,8 @@ const std::map vpf2pf{ + { formats::SGRBG10_CSI2P, "10-bit Bayer GRGR/BGBG Packed" } }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), + { formats::SRGGB10_CSI2P, "10-bit Bayer RGRG/GBGB Packed" } }, ++ { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P), ++ { formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, "10-bit Bayer GRGB/IGIG/GBGR/IGIG Packed" } }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), + { formats::SBGGR12, "12-bit Bayer BGBG/GRGR" } }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), +diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp +index 15e8206a..4ad37aaf 100644 +--- a/src/libcamera/v4l2_subdevice.cpp ++++ b/src/libcamera/v4l2_subdevice.cpp +@@ -128,6 +128,7 @@ const std::map formatInfoMap = { + { MEDIA_BUS_FMT_SGBRG10_1X10, { 10, "SGBRG10_1X10", PixelFormatInfo::ColourEncodingRAW } }, + { MEDIA_BUS_FMT_SGRBG10_1X10, { 10, "SGRBG10_1X10", PixelFormatInfo::ColourEncodingRAW } }, + { MEDIA_BUS_FMT_SRGGB10_1X10, { 10, "SRGGB10_1X10", PixelFormatInfo::ColourEncodingRAW } }, ++ { MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { 10, "SIGIG_GBGR_IGIG_GRGB10_1X10", PixelFormatInfo::ColourEncodingRAW } }, + { MEDIA_BUS_FMT_SBGGR12_1X12, { 12, "SBGGR12_1X12", PixelFormatInfo::ColourEncodingRAW } }, + { MEDIA_BUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12", PixelFormatInfo::ColourEncodingRAW } }, + { MEDIA_BUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12", PixelFormatInfo::ColourEncodingRAW } }, +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch b/users/flokli/ipu6-softisp/libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch new file mode 100644 index 000000000000..44985a94e16f --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch @@ -0,0 +1,131 @@ +From f939e68a3ef556e572f0140df6d7ef17d72f457e Mon Sep 17 00:00:00 2001 +From: Marttico +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 +Signed-off-by: Dennis Bonke +Co-authored-by: Toon Langendam +Signed-off-by: Toon Langendam +Signed-off-by: Marttico +Signed-off-by: Hans de Goede +--- + .../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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch b/users/flokli/ipu6-softisp/libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch new file mode 100644 index 000000000000..4054a9563f35 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch @@ -0,0 +1,315 @@ +From e3638943a8bd3f93b8d81c3996035c60755b97f6 Mon Sep 17 00:00:00 2001 +From: Marttico +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 +Signed-off-by: Dennis Bonke +Co-authored-by: Toon Langendam +Signed-off-by: Toon Langendam +Signed-off-by: Marttico +Signed-off-by: Hans de Goede +--- + .../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({ 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 + diff --git a/users/flokli/ipu6-softisp/libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch b/users/flokli/ipu6-softisp/libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch new file mode 100644 index 000000000000..ec4991769913 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch @@ -0,0 +1,130 @@ +From 26e96232c314f9d34f6ee3be365c04918967084e Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 22 Jan 2024 17:18:00 +0100 +Subject: [PATCH 23/25] libcamera: Add "Software ISP benchmarking" + documentation + +Add a "Software ISP benchmarking" documentation section which describes +the performance/power consumption measurements used during +the Software ISP's development. + +Signed-off-by: Hans de Goede +--- + Documentation/index.rst | 1 + + Documentation/meson.build | 1 + + Documentation/software-isp-benchmarking.rst | 82 +++++++++++++++++++++ + 3 files changed, 84 insertions(+) + create mode 100644 Documentation/software-isp-benchmarking.rst + +diff --git a/Documentation/index.rst b/Documentation/index.rst +index 63fac72d..5442ae75 100644 +--- a/Documentation/index.rst ++++ b/Documentation/index.rst +@@ -24,3 +24,4 @@ + Lens driver requirements + Python Bindings + Camera Sensor Model ++ SoftwareISP Benchmarking +diff --git a/Documentation/meson.build b/Documentation/meson.build +index 7a58fec8..3872e0a8 100644 +--- a/Documentation/meson.build ++++ b/Documentation/meson.build +@@ -80,6 +80,7 @@ if sphinx.found() + 'lens_driver_requirements.rst', + 'python-bindings.rst', + 'sensor_driver_requirements.rst', ++ 'software-isp-benchmarking.rst', + '../README.rst', + ] + +diff --git a/Documentation/software-isp-benchmarking.rst b/Documentation/software-isp-benchmarking.rst +new file mode 100644 +index 00000000..738c8c65 +--- /dev/null ++++ b/Documentation/software-isp-benchmarking.rst +@@ -0,0 +1,82 @@ ++.. SPDX-License-Identifier: CC-BY-SA-4.0 ++ ++.. _software-isp-benchmarking: ++ ++Software ISP benchmarking ++========================= ++ ++The Software ISP is paricular sensitive to performance regressions ++therefor it is a good idea to always benchmark the Software ISP ++before and after making changes to it and ensure that there are ++no performance regressions. ++ ++DebayerCpu class builtin benchmark ++---------------------------------- ++ ++The DebayerCpu class has a builtin benchmark. This benchmark ++measures the time spend on processing (collecting statistics ++and debayering) only, it does not measure the time spend on ++capturing or outputting the frames. ++ ++The builtin benchmark always runs. So this can be used by simply ++running "cam" or "qcam" with a pipeline using the Software ISP. ++ ++When it runs it will skip measuring the first 30 frames to ++allow the caches and the CPU temperature (turbo-ing) to warm-up ++and then it measures 30 fps and shows the total and per frame ++processing time using an info level log message: ++ ++.. code-block:: ++ ++ INFO Debayer debayer_cpu.cpp:907 Processed 30 frames in 244317us, 8143 us/frame ++ ++To get stable measurements it is advised to disable any other processes which ++may cause significant CPU usage (e.g. disable wifi, bluetooth and browsers). ++When possible it is also advisable to disable CPU turbo-ing and ++frequency-scaling. ++ ++For example when benchmarking on a Lenovo ThinkPad X1 Yoga Gen 8, with ++the charger plugged in, the CPU can be fixed to run at 2 GHz using: ++ ++.. code-block:: ++ ++ sudo x86_energy_perf_policy --turbo-enable 0 ++ sudo cpupower frequency-set -d 2GHz -u 2GHz ++ ++with these settings the builtin bench reports a processing time of ~7.8ms/frame ++on this laptop for FHD SGRBG10 (unpacked) bayer data. ++ ++Measuring power consumption ++--------------------------- ++ ++Since the Software ISP is often used on mobile devices it is also ++important to measure power consumption and ensure that that does ++not regress. ++ ++For example to measure power consumption on a Lenovo ThinkPad X1 Yoga Gen 8 ++it needs to be running on battery and it should be configured with its ++platform-profile (/sys/firmware/acpi/platform_profile) set to balanced and ++with its default turbo and frequency-scaling behavior to match real world usage. ++ ++Then start qcam to capture a FHD picture at 30 fps and position the qcam window ++so that it is fully visible. After this run the following command to monitor ++the power consumption: ++ ++.. code-block:: ++ ++ watch -n 10 cat /sys/class/power_supply/BAT0/power_now /sys/class/hwmon/hwmon6/fan?_input ++ ++Note this not only measures the power consumption in ųW it also monitors ++the speed of this laptop's 2 fans. This is important because depending on ++the ambient temperature the 2 fans may spin up while testing and this ++will cause an additional power consumption of approx. 0.5W messing up ++the measurement. ++ ++After starting qcam + the watch command let the laptop sit without using ++it for 2 minutes for the readings to stabilize. Then check that the fans ++have not turned on and manually take a couple of consecutive power readings ++and avarage these. ++ ++On the example Lenovo ThinkPad X1 Yoga Gen 8 laptop this results in ++a measured power consumption of approx. 13W while running qcam versus ++approx. 4-5W while setting idle with its OLED panel on. +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch b/users/flokli/ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch new file mode 100644 index 000000000000..3b558e06d0e9 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch @@ -0,0 +1,95 @@ +From 9bec33e5c7e6765734eeef2d22d7f7f65dee2264 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 19 Dec 2023 15:45:51 +0100 +Subject: [PATCH 24/25] ov01a1s HACK + +Signed-off-by: Hans de Goede +--- + src/libcamera/camera_sensor.cpp | 6 ++++++ + src/libcamera/software_isp/debayer_cpu.cpp | 8 ++++++++ + src/libcamera/software_isp/swstats_cpu.cpp | 5 +++++ + 3 files changed, 19 insertions(+) + +diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp +index f19f72ea..7ad4b9ef 100644 +--- a/src/libcamera/camera_sensor.cpp ++++ b/src/libcamera/camera_sensor.cpp +@@ -34,6 +34,9 @@ + + namespace libcamera { + ++// HACK HACK ++bool is_ov01a1s = false; ++ + LOG_DEFINE_CATEGORY(CameraSensor) + + /** +@@ -426,6 +429,9 @@ int CameraSensor::initProperties() + model_ = subdev_->model(); + properties_.set(properties::Model, utils::toAscii(model_)); + ++ if (model_ == "ov01a1s") ++ is_ov01a1s = true; ++ + /* Generate a unique ID for the sensor. */ + int ret = generateId(); + if (ret) +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +index 41c8805f..b6393925 100644 +--- a/src/libcamera/software_isp/debayer_cpu.cpp ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -23,6 +23,8 @@ + + namespace libcamera { + ++extern bool is_ov01a1s; ++ + DebayerCpu::DebayerCpu(std::unique_ptr stats) + : stats_(std::move(stats)), gamma_correction_(1.0) + { +@@ -471,6 +473,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputFormat); + ++ if (is_ov01a1s) ++ bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB; ++ + if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) && + bayerFormat.packing == BayerFormat::Packing::None && + isStandardBayerOrder(bayerFormat.order)) { +@@ -548,6 +553,9 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputFormat); + ++ if (is_ov01a1s) ++ bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB; ++ + swapRedBlueGains_ = false; + + switch (outputFormat) { +diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp +index 96e21be5..503ce799 100644 +--- a/src/libcamera/software_isp/swstats_cpu.cpp ++++ b/src/libcamera/software_isp/swstats_cpu.cpp +@@ -19,6 +19,8 @@ + + namespace libcamera { + ++extern bool is_ov01a1s; ++ + SwStatsCpu::SwStatsCpu() + : SwStats() + { +@@ -301,6 +303,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg) + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputCfg.pixelFormat); + ++ if (is_ov01a1s) ++ bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB; ++ + startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats; + finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats; + +-- +2.43.0 + diff --git a/users/flokli/ipu6-softisp/libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch b/users/flokli/ipu6-softisp/libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch new file mode 100644 index 000000000000..1f267353c5e3 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch @@ -0,0 +1,40 @@ +From 4f2c94ba8b7f9f4d85a1d7e03f4c5272d92c3361 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +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 +--- + 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 + -- cgit 1.4.1