From 9948eb64d1d0a96c175114cfb2069cd301df740d Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Sun, 17 Mar 2024 16:11:22 +0200 Subject: chore(users/flokli/ipu6-softisp): refresh libcamera patches Refresh them with the patches from https://patchwork.libcamera.org/cover/19663/. This is still based off v0.2.0. Change-Id: I875fd64e3bb71a95c92af1108a23d27c0f3494e0 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11179 Tested-by: BuildkiteCI Reviewed-by: flokli Autosubmit: flokli --- users/flokli/ipu6-softisp/config.nix | 43 +- ...ipeline-simple-fix-size-adjustment-in-val.patch | 71 +- ...nternal-Move-dma_heaps.-h-cpp-to-common-d.patch | 332 ++++++--- ...ma_heaps-extend-DmaHeap-class-to-support-.patch | 144 ++-- ...nternal-Move-SharedMemObject-class-to-a-c.patch | 36 +- ...nternal-Document-the-SharedMemObject-clas.patch | 139 ---- ...hared_mem_object-reorganize-the-code-and-.patch | 403 ++++++++++ ...006-libcamera-introduce-SoftwareIsp-class.patch | 354 --------- ...bcamera-software_isp-Add-SwStatsCpu-class.patch | 523 +++++++++++++ ...amera-software_isp-Add-Debayer-base-class.patch | 255 +++++++ ...amera-software_isp-Add-SwStats-base-class.patch | 382 ---------- ...bcamera-software_isp-Add-DebayerCpu-class.patch | 825 +++++++++++++++++++++ ...bcamera-software_isp-Add-SwStatsCpu-class.patch | 272 ------- .../0009-libcamera-ipa-add-Soft-IPA.patch | 506 +++++++++++++ ...amera-software_isp-Add-Debayer-base-class.patch | 272 ------- .../0010-libcamera-introduce-SoftwareIsp.patch | 507 +++++++++++++ ...bcamera-software_isp-Add-DebayerCpu-class.patch | 727 ------------------ ...1-libcamera-ipa-add-Soft-IPA-common-files.patch | 256 ------- ...ipeline-simple-rename-converterBuffers_-a.patch | 240 ++++++ ...pa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch | 407 ---------- ...ipeline-simple-enable-use-of-Soft-ISP-and.patch | 302 ++++++++ ...oftware_isp-add-Simple-SoftwareIsp-implem.patch | 483 ------------ ...wstats_cpu-Add-support-for-8-10-and-12-bp.patch | 203 +++++ ...ebayer_cpu-Add-support-for-8-10-and-12-bp.patch | 234 ++++++ ...ipeline-simple-rename-converterBuffers_-a.patch | 238 ------ ...era-debayer_cpu-Add-BGR888-output-support.patch | 127 ++++ ...ipeline-simple-enable-use-of-Soft-ISP-and.patch | 243 ------ ...dd-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch | 237 ++++++ ...wstats_cpu-Add-support-for-8-10-and-12-bp.patch | 199 ----- ...dd-Software-ISP-benchmarking-documentatio.patch | 132 ++++ ...ebayer_cpu-Add-support-for-8-10-and-12-bp.patch | 237 ------ ...era-debayer_cpu-Add-BGR888-output-support.patch | 125 ---- ...oftware_isp-Apply-black-level-compensatio.patch | 396 ++++++++++ ...oft-IPA-use-CameraSensorHelper-for-analog.patch | 239 ++++++ ...ipeline-simple-Enable-simplepipeline-for-.patch | 30 - ...dd-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch | 237 ------ .../ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch | 95 +++ ...ebayer_cpu-Make-the-minimum-size-1280x720.patch | 42 ++ ...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 - 43 files changed, 5705 insertions(+), 5499 deletions(-) delete mode 100644 users/flokli/ipu6-softisp/libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch delete 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-DebayerCpu-class.patch delete 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-ipa-add-Soft-IPA.patch delete 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-introduce-SoftwareIsp.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch delete 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/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch delete 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-debayer_cpu-Add-BGR888-output-support.patch delete 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-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch delete 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-Add-Software-ISP-benchmarking-documentatio.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch delete 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/0020-ov01a1s-HACK.patch create mode 100644 users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch delete mode 100644 users/flokli/ipu6-softisp/libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch diff --git a/users/flokli/ipu6-softisp/config.nix b/users/flokli/ipu6-softisp/config.nix index ff945dfbe3..a62dfcf709 100644 --- a/users/flokli/ipu6-softisp/config.nix +++ b/users/flokli/ipu6-softisp/config.nix @@ -3,39 +3,36 @@ let libcamera = pkgs.libcamera.overrideAttrs (old: { mesonFlags = old.mesonFlags or [ ] ++ [ - "-Dpipelines=simple/simple,ipu3,uvcvideo" - "-Dipas=simple/simple,ipu3" + "-Dpipelines=simple,ipu3,uvcvideo" + "-Dipas=simple,ipu3" ]; # This is # https://copr-dist-git.fedorainfracloud.org/cgit/jwrdegoede/ipu6-softisp/libcamera.git/plain/libcamera-0.2.0-softisp.patch?h=f39&id=60e6b3d5e366a360a75942073dc0d642e4900982, # but manually piped to git and back, as some renames were not processed properly. + # It was later refreshed with https://patchwork.libcamera.org/cover/19663/ patches = old.patches or [ ] ++ [ ./libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch ./libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch ./libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch ./libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch - ./libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch - ./libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch - ./libcamera/0007-libcamera-software_isp-Add-SwStats-base-class.patch - ./libcamera/0008-libcamera-software_isp-Add-SwStatsCpu-class.patch - ./libcamera/0009-libcamera-software_isp-Add-Debayer-base-class.patch - ./libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch - ./libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch - ./libcamera/0012-libcamera-ipa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch - ./libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch - ./libcamera/0014-libcamera-pipeline-simple-rename-converterBuffers_-a.patch - ./libcamera/0015-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch - ./libcamera/0016-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch - ./libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch - ./libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch - ./libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch - ./libcamera/0020-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch - ./libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch - ./libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch - ./libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch - ./libcamera/0024-ov01a1s-HACK.patch - ./libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch + ./libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch + ./libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch + ./libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch + ./libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch + ./libcamera/0009-libcamera-ipa-add-Soft-IPA.patch + ./libcamera/0010-libcamera-introduce-SoftwareIsp.patch + ./libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch + ./libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch + ./libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch + ./libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch + ./libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch + ./libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch + ./libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch + ./libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch + ./libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch + ./libcamera/0020-ov01a1s-HACK.patch + ./libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch ]; }); 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 index 7a71ed1db7..b640ddaa24 100644 --- 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 @@ -1,34 +1,73 @@ -From aa818f7b749122f916be1ced48d1a3a2b3aeb47e Mon Sep 17 00:00:00 2001 +From d86746fc1739f678e4bafe43f5047cba9b6b053e Mon Sep 17 00:00:00 2001 From: Andrey Konovalov -Date: Tue, 2 Jan 2024 23:47:20 +0300 -Subject: [PATCH 01/25] libcamera: pipeline: simple: fix size adjustment in +Date: Mon, 11 Mar 2024 15:15:05 +0100 +Subject: [PATCH 01/21] libcamera: pipeline: simple: fix size adjustment in validate() -SimpleCameraConfiguration::validate() adjusts the configuration -of its streams (if the size is not in the outputSizes) to -the captureSize. But the captureSize itself can be not in the -outputSizes, and then the adjusted configuration won't be -valid resulting in camera configuration failure. +SimpleCameraConfiguration::validate() adjusts the configuration of its +streams (if the size is not in the outputSizes) to the captureSize. But +the captureSize itself can be not in the outputSizes, and then the +adjusted configuration won't be valid resulting in camera configuration +failure. -Signed-off-by: Andrey Konovalov -Signed-off-by: Hans de Goede Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s Tested-by: Pavel Machek +Reviewed-by: Milan Zamazal +Reviewed-by: Pavel Machek +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede --- - src/libcamera/pipeline/simple/simple.cpp | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) + src/libcamera/pipeline/simple/simple.cpp | 37 ++++++++++++++++++++++-- + 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp -index 911051b2..4d0e7255 100644 +index 911051b2..a84f6760 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp -@@ -997,10 +997,13 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate() +@@ -881,6 +881,30 @@ SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera, + { + } + ++namespace { ++ ++static Size adjustSize(const Size &requestedSize, const SizeRange &supportedSizes) ++{ ++ ASSERT(supportedSizes.min <= supportedSizes.max); ++ ++ if (supportedSizes.min == supportedSizes.max) ++ return supportedSizes.max; ++ ++ unsigned int hStep = supportedSizes.hStep; ++ unsigned int vStep = supportedSizes.vStep; ++ ++ if (hStep == 0) ++ hStep = supportedSizes.max.width - supportedSizes.min.width; ++ if (vStep == 0) ++ vStep = supportedSizes.max.height - supportedSizes.min.height; ++ ++ Size adjusted = requestedSize.boundedTo(supportedSizes.max).expandedTo(supportedSizes.min); ++ ++ return adjusted.shrunkBy(supportedSizes.min).alignedDownTo(hStep, vStep).grownBy(supportedSizes.min); ++} ++ ++} /* namespace */ ++ + CameraConfiguration::Status SimpleCameraConfiguration::validate() + { + const CameraSensor *sensor = data_->sensor_.get(); +@@ -997,10 +1021,19 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate() } if (!pipeConfig_->outputSizes.contains(cfg.size)) { + Size adjustedSize = pipeConfig_->captureSize; ++ /* ++ * The converter (when present) may not be able to output ++ * a size identical to its input size. The capture size is thus ++ * not guaranteed to be a valid output size. In such cases, use ++ * the smaller valid output size closest to the requested. ++ */ + if (!pipeConfig_->outputSizes.contains(adjustedSize)) -+ adjustedSize = pipeConfig_->outputSizes.max; ++ adjustedSize = adjustSize(cfg.size, pipeConfig_->outputSizes); LOG(SimplePipeline, Debug) << "Adjusting size from " << cfg.size - << " to " << pipeConfig_->captureSize; @@ -39,5 +78,5 @@ index 911051b2..4d0e7255 100644 } -- -2.43.0 +2.43.2 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 index 85a27ba8e2..450a0a21f1 100644 --- 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 @@ -1,7 +1,7 @@ -From ca3bcfde49f069a85f7860f61d8c3bd196f97139 Mon Sep 17 00:00:00 2001 +From 96e50c6a43352a9cb81d558fea27e580f2b26585 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov -Date: Tue, 26 Dec 2023 16:55:08 +0300 -Subject: [PATCH 02/25] libcamera: internal: Move dma_heaps.[h,cpp] to common +Date: Mon, 11 Mar 2024 15:15:06 +0100 +Subject: [PATCH 02/21] libcamera: internal: Move dma_heaps.[h, cpp] to common directories DmaHeap class is useful outside the RPi pipeline handler too. @@ -9,20 +9,26 @@ DmaHeap class is useful outside the RPi pipeline handler too. Move dma_heaps.h and dma_heaps.cpp to common directories. Update the build files and RPi vc4 pipeline handler accordingly. -Signed-off-by: Andrey Konovalov -Signed-off-by: Hans de Goede Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s Tested-by: Pavel Machek +Reviewed-by: Kieran Bingham +Reviewed-by: Naushir Patuck +Reviewed-by: Pavel Machek +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Reviewed-by: Milan Zamazal --- - .../libcamera/internal}/dma_heaps.h | 4 ---- - include/libcamera/internal/meson.build | 1 + - .../{pipeline/rpi/vc4 => }/dma_heaps.cpp | 18 +++++++----------- - src/libcamera/meson.build | 1 + - src/libcamera/pipeline/rpi/vc4/meson.build | 1 - - src/libcamera/pipeline/rpi/vc4/vc4.cpp | 5 ++--- - 6 files changed, 11 insertions(+), 19 deletions(-) + .../libcamera/internal}/dma_heaps.h | 4 - + include/libcamera/internal/meson.build | 1 + + src/libcamera/dma_heaps.cpp | 127 ++++++++++++++++++ + src/libcamera/meson.build | 1 + + src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp | 90 ------------- + src/libcamera/pipeline/rpi/vc4/meson.build | 1 - + src/libcamera/pipeline/rpi/vc4/vc4.cpp | 5 +- + 7 files changed, 131 insertions(+), 98 deletions(-) rename {src/libcamera/pipeline/rpi/vc4 => include/libcamera/internal}/dma_heaps.h (92%) - rename src/libcamera/{pipeline/rpi/vc4 => }/dma_heaps.cpp (83%) + create mode 100644 src/libcamera/dma_heaps.cpp + delete mode 100644 src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h b/include/libcamera/internal/dma_heaps.h similarity index 92% @@ -59,85 +65,139 @@ index 7f1f3440..33eb0fb3 100644 'formats.h', 'framebuffer.h', 'ipa_manager.h', -diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp -similarity index 83% -rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp -rename to src/libcamera/dma_heaps.cpp -index 317b1fc1..7444d9c2 100644 ---- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp +diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp +new file mode 100644 +index 00000000..38ef175a +--- /dev/null +++ b/src/libcamera/dma_heaps.cpp -@@ -5,8 +5,6 @@ - * dma_heaps.h - Helper class for dma-heap allocations. - */ - --#include "dma_heaps.h" -- - #include - #include - #include -@@ -16,6 +14,8 @@ - - #include - +@@ -0,0 +1,127 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2020, Raspberry Pi Ltd ++ * ++ * dma_heaps.h - Helper class for dma-heap allocations. ++ */ ++ +#include "libcamera/internal/dma_heaps.h" + - /* - * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma - * to only have to worry about importing. -@@ -30,9 +30,7 @@ static constexpr std::array heapNames = { - - namespace libcamera { - --LOG_DECLARE_CATEGORY(RPI) -- --namespace RPi { ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++/** ++ * \file dma_heaps.cpp ++ * \brief CMA dma-heap allocator ++ */ ++ ++/* ++ * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma ++ * to only have to worry about importing. ++ * ++ * Annoyingly, should the cma heap size be specified on the kernel command line ++ * instead of DT, the heap gets named "reserved" instead. ++ */ ++static constexpr std::array heapNames = { ++ "/dev/dma_heap/linux,cma", ++ "/dev/dma_heap/reserved" ++}; ++ ++namespace libcamera { ++ +LOG_DEFINE_CATEGORY(DmaHeap) - - DmaHeap::DmaHeap() - { -@@ -40,7 +38,7 @@ DmaHeap::DmaHeap() - int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); - if (ret < 0) { - ret = errno; -- LOG(RPI, Debug) << "Failed to open " << name << ": " -+ LOG(DmaHeap, Debug) << "Failed to open " << name << ": " - << strerror(ret); - continue; - } -@@ -50,7 +48,7 @@ DmaHeap::DmaHeap() - } - - if (!dmaHeapHandle_.isValid()) -- LOG(RPI, Error) << "Could not open any dmaHeap device"; ++ ++/** ++ * \class DmaHeap ++ * \brief Helper class for CMA dma-heap allocations ++ */ ++ ++/** ++ * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor ++ * ++ * Goes through the internal list of possible names of the CMA dma-heap devices ++ * until a CMA dma-heap device is successfully opened. If it fails to open any ++ * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap ++ * object owns a wrapped dma-heap file descriptor. ++ * ++ * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it. ++ */ ++DmaHeap::DmaHeap() ++{ ++ for (const char *name : heapNames) { ++ int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); ++ if (ret < 0) { ++ ret = errno; ++ LOG(DmaHeap, Debug) ++ << "Failed to open " << name << ": " ++ << strerror(ret); ++ continue; ++ } ++ ++ dmaHeapHandle_ = UniqueFD(ret); ++ break; ++ } ++ ++ if (!dmaHeapHandle_.isValid()) + LOG(DmaHeap, Error) << "Could not open any dmaHeap device"; - } - - DmaHeap::~DmaHeap() = default; -@@ -69,7 +67,7 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size) - - ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc); - if (ret < 0) { -- LOG(RPI, Error) << "dmaHeap allocation failure for " -+ LOG(DmaHeap, Error) << "dmaHeap allocation failure for " - << name; - return {}; - } -@@ -77,7 +75,7 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size) - UniqueFD allocFd(alloc.fd); - ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name); - if (ret < 0) { -- LOG(RPI, Error) << "dmaHeap naming failure for " -+ LOG(DmaHeap, Error) << "dmaHeap naming failure for " - << name; - return {}; - } -@@ -85,6 +83,4 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size) - return allocFd; - } - --} /* namespace RPi */ -- - } /* namespace libcamera */ ++} ++ ++/** ++ * \brief Destroy the DmaHeap instance ++ * ++ * Destroying a DmaHeap instance which owns a wrapped dma-heap file descriptor ++ * closes the descriptor automatically. ++ */ ++DmaHeap::~DmaHeap() = default; ++ ++/** ++ * \fn DmaHeap::isValid() ++ * \brief Check if the DmaHeap instance is valid ++ * \return True if the DmaHeap is valid, false otherwise ++ */ ++ ++/** ++ * \brief Allocate a dma-buf from the DmaHeap ++ * \param [in] name The name to set for the allocated buffer ++ * \param [in] size The size of the buffer to allocate ++ * \return The \ref UniqueFD of the allocated buffer ++ * ++ * Allocates a dma-buf with read/write access. ++ * If the allocation fails returns invalid UniqueFD. ++ */ ++UniqueFD DmaHeap::alloc(const char *name, std::size_t size) ++{ ++ int ret; ++ ++ if (!name) ++ return {}; ++ ++ struct dma_heap_allocation_data alloc = {}; ++ ++ alloc.len = size; ++ alloc.fd_flags = O_CLOEXEC | O_RDWR; ++ ++ ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc); ++ if (ret < 0) { ++ LOG(DmaHeap, Error) << "dmaHeap allocation failure for " << name; ++ return {}; ++ } ++ ++ UniqueFD allocFd(alloc.fd); ++ ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name); ++ if (ret < 0) { ++ LOG(DmaHeap, Error) << "dmaHeap naming failure for " << name; ++ return {}; ++ } ++ ++ return allocFd; ++} ++ ++} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 45f63e93..3c5e43df 100644 --- a/src/libcamera/meson.build @@ -150,6 +210,102 @@ index 45f63e93..3c5e43df 100644 'fence.cpp', 'formats.cpp', 'framebuffer.cpp', +diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp +deleted file mode 100644 +index 317b1fc1..00000000 +--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp ++++ /dev/null +@@ -1,90 +0,0 @@ +-/* SPDX-License-Identifier: LGPL-2.1-or-later */ +-/* +- * Copyright (C) 2020, Raspberry Pi Ltd +- * +- * dma_heaps.h - Helper class for dma-heap allocations. +- */ +- +-#include "dma_heaps.h" +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-/* +- * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma +- * to only have to worry about importing. +- * +- * Annoyingly, should the cma heap size be specified on the kernel command line +- * instead of DT, the heap gets named "reserved" instead. +- */ +-static constexpr std::array heapNames = { +- "/dev/dma_heap/linux,cma", +- "/dev/dma_heap/reserved" +-}; +- +-namespace libcamera { +- +-LOG_DECLARE_CATEGORY(RPI) +- +-namespace RPi { +- +-DmaHeap::DmaHeap() +-{ +- for (const char *name : heapNames) { +- int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); +- if (ret < 0) { +- ret = errno; +- LOG(RPI, Debug) << "Failed to open " << name << ": " +- << strerror(ret); +- continue; +- } +- +- dmaHeapHandle_ = UniqueFD(ret); +- break; +- } +- +- if (!dmaHeapHandle_.isValid()) +- LOG(RPI, Error) << "Could not open any dmaHeap device"; +-} +- +-DmaHeap::~DmaHeap() = default; +- +-UniqueFD DmaHeap::alloc(const char *name, std::size_t size) +-{ +- int ret; +- +- if (!name) +- return {}; +- +- struct dma_heap_allocation_data alloc = {}; +- +- alloc.len = size; +- alloc.fd_flags = O_CLOEXEC | O_RDWR; +- +- ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc); +- if (ret < 0) { +- LOG(RPI, Error) << "dmaHeap allocation failure for " +- << name; +- return {}; +- } +- +- UniqueFD allocFd(alloc.fd); +- ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name); +- if (ret < 0) { +- LOG(RPI, Error) << "dmaHeap naming failure for " +- << name; +- return {}; +- } +- +- return allocFd; +-} +- +-} /* namespace RPi */ +- +-} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/rpi/vc4/meson.build b/src/libcamera/pipeline/rpi/vc4/meson.build index cdb049c5..386e2296 100644 --- a/src/libcamera/pipeline/rpi/vc4/meson.build @@ -190,5 +346,5 @@ index 26102ea7..3a42e75e 100644 struct Config { -- -2.43.0 +2.43.2 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 index de81924487..6e5ef9445a 100644 --- 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 @@ -1,7 +1,7 @@ -From 6d5f3b0b54df4ff66079675a4c1f0f0b76778e22 Mon Sep 17 00:00:00 2001 +From 5df9bc3b2a3d86bcc8504896cc87d7fcb5aea3a4 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov -Date: Wed, 10 Jan 2024 23:51:25 +0300 -Subject: [PATCH 03/25] libcamera: dma_heaps: extend DmaHeap class to support +Date: Mon, 11 Mar 2024 15:15:07 +0100 +Subject: [PATCH 03/21] libcamera: dma_heaps: extend DmaHeap class to support system heap Add an argument to the constructor to specify dma heaps type(s) @@ -9,17 +9,19 @@ to use. Can be DmaHeapFlag::Cma and/or DmaHeapFlag::System. By default DmaHeapFlag::Cma is used. If both DmaHeapFlag::Cma and DmaHeapFlag::System are set, CMA heap is tried first. -Signed-off-by: Andrey Konovalov -Signed-off-by: Hans de Goede Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s Tested-by: Pavel Machek +Reviewed-by: Milan Zamazal +Reviewed-by: Pavel Machek +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede --- - include/libcamera/internal/dma_heaps.h | 12 +++++++- - src/libcamera/dma_heaps.cpp | 39 +++++++++++++++----------- - 2 files changed, 34 insertions(+), 17 deletions(-) + include/libcamera/internal/dma_heaps.h | 12 ++++- + src/libcamera/dma_heaps.cpp | 67 ++++++++++++++++++++------ + 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/include/libcamera/internal/dma_heaps.h b/include/libcamera/internal/dma_heaps.h -index cff8f140..22aa1007 100644 +index cff8f140..80bf29e7 100644 --- a/include/libcamera/internal/dma_heaps.h +++ b/include/libcamera/internal/dma_heaps.h @@ -9,6 +9,7 @@ @@ -36,8 +38,8 @@ index cff8f140..22aa1007 100644 public: - DmaHeap(); + enum class DmaHeapFlag { -+ Cma = (1 << 0), -+ System = (1 << 1), ++ Cma = 1 << 0, ++ System = 1 << 1, + }; + + using DmaHeapFlags = Flags; @@ -54,68 +56,114 @@ index cff8f140..22aa1007 100644 + } /* namespace libcamera */ diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp -index 7444d9c2..177de31b 100644 +index 38ef175a..d0e33ce6 100644 --- a/src/libcamera/dma_heaps.cpp +++ b/src/libcamera/dma_heaps.cpp -@@ -16,6 +16,8 @@ +@@ -19,9 +19,11 @@ - #include "libcamera/internal/dma_heaps.h" + /** + * \file dma_heaps.cpp +- * \brief CMA dma-heap allocator ++ * \brief dma-heap allocator + */ +namespace libcamera { + /* - * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma + * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma * to only have to worry about importing. -@@ -23,28 +25,33 @@ +@@ -29,42 +31,77 @@ * Annoyingly, should the cma heap size be specified on the kernel command line * instead of DT, the heap gets named "reserved" instead. */ -static constexpr std::array 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") ++ ++/** ++ * \struct DmaHeapInfo ++ * \brief Tells what type of dma-heap the dma-heap represented by the device node name is ++ * \var DmaHeapInfo::flag ++ * \brief The type of the dma-heap ++ * \var DmaHeapInfo::name ++ * \brief The dma-heap's device node name ++ */ ++struct DmaHeapInfo { ++ DmaHeap::DmaHeapFlag flag; ++ const char *name; }; -namespace libcamera { -- ++static constexpr std::array heapInfos = { ++ { /* CMA heap names first */ ++ { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma" }, ++ { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved" }, ++ { DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system" } } ++}; + LOG_DEFINE_CATEGORY(DmaHeap) + /** + * \class DmaHeap +- * \brief Helper class for CMA dma-heap allocations ++ * \brief Helper class for dma-heap allocations + */ + + /** +- * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor ++ * \enum DmaHeap::DmaHeapFlag ++ * \brief Type of the dma-heap ++ * \var DmaHeap::Cma ++ * \brief Allocate from a CMA dma-heap ++ * \var DmaHeap::System ++ * \brief Allocate from the system dma-heap ++ */ ++ ++/** ++ * \typedef DmaHeap::DmaHeapFlags ++ * \brief A bitwise combination of DmaHeap::DmaHeapFlag values ++ */ ++ ++/** ++ * \brief Construct a DmaHeap that owns a CMA or system dma-heap file descriptor ++ * \param [in] flags The type(s) of the dma-heap(s) to allocate from + * +- * Goes through the internal list of possible names of the CMA dma-heap devices +- * until a CMA dma-heap device is successfully opened. If it fails to open any +- * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap +- * object owns a wrapped dma-heap file descriptor. ++ * By default \a flags are set to DmaHeap::DmaHeapFlag::Cma. The constructor goes ++ * through the internal list of possible names of the CMA and system dma-heap devices ++ * until the dma-heap device of the requested type is successfully opened. If more ++ * than one dma-heap type is specified in flags the CMA heap is tried first. If it ++ * fails to open any dma-heap device an invalid DmaHeap object is constructed. ++ * A valid DmaHeap object owns a wrapped dma-heap file descriptor. + * + * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it. + */ -DmaHeap::DmaHeap() +DmaHeap::DmaHeap(DmaHeapFlags flags) { - for (const char *name : heapNames) { - int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); -- if (ret < 0) { -- ret = errno; -- LOG(DmaHeap, Debug) << "Failed to open " << name << ": " -- << strerror(ret); -- continue; -- } -+ int ret; - -- dmaHeapHandle_ = UniqueFD(ret); -- break; -+ for (const auto &name : heapNames) { -+ if (flags & name.first) { -+ ret = ::open(name.second, O_RDWR | O_CLOEXEC, 0); -+ if (ret < 0) { -+ ret = errno; -+ LOG(DmaHeap, Debug) << "Failed to open " << name.second << ": " -+ << strerror(ret); -+ continue; -+ } ++ for (const auto &info : heapInfos) { ++ if (!(flags & info.flag)) ++ continue; + -+ LOG(DmaHeap, Debug) << "Using " << name.second; -+ dmaHeapHandle_ = UniqueFD(ret); -+ break; -+ } - } ++ int ret = ::open(info.name, O_RDWR | O_CLOEXEC, 0); + if (ret < 0) { + ret = errno; + LOG(DmaHeap, Debug) +- << "Failed to open " << name << ": " ++ << "Failed to open " << info.name << ": " + << strerror(ret); + continue; + } - if (!dmaHeapHandle_.isValid()) ++ LOG(DmaHeap, Debug) << "Using " << info.name; + dmaHeapHandle_ = UniqueFD(ret); + break; + } -- -2.43.0 +2.43.2 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 index 022270723a..48f10aa47a 100644 --- 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 @@ -1,21 +1,23 @@ -From 006a4a31a6803e92ec67f48b66da2cdff8b2f6ab Mon Sep 17 00:00:00 2001 +From a6777760a2121f02808baecea504ac0e242f860b Mon Sep 17 00:00:00 2001 From: Andrey Konovalov -Date: Sun, 29 Oct 2023 15:56:48 +0300 -Subject: [PATCH 04/25] libcamera: internal: Move SharedMemObject class to a +Date: Mon, 11 Mar 2024 15:15:08 +0100 +Subject: [PATCH 04/21] libcamera: internal: Move SharedMemObject class to a common directory Move SharedMemObject class out of RPi namespace and put it into include/libcamera/internal so that everyone could use it. -Signed-off-by: Andrey Konovalov -Signed-off-by: Hans de Goede Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +Reviewed-by: Milan Zamazal --- - include/libcamera/internal/meson.build | 1 + - .../common => include/libcamera/internal}/shared_mem_object.h | 4 ---- - 2 files changed, 1 insertion(+), 4 deletions(-) - rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (98%) + include/libcamera/internal/meson.build | 1 + + .../libcamera/internal}/shared_mem_object.h | 6 +----- + 2 files changed, 2 insertions(+), 5 deletions(-) + rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (97%) diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 33eb0fb3..5807dfd9 100644 @@ -30,12 +32,22 @@ index 33eb0fb3..5807dfd9 100644 'sysfs.h', 'v4l2_device.h', diff --git a/src/libcamera/pipeline/rpi/common/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h -similarity index 98% +similarity index 97% rename from src/libcamera/pipeline/rpi/common/shared_mem_object.h rename to include/libcamera/internal/shared_mem_object.h -index aa56c220..bfb639ee 100644 +index aa56c220..98636b44 100644 --- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h +++ b/include/libcamera/internal/shared_mem_object.h +@@ -6,8 +6,8 @@ + */ + #pragma once + +-#include + #include ++#include + #include + #include + #include @@ -19,8 +19,6 @@ namespace libcamera { @@ -53,5 +65,5 @@ index aa56c220..bfb639ee 100644 - } /* namespace libcamera */ -- -2.43.0 +2.43.2 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 deleted file mode 100644 index a20d27059d..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch +++ /dev/null @@ -1,139 +0,0 @@ -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/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch b/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch new file mode 100644 index 0000000000..d2143febf7 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch @@ -0,0 +1,403 @@ +From f94af21adc1889706127d07c5425f44c9cec9a95 Mon Sep 17 00:00:00 2001 +From: Andrei Konovalov +Date: Mon, 11 Mar 2024 15:15:09 +0100 +Subject: [PATCH 05/21] libcamera: shared_mem_object: reorganize the code and + document the SharedMemObject class + +Split the parts which doesn't otherwise depend on the type T or +arguments Args out of the SharedMemObject class into a new +SharedMem class. + +Doxygen documentation by Dennis Bonke and Andrei Konovalov. + +Reviewed-by: Pavel Machek +Co-developed-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Signed-off-by: Andrei Konovalov +Signed-off-by: Hans de Goede +Reviewed-by: Milan Zamazal +--- + .../libcamera/internal/shared_mem_object.h | 101 ++++++---- + src/libcamera/meson.build | 1 + + src/libcamera/shared_mem_object.cpp | 190 ++++++++++++++++++ + 3 files changed, 253 insertions(+), 39 deletions(-) + create mode 100644 src/libcamera/shared_mem_object.cpp + +diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h +index 98636b44..43b07c9d 100644 +--- a/include/libcamera/internal/shared_mem_object.h ++++ b/include/libcamera/internal/shared_mem_object.h +@@ -6,12 +6,9 @@ + */ + #pragma once + +-#include + #include + #include + #include +-#include +-#include + #include + + #include +@@ -19,58 +16,92 @@ + + namespace libcamera { + ++class SharedMem ++{ ++public: ++ SharedMem() ++ : mem_(nullptr) ++ { ++ } ++ ++ SharedMem(const std::string &name, std::size_t size); ++ ++ SharedMem(SharedMem &&rhs) ++ { ++ this->name_ = std::move(rhs.name_); ++ this->fd_ = std::move(rhs.fd_); ++ this->mem_ = rhs.mem_; ++ rhs.mem_ = nullptr; ++ } ++ ++ virtual ~SharedMem() ++ { ++ if (mem_) ++ munmap(mem_, size_); ++ } ++ ++ /* Make SharedMem non-copyable for now. */ ++ LIBCAMERA_DISABLE_COPY(SharedMem) ++ ++ SharedMem &operator=(SharedMem &&rhs) ++ { ++ this->name_ = std::move(rhs.name_); ++ this->fd_ = std::move(rhs.fd_); ++ this->mem_ = rhs.mem_; ++ rhs.mem_ = nullptr; ++ return *this; ++ } ++ ++ const SharedFD &fd() const ++ { ++ return fd_; ++ } ++ ++ void *mem() const ++ { ++ return mem_; ++ } ++ ++private: ++ std::string name_; ++ SharedFD fd_; ++ size_t size_; ++protected: ++ void *mem_; ++}; ++ + template +-class SharedMemObject ++class SharedMemObject : public SharedMem + { + public: + static constexpr std::size_t SIZE = sizeof(T); + + SharedMemObject() +- : obj_(nullptr) ++ : SharedMem(), obj_(nullptr) + { + } + + template + SharedMemObject(const std::string &name, Args &&...args) +- : name_(name), obj_(nullptr) ++ : SharedMem(name, SIZE), obj_(nullptr) + { +- void *mem; +- int ret; +- +- ret = memfd_create(name_.c_str(), MFD_CLOEXEC); +- if (ret < 0) +- return; +- +- fd_ = SharedFD(std::move(ret)); +- if (!fd_.isValid()) +- return; +- +- ret = ftruncate(fd_.get(), SIZE); +- if (ret < 0) ++ if (mem_ == nullptr) + return; + +- mem = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, +- fd_.get(), 0); +- if (mem == MAP_FAILED) +- return; +- +- obj_ = new (mem) T(std::forward(args)...); ++ obj_ = new (mem_) T(std::forward(args)...); + } + + SharedMemObject(SharedMemObject &&rhs) ++ : SharedMem(std::move(rhs)) + { +- this->name_ = std::move(rhs.name_); +- this->fd_ = std::move(rhs.fd_); + this->obj_ = rhs.obj_; + rhs.obj_ = nullptr; + } + + ~SharedMemObject() + { +- if (obj_) { ++ if (obj_) + obj_->~T(); +- munmap(obj_, SIZE); +- } + } + + /* Make SharedMemObject non-copyable for now. */ +@@ -78,8 +109,7 @@ public: + + SharedMemObject &operator=(SharedMemObject &&rhs) + { +- this->name_ = std::move(rhs.name_); +- this->fd_ = std::move(rhs.fd_); ++ SharedMem::operator=(std::move(rhs)); + this->obj_ = rhs.obj_; + rhs.obj_ = nullptr; + return *this; +@@ -105,19 +135,12 @@ public: + return *obj_; + } + +- const SharedFD &fd() const +- { +- return fd_; +- } +- + explicit operator bool() const + { + return !!obj_; + } + + private: +- std::string name_; +- SharedFD fd_; + T *obj_; + }; + +diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build +index 3c5e43df..94a95ae3 100644 +--- a/src/libcamera/meson.build ++++ b/src/libcamera/meson.build +@@ -41,6 +41,7 @@ libcamera_sources = files([ + 'process.cpp', + 'pub_key.cpp', + 'request.cpp', ++ 'shared_mem_object.cpp', + 'source_paths.cpp', + 'stream.cpp', + 'sysfs.cpp', +diff --git a/src/libcamera/shared_mem_object.cpp b/src/libcamera/shared_mem_object.cpp +new file mode 100644 +index 00000000..44fe74c2 +--- /dev/null ++++ b/src/libcamera/shared_mem_object.cpp +@@ -0,0 +1,190 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Raspberry Pi Ltd ++ * ++ * shared_mem_object.cpp - Helper class for shared memory allocations ++ */ ++ ++#include "libcamera/internal/shared_mem_object.h" ++ ++#include ++#include ++ ++/** ++ * \file shared_mem_object.cpp ++ * \brief Helper class for shared memory allocations ++ */ ++ ++namespace libcamera { ++ ++/** ++ * \class SharedMem ++ * \brief Helper class for allocating shared memory ++ * ++ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries. ++ * ++ * SharedMem allocates the shared memory of the given size and maps it. ++ * To check that the shared memory was allocated and mapped successfully, one ++ * needs to verify that the pointer to the shared memory returned by SharedMem::mem() ++ * is not nullptr. ++ * ++ * To access the shared memory from another process the SharedFD should be passed ++ * to that process, and then the shared memory should be mapped into that process ++ * address space by calling mmap(). ++ * ++ * A single memfd is created for every SharedMem. If there is a need to allocate ++ * a large number of objects in shared memory, these objects should be grouped ++ * together and use the shared memory allocated by a single SharedMem object if ++ * possible. This will help to minimize the number of created memfd's. ++ */ ++ ++/** ++ * \fn SharedMem::SharedMem(const std::string &name, std::size_t size) ++ * \brief Constructor for the SharedMem ++ * \param[in] name Name of the SharedMem ++ * \param[in] size Size of the shared memory to allocate and map ++ */ ++ ++/** ++ * \fn SharedMem::SharedMem(SharedMem &&rhs) ++ * \brief Move constructor for SharedMem ++ * \param[in] rhs The object to move ++ */ ++ ++/** ++ * \fn SharedMem::~SharedMem() ++ * \brief SharedMem destructor ++ * ++ * Unmaps the allocated shared memory. Decrements the shared memory descriptor use ++ * count. ++ */ ++ ++/** ++ * \fn SharedMem &SharedMem::operator=(SharedMem &&rhs) ++ * \brief Move constructor for SharedMem ++ * \param[in] rhs The object to move ++ */ ++ ++/** ++ * \fn const SharedFD &SharedMem::fd() const ++ * \brief Gets the file descriptor for the underlying shared memory ++ * \return The file descriptor ++ */ ++ ++/** ++ * \fn void *SharedMem::mem() const ++ * \brief Gets the pointer to the underlying shared memory ++ * \return The pointer to the shared memory ++ */ ++ ++SharedMem::SharedMem(const std::string &name, std::size_t size) ++ : name_(name), size_(size), mem_(nullptr) ++{ ++ int fd = memfd_create(name_.c_str(), MFD_CLOEXEC); ++ if (fd < 0) ++ return; ++ ++ fd_ = SharedFD(std::move(fd)); ++ if (!fd_.isValid()) ++ return; ++ ++ if (ftruncate(fd_.get(), size_) < 0) ++ return; ++ ++ mem_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, ++ fd_.get(), 0); ++ if (mem_ == MAP_FAILED) ++ mem_ = nullptr; ++} ++ ++/** ++ * \var SharedMem::mem_ ++ * \brief Pointer to the shared memory allocated ++ */ ++ ++/** ++ * \class SharedMemObject ++ * \brief Helper class for allocating objects in shared memory ++ * ++ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries. ++ * ++ * Given the type of the object to be created in shared memory and the arguments ++ * to pass to this object's constructor, SharedMemObject allocates the shared memory ++ * of the size of the object and constructs the object in this memory. To ensure ++ * that the SharedMemObject was created successfully, one needs to verify that the ++ * overloaded bool() operator returns true. The object created in the shared memory ++ * can be accessed using the SharedMemObject::operator*() indirection operator. Its ++ * members can be accessed with the SharedMemObject::operator->() member of pointer ++ * operator. ++ * ++ * To access the object from another process the SharedFD should be passed to that ++ * process, and the shared memory should be mapped by calling mmap(). ++ * ++ * A single memfd is created for every SharedMemObject. If there is a need to allocate ++ * a large number of objects in shared memory, these objects should be grouped into a ++ * single large object to keep the number of created memfd's reasonably small. ++ */ ++ ++/** ++ * \var SharedMemObject::SIZE ++ * \brief The size of the object that is going to be stored here ++ */ ++ ++/** ++ * \fn SharedMemObject< T >::SharedMemObject(const std::string &name, Args &&...args) ++ * \brief Constructor for the SharedMemObject ++ * \param[in] name Name of the SharedMemObject ++ * \param[in] args Args to pass to the constructor of the object in shared memory ++ */ ++ ++/** ++ * \fn SharedMemObject::SharedMemObject(SharedMemObject &&rhs) ++ * \brief Move constructor for SharedMemObject ++ * \param[in] rhs The object to move ++ */ ++ ++/** ++ * \fn SharedMemObject::~SharedMemObject() ++ * \brief SharedMemObject destructor ++ * ++ * Destroys the object created in the shared memory and then unmaps the shared memory. ++ * Decrements the shared memory descriptor use count. ++ */ ++ ++/** ++ * \fn SharedMemObject::operator=(SharedMemObject &&rhs) ++ * \brief Operator= for SharedMemObject ++ * \param[in] rhs The SharedMemObject object to take the data from ++ */ ++ ++/** ++ * \fn SharedMemObject::operator->() ++ * \brief Operator-> for SharedMemObject ++ * \return The pointer to the object ++ */ ++ ++/** ++ * \fn const T *SharedMemObject::operator->() const ++ * \brief Operator-> for SharedMemObject ++ * \return The pointer to the const object ++ */ ++ ++/** ++ * \fn SharedMemObject::operator*() ++ * \brief Operator* for SharedMemObject ++ * \return The reference to the object ++ */ ++ ++/** ++ * \fn const T &SharedMemObject::operator*() const ++ * \brief Operator* for SharedMemObject ++ * \return Const reference to the object ++ */ ++ ++/** ++ * \fn SharedMemObject::operator bool() ++ * \brief Operator bool() for SharedMemObject ++ * \return True if the object was created OK in the shared memory, false otherwise ++ */ ++ ++} // namespace libcamera +-- +2.43.2 + 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 deleted file mode 100644 index ebda98d267..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch +++ /dev/null @@ -1,354 +0,0 @@ -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/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch new file mode 100644 index 0000000000..9f80b69f16 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch @@ -0,0 +1,523 @@ +From 4259b01930333c6666a185d923e6e68ec915a4fd Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Mar 2024 15:15:10 +0100 +Subject: [PATCH 06/21] libcamera: software_isp: Add SwStatsCpu class + +Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use. + +This implementation offers a configure function + functions to gather +statistics on a line by line basis. This allows CPU based software +debayering to call into interlace debayering and statistics gathering +on a line by line bases while the input data is still hot in the cache. + +This implementation also allows specifying a window over which to gather +statistics instead of processing the whole frame. + +Doxygen documentation by Dennis Bonke. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Reviewed-by: Milan Zamazal +Co-developed-by: Andrey Konovalov +Signed-off-by: Andrey Konovalov +Co-developed-by: Pavel Machek +Signed-off-by: Pavel Machek +Co-developed-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Co-developed-by: Marttico +Signed-off-by: Marttico +Co-developed-by: Toon Langendam +Signed-off-by: Toon Langendam +Signed-off-by: Hans de Goede +--- + include/libcamera/internal/meson.build | 1 + + .../internal/software_isp/meson.build | 5 + + .../internal/software_isp/swisp_stats.h | 38 ++++ + src/libcamera/meson.build | 1 + + src/libcamera/software_isp/meson.build | 12 + + src/libcamera/software_isp/swstats_cpu.cpp | 208 ++++++++++++++++++ + src/libcamera/software_isp/swstats_cpu.h | 159 +++++++++++++ + 7 files changed, 424 insertions(+) + create mode 100644 include/libcamera/internal/software_isp/meson.build + create mode 100644 include/libcamera/internal/software_isp/swisp_stats.h + create mode 100644 src/libcamera/software_isp/meson.build + create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp + create mode 100644 src/libcamera/software_isp/swstats_cpu.h + +diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build +index 5807dfd9..160fdc37 100644 +--- a/include/libcamera/internal/meson.build ++++ b/include/libcamera/internal/meson.build +@@ -50,3 +50,4 @@ libcamera_internal_headers = files([ + ]) + + subdir('converter') ++subdir('software_isp') +diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build +new file mode 100644 +index 00000000..66c9c3fb +--- /dev/null ++++ b/include/libcamera/internal/software_isp/meson.build +@@ -0,0 +1,5 @@ ++# SPDX-License-Identifier: CC0-1.0 ++ ++libcamera_internal_headers += files([ ++ 'swisp_stats.h', ++]) +diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h +new file mode 100644 +index 00000000..afe42c9a +--- /dev/null ++++ b/include/libcamera/internal/software_isp/swisp_stats.h +@@ -0,0 +1,38 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * ++ * swisp_stats.h - Statistics data format used by the software ISP and software IPA ++ */ ++ ++#pragma once ++ ++namespace libcamera { ++ ++/** ++ * \brief Struct that holds the statistics for the Software ISP. ++ */ ++struct SwIspStats { ++ /** ++ * \brief Holds the sum of all sampled red pixels. ++ */ ++ unsigned long sumR_; ++ /** ++ * \brief Holds the sum of all sampled green pixels. ++ */ ++ unsigned long sumG_; ++ /** ++ * \brief Holds the sum of all sampled blue pixels. ++ */ ++ unsigned long sumB_; ++ /** ++ * \brief Number of bins in the yHistogram. ++ */ ++ static constexpr unsigned int kYHistogramSize = 16; ++ /** ++ * \brief A histogram of luminance values. ++ */ ++ std::array yHistogram; ++}; ++ ++} /* namespace libcamera */ +diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build +index 94a95ae3..91e4cc60 100644 +--- a/src/libcamera/meson.build ++++ b/src/libcamera/meson.build +@@ -71,6 +71,7 @@ subdir('converter') + subdir('ipa') + subdir('pipeline') + subdir('proxy') ++subdir('software_isp') + + null_dep = dependency('', required : false) + +diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build +new file mode 100644 +index 00000000..fcfff74a +--- /dev/null ++++ b/src/libcamera/software_isp/meson.build +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: CC0-1.0 ++ ++softisp_enabled = pipelines.contains('simple') ++summary({'SoftISP support' : softisp_enabled}, section : 'Configuration') ++ ++if not (softisp_enabled) ++ subdir_done() ++endif ++ ++libcamera_sources += files([ ++ 'swstats_cpu.cpp', ++]) +diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp +new file mode 100644 +index 00000000..448d0e4c +--- /dev/null ++++ b/src/libcamera/software_isp/swstats_cpu.cpp +@@ -0,0 +1,208 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * swstats_cpu.cpp - CPU based software statistics implementation ++ */ ++ ++#include "swstats_cpu.h" ++ ++#include ++ ++#include ++ ++#include "libcamera/internal/bayer_format.h" ++ ++namespace libcamera { ++ ++/** ++ * \class SwStatsCpu ++ * \brief Class for gathering statistics on the CPU ++ * ++ * CPU based software ISP statistics implementation. ++ * ++ * This class offers a configure function + functions to gather statistics ++ * on a line by line basis. This allows CPU based software debayering to ++ * interlace debayering and statistics gathering on a line by line basis ++ * while the input data is still hot in the cache. ++ * ++ * It is also possible to specify a window over which to gather ++ * statistics instead of processing the whole frame. ++ */ ++ ++LOG_DEFINE_CATEGORY(SwStatsCpu) ++ ++SwStatsCpu::SwStatsCpu() ++{ ++ sharedStats_ = SharedMemObject("softIsp_stats"); ++ if (!sharedStats_.fd().isValid()) ++ LOG(SwStatsCpu, Error) ++ << "Failed to create shared memory for statistics"; ++} ++ ++static const unsigned int kRedYMul = 77; /* 0.299 * 256 */ ++static const unsigned int kGreenYMul = 150; /* 0.587 * 256 */ ++static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */ ++ ++#define SWSTATS_START_LINE_STATS(pixel_t) \ ++ pixel_t r, g, g2, b; \ ++ unsigned int yVal; \ ++ \ ++ unsigned int sumR = 0; \ ++ unsigned int sumG = 0; \ ++ unsigned int sumB = 0; ++ ++#define SWSTATS_ACCUMULATE_LINE_STATS(div) \ ++ sumR += r; \ ++ sumG += g; \ ++ sumB += b; \ ++ \ ++ yVal = r * kRedYMul; \ ++ yVal += g * kGreenYMul; \ ++ yVal += b * kBlueYMul; \ ++ stats_.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++; ++ ++#define SWSTATS_FINISH_LINE_STATS() \ ++ stats_.sumR_ += sumR; \ ++ stats_.sumG_ += sumG; \ ++ stats_.sumB_ += sumB; ++ ++void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[]) ++{ ++ const uint8_t *src0 = src[1] + window_.x * 5 / 4; ++ const uint8_t *src1 = src[2] + window_.x * 5 / 4; ++ const int widthInBytes = window_.width * 5 / 4; ++ ++ if (swapLines_) ++ std::swap(src0, src1); ++ ++ SWSTATS_START_LINE_STATS(uint8_t) ++ ++ /* x += 5 sample every other 2x2 block */ ++ for (int x = 0; x < widthInBytes; x += 5) { ++ /* BGGR */ ++ b = src0[x]; ++ g = src0[x + 1]; ++ g2 = src1[x]; ++ r = src1[x + 1]; ++ g = (g + g2) / 2; ++ /* Data is already 8 bits, divide by 1 */ ++ SWSTATS_ACCUMULATE_LINE_STATS(1) ++ } ++ ++ SWSTATS_FINISH_LINE_STATS() ++} ++ ++void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[]) ++{ ++ const uint8_t *src0 = src[1] + window_.x * 5 / 4; ++ const uint8_t *src1 = src[2] + window_.x * 5 / 4; ++ const int widthInBytes = window_.width * 5 / 4; ++ ++ if (swapLines_) ++ std::swap(src0, src1); ++ ++ SWSTATS_START_LINE_STATS(uint8_t) ++ ++ /* x += 5 sample every other 2x2 block */ ++ for (int x = 0; x < widthInBytes; x += 5) { ++ /* GBRG */ ++ g = src0[x]; ++ b = src0[x + 1]; ++ r = src1[x]; ++ g2 = src1[x + 1]; ++ g = (g + g2) / 2; ++ /* Data is already 8 bits, divide by 1 */ ++ SWSTATS_ACCUMULATE_LINE_STATS(1) ++ } ++ ++ SWSTATS_FINISH_LINE_STATS() ++} ++ ++/** ++ * \brief Reset state to start statistics gathering for a new frame. ++ * ++ * This may only be called after a successful setWindow() call. ++ */ ++void SwStatsCpu::startFrame(void) ++{ ++ stats_.sumR_ = 0; ++ stats_.sumB_ = 0; ++ stats_.sumG_ = 0; ++ stats_.yHistogram.fill(0); ++} ++ ++/** ++ * \brief Finish statistics calculation for the current frame. ++ * ++ * This may only be called after a successful setWindow() call. ++ */ ++void SwStatsCpu::finishFrame(void) ++{ ++ *sharedStats_ = stats_; ++ statsReady.emit(0); ++} ++ ++/** ++ * \brief Configure the statistics object for the passed in input format. ++ * \param[in] inputCfg The input format ++ * ++ * \return 0 on success, a negative errno value on failure ++ */ ++int SwStatsCpu::configure(const StreamConfiguration &inputCfg) ++{ ++ BayerFormat bayerFormat = ++ BayerFormat::fromPixelFormat(inputCfg.pixelFormat); ++ ++ if (bayerFormat.bitDepth == 10 && ++ bayerFormat.packing == BayerFormat::Packing::CSI2) { ++ patternSize_.height = 2; ++ patternSize_.width = 4; /* 5 bytes per *4* pixels */ ++ /* Skip every 3th and 4th line, sample every other 2x2 block */ ++ ySkipMask_ = 0x02; ++ xShift_ = 0; ++ ++ switch (bayerFormat.order) { ++ case BayerFormat::BGGR: ++ case BayerFormat::GRBG: ++ stats0_ = &SwStatsCpu::statsBGGR10PLine0; ++ swapLines_ = bayerFormat.order == BayerFormat::GRBG; ++ return 0; ++ case BayerFormat::GBRG: ++ case BayerFormat::RGGB: ++ stats0_ = &SwStatsCpu::statsGBRG10PLine0; ++ swapLines_ = bayerFormat.order == BayerFormat::RGGB; ++ return 0; ++ default: ++ break; ++ } ++ } ++ ++ LOG(SwStatsCpu, Info) ++ << "Unsupported input format " << inputCfg.pixelFormat.toString(); ++ return -EINVAL; ++} ++ ++/** ++ * \brief Specify window coordinates over which to gather statistics. ++ * \param[in] window The window object. ++ */ ++void SwStatsCpu::setWindow(Rectangle window) ++{ ++ window_ = window; ++ ++ window_.x &= ~(patternSize_.width - 1); ++ window_.x += xShift_; ++ window_.y &= ~(patternSize_.height - 1); ++ ++ /* width_ - xShift_ to make sure the window fits */ ++ window_.width -= xShift_; ++ window_.width &= ~(patternSize_.width - 1); ++ window_.height &= ~(patternSize_.height - 1); ++} ++ ++} /* namespace libcamera */ +diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h +new file mode 100644 +index 00000000..0ac9ae71 +--- /dev/null ++++ b/src/libcamera/software_isp/swstats_cpu.h +@@ -0,0 +1,159 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * swstats_cpu.h - CPU based software statistics implementation ++ */ ++ ++#pragma once ++ ++#include ++ ++#include ++ ++#include ++ ++#include "libcamera/internal/shared_mem_object.h" ++#include "libcamera/internal/software_isp/swisp_stats.h" ++ ++namespace libcamera { ++ ++class PixelFormat; ++struct StreamConfiguration; ++ ++class SwStatsCpu ++{ ++public: ++ SwStatsCpu(); ++ ~SwStatsCpu() = default; ++ ++ /** ++ * \brief Gets whether the statistics object is valid. ++ * ++ * \return true if it's valid, false otherwise ++ */ ++ bool isValid() const { return sharedStats_.fd().isValid(); } ++ ++ /** ++ * \brief Get the file descriptor for the statistics. ++ * ++ * \return the file descriptor ++ */ ++ const SharedFD &getStatsFD() { return sharedStats_.fd(); } ++ ++ /** ++ * \brief Get the pattern size. ++ * ++ * For some input-formats, e.g. Bayer data, processing is done multiple lines ++ * and/or columns at a time. Get width and height at which the (bayer) pattern ++ * repeats. Window values are rounded down to a multiple of this and the height ++ * also indicates if processLine2() should be called or not. ++ * This may only be called after a successful configure() call. ++ * ++ * \return the pattern size ++ */ ++ const Size &patternSize() { return patternSize_; } ++ ++ int configure(const StreamConfiguration &inputCfg); ++ void setWindow(Rectangle window); ++ void startFrame(); ++ void finishFrame(); ++ ++ /** ++ * \brief Process line 0. ++ * \param[in] y The y coordinate. ++ * \param[in] src The input data. ++ * ++ * This function processes line 0 for input formats with patternSize height == 1. ++ * It'll process line 0 and 1 for input formats with patternSize height >= 2. ++ * This function may only be called after a successful setWindow() call. ++ */ ++ void processLine0(unsigned int y, const uint8_t *src[]) ++ { ++ if ((y & ySkipMask_) || y < (unsigned int)window_.y || ++ y >= (window_.y + window_.height)) ++ return; ++ ++ (this->*stats0_)(src); ++ } ++ ++ /** ++ * \brief Process line 2 and 3. ++ * \param[in] y The y coordinate. ++ * \param[in] src The input data. ++ * ++ * This function processes line 2 and 3 for input formats with patternSize height == 4. ++ * This function may only be called after a successful setWindow() call. ++ */ ++ void processLine2(unsigned int y, const uint8_t *src[]) ++ { ++ if ((y & ySkipMask_) || y < (unsigned int)window_.y || ++ y >= (window_.y + window_.height)) ++ return; ++ ++ (this->*stats2_)(src); ++ } ++ ++ /** ++ * \brief Signals that the statistics are ready. ++ * ++ * The int parameter isn't actually used. ++ */ ++ Signal statsReady; ++ ++private: ++ /** ++ * \brief Called when there is data to get statistics from. ++ * \param[in] src The input data ++ * ++ * These functions take an array of (patternSize_.height + 1) src ++ * pointers each pointing to a line in the source image. The middle ++ * element of the array will point to the actual line being processed. ++ * Earlier element(s) will point to the previous line(s) and later ++ * element(s) to the next line(s). ++ * ++ * See the documentation of DebayerCpu::debayerFn for more details. ++ */ ++ using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]); ++ ++ void statsBGGR10PLine0(const uint8_t *src[]); ++ void statsGBRG10PLine0(const uint8_t *src[]); ++ ++ /* Variables set by configure(), used every line */ ++ statsProcessFn stats0_; ++ statsProcessFn stats2_; ++ bool swapLines_; ++ ++ /** ++ * \brief Skip lines where this bitmask is set in y. ++ */ ++ unsigned int ySkipMask_; ++ ++ /** ++ * \brief Statistics window, set by setWindow(), used ever line. ++ */ ++ Rectangle window_; ++ ++ /** ++ * \brief The size of the bayer pattern. ++ * ++ * Valid sizes are: 2x2, 4x2 or 4x4. ++ */ ++ Size patternSize_; ++ ++ /** ++ * \brief The offset of x, applied to window_.x for bayer variants. ++ * ++ * This can either be 0 or 1. ++ */ ++ unsigned int xShift_; ++ ++ SharedMemObject sharedStats_; ++ SwIspStats stats_; ++}; ++ ++} /* namespace libcamera */ +-- +2.43.2 + diff --git a/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch b/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch new file mode 100644 index 0000000000..7c71709896 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch @@ -0,0 +1,255 @@ +From 25e6893e46bd2174f6913eea79817988d9280706 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Mar 2024 15:15:11 +0100 +Subject: [PATCH 07/21] libcamera: software_isp: Add Debayer base class + +Add a base class for debayer implementations. This is intended to be +suitable for both GPU (or otherwise) accelerated debayer implementations +as well as CPU based debayering. + +Doxygen documentation by Dennis Bonke. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Reviewed-by: Milan Zamazal +Co-developed-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Co-developed-by: Andrey Konovalov +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +--- + .../internal/software_isp/debayer_params.h | 48 ++++++++ + .../internal/software_isp/meson.build | 1 + + src/libcamera/software_isp/debayer.cpp | 29 +++++ + src/libcamera/software_isp/debayer.h | 104 ++++++++++++++++++ + src/libcamera/software_isp/meson.build | 1 + + 5 files changed, 183 insertions(+) + create mode 100644 include/libcamera/internal/software_isp/debayer_params.h + create mode 100644 src/libcamera/software_isp/debayer.cpp + create mode 100644 src/libcamera/software_isp/debayer.h + +diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h +new file mode 100644 +index 00000000..98965fa1 +--- /dev/null ++++ b/include/libcamera/internal/software_isp/debayer_params.h +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * debayer_params.h - DebayerParams header ++ */ ++ ++#pragma once ++ ++namespace libcamera { ++ ++/** ++ * \brief Struct to hold the debayer parameters. ++ */ ++struct DebayerParams { ++ /** ++ * \brief const value for 1.0 gain ++ */ ++ static constexpr unsigned int kGain10 = 256; ++ ++ /** ++ * \brief Red Gain ++ * ++ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc. ++ */ ++ unsigned int gainR; ++ /** ++ * \brief Green Gain ++ * ++ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc. ++ */ ++ unsigned int gainG; ++ /** ++ * \brief Blue Gain ++ * ++ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc. ++ */ ++ unsigned int gainB; ++ /** ++ * \brief Gamma correction, 1.0 is no correction ++ */ ++ float gamma; ++}; ++ ++} /* namespace libcamera */ +diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build +index 66c9c3fb..a620e16d 100644 +--- a/include/libcamera/internal/software_isp/meson.build ++++ b/include/libcamera/internal/software_isp/meson.build +@@ -1,5 +1,6 @@ + # SPDX-License-Identifier: CC0-1.0 + + libcamera_internal_headers += files([ ++ 'debayer_params.h', + 'swisp_stats.h', + ]) +diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp +new file mode 100644 +index 00000000..64f0b5a0 +--- /dev/null ++++ b/src/libcamera/software_isp/debayer.cpp +@@ -0,0 +1,29 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * debayer.cpp - debayer base class ++ */ ++ ++#include "debayer.h" ++ ++namespace libcamera { ++ ++/** ++ * \class Debayer ++ * \brief Base debayering class ++ * ++ * Base class that provides functions for setting up the debayering process. ++ */ ++ ++LOG_DEFINE_CATEGORY(Debayer) ++ ++Debayer::~Debayer() ++{ ++} ++ ++} /* namespace libcamera */ +diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h +new file mode 100644 +index 00000000..8880ff99 +--- /dev/null ++++ b/src/libcamera/software_isp/debayer.h +@@ -0,0 +1,104 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * 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 ++{ ++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. ++ * ++ * Valid sizes are: 2x2, 4x2 or 4x4. ++ * ++ * \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. ++ */ ++ virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0; ++ ++ /** ++ * \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/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build +index fcfff74a..62095f61 100644 +--- a/src/libcamera/software_isp/meson.build ++++ b/src/libcamera/software_isp/meson.build +@@ -8,5 +8,6 @@ if not (softisp_enabled) + endif + + libcamera_sources += files([ ++ 'debayer.cpp', + 'swstats_cpu.cpp', + ]) +-- +2.43.2 + 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 deleted file mode 100644 index 9d6f2ac07f..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-SwStats-base-class.patch +++ /dev/null @@ -1,382 +0,0 @@ -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-DebayerCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch new file mode 100644 index 0000000000..f549769f2f --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch @@ -0,0 +1,825 @@ +From 5f57a52ea1054cac73344d83ff605cba0df0d279 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Mar 2024 15:15:12 +0100 +Subject: [PATCH 08/21] libcamera: software_isp: Add DebayerCpu class + +Add CPU based debayering implementation. This initial implementation +only supports debayering packed 10 bits per pixel bayer data in +the 4 standard bayer orders. + +Doxygen documentation by Dennis Bonke. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Co-developed-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Co-developed-by: Andrey Konovalov +Signed-off-by: Andrey Konovalov +Co-developed-by: Pavel Machek +Signed-off-by: Pavel Machek +Signed-off-by: Hans de Goede +Reviewed-by: Milan Zamazal +--- + src/libcamera/software_isp/debayer_cpu.cpp | 626 +++++++++++++++++++++ + src/libcamera/software_isp/debayer_cpu.h | 143 +++++ + src/libcamera/software_isp/meson.build | 1 + + 3 files changed, 770 insertions(+) + create mode 100644 src/libcamera/software_isp/debayer_cpu.cpp + create mode 100644 src/libcamera/software_isp/debayer_cpu.h + +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +new file mode 100644 +index 00000000..f932362c +--- /dev/null ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -0,0 +1,626 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * debayer_cpu.cpp - CPU based debayering class ++ */ ++ ++#include "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 { ++ ++/** ++ * \class DebayerCpu ++ * \brief Class for debayering on the CPU ++ * ++ * Implementation for CPU based debayering ++ */ ++ ++/** ++ * \brief Constructs a DebayerCpu object. ++ * \param[in] stats Pointer to the stats object to use. ++ */ ++DebayerCpu::DebayerCpu(std::unique_ptr 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 (unsigned int i = 0; i < kGammaLookupSize; i++) ++ gamma_[i] = i / (kGammaLookupSize / kRGBLookupSize); ++ ++ for (unsigned int i = 0; i < kMaxLineBuffers; i++) ++ lineBuffers_[i] = nullptr; ++} ++ ++DebayerCpu::~DebayerCpu() ++{ ++ for (unsigned int i = 0; i < kMaxLineBuffers; 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. ++ */ ++ for (int x = 0; x < width_in_bytes;) { ++ /* First pixel */ ++ BGGR_BGR888(2, 1, 1) ++ /* Second pixel BGGR -> GBRG */ ++ GBRG_BGR888(1, 1, 1) ++ /* Same thing for third and fourth pixels */ ++ BGGR_BGR888(1, 1, 1) ++ GBRG_BGR888(1, 2, 1) ++ /* Skip 5th src byte with 4 x 2 least-significant-bits */ ++ x++; ++ } ++} ++ ++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;) { ++ /* First pixel */ ++ GRBG_BGR888(2, 1, 1) ++ /* Second pixel GRBG -> RGGB */ ++ RGGB_BGR888(1, 1, 1) ++ /* Same thing for third and fourth pixels */ ++ GRBG_BGR888(1, 1, 1) ++ RGGB_BGR888(1, 2, 1) ++ /* Skip 5th src byte with 4 x 2 least-significant-bits */ ++ x++; ++ } ++} ++ ++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;) { ++ /* 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) ++ /* Skip 5th src byte with 4 x 2 least-significant-bits */ ++ x++; ++ } ++} ++ ++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;) { ++ /* 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) ++ /* Skip 5th src byte with 4 x 2 least-significant-bits */ ++ x++; ++ } ++} ++ ++static bool isStandardBayerOrder(BayerFormat::Order order) ++{ ++ return order == BayerFormat::BGGR || order == BayerFormat::GBRG || ++ order == BayerFormat::GRBG || order == BayerFormat::RGGB; ++} ++ ++/* ++ * Setup the Debayer object according to the passed in parameters. ++ * Return 0 on success, a negative errno value on failure ++ * (unsupported parameters). ++ */ ++int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config) ++{ ++ BayerFormat bayerFormat = ++ 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())); ++ ++ /* pad with patternSize.Width on both left and right side */ ++ lineBufferPadding_ = inputConfig_.patternSize.width * inputConfig_.bpp / 8; ++ lineBufferLength_ = window_.width * inputConfig_.bpp / 8 + ++ 2 * lineBufferPadding_; ++ for (unsigned int i = 0; ++ i < (inputConfig_.patternSize.height + 1) && enableInputMemcpy_; ++ i++) { ++ free(lineBuffers_[i]); ++ lineBuffers_[i] = (uint8_t *)malloc(lineBufferLength_); ++ if (!lineBuffers_[i]) ++ return -ENOMEM; ++ } ++ ++ measuredFrames_ = 0; ++ frameProcessTime_ = 0; ++ ++ return 0; ++} ++ ++/* ++ * Get width and height at which the bayer-pattern repeats. ++ * Return pattern-size or an empty Size for an unsupported inputFormat. ++ */ ++Size DebayerCpu::patternSize(PixelFormat inputFormat) ++{ ++ DebayerCpu::DebayerInputConfig config; ++ ++ 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::setupInputMemcpy(const uint8_t *linePointers[]) ++{ ++ const unsigned int patternHeight = inputConfig_.patternSize.height; ++ ++ if (!enableInputMemcpy_) ++ return; ++ ++ for (unsigned int i = 0; i < patternHeight; i++) { ++ memcpy(lineBuffers_[i], linePointers[i + 1] - lineBufferPadding_, ++ lineBufferLength_); ++ linePointers[i + 1] = lineBuffers_[i] + lineBufferPadding_; ++ } ++ ++ /* Point lineBufferIndex_ to first unused lineBuffer */ ++ lineBufferIndex_ = patternHeight; ++} ++ ++void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src) ++{ ++ const unsigned int patternHeight = inputConfig_.patternSize.height; ++ ++ for (unsigned int i = 0; i < patternHeight; i++) ++ linePointers[i] = linePointers[i + 1]; ++ ++ linePointers[patternHeight] = src + ++ (patternHeight / 2) * (int)inputConfig_.stride; ++} ++ ++void DebayerCpu::memcpyNextLine(const uint8_t *linePointers[]) ++{ ++ const unsigned int patternHeight = inputConfig_.patternSize.height; ++ ++ if (!enableInputMemcpy_) ++ return; ++ ++ memcpy(lineBuffers_[lineBufferIndex_], linePointers[patternHeight] - lineBufferPadding_, ++ lineBufferLength_); ++ linePointers[patternHeight] = lineBuffers_[lineBufferIndex_] + lineBufferPadding_; ++ ++ lineBufferIndex_ = (lineBufferIndex_ + 1) % (patternHeight + 1); ++} ++ ++void DebayerCpu::process2(const uint8_t *src, uint8_t *dst) ++{ ++ unsigned int y_end = window_.y + window_.height; ++ /* Holds [0] previous- [1] current- [2] next-line */ ++ const uint8_t *linePointers[3]; ++ ++ /* Adjust src to top left corner of the window */ ++ src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8; ++ ++ /* [x] becomes [x - 1] after initial shiftLinePointers() call */ ++ if (window_.y) { ++ linePointers[1] = src - inputConfig_.stride; /* previous-line */ ++ linePointers[2] = src; ++ } else { ++ /* window_.y == 0, use the next line as prev line */ ++ linePointers[1] = src + inputConfig_.stride; ++ linePointers[2] = src; ++ /* Last 2 lines also need special handling */ ++ y_end -= 2; ++ } ++ ++ setupInputMemcpy(linePointers); ++ ++ for (unsigned int y = window_.y; y < y_end; y += 2) { ++ shiftLinePointers(linePointers, src); ++ memcpyNextLine(linePointers); ++ stats_->processLine0(y, linePointers); ++ (this->*debayer0_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ memcpyNextLine(linePointers); ++ (this->*debayer1_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ } ++ ++ if (window_.y == 0) { ++ shiftLinePointers(linePointers, src); ++ memcpyNextLine(linePointers); ++ stats_->processLine0(y_end, linePointers); ++ (this->*debayer0_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ /* next line may point outside of src, use prev. */ ++ linePointers[2] = linePointers[0]; ++ (this->*debayer1_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ } ++} ++ ++void DebayerCpu::process4(const uint8_t *src, uint8_t *dst) ++{ ++ const unsigned int y_end = window_.y + window_.height; ++ /* ++ * This holds pointers to [0] 2-lines-up [1] 1-line-up [2] current-line ++ * [3] 1-line-down [4] 2-lines-down. ++ */ ++ const uint8_t *linePointers[5]; ++ ++ /* Adjust src to top left corner of the window */ ++ src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8; ++ ++ /* [x] becomes [x - 1] after initial shiftLinePointers() call */ ++ linePointers[1] = src - 2 * inputConfig_.stride; ++ linePointers[2] = src - inputConfig_.stride; ++ linePointers[3] = src; ++ linePointers[4] = src + inputConfig_.stride; ++ ++ setupInputMemcpy(linePointers); ++ ++ for (unsigned int y = window_.y; y < y_end; y += 4) { ++ shiftLinePointers(linePointers, src); ++ memcpyNextLine(linePointers); ++ stats_->processLine0(y, linePointers); ++ (this->*debayer0_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ memcpyNextLine(linePointers); ++ (this->*debayer1_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ memcpyNextLine(linePointers); ++ stats_->processLine2(y, linePointers); ++ (this->*debayer2_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ ++ shiftLinePointers(linePointers, src); ++ memcpyNextLine(linePointers); ++ (this->*debayer3_)(dst, linePointers); ++ src += inputConfig_.stride; ++ dst += outputConfig_.stride; ++ } ++} ++ ++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::kLastFrameToMeasure) { ++ frameStartTime = {}; ++ clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime); ++ } ++ ++ /* Apply DebayerParams */ ++ if (params.gamma != gamma_correction_) { ++ for (unsigned int i = 0; i < kGammaLookupSize; i++) ++ gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma); ++ ++ gamma_correction_ = params.gamma; ++ } ++ ++ for (unsigned int i = 0; i < kRGBLookupSize; i++) { ++ constexpr unsigned int div = ++ kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize; ++ unsigned int idx; ++ ++ /* Apply gamma after gain! */ ++ idx = std::min({ i * params.gainR / div, (kGammaLookupSize - 1) }); ++ red_[i] = gamma_[idx]; ++ ++ idx = std::min({ i * params.gainG / div, (kGammaLookupSize - 1) }); ++ green_[i] = gamma_[idx]; ++ ++ idx = std::min({ i * params.gainB / div, (kGammaLookupSize - 1) }); ++ 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::kLastFrameToMeasure && ++ ++measuredFrames_ > DebayerCpu::kFramesToSkip) { ++ timespec frameEndTime = {}; ++ clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime); ++ frameProcessTime_ += timeDiff(frameEndTime, frameStartTime); ++ if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) { ++ const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure - ++ DebayerCpu::kFramesToSkip; ++ LOG(Debayer, Info) ++ << "Processed " << measuredFrames ++ << " frames in " << frameProcessTime_ / 1000 << "us, " ++ << frameProcessTime_ / (1000 * measuredFrames) ++ << " us/frame"; ++ } ++ } ++ ++ stats_->finishFrame(); ++ outputBufferReady.emit(output); ++ inputBufferReady.emit(input); ++} ++ ++SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize) ++{ ++ Size pattern_size = patternSize(inputFormat); ++ unsigned int border_height = pattern_size.height; ++ ++ if (pattern_size.isNull()) ++ return {}; ++ ++ /* No need for top/bottom border with a pattern height of 2 */ ++ if (pattern_size.height == 2) ++ border_height = 0; ++ ++ /* ++ * For debayer interpolation a border is kept around the entire image ++ * and the minimum output size is pattern-height x pattern-width. ++ */ ++ if (inputSize.width < (3 * pattern_size.width) || ++ inputSize.height < (2 * border_height + pattern_size.height)) { ++ LOG(Debayer, Warning) ++ << "Input format size too small: " << inputSize.toString(); ++ return {}; ++ } ++ ++ return SizeRange(Size(pattern_size.width, pattern_size.height), ++ Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1), ++ (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)), ++ pattern_size.width, pattern_size.height); ++} ++ ++} /* namespace libcamera */ +diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h +new file mode 100644 +index 00000000..8a51ed85 +--- /dev/null ++++ b/src/libcamera/software_isp/debayer_cpu.h +@@ -0,0 +1,143 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * Copyright (C) 2023, Red Hat Inc. ++ * ++ * Authors: ++ * Hans de Goede ++ * ++ * debayer_cpu.h - CPU based debayering header ++ */ ++ ++#pragma once ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "debayer.h" ++#include "swstats_cpu.h" ++ ++namespace libcamera { ++ ++class DebayerCpu : public Debayer, public Object ++{ ++public: ++ DebayerCpu(std::unique_ptr stats); ++ ~DebayerCpu(); ++ ++ int configure(const StreamConfiguration &inputCfg, ++ const std::vector> &outputCfgs); ++ 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); ++ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize); ++ ++ /** ++ * \brief Get the file descriptor for the statistics. ++ * ++ * \return the file descriptor pointing to the statistics. ++ */ ++ const SharedFD &getStatsFD() { return stats_->getStatsFD(); } ++ ++ /** ++ * \brief Get the output frame size. ++ * ++ * \return The output frame size. ++ */ ++ unsigned int frameSize() { return outputConfig_.frameSize; } ++ ++private: ++ /** ++ * \brief Called to debayer 1 line of Bayer input data to output format ++ * \param[out] dst Pointer to the start of the output line to write ++ * \param[in] src The input data ++ * ++ * Input data is an array of (patternSize_.height + 1) src ++ * pointers each pointing to a line in the Bayer source. The middle ++ * element of the array will point to the actual line being processed. ++ * Earlier element(s) will point to the previous line(s) and later ++ * element(s) to the next line(s). ++ * ++ * These functions take an array of src pointers, rather than ++ * a single src pointer + a stride for the source, so that when the src ++ * is slow uncached memory it can be copied to faster memory before ++ * debayering. Debayering a standard 2x2 Bayer pattern requires access ++ * to the previous and next src lines for interpolating the missing ++ * colors. To allow copying the src lines only once 3 temporary buffers ++ * each holding a single line are used, re-using the oldest buffer for ++ * the next line and the pointers are swizzled so that: ++ * src[0] = previous-line, src[1] = currrent-line, src[2] = next-line. ++ * This way the 3 pointers passed to the debayer functions form ++ * a sliding window over the src avoiding the need to copy each ++ * line more than once. ++ * ++ * Similarly for bayer patterns which repeat every 4 lines, 5 src ++ * pointers are passed holding: src[0] = 2-lines-up, src[1] = 1-line-up ++ * src[2] = current-line, src[3] = 1-line-down, src[4] = 2-lines-down. ++ */ ++ using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]); ++ ++ /* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */ ++ void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); ++ void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); ++ void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]); ++ void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]); ++ ++ struct DebayerInputConfig { ++ Size patternSize; ++ unsigned int bpp; /* Memory used per pixel, not precision */ ++ unsigned int stride; ++ std::vector outputFormats; ++ }; ++ ++ struct DebayerOutputConfig { ++ unsigned int bpp; /* Memory used per pixel, not precision */ ++ unsigned int stride; ++ unsigned int frameSize; ++ }; ++ ++ int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config); ++ int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config); ++ int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat); ++ void setupInputMemcpy(const uint8_t *linePointers[]); ++ void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src); ++ void memcpyNextLine(const uint8_t *linePointers[]); ++ void process2(const uint8_t *src, uint8_t *dst); ++ void process4(const uint8_t *src, uint8_t *dst); ++ ++ static constexpr unsigned int kGammaLookupSize = 1024; ++ static constexpr unsigned int kRGBLookupSize = 256; ++ /* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */ ++ static constexpr unsigned int kMaxLineBuffers = 5; ++ ++ std::array gamma_; ++ std::array red_; ++ std::array green_; ++ std::array blue_; ++ debayerFn debayer0_; ++ debayerFn debayer1_; ++ debayerFn debayer2_; ++ debayerFn debayer3_; ++ Rectangle window_; ++ DebayerInputConfig inputConfig_; ++ DebayerOutputConfig outputConfig_; ++ std::unique_ptr stats_; ++ uint8_t *lineBuffers_[kMaxLineBuffers]; ++ unsigned int lineBufferLength_; ++ unsigned int lineBufferPadding_; ++ unsigned int lineBufferIndex_; ++ bool enableInputMemcpy_; ++ float gamma_correction_; ++ unsigned int measuredFrames_; ++ int64_t frameProcessTime_; ++ /* Skip 30 frames for things to stabilize then measure 30 frames */ ++ static constexpr unsigned int kFramesToSkip = 30; ++ static constexpr unsigned int kLastFrameToMeasure = 60; ++}; ++ ++} /* namespace libcamera */ +diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build +index 62095f61..71b46539 100644 +--- a/src/libcamera/software_isp/meson.build ++++ b/src/libcamera/software_isp/meson.build +@@ -9,5 +9,6 @@ endif + + libcamera_sources += files([ + 'debayer.cpp', ++ 'debayer_cpu.cpp', + 'swstats_cpu.cpp', + ]) +-- +2.43.2 + 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 deleted file mode 100644 index d48eadd994..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-SwStatsCpu-class.patch +++ /dev/null @@ -1,272 +0,0 @@ -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-ipa-add-Soft-IPA.patch b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch new file mode 100644 index 0000000000..40f9403ba9 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch @@ -0,0 +1,506 @@ +From 5261c801d8425fa82bcbd3da0199d06153eb5bd7 Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Mon, 11 Mar 2024 15:15:13 +0100 +Subject: [PATCH 09/21] libcamera: ipa: add Soft IPA + +Define the Soft IPA main and event interfaces, add the Soft IPA +implementation. + +The current src/ipa/meson.build assumes the IPA name to match the +pipeline name. For this reason "-Dipas=simple" is used for the +Soft IPA module. + +Auto exposure/gain and AWB implementation by Dennis, Toon and Martti. + +Auto exposure/gain targets a Mean Sample Value of 2.5 following +the MSV calculation algorithm from: +https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Signed-off-by: Andrey Konovalov +Co-developed-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Co-developed-by: Marttico +Signed-off-by: Marttico +Co-developed-by: Toon Langendam +Signed-off-by: Toon Langendam +Signed-off-by: Hans de Goede +--- + Documentation/Doxyfile.in | 1 + + include/libcamera/ipa/meson.build | 1 + + include/libcamera/ipa/soft.mojom | 28 +++ + meson_options.txt | 2 +- + src/ipa/simple/data/meson.build | 9 + + src/ipa/simple/data/soft.conf | 3 + + src/ipa/simple/meson.build | 25 +++ + src/ipa/simple/soft_simple.cpp | 326 ++++++++++++++++++++++++++++++ + 8 files changed, 394 insertions(+), 1 deletion(-) + create mode 100644 include/libcamera/ipa/soft.mojom + create mode 100644 src/ipa/simple/data/meson.build + create mode 100644 src/ipa/simple/data/soft.conf + create mode 100644 src/ipa/simple/meson.build + create mode 100644 src/ipa/simple/soft_simple.cpp + +diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in +index a86ea6c1..2be8d47b 100644 +--- a/Documentation/Doxyfile.in ++++ b/Documentation/Doxyfile.in +@@ -44,6 +44,7 @@ EXCLUDE = @TOP_SRCDIR@/include/libcamera/base/span.h \ + @TOP_SRCDIR@/src/libcamera/pipeline/ \ + @TOP_SRCDIR@/src/libcamera/tracepoints.cpp \ + @TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \ ++ @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \ + @TOP_BUILDDIR@/src/libcamera/proxy/ + + EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \ +diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build +index f3b4881c..3352d08f 100644 +--- a/include/libcamera/ipa/meson.build ++++ b/include/libcamera/ipa/meson.build +@@ -65,6 +65,7 @@ pipeline_ipa_mojom_mapping = { + 'ipu3': 'ipu3.mojom', + 'rkisp1': 'rkisp1.mojom', + 'rpi/vc4': 'raspberrypi.mojom', ++ 'simple': 'soft.mojom', + 'vimc': 'vimc.mojom', + } + +diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom +new file mode 100644 +index 00000000..c249bd75 +--- /dev/null ++++ b/include/libcamera/ipa/soft.mojom +@@ -0,0 +1,28 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++ ++/* ++ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry. ++ */ ++ ++module ipa.soft; ++ ++import "include/libcamera/ipa/core.mojom"; ++ ++interface IPASoftInterface { ++ init(libcamera.IPASettings settings, ++ libcamera.SharedFD fdStats, ++ libcamera.SharedFD fdParams, ++ libcamera.ControlInfoMap sensorCtrlInfoMap) ++ => (int32 ret); ++ start() => (int32 ret); ++ stop(); ++ configure(libcamera.ControlInfoMap sensorCtrlInfoMap) ++ => (int32 ret); ++ ++ [async] processStats(libcamera.ControlList sensorControls); ++}; ++ ++interface IPASoftEventInterface { ++ setSensorControls(libcamera.ControlList sensorControls); ++ setIspParams(int32 dummy); ++}; +diff --git a/meson_options.txt b/meson_options.txt +index 5fdc7be8..94372e47 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -27,7 +27,7 @@ option('gstreamer', + + option('ipas', + type : 'array', +- choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'], ++ choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'], + description : 'Select which IPA modules to build') + + option('lc-compliance', +diff --git a/src/ipa/simple/data/meson.build b/src/ipa/simple/data/meson.build +new file mode 100644 +index 00000000..33548cc6 +--- /dev/null ++++ b/src/ipa/simple/data/meson.build +@@ -0,0 +1,9 @@ ++# SPDX-License-Identifier: CC0-1.0 ++ ++conf_files = files([ ++ 'soft.conf', ++]) ++ ++install_data(conf_files, ++ install_dir : ipa_data_dir / 'soft', ++ install_tag : 'runtime') +diff --git a/src/ipa/simple/data/soft.conf b/src/ipa/simple/data/soft.conf +new file mode 100644 +index 00000000..0c70e7c0 +--- /dev/null ++++ b/src/ipa/simple/data/soft.conf +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: LGPL-2.1-or-later ++# ++# Dummy configuration file for the soft IPA. +diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build +new file mode 100644 +index 00000000..3e863db7 +--- /dev/null ++++ b/src/ipa/simple/meson.build +@@ -0,0 +1,25 @@ ++# SPDX-License-Identifier: CC0-1.0 ++ ++ipa_name = 'ipa_soft_simple' ++ ++mod = shared_module(ipa_name, ++ ['soft_simple.cpp', libcamera_generated_ipa_headers], ++ name_prefix : '', ++ include_directories : [ipa_includes, libipa_includes], ++ dependencies : libcamera_private, ++ link_with : libipa, ++ install : true, ++ install_dir : ipa_install_dir) ++ ++if ipa_sign_module ++ custom_target(ipa_name + '.so.sign', ++ input : mod, ++ output : ipa_name + '.so.sign', ++ command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'], ++ install : false, ++ build_by_default : true) ++endif ++ ++subdir('data') ++ ++ipa_names += ipa_name +diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp +new file mode 100644 +index 00000000..312df4ba +--- /dev/null ++++ b/src/ipa/simple/soft_simple.cpp +@@ -0,0 +1,326 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * ++ * soft_simple.cpp - Simple Software Image Processing Algorithm module ++ */ ++ ++#include ++ ++#include ++#include ++#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" ++ ++namespace libcamera { ++ ++LOG_DEFINE_CATEGORY(IPASoft) ++ ++namespace ipa::soft { ++ ++class IPASoftSimple : public ipa::soft::IPASoftInterface ++{ ++public: ++ IPASoftSimple() ++ : params_(static_cast(MAP_FAILED)), ++ stats_(static_cast(MAP_FAILED)), ignore_updates_(0) ++ { ++ } ++ ++ ~IPASoftSimple() ++ { ++ if (stats_ != MAP_FAILED) ++ munmap(stats_, sizeof(SwIspStats)); ++ if (params_ != MAP_FAILED) ++ munmap(params_, sizeof(DebayerParams)); ++ } ++ ++ int init(const IPASettings &settings, ++ const SharedFD &fdStats, ++ const SharedFD &fdParams, ++ const ControlInfoMap &sensorInfoMap) override; ++ int configure(const ControlInfoMap &sensorInfoMap) override; ++ ++ int start() override; ++ void stop() override; ++ ++ void processStats(const ControlList &sensorControls) override; ++ ++private: ++ void updateExposure(double exposureMSV); ++ ++ SharedFD fdStats_; ++ SharedFD fdParams_; ++ DebayerParams *params_; ++ SwIspStats *stats_; ++ ++ int32_t exposure_min_, exposure_max_; ++ int32_t again_min_, again_max_; ++ int32_t again_, exposure_; ++ unsigned int ignore_updates_; ++}; ++ ++int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings, ++ const SharedFD &fdStats, ++ const SharedFD &fdParams, ++ const ControlInfoMap &sensorInfoMap) ++{ ++ fdStats_ = fdStats; ++ if (!fdStats_.isValid()) { ++ LOG(IPASoft, Error) << "Invalid Statistics handle"; ++ return -ENODEV; ++ } ++ ++ fdParams_ = fdParams; ++ if (!fdParams_.isValid()) { ++ LOG(IPASoft, Error) << "Invalid Parameters handle"; ++ return -ENODEV; ++ } ++ ++ params_ = static_cast(mmap(nullptr, sizeof(DebayerParams), ++ PROT_WRITE, MAP_SHARED, ++ fdParams_.get(), 0)); ++ if (params_ == MAP_FAILED) { ++ LOG(IPASoft, Error) << "Unable to map Parameters"; ++ return -errno; ++ } ++ ++ stats_ = static_cast(mmap(nullptr, sizeof(SwIspStats), ++ PROT_READ, MAP_SHARED, ++ fdStats_.get(), 0)); ++ if (stats_ == MAP_FAILED) { ++ LOG(IPASoft, Error) << "Unable to map Statistics"; ++ return -errno; ++ } ++ ++ if (sensorInfoMap.find(V4L2_CID_EXPOSURE) == sensorInfoMap.end()) { ++ LOG(IPASoft, Error) << "Don't have exposure control"; ++ return -EINVAL; ++ } ++ ++ if (sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN) == sensorInfoMap.end()) { ++ LOG(IPASoft, Error) << "Don't have gain control"; ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap) ++{ ++ const ControlInfo &exposure_info = sensorInfoMap.find(V4L2_CID_EXPOSURE)->second; ++ const ControlInfo &gain_info = sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN)->second; ++ ++ exposure_min_ = exposure_info.min().get(); ++ exposure_max_ = exposure_info.max().get(); ++ if (!exposure_min_) { ++ LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear"; ++ exposure_min_ = 1; ++ } ++ ++ again_min_ = gain_info.min().get(); ++ again_max_ = gain_info.max().get(); ++ /* ++ * The camera sensor gain (g) is usually not equal to the value written ++ * into the gain register (x). But the way how the AGC algorithm changes ++ * the gain value to make the total exposure closer to the optimum assumes ++ * that g(x) is not too far from linear function. If the minimal gain is 0, ++ * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c). ++ * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near ++ * one edge, and very small near the other) we limit the range of the gain ++ * values used. ++ */ ++ if (!again_min_) { ++ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear"; ++ again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2); ++ } ++ ++ LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_ ++ << ", gain " << again_min_ << "-" << again_max_; ++ ++ return 0; ++} ++ ++int IPASoftSimple::start() ++{ ++ return 0; ++} ++ ++void IPASoftSimple::stop() ++{ ++} ++ ++/* ++ * The number of bins to use for the optimal exposure calculations. ++ */ ++static constexpr unsigned int kExposureBinsCount = 5; ++/* ++ * The exposure is optimal when the mean sample value of the histogram is ++ * in the middle of the range. ++ */ ++static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; ++/* ++ * The below value implements the hysteresis for the exposure adjustment. ++ * It is small enough to have the exposure close to the optimal, and is big ++ * enough to prevent the exposure from wobbling around the optimal value. ++ */ ++static constexpr float kExposureSatisfactory = 0.2; ++ ++void IPASoftSimple::processStats(const ControlList &sensorControls) ++{ ++ /* ++ * Calculate red and blue gains for AWB. ++ * Clamp max gain at 4.0, this also avoids 0 division. ++ */ ++ if (stats_->sumR_ <= stats_->sumG_ / 4) ++ params_->gainR = 1024; ++ else ++ params_->gainR = 256 * stats_->sumG_ / stats_->sumR_; ++ ++ if (stats_->sumB_ <= stats_->sumG_ / 4) ++ params_->gainB = 1024; ++ else ++ params_->gainB = 256 * stats_->sumG_ / stats_->sumB_; ++ ++ /* Green gain and gamma values are fixed */ ++ params_->gainG = 256; ++ params_->gamma = 0.5; ++ ++ setIspParams.emit(0); ++ ++ /* ++ * AE / AGC, use 2 frames delay to make sure that the exposure and ++ * the gain set have applied to the camera sensor. ++ */ ++ if (ignore_updates_ > 0) { ++ --ignore_updates_; ++ return; ++ } ++ ++ /* ++ * Calculate Mean Sample Value (MSV) according to formula from: ++ * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf ++ */ ++ constexpr unsigned int yHistValsPerBin = ++ SwIspStats::kYHistogramSize / kExposureBinsCount; ++ constexpr unsigned int yHistValsPerBinMod = ++ SwIspStats::kYHistogramSize / ++ (SwIspStats::kYHistogramSize % kExposureBinsCount + 1); ++ int ExposureBins[kExposureBinsCount] = {}; ++ unsigned int denom = 0; ++ unsigned int num = 0; ++ ++ for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) { ++ unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin; ++ ExposureBins[idx] += stats_->yHistogram[i]; ++ } ++ ++ for (unsigned int i = 0; i < kExposureBinsCount; i++) { ++ LOG(IPASoft, Debug) << i << ": " << ExposureBins[i]; ++ denom += ExposureBins[i]; ++ num += ExposureBins[i] * (i + 1); ++ } ++ ++ float exposureMSV = (float)num / denom; ++ ++ /* sanity check */ ++ if (!sensorControls.contains(V4L2_CID_EXPOSURE) || ++ !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) { ++ LOG(IPASoft, Error) << "Control(s) missing"; ++ return; ++ } ++ ++ ControlList ctrls(sensorControls); ++ ++ exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get(); ++ again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get(); ++ ++ updateExposure(exposureMSV); ++ ++ ctrls.set(V4L2_CID_EXPOSURE, exposure_); ++ ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_); ++ ++ ignore_updates_ = 2; ++ ++ setSensorControls.emit(ctrls); ++ ++ LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV ++ << " exp " << exposure_ << " again " << again_ ++ << " gain R/B " << params_->gainR << "/" << params_->gainB; ++} ++ ++void IPASoftSimple::updateExposure(double exposureMSV) ++{ ++ /* DENOMINATOR of 10 gives ~10% increment/decrement; DENOMINATOR of 5 - about ~20% */ ++ static constexpr uint8_t kExpDenominator = 10; ++ static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1; ++ static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1; ++ ++ int next; ++ ++ if (exposureMSV < kExposureOptimal - kExposureSatisfactory) { ++ next = exposure_ * kExpNumeratorUp / kExpDenominator; ++ if (next - exposure_ < 1) ++ exposure_ += 1; ++ else ++ exposure_ = next; ++ if (exposure_ >= exposure_max_) { ++ next = again_ * kExpNumeratorUp / kExpDenominator; ++ if (next - again_ < 1) ++ again_ += 1; ++ else ++ again_ = next; ++ } ++ } ++ ++ if (exposureMSV > kExposureOptimal + kExposureSatisfactory) { ++ if (exposure_ == exposure_max_ && again_ != again_min_) { ++ next = again_ * kExpNumeratorDown / kExpDenominator; ++ if (again_ - next < 1) ++ again_ -= 1; ++ else ++ again_ = next; ++ } else { ++ next = exposure_ * kExpNumeratorDown / kExpDenominator; ++ if (exposure_ - next < 1) ++ exposure_ -= 1; ++ else ++ exposure_ = next; ++ } ++ } ++ ++ exposure_ = std::clamp(exposure_, exposure_min_, exposure_max_); ++ again_ = std::clamp(again_, again_min_, again_max_); ++} ++ ++} /* namespace ipa::soft */ ++ ++/* ++ * External IPA module interface ++ */ ++extern "C" { ++const struct IPAModuleInfo ipaModuleInfo = { ++ IPA_MODULE_API_VERSION, ++ 0, ++ "SimplePipelineHandler", ++ "simple", ++}; ++ ++IPAInterface *ipaCreate() ++{ ++ return new ipa::soft::IPASoftSimple(); ++} ++ ++} /* extern "C" */ ++ ++} /* namespace libcamera */ +-- +2.43.2 + 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 deleted file mode 100644 index f43b3368e6..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0009-libcamera-software_isp-Add-Debayer-base-class.patch +++ /dev/null @@ -1,272 +0,0 @@ -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-introduce-SoftwareIsp.patch b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch new file mode 100644 index 0000000000..9f2d66c2f8 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch @@ -0,0 +1,507 @@ +From ad41ea12fe4b8ca0ace20781c775a63ed0d66f4c Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Mon, 11 Mar 2024 15:15:14 +0100 +Subject: [PATCH 10/21] libcamera: introduce SoftwareIsp + +Doxygen documentation by Dennis Bonke. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Co-developed-by: Dennis Bonke +Signed-off-by: Dennis Bonke +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +--- + .../internal/software_isp/meson.build | 1 + + .../internal/software_isp/software_isp.h | 98 +++++ + src/libcamera/software_isp/meson.build | 1 + + src/libcamera/software_isp/software_isp.cpp | 349 ++++++++++++++++++ + 4 files changed, 449 insertions(+) + create mode 100644 include/libcamera/internal/software_isp/software_isp.h + create mode 100644 src/libcamera/software_isp/software_isp.cpp + +diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build +index a620e16d..508ddddc 100644 +--- a/include/libcamera/internal/software_isp/meson.build ++++ b/include/libcamera/internal/software_isp/meson.build +@@ -2,5 +2,6 @@ + + libcamera_internal_headers += files([ + 'debayer_params.h', ++ 'software_isp.h', + 'swisp_stats.h', + ]) +diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h +new file mode 100644 +index 00000000..8d25e979 +--- /dev/null ++++ b/include/libcamera/internal/software_isp/software_isp.h +@@ -0,0 +1,98 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * ++ * software_isp.h - Simple software ISP implementation ++ */ ++ ++#pragma once ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "libcamera/internal/dma_heaps.h" ++#include "libcamera/internal/pipeline_handler.h" ++#include "libcamera/internal/shared_mem_object.h" ++#include "libcamera/internal/software_isp/debayer_params.h" ++ ++namespace libcamera { ++ ++class DebayerCpu; ++class FrameBuffer; ++class PixelFormat; ++struct StreamConfiguration; ++ ++LOG_DECLARE_CATEGORY(SoftwareIsp) ++ ++class SoftwareIsp ++{ ++public: ++ SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls); ++ ~SoftwareIsp(); ++ ++ int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; } ++ ++ bool isValid() const; ++ ++ std::vector formats(PixelFormat input); ++ ++ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize); ++ ++ std::tuple ++ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size); ++ ++ int configure(const StreamConfiguration &inputCfg, ++ const std::vector> &outputCfgs, ++ const ControlInfoMap &sensorControls); ++ ++ int exportBuffers(unsigned int output, unsigned int count, ++ std::vector> *buffers); ++ ++ void processStats(const ControlList &sensorControls); ++ ++ int start(); ++ void stop(); ++ ++ int queueBuffers(FrameBuffer *input, ++ const std::map &outputs); ++ ++ void process(FrameBuffer *input, FrameBuffer *output); ++ ++ Signal inputBufferReady; ++ Signal outputBufferReady; ++ Signal ispStatsReady; ++ Signal setSensorControls; ++ ++private: ++ void saveIspParams(int dummy); ++ void setSensorCtrls(const ControlList &sensorControls); ++ void statsReady(int dummy); ++ void inputReady(FrameBuffer *input); ++ void outputReady(FrameBuffer *output); ++ ++ std::unique_ptr 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 71b46539..e9266e54 100644 +--- a/src/libcamera/software_isp/meson.build ++++ b/src/libcamera/software_isp/meson.build +@@ -10,5 +10,6 @@ endif + libcamera_sources += files([ + 'debayer.cpp', + 'debayer_cpu.cpp', ++ 'software_isp.cpp', + 'swstats_cpu.cpp', + ]) +diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp +new file mode 100644 +index 00000000..388b4496 +--- /dev/null ++++ b/src/libcamera/software_isp/software_isp.cpp +@@ -0,0 +1,349 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2023, Linaro Ltd ++ * ++ * software_isp.cpp - Simple software ISP implementation ++ */ ++ ++#include "libcamera/internal/software_isp/software_isp.h" ++ ++#include ++#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" ++ ++#include "debayer_cpu.h" ++ ++/** ++ * \file software_isp.cpp ++ * \brief Simple software ISP implementation ++ */ ++ ++namespace libcamera { ++ ++LOG_DEFINE_CATEGORY(SoftwareIsp) ++ ++/** ++ * \class SoftwareIsp ++ * \brief Class for the Software ISP ++ */ ++ ++/** ++ * \var SoftwareIsp::inputBufferReady ++ * \brief A signal emitted when the input frame buffer completes ++ */ ++ ++/** ++ * \var SoftwareIsp::outputBufferReady ++ * \brief A signal emitted when the output frame buffer completes ++ */ ++ ++/** ++ * \var SoftwareIsp::ispStatsReady ++ * \brief A signal emitted when the statistics for IPA are ready ++ * ++ * The int parameter isn't actually used. ++ */ ++ ++/** ++ * \var SoftwareIsp::setSensorControls ++ * \brief A signal emitted when the values to write to the sensor controls are ready ++ */ ++ ++/** ++ * \brief Constructs SoftwareIsp object ++ * \param[in] pipe The pipeline handler in use ++ * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor ++ */ ++SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls) ++ : debayer_(nullptr), ++ debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f }, ++ dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System) ++{ ++ if (!dmaHeap_.isValid()) { ++ LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object"; ++ return; ++ } ++ ++ sharedParams_ = SharedMemObject("softIsp_params"); ++ if (!sharedParams_) { ++ LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters"; ++ return; ++ } ++ ++ auto stats = std::make_unique(); ++ if (!stats->isValid()) { ++ LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object"; ++ return; ++ } ++ stats->statsReady.connect(this, &SoftwareIsp::statsReady); ++ ++ debayer_ = std::make_unique(std::move(stats)); ++ debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady); ++ debayer_->outputBufferReady.connect(this, &SoftwareIsp::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, &SoftwareIsp::saveIspParams); ++ ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls); ++ ++ debayer_->moveToThread(&ispWorkerThread_); ++} ++ ++SoftwareIsp::~SoftwareIsp() ++{ ++ /* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */ ++ debayer_.reset(); ++} ++ ++/** ++ * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename) ++ * \brief Load a configuration from a file ++ * \param[in] filename The file to load the configuration data from ++ * ++ * Currently is a stub doing nothing and always returning "success". ++ * ++ * \return 0 on success ++ */ ++ ++/** ++ * \brief Process the statistics gathered ++ * \param[in] sensorControls The sensor controls ++ * ++ * Requests the IPA to calculate new parameters for ISP and new control ++ * values for the sensor. ++ */ ++void SoftwareIsp::processStats(const ControlList &sensorControls) ++{ ++ ASSERT(ipa_); ++ ipa_->processStats(sensorControls); ++} ++ ++/** ++ * \brief Check the validity of Software Isp object ++ * \return True if Software Isp is valid, false otherwise ++ */ ++bool SoftwareIsp::isValid() const ++{ ++ return !!debayer_; ++} ++ ++/** ++ * \brief Get the output formats supported for the given input format ++ * \param[in] inputFormat The input format ++ * \return All the supported output formats or an empty vector if there are none ++ */ ++std::vector SoftwareIsp::formats(PixelFormat inputFormat) ++{ ++ ASSERT(debayer_ != nullptr); ++ ++ return debayer_->formats(inputFormat); ++} ++ ++/** ++ * \brief Get the supported output sizes for the given input format and size ++ * \param[in] inputFormat The input format ++ * \param[in] inputSize The input frame size ++ * \return The valid size range or an empty range if there are none ++ */ ++SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize) ++{ ++ ASSERT(debayer_ != nullptr); ++ ++ return debayer_->sizes(inputFormat, inputSize); ++} ++ ++/** ++ * Get the output stride and the frame size in bytes for the given output format and size ++ * \param[in] outputFormat The output format ++ * \param[in] size The output size (width and height in pixels) ++ * \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0 ++ * if there is no valid output config ++ */ ++std::tuple ++SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) ++{ ++ ASSERT(debayer_ != nullptr); ++ ++ return debayer_->strideAndFrameSize(outputFormat, size); ++} ++ ++/** ++ * \brief Configure the SoftwareIsp object according to the passed in parameters ++ * \param[in] inputCfg The input configuration ++ * \param[in] outputCfgs The output configurations ++ * \param[in] sensorControls ControlInfoMap of the controls supported by the sensor ++ * \return 0 on success, a negative errno on failure ++ */ ++int SoftwareIsp::configure(const StreamConfiguration &inputCfg, ++ const std::vector> &outputCfgs, ++ const ControlInfoMap &sensorControls) ++{ ++ ASSERT(ipa_ != nullptr && debayer_ != nullptr); ++ ++ int ret = ipa_->configure(sensorControls); ++ if (ret < 0) ++ return ret; ++ ++ return debayer_->configure(inputCfg, outputCfgs); ++} ++ ++/** ++ * \brief Export the buffers from the Software ISP ++ * \param[in] output Output stream index exporting the buffers ++ * \param[in] count Number of buffers to allocate ++ * \param[out] buffers Vector to store the allocated buffers ++ * \return The number of allocated buffers on success or a negative error code ++ * otherwise ++ */ ++int SoftwareIsp::exportBuffers(unsigned int output, unsigned int count, ++ std::vector> *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; ++} ++ ++/** ++ * \brief Queue buffers to Software ISP ++ * \param[in] input The input framebuffer ++ * \param[in] outputs The container holding the output stream indexes and ++ * their respective frame buffer outputs ++ * \return 0 on success, a negative errno on failure ++ */ ++int SoftwareIsp::queueBuffers(FrameBuffer *input, ++ const std::map &outputs) ++{ ++ unsigned int mask = 0; ++ ++ /* ++ * Validate the outputs as a sanity check: at least one output is ++ * required, all outputs must reference a valid stream and no two ++ * outputs can reference the same stream. ++ */ ++ if (outputs.empty()) ++ return -EINVAL; ++ ++ for (auto [index, buffer] : outputs) { ++ if (!buffer) ++ return -EINVAL; ++ if (index >= 1) /* only single stream atm */ ++ return -EINVAL; ++ if (mask & (1 << index)) ++ return -EINVAL; ++ ++ mask |= 1 << index; ++ } ++ ++ process(input, outputs.at(0)); ++ ++ return 0; ++} ++ ++/** ++ * \brief Starts the Software ISP streaming operation ++ * \return 0 on success, any other value indicates an error ++ */ ++int SoftwareIsp::start() ++{ ++ int ret = ipa_->start(); ++ if (ret) ++ return ret; ++ ++ ispWorkerThread_.start(); ++ return 0; ++} ++ ++/** ++ * \brief Stops the Software ISP streaming operation ++ */ ++void SoftwareIsp::stop() ++{ ++ ispWorkerThread_.exit(); ++ ispWorkerThread_.wait(); ++ ++ ipa_->stop(); ++} ++ ++/** ++ * \brief Passes the input framebuffer to the ISP worker to process ++ * \param[in] input The input framebuffer ++ * \param[out] output The framebuffer to write the processed frame to ++ */ ++void SoftwareIsp::process(FrameBuffer *input, FrameBuffer *output) ++{ ++ debayer_->invokeMethod(&DebayerCpu::process, ++ ConnectionTypeQueued, input, output, debayerParams_); ++} ++ ++void SoftwareIsp::saveIspParams([[maybe_unused]] int dummy) ++{ ++ debayerParams_ = *sharedParams_; ++} ++ ++void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls) ++{ ++ setSensorControls.emit(sensorControls); ++} ++ ++void SoftwareIsp::statsReady(int dummy) ++{ ++ ispStatsReady.emit(dummy); ++} ++ ++void SoftwareIsp::inputReady(FrameBuffer *input) ++{ ++ inputBufferReady.emit(input); ++} ++ ++void SoftwareIsp::outputReady(FrameBuffer *output) ++{ ++ outputBufferReady.emit(output); ++} ++ ++} /* namespace libcamera */ +-- +2.43.2 + 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 deleted file mode 100644 index 56bc87336c..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch +++ /dev/null @@ -1,727 +0,0 @@ -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 deleted file mode 100644 index d5fd94f6af..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch +++ /dev/null @@ -1,256 +0,0 @@ -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/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch b/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch new file mode 100644 index 0000000000..5c2237a8eb --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch @@ -0,0 +1,240 @@ +From 050440eed6ab90686df217f5ff7dea0b241e3898 Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Mon, 11 Mar 2024 15:15:15 +0100 +Subject: [PATCH 11/21] libcamera: pipeline: simple: rename converterBuffers_ + and related vars + +The converterBuffers_ and the converterQueue_ are not that specific +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. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Kieran Bingham +Reviewed-by: Pavel Machek +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +--- + 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 a84f6760..78854ef8 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(); + +@@ -1189,14 +1190,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); + } + +@@ -1222,7 +1223,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream, + * Export buffers on the converter or capture video node, depending on + * whether the converter is used or not. + */ +- if (data->useConverter_) ++ if (data->useConversion_) + return data->converter_->exportBuffers(data->streamIndex(stream), + count, buffers); + else +@@ -1243,13 +1244,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]; +@@ -1268,7 +1269,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); +@@ -1276,7 +1277,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()); + } + +@@ -1288,7 +1289,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(); +@@ -1296,7 +1297,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera) + + video->bufferReady.disconnect(data, &SimpleCameraData::bufferReady); + +- data->converterBuffers_.clear(); ++ data->conversionBuffers_.clear(); + + releasePipeline(data); + } +@@ -1314,7 +1315,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); +@@ -1323,8 +1324,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.2 + 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 deleted file mode 100644 index b7cb27455b..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0012-libcamera-ipa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch +++ /dev/null @@ -1,407 +0,0 @@ -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/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch b/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch new file mode 100644 index 0000000000..378a43604f --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch @@ -0,0 +1,302 @@ +From d64b0fca22ef25b8a14d7fc97dfab64eb1c4f21a Mon Sep 17 00:00:00 2001 +From: Andrey Konovalov +Date: Mon, 11 Mar 2024 15:15:16 +0100 +Subject: [PATCH 12/21] libcamera: pipeline: simple: enable use of Soft ISP and + Soft IPA + +To enable the Simple Soft ISP and Soft IPA for simple pipeline handler +configure the build with: + -Dpipelines=simple -Dipas=simple + +Also using the Soft ISP for the particular hardware platform must +be enabled in the supportedDevices[] table. + +If the pipeline uses Converter, Soft ISP and Soft IPA aren't +available. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Signed-off-by: Andrey Konovalov +Signed-off-by: Hans de Goede +--- + src/libcamera/pipeline/simple/simple.cpp | 137 ++++++++++++++++++----- + 1 file changed, 109 insertions(+), 28 deletions(-) + +diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp +index 78854ef8..c3ebb7b7 100644 +--- a/src/libcamera/pipeline/simple/simple.cpp ++++ b/src/libcamera/pipeline/simple/simple.cpp +@@ -34,6 +34,7 @@ + #include "libcamera/internal/device_enumerator.h" + #include "libcamera/internal/media_device.h" + #include "libcamera/internal/pipeline_handler.h" ++#include "libcamera/internal/software_isp/software_isp.h" + #include "libcamera/internal/v4l2_subdevice.h" + #include "libcamera/internal/v4l2_videodevice.h" + +@@ -185,17 +186,22 @@ struct SimplePipelineInfo { + * and the number of streams it supports. + */ + std::vector> converters; ++ /* ++ * Using Software ISP is to be enabled per driver. ++ * The Software ISP can't be used together with the converters. ++ */ ++ bool swIspEnabled; + }; + + namespace { + + static const SimplePipelineInfo supportedDevices[] = { +- { "dcmipp", {} }, +- { "imx7-csi", { { "pxp", 1 } } }, +- { "j721e-csi2rx", {} }, +- { "mxc-isi", {} }, +- { "qcom-camss", {} }, +- { "sun6i-csi", {} }, ++ { "dcmipp", {}, false }, ++ { "imx7-csi", { { "pxp", 1 } }, false }, ++ { "j721e-csi2rx", {}, false }, ++ { "mxc-isi", {}, false }, ++ { "qcom-camss", {}, true }, ++ { "sun6i-csi", {}, false }, + }; + + } /* namespace */ +@@ -274,6 +280,7 @@ public: + bool useConversion_; + + std::unique_ptr converter_; ++ std::unique_ptr swIsp_; + + private: + void tryPipeline(unsigned int code, const Size &size); +@@ -281,6 +288,9 @@ private: + + void conversionInputDone(FrameBuffer *buffer); + void conversionOutputDone(FrameBuffer *buffer); ++ ++ void ispStatsReady(int dummy); ++ void setSensorControls(const ControlList &sensorControls); + }; + + class SimpleCameraConfiguration : public CameraConfiguration +@@ -332,6 +342,7 @@ public: + V4L2VideoDevice *video(const MediaEntity *entity); + V4L2Subdevice *subdev(const MediaEntity *entity); + MediaDevice *converter() { return converter_; } ++ bool swIspEnabled() { return swIspEnabled_; } + + protected: + int queueRequestDevice(Camera *camera, Request *request) override; +@@ -360,6 +371,7 @@ private: + std::map entities_; + + MediaDevice *converter_; ++ bool swIspEnabled_; + }; + + /* ----------------------------------------------------------------------------- +@@ -509,6 +521,29 @@ int SimpleCameraData::init() + } + } + ++ /* ++ * Instantiate Soft ISP if this is enabled for the given driver and no converter is used. ++ */ ++ if (!converter_ && pipe->swIspEnabled()) { ++ swIsp_ = std::make_unique(pipe, sensor_->controls()); ++ if (!swIsp_->isValid()) { ++ LOG(SimplePipeline, Warning) ++ << "Failed to create software ISP, disabling software debayering"; ++ swIsp_.reset(); ++ } else { ++ /* ++ * \todo explain why SimpleCameraData::conversionInputDone() can't be directly ++ * connected to inputBufferReady signal. ++ */ ++ swIsp_->inputBufferReady.connect(pipe, [this](FrameBuffer *buffer) { ++ this->conversionInputDone(buffer); ++ }); ++ swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone); ++ swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady); ++ swIsp_->setSensorControls.connect(this, &SimpleCameraData::setSensorControls); ++ } ++ } ++ + video_ = pipe->video(entities_.back().entity); + ASSERT(video_); + +@@ -599,12 +634,21 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size) + config.captureFormat = pixelFormat; + config.captureSize = format.size; + +- if (!converter_) { ++ ++ 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; +- } else { +- config.outputFormats = converter_->formats(pixelFormat); +- config.outputSizes = converter_->sizes(format.size); + } + + configs_.push_back(config); +@@ -750,9 +794,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 +842,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 +852,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 +882,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) + { +@@ -1046,8 +1106,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 { +@@ -1210,7 +1272,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, +@@ -1224,8 +1288,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); + } +@@ -1270,10 +1336,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. */ +@@ -1289,8 +1363,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(); +@@ -1452,6 +1531,8 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator) + } + } + ++ swIspEnabled_ = info->swIspEnabled; ++ + /* Locate the sensors. */ + std::vector sensors = locateSensors(); + if (sensors.empty()) { +-- +2.43.2 + 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 deleted file mode 100644 index 24636ed756..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch +++ /dev/null @@ -1,483 +0,0 @@ -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/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch b/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch new file mode 100644 index 0000000000..1a57d690ff --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch @@ -0,0 +1,203 @@ +From aabc53453d542495d9da25411f57308c01f2bc28 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Mar 2024 15:15:17 +0100 +Subject: [PATCH 13/21] libcamera: swstats_cpu: Add support for 8, 10 and 12 + bpp unpacked bayer input + +Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard +bayer orders. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Reviewed-by: Milan Zamazal +Signed-off-by: Kieran Bingham +Signed-off-by: Hans de Goede +--- + src/libcamera/software_isp/swstats_cpu.cpp | 128 +++++++++++++++++++++ + src/libcamera/software_isp/swstats_cpu.h | 9 ++ + 2 files changed, 137 insertions(+) + +diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp +index 448d0e4c..be310f56 100644 +--- a/src/libcamera/software_isp/swstats_cpu.cpp ++++ b/src/libcamera/software_isp/swstats_cpu.cpp +@@ -71,6 +71,83 @@ static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */ + stats_.sumG_ += sumG; \ + stats_.sumB_ += sumB; + ++void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[]) ++{ ++ const uint8_t *src0 = src[1] + window_.x; ++ const uint8_t *src1 = src[2] + window_.x; ++ ++ SWSTATS_START_LINE_STATS(uint8_t) ++ ++ if (swapLines_) ++ 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; ++ ++ SWSTATS_ACCUMULATE_LINE_STATS(1) ++ } ++ ++ SWSTATS_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; ++ ++ SWSTATS_START_LINE_STATS(uint16_t) ++ ++ if (swapLines_) ++ 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 */ ++ SWSTATS_ACCUMULATE_LINE_STATS(4) ++ } ++ ++ SWSTATS_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; ++ ++ SWSTATS_START_LINE_STATS(uint16_t) ++ ++ if (swapLines_) ++ 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 */ ++ SWSTATS_ACCUMULATE_LINE_STATS(16) ++ } ++ ++ SWSTATS_FINISH_LINE_STATS() ++} ++ + void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[]) + { + const uint8_t *src0 = src[1] + window_.x * 5 / 4; +@@ -147,6 +224,42 @@ void SwStatsCpu::finishFrame(void) + statsReady.emit(0); + } + ++/** ++ * \brief Setup SwStatsCpu object for standard Bayer orders ++ * \param[in] order The Bayer order ++ * ++ * Check if order is a standard Bayer order and setup xShift_ and swapLines_ ++ * so that a single BGGR stats function can be used for all 4 standard orders. ++ */ ++int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order) ++{ ++ switch (order) { ++ case BayerFormat::BGGR: ++ xShift_ = 0; ++ swapLines_ = false; ++ break; ++ case BayerFormat::GBRG: ++ xShift_ = 1; /* BGGR -> GBRG */ ++ swapLines_ = false; ++ break; ++ case BayerFormat::GRBG: ++ xShift_ = 0; ++ swapLines_ = true; /* BGGR -> GRBG */ ++ break; ++ case BayerFormat::RGGB: ++ xShift_ = 1; /* BGGR -> GBRG */ ++ swapLines_ = true; /* GBRG -> RGGB */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ patternSize_.height = 2; ++ patternSize_.width = 2; ++ ySkipMask_ = 0x02; /* Skip every 3th and 4th line */ ++ return 0; ++} ++ + /** + * \brief Configure the statistics object for the passed in input format. + * \param[in] inputCfg The input format +@@ -158,6 +271,21 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg) + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputCfg.pixelFormat); + ++ if (bayerFormat.packing == BayerFormat::Packing::None && ++ setupStandardBayerOrder(bayerFormat.order) == 0) { ++ switch (bayerFormat.bitDepth) { ++ case 8: ++ stats0_ = &SwStatsCpu::statsBGGR8Line0; ++ return 0; ++ case 10: ++ stats0_ = &SwStatsCpu::statsBGGR10Line0; ++ return 0; ++ case 12: ++ stats0_ = &SwStatsCpu::statsBGGR12Line0; ++ return 0; ++ } ++ } ++ + if (bayerFormat.bitDepth == 10 && + bayerFormat.packing == BayerFormat::Packing::CSI2) { + patternSize_.height = 2; +diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h +index 0ac9ae71..bbbcf69b 100644 +--- a/src/libcamera/software_isp/swstats_cpu.h ++++ b/src/libcamera/software_isp/swstats_cpu.h +@@ -17,6 +17,7 @@ + + #include + ++#include "libcamera/internal/bayer_format.h" + #include "libcamera/internal/shared_mem_object.h" + #include "libcamera/internal/software_isp/swisp_stats.h" + +@@ -120,6 +121,14 @@ private: + */ + using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]); + ++ int setupStandardBayerOrder(BayerFormat::Order order); ++ /* Bayer 8 bpp unpacked */ ++ void statsBGGR8Line0(const uint8_t *src[]); ++ /* Bayer 10 bpp unpacked */ ++ void statsBGGR10Line0(const uint8_t *src[]); ++ /* Bayer 12 bpp unpacked */ ++ void statsBGGR12Line0(const uint8_t *src[]); ++ /* Bayer 10 bpp packed */ + void statsBGGR10PLine0(const uint8_t *src[]); + void statsGBRG10PLine0(const uint8_t *src[]); + +-- +2.43.2 + diff --git a/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch b/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch new file mode 100644 index 0000000000..c7edf49828 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch @@ -0,0 +1,234 @@ +From 5f3647bd4f12dd62256a425c49fd18a0f5990930 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Mar 2024 15:15:18 +0100 +Subject: [PATCH 14/21] libcamera: debayer_cpu: Add support for 8, 10 and 12 + bpp unpacked bayer input + +Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard +bayer orders. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Pavel Machek +Reviewed-by: Milan Zamazal +Signed-off-by: Kieran Bingham +Signed-off-by: Hans de Goede +--- + src/libcamera/software_isp/debayer_cpu.cpp | 128 +++++++++++++++++++++ + src/libcamera/software_isp/debayer_cpu.h | 13 +++ + 2 files changed, 141 insertions(+) + +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +index f932362c..eb1c2718 100644 +--- a/src/libcamera/software_isp/debayer_cpu.cpp ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -56,6 +56,11 @@ DebayerCpu::~DebayerCpu() + free(lineBuffers_[i]); + } + ++#define DECLARE_SRC_POINTERS(pixel_t) \ ++ const pixel_t *prev = (const pixel_t *)src[0] + xShift_; \ ++ const pixel_t *curr = (const pixel_t *)src[1] + xShift_; \ ++ const pixel_t *next = (const pixel_t *)src[2] + xShift_; ++ + // RGR + // GBG + // RGR +@@ -92,6 +97,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; +@@ -193,6 +262,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)) { +@@ -220,12 +299,61 @@ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &c + return -EINVAL; + } + ++/* ++ * Check for standard Bayer orders and set xShift_ and swap debayer0/1, so that ++ * a single pair of BGGR debayer functions can be used for all 4 standard orders. ++ */ ++int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order) ++{ ++ switch (order) { ++ case BayerFormat::BGGR: ++ break; ++ case BayerFormat::GBRG: ++ xShift_ = 1; /* BGGR -> GBRG */ ++ break; ++ case BayerFormat::GRBG: ++ std::swap(debayer0_, debayer1_); /* BGGR -> GRBG */ ++ break; ++ case BayerFormat::RGGB: ++ xShift_ = 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); + ++ xShift_ = 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) { +diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h +index 8a51ed85..fd1fa180 100644 +--- a/src/libcamera/software_isp/debayer_cpu.h ++++ b/src/libcamera/software_isp/debayer_cpu.h +@@ -17,6 +17,8 @@ + + #include + ++#include "libcamera/internal/bayer_format.h" ++ + #include "debayer.h" + #include "swstats_cpu.h" + +@@ -82,6 +84,15 @@ private: + */ + using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]); + ++ /* 8-bit raw bayer format */ ++ void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); ++ void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); ++ /* unpacked 10-bit raw bayer format */ ++ void debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); ++ void debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); ++ /* unpacked 12-bit raw bayer format */ ++ void debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); ++ void debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); + /* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */ + void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); + void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); +@@ -103,6 +114,7 @@ private: + + int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config); + int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config); ++ int setupStandardBayerOrder(BayerFormat::Order order); + int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat); + void setupInputMemcpy(const uint8_t *linePointers[]); + void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src); +@@ -131,6 +143,7 @@ private: + unsigned int lineBufferLength_; + unsigned int lineBufferPadding_; + unsigned int lineBufferIndex_; ++ unsigned int xShift_; /* Offset of 0/1 applied to window_.x */ + bool enableInputMemcpy_; + float gamma_correction_; + unsigned int measuredFrames_; +-- +2.43.2 + 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 deleted file mode 100644 index fc603fc9c8..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0014-libcamera-pipeline-simple-rename-converterBuffers_-a.patch +++ /dev/null @@ -1,238 +0,0 @@ -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-debayer_cpu-Add-BGR888-output-support.patch b/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch new file mode 100644 index 0000000000..0abca2ea82 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch @@ -0,0 +1,127 @@ +From 186db51d54bcbd4d5096bea1e4396966c2dad001 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Mar 2024 15:15:19 +0100 +Subject: [PATCH 15/21] libcamera: debayer_cpu: Add BGR888 output support + +BGR888 is RGB888 with the red and blue pixels swapped, adjust +the debayering to swap the red and blue pixels in the bayer pattern +to add support for writing formats::BGR888. + +Tested-by: Bryan O'Donoghue # sc8280xp Lenovo x13s +Tested-by: Pavel Machek +Reviewed-by: Milan Zamazal +Signed-off-by: Hans de Goede +Reviewed-by: Stefan Klug +--- + src/libcamera/software_isp/debayer_cpu.cpp | 42 +++++++++++++++++++--- + src/libcamera/software_isp/debayer_cpu.h | 1 + + 2 files changed, 38 insertions(+), 5 deletions(-) + +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +index eb1c2718..a1692693 100644 +--- a/src/libcamera/software_isp/debayer_cpu.cpp ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -268,7 +268,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf + config.bpp = (bayerFormat.bitDepth + 7) & ~7; + config.patternSize.width = 2; + config.patternSize.height = 2; +- config.outputFormats = std::vector({ formats::RGB888 }); ++ config.outputFormats = std::vector({ formats::RGB888, formats::BGR888 }); + return 0; + } + +@@ -278,7 +278,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf + config.bpp = 10; + config.patternSize.width = 4; /* 5 bytes per *4* pixels */ + config.patternSize.height = 2; +- config.outputFormats = std::vector({ formats::RGB888 }); ++ config.outputFormats = std::vector({ formats::RGB888, formats::BGR888 }); + return 0; + } + +@@ -289,7 +289,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; + } +@@ -325,13 +325,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); + + xShift_ = 0; ++ 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; ++ } + + if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) && + bayerFormat.packing == BayerFormat::Packing::None && +@@ -378,6 +406,7 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi + } + } + ++invalid_fmt: + LOG(Debayer, Error) << "Unsupported input output format combination"; + return -EINVAL; + } +@@ -661,6 +690,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams + gamma_correction_ = params.gamma; + } + ++ if (swapRedBlueGains_) ++ std::swap(params.gainR, params.gainB); ++ + for (unsigned int i = 0; i < kRGBLookupSize; i++) { + constexpr unsigned int div = + kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize; +diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h +index fd1fa180..5f44fc65 100644 +--- a/src/libcamera/software_isp/debayer_cpu.h ++++ b/src/libcamera/software_isp/debayer_cpu.h +@@ -145,6 +145,7 @@ private: + unsigned int lineBufferIndex_; + unsigned int xShift_; /* Offset of 0/1 applied to window_.x */ + bool enableInputMemcpy_; ++ bool swapRedBlueGains_; + float gamma_correction_; + unsigned int measuredFrames_; + int64_t frameProcessTime_; +-- +2.43.2 + 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 deleted file mode 100644 index f639a3b74c..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0015-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch +++ /dev/null @@ -1,243 +0,0 @@ -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-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch b/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch new file mode 100644 index 0000000000..724b67033f --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch @@ -0,0 +1,237 @@ +From e9580d30a1a79fce1ebd72ae74ceb4a3d1cf8fbb Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 19 Dec 2023 11:16:26 +0100 +Subject: [PATCH 16/21] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer + order DNU + +The ov01a1s sensor has the following bayer pattern (4x4 tile repeating): + +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.2 + 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 deleted file mode 100644 index 04388859a5..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0016-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch +++ /dev/null @@ -1,199 +0,0 @@ -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-Add-Software-ISP-benchmarking-documentatio.patch b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch new file mode 100644 index 0000000000..2343e9c46f --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch @@ -0,0 +1,132 @@ +From 6c509a3d144d46a11454d32d128d16e16602b50f Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 11 Mar 2024 15:15:20 +0100 +Subject: [PATCH 17/21] libcamera: Add "Software ISP benchmarking" + documentation + +Add a "Software ISP benchmarking" documentation section which describes +the performance/power consumption measurements used during +the Software ISP's development. + +Reviewed-by: Milan Zamazal +Signed-off-by: Hans de Goede +Reviewed-by: Stefan Klug +--- + 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..b2803953 +--- /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 particularly sensitive to performance regressions ++therefore it is a good idea to always benchmark the Software ISP ++before and after making changes to it and ensure that there are ++no performance regressions. ++ ++DebayerCpu class builtin benchmark ++---------------------------------- ++ ++The DebayerCpu class has a builtin benchmark. This benchmark ++measures the time spent on processing (collecting statistics ++and debayering) only, it does not measure the time spent on ++capturing or outputting the frames. ++ ++The builtin benchmark always runs. So this can be used by simply ++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:: text ++ ++ 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:: shell ++ ++ 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:: shell ++ ++ watch -n 10 cat /sys/class/power_supply/BAT0/power_now /sys/class/hwmon/hwmon6/fan?_input ++ ++Note this not only measures the power consumption in µW it also monitors ++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.5 W 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. 13 W while running qcam versus ++approx. 4-5 W while setting idle with its OLED panel on. +-- +2.43.2 + 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 deleted file mode 100644 index a5a992f876..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch +++ /dev/null @@ -1,237 +0,0 @@ -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 deleted file mode 100644 index 50d826be7a..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch +++ /dev/null @@ -1,125 +0,0 @@ -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/0018-libcamera-software_isp-Apply-black-level-compensatio.patch b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch new file mode 100644 index 0000000000..c746b74dba --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch @@ -0,0 +1,396 @@ +From bb608d177135d74e3c98b8a61fb459ebe254bca5 Mon Sep 17 00:00:00 2001 +From: Milan Zamazal +Date: Mon, 11 Mar 2024 15:15:21 +0100 +Subject: [PATCH 18/21] libcamera: software_isp: Apply black level compensation + +Black may not be represented as 0 pixel value for given hardware, it may be +higher. If this is not compensated then various problems may occur such as low +contrast or suboptimal exposure. + +The black pixel value can be either retrieved from a tuning file for the given +hardware, or automatically on fly. The former is the right and correct method, +while the latter can be used when a tuning file is not available for the given +hardware. Since there is currently no support for tuning files in software ISP, +the automatic, hardware independent way, is always used. Support for tuning +files should be added in future but it will require more work than this patch. + +The patch looks at the image histogram and assumes that black starts when pixel +values start occurring on the left. A certain amount of the darkest pixels is +ignored; it doesn't matter whether they represent various kinds of noise or are +real, they are better to omit in any case to make the image looking better. It +also doesn't matter whether the darkest pixels occur around the supposed black +level or are spread between 0 and the black level, the difference is not +important. + +An arbitrary threshold of 2% darkest pixels is applied; there is no magic about +that value. + +The patch assumes that the black values for different colors are the same and +doesn't attempt any other non-primitive enhancements. It cannot completely +replace tuning files and simplicity, while providing visible benefit, is its +goal. Anything more sophisticated is left for future patches. + +A possible cheap enhancement, if needed, could be setting exposure + gain to +minimum values temporarily, before setting the black level. In theory, the +black level should be fixed but it may not be reached in all images. For this +reason, the patch updates black level only if the observed value is lower than +the current one; it should be never increased. + +The purpose of the patch is to compensate for hardware properties. General +image contrast enhancements are out of scope of this patch. + +Stats are still gathered as an uncorrected histogram, to avoid any confusion and +to represent the raw image data. Exposure must be determined after the black +level correction -- it has no influence on the sub-black area and must be +correct after applying the black level correction. The granularity of the +histogram is increased from 16 to 64 to provide a better precision (there is no +theory behind either of those numbers). + +Reviewed-by: Hans de Goede +Signed-off-by: Milan Zamazal +Signed-off-by: Hans de Goede +--- + .../internal/software_isp/debayer_params.h | 4 + + .../internal/software_isp/swisp_stats.h | 10 ++- + src/ipa/simple/black_level.cpp | 86 +++++++++++++++++++ + src/ipa/simple/black_level.h | 28 ++++++ + src/ipa/simple/meson.build | 7 +- + src/ipa/simple/soft_simple.cpp | 28 ++++-- + src/libcamera/software_isp/debayer_cpu.cpp | 13 ++- + src/libcamera/software_isp/debayer_cpu.h | 1 + + src/libcamera/software_isp/software_isp.cpp | 2 +- + 9 files changed, 162 insertions(+), 17 deletions(-) + create mode 100644 src/ipa/simple/black_level.cpp + create mode 100644 src/ipa/simple/black_level.h + +diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h +index 98965fa1..5e38e08b 100644 +--- a/include/libcamera/internal/software_isp/debayer_params.h ++++ b/include/libcamera/internal/software_isp/debayer_params.h +@@ -43,6 +43,10 @@ struct DebayerParams { + * \brief Gamma correction, 1.0 is no correction + */ + float gamma; ++ /** ++ * \brief Level of the black point, 0..255, 0 is no correction. ++ */ ++ unsigned int blackLevel; + }; + + } /* namespace libcamera */ +diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h +index afe42c9a..25cd5abd 100644 +--- a/include/libcamera/internal/software_isp/swisp_stats.h ++++ b/include/libcamera/internal/software_isp/swisp_stats.h +@@ -7,6 +7,8 @@ + + #pragma once + ++#include ++ + namespace libcamera { + + /** +@@ -28,11 +30,15 @@ struct SwIspStats { + /** + * \brief Number of bins in the yHistogram. + */ +- static constexpr unsigned int kYHistogramSize = 16; ++ static constexpr unsigned int kYHistogramSize = 64; ++ /** ++ * \brief Type of the histogram. ++ */ ++ using histogram = std::array; + /** + * \brief A histogram of luminance values. + */ +- std::array yHistogram; ++ histogram yHistogram; + }; + + } /* namespace libcamera */ +diff --git a/src/ipa/simple/black_level.cpp b/src/ipa/simple/black_level.cpp +new file mode 100644 +index 00000000..8d52201b +--- /dev/null ++++ b/src/ipa/simple/black_level.cpp +@@ -0,0 +1,86 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2024, Red Hat Inc. ++ * ++ * black_level.cpp - black level handling ++ */ ++ ++#include "black_level.h" ++ ++#include ++ ++#include ++ ++namespace libcamera { ++ ++LOG_DEFINE_CATEGORY(IPASoftBL) ++ ++/** ++ * \class BlackLevel ++ * \brief Object providing black point level for software ISP ++ * ++ * Black level can be provided in hardware tuning files or, if no tuning file is ++ * available for the given hardware, guessed automatically, with less accuracy. ++ * As tuning files are not yet implemented for software ISP, BlackLevel ++ * currently provides only guessed black levels. ++ * ++ * This class serves for tracking black level as a property of the underlying ++ * hardware, not as means of enhancing a particular scene or image. ++ * ++ * The class is supposed to be instantiated for the given camera stream. ++ * The black level can be retrieved using BlackLevel::get() method. It is ++ * initially 0 and may change when updated using BlackLevel::update() method. ++ */ ++ ++BlackLevel::BlackLevel() ++ : blackLevel_(255), blackLevelSet_(false) ++{ ++} ++ ++/** ++ * \brief Return the current black level ++ * ++ * \return The black level, in the range from 0 (minimum) to 255 (maximum). ++ * If the black level couldn't be determined yet, return 0. ++ */ ++unsigned int BlackLevel::get() const ++{ ++ return blackLevelSet_ ? blackLevel_ : 0; ++} ++ ++/** ++ * \brief Update black level from the provided histogram ++ * \param[in] yHistogram The histogram to be used for updating black level ++ * ++ * The black level is property of the given hardware, not image. It is updated ++ * only if it has not been yet set or if it is lower than the lowest value seen ++ * so far. ++ */ ++void BlackLevel::update(SwIspStats::histogram &yHistogram) ++{ ++ // The constant is selected to be "good enough", not overly conservative or ++ // aggressive. There is no magic about the given value. ++ constexpr float ignoredPercentage_ = 0.02; ++ const unsigned int total = ++ std::accumulate(begin(yHistogram), end(yHistogram), 0); ++ const unsigned int pixelThreshold = ignoredPercentage_ * total; ++ const unsigned int currentBlackIdx = ++ blackLevel_ / (256 / SwIspStats::kYHistogramSize); ++ ++ for (unsigned int i = 0, seen = 0; ++ i < currentBlackIdx && i < SwIspStats::kYHistogramSize; ++ i++) { ++ seen += yHistogram[i]; ++ if (seen >= pixelThreshold) { ++ blackLevel_ = i * (256 / SwIspStats::kYHistogramSize); ++ blackLevelSet_ = true; ++ LOG(IPASoftBL, Debug) ++ << "Auto-set black level: " ++ << i << "/" << SwIspStats::kYHistogramSize ++ << " (" << 100 * (seen - yHistogram[i]) / total << "% below, " ++ << 100 * seen / total << "% at or below)"; ++ break; ++ } ++ }; ++} ++} // namespace libcamera +diff --git a/src/ipa/simple/black_level.h b/src/ipa/simple/black_level.h +new file mode 100644 +index 00000000..b3785db0 +--- /dev/null ++++ b/src/ipa/simple/black_level.h +@@ -0,0 +1,28 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++/* ++ * Copyright (C) 2024, Red Hat Inc. ++ * ++ * black_level.h - black level handling ++ */ ++ ++#pragma once ++ ++#include ++ ++#include "libcamera/internal/software_isp/swisp_stats.h" ++ ++namespace libcamera { ++ ++class BlackLevel ++{ ++public: ++ BlackLevel(); ++ unsigned int get() const; ++ void update(std::array &yHistogram); ++ ++private: ++ unsigned int blackLevel_; ++ bool blackLevelSet_; ++}; ++ ++} // namespace libcamera +diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build +index 3e863db7..44b5f1d7 100644 +--- a/src/ipa/simple/meson.build ++++ b/src/ipa/simple/meson.build +@@ -2,8 +2,13 @@ + + ipa_name = 'ipa_soft_simple' + ++soft_simple_sources = files([ ++ 'soft_simple.cpp', ++ 'black_level.cpp', ++]) ++ + mod = shared_module(ipa_name, +- ['soft_simple.cpp', libcamera_generated_ipa_headers], ++ [soft_simple_sources, libcamera_generated_ipa_headers], + name_prefix : '', + include_directories : [ipa_includes, libipa_includes], + dependencies : libcamera_private, +diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp +index 312df4ba..ac027568 100644 +--- a/src/ipa/simple/soft_simple.cpp ++++ b/src/ipa/simple/soft_simple.cpp +@@ -22,6 +22,8 @@ + #include "libcamera/internal/software_isp/debayer_params.h" + #include "libcamera/internal/software_isp/swisp_stats.h" + ++#include "black_level.h" ++ + namespace libcamera { + + LOG_DEFINE_CATEGORY(IPASoft) +@@ -33,7 +35,8 @@ class IPASoftSimple : public ipa::soft::IPASoftInterface + public: + IPASoftSimple() + : params_(static_cast(MAP_FAILED)), +- stats_(static_cast(MAP_FAILED)), ignore_updates_(0) ++ stats_(static_cast(MAP_FAILED)), ++ blackLevel_(BlackLevel()), ignore_updates_(0) + { + } + +@@ -63,6 +66,7 @@ private: + SharedFD fdParams_; + DebayerParams *params_; + SwIspStats *stats_; ++ BlackLevel blackLevel_; + + int32_t exposure_min_, exposure_max_; + int32_t again_min_, again_max_; +@@ -196,6 +200,10 @@ void IPASoftSimple::processStats(const ControlList &sensorControls) + params_->gainG = 256; + params_->gamma = 0.5; + ++ if (ignore_updates_ > 0) ++ blackLevel_.update(stats_->yHistogram); ++ params_->blackLevel = blackLevel_.get(); ++ + setIspParams.emit(0); + + /* +@@ -211,18 +219,19 @@ void IPASoftSimple::processStats(const ControlList &sensorControls) + * Calculate Mean Sample Value (MSV) according to formula from: + * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf + */ +- constexpr unsigned int yHistValsPerBin = +- SwIspStats::kYHistogramSize / kExposureBinsCount; +- constexpr unsigned int yHistValsPerBinMod = +- SwIspStats::kYHistogramSize / +- (SwIspStats::kYHistogramSize % kExposureBinsCount + 1); ++ const unsigned int blackLevelHistIdx = ++ params_->blackLevel / (256 / SwIspStats::kYHistogramSize); ++ const unsigned int histogramSize = SwIspStats::kYHistogramSize - blackLevelHistIdx; ++ const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount; ++ const unsigned int yHistValsPerBinMod = ++ histogramSize / (histogramSize % kExposureBinsCount + 1); + int ExposureBins[kExposureBinsCount] = {}; + unsigned int denom = 0; + unsigned int num = 0; + +- for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) { ++ for (unsigned int i = 0; i < histogramSize; i++) { + unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin; +- ExposureBins[idx] += stats_->yHistogram[i]; ++ ExposureBins[idx] += stats_->yHistogram[blackLevelHistIdx + i]; + } + + for (unsigned int i = 0; i < kExposureBinsCount; i++) { +@@ -256,7 +265,8 @@ void IPASoftSimple::processStats(const ControlList &sensorControls) + + LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV + << " exp " << exposure_ << " again " << again_ +- << " gain R/B " << params_->gainR << "/" << params_->gainB; ++ << " gain R/B " << params_->gainR << "/" << params_->gainB ++ << " black level " << params_->blackLevel; + } + + void IPASoftSimple::updateExposure(double exposureMSV) +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +index a1692693..3be3cdfe 100644 +--- a/src/libcamera/software_isp/debayer_cpu.cpp ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -35,7 +35,7 @@ namespace libcamera { + * \param[in] stats Pointer to the stats object to use. + */ + DebayerCpu::DebayerCpu(std::unique_ptr stats) +- : stats_(std::move(stats)), gamma_correction_(1.0) ++ : stats_(std::move(stats)), gamma_correction_(1.0), blackLevel_(0) + { + #ifdef __x86_64__ + enableInputMemcpy_ = false; +@@ -683,11 +683,16 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams + } + + /* Apply DebayerParams */ +- if (params.gamma != gamma_correction_) { +- for (unsigned int i = 0; i < kGammaLookupSize; i++) +- gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma); ++ if (params.gamma != gamma_correction_ || params.blackLevel != blackLevel_) { ++ const unsigned int blackIndex = ++ params.blackLevel * kGammaLookupSize / 256; ++ std::fill(gamma_.begin(), gamma_.begin() + blackIndex, 0); ++ const float divisor = kGammaLookupSize - blackIndex - 1.0; ++ for (unsigned int i = blackIndex; i < kGammaLookupSize; i++) ++ gamma_[i] = UINT8_MAX * powf((i - blackIndex) / divisor, params.gamma); + + gamma_correction_ = params.gamma; ++ blackLevel_ = params.blackLevel; + } + + if (swapRedBlueGains_) +diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h +index 5f44fc65..ea02f909 100644 +--- a/src/libcamera/software_isp/debayer_cpu.h ++++ b/src/libcamera/software_isp/debayer_cpu.h +@@ -147,6 +147,7 @@ private: + bool enableInputMemcpy_; + bool swapRedBlueGains_; + float gamma_correction_; ++ unsigned int blackLevel_; + unsigned int measuredFrames_; + int64_t frameProcessTime_; + /* Skip 30 frames for things to stabilize then measure 30 frames */ +diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp +index 388b4496..9b49be41 100644 +--- a/src/libcamera/software_isp/software_isp.cpp ++++ b/src/libcamera/software_isp/software_isp.cpp +@@ -64,7 +64,7 @@ LOG_DEFINE_CATEGORY(SoftwareIsp) + */ + SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls) + : debayer_(nullptr), +- debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f }, ++ debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 }, + dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System) + { + if (!dmaHeap_.isValid()) { +-- +2.43.2 + diff --git a/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch b/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch new file mode 100644 index 0000000000..5b562c603c --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch @@ -0,0 +1,239 @@ +From b0c07674abecb05dc0af93a4b749971f057bc3c6 Mon Sep 17 00:00:00 2001 +From: Andrei Konovalov +Date: Mon, 11 Mar 2024 15:15:22 +0100 +Subject: [PATCH 19/21] libcamera: Soft IPA: use CameraSensorHelper for + analogue gain + +Use CameraSensorHelper to convert the analogue gain code read from the +camera sensor into real analogue gain value. In the future this makes +it possible to use faster AE/AGC algorithm. For now the same AE/AGC +algorithm is used, but even then the CameraSensorHelper lets us use the +full range of analogue gain values. + +If there is no CameraSensorHelper for the camera sensor in use, a +warning log message is printed, and the AE/AGC works exactly as before +this change. + +Signed-off-by: Andrei Konovalov +Reviewed-by: Hans de Goede +Signed-off-by: Hans de Goede +Reviewed-by: Milan Zamazal +--- + .../internal/software_isp/software_isp.h | 3 +- + src/ipa/simple/soft_simple.cpp | 77 ++++++++++++------- + src/libcamera/pipeline/simple/simple.cpp | 2 +- + src/libcamera/software_isp/software_isp.cpp | 8 +- + 4 files changed, 57 insertions(+), 33 deletions(-) + +diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h +index 8d25e979..2a6db7ba 100644 +--- a/include/libcamera/internal/software_isp/software_isp.h ++++ b/include/libcamera/internal/software_isp/software_isp.h +@@ -26,6 +26,7 @@ + #include + #include + ++#include "libcamera/internal/camera_sensor.h" + #include "libcamera/internal/dma_heaps.h" + #include "libcamera/internal/pipeline_handler.h" + #include "libcamera/internal/shared_mem_object.h" +@@ -43,7 +44,7 @@ LOG_DECLARE_CATEGORY(SoftwareIsp) + class SoftwareIsp + { + public: +- SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls); ++ SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor); + ~SoftwareIsp(); + + int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; } +diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp +index ac027568..e4d64762 100644 +--- a/src/ipa/simple/soft_simple.cpp ++++ b/src/ipa/simple/soft_simple.cpp +@@ -22,6 +22,8 @@ + #include "libcamera/internal/software_isp/debayer_params.h" + #include "libcamera/internal/software_isp/swisp_stats.h" + ++#include "libipa/camera_sensor_helper.h" ++ + #include "black_level.h" + + namespace libcamera { +@@ -67,18 +69,27 @@ private: + DebayerParams *params_; + SwIspStats *stats_; + BlackLevel blackLevel_; ++ std::unique_ptr camHelper_; + + int32_t exposure_min_, exposure_max_; +- int32_t again_min_, again_max_; +- int32_t again_, exposure_; ++ int32_t exposure_; ++ double again_min_, again_max_, againMinStep_; ++ double again_; + unsigned int ignore_updates_; + }; + +-int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings, ++int IPASoftSimple::init(const IPASettings &settings, + const SharedFD &fdStats, + const SharedFD &fdParams, + const ControlInfoMap &sensorInfoMap) + { ++ camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel); ++ if (camHelper_ == nullptr) { ++ LOG(IPASoft, Warning) ++ << "Failed to create camera sensor helper for " ++ << settings.sensorModel; ++ } ++ + fdStats_ = fdStats; + if (!fdStats_.isValid()) { + LOG(IPASoft, Error) << "Invalid Statistics handle"; +@@ -132,25 +143,35 @@ int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap) + exposure_min_ = 1; + } + +- again_min_ = gain_info.min().get(); +- again_max_ = gain_info.max().get(); +- /* +- * The camera sensor gain (g) is usually not equal to the value written +- * into the gain register (x). But the way how the AGC algorithm changes +- * the gain value to make the total exposure closer to the optimum assumes +- * that g(x) is not too far from linear function. If the minimal gain is 0, +- * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c). +- * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near +- * one edge, and very small near the other) we limit the range of the gain +- * values used. +- */ +- if (!again_min_) { +- LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear"; +- again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2); ++ int32_t again_min = gain_info.min().get(); ++ int32_t again_max = gain_info.max().get(); ++ ++ if (camHelper_) { ++ again_min_ = camHelper_->gain(again_min); ++ again_max_ = camHelper_->gain(again_max); ++ againMinStep_ = (again_max_ - again_min_) / 100.0; ++ } else { ++ /* ++ * The camera sensor gain (g) is usually not equal to the value written ++ * into the gain register (x). But the way how the AGC algorithm changes ++ * the gain value to make the total exposure closer to the optimum assumes ++ * that g(x) is not too far from linear function. If the minimal gain is 0, ++ * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c). ++ * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near ++ * one edge, and very small near the other) we limit the range of the gain ++ * values used. ++ */ ++ again_max_ = again_max; ++ if (!again_min) { ++ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear"; ++ again_min_ = std::min(100, again_min / 2 + again_max / 2); ++ } ++ againMinStep_ = 1.0; + } + + LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_ +- << ", gain " << again_min_ << "-" << again_max_; ++ << ", gain " << again_min_ << "-" << again_max_ ++ << " (" << againMinStep_ << ")"; + + return 0; + } +@@ -252,12 +273,14 @@ void IPASoftSimple::processStats(const ControlList &sensorControls) + ControlList ctrls(sensorControls); + + exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get(); +- again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get(); ++ int32_t again = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get(); ++ again_ = camHelper_ ? camHelper_->gain(again) : again; + + updateExposure(exposureMSV); + + ctrls.set(V4L2_CID_EXPOSURE, exposure_); +- ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_); ++ ctrls.set(V4L2_CID_ANALOGUE_GAIN, ++ static_cast(camHelper_ ? camHelper_->gainCode(again_) : again_)); + + ignore_updates_ = 2; + +@@ -276,7 +299,7 @@ void IPASoftSimple::updateExposure(double exposureMSV) + static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1; + static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1; + +- int next; ++ double next; + + if (exposureMSV < kExposureOptimal - kExposureSatisfactory) { + next = exposure_ * kExpNumeratorUp / kExpDenominator; +@@ -286,18 +309,18 @@ void IPASoftSimple::updateExposure(double exposureMSV) + exposure_ = next; + if (exposure_ >= exposure_max_) { + next = again_ * kExpNumeratorUp / kExpDenominator; +- if (next - again_ < 1) +- again_ += 1; ++ if (next - again_ < againMinStep_) ++ again_ += againMinStep_; + else + again_ = next; + } + } + + if (exposureMSV > kExposureOptimal + kExposureSatisfactory) { +- if (exposure_ == exposure_max_ && again_ != again_min_) { ++ if (exposure_ == exposure_max_ && again_ > again_min_) { + next = again_ * kExpNumeratorDown / kExpDenominator; +- if (again_ - next < 1) +- again_ -= 1; ++ if (again_ - next < againMinStep_) ++ again_ -= againMinStep_; + else + again_ = next; + } else { +diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp +index c3ebb7b7..7e932a14 100644 +--- a/src/libcamera/pipeline/simple/simple.cpp ++++ b/src/libcamera/pipeline/simple/simple.cpp +@@ -525,7 +525,7 @@ int SimpleCameraData::init() + * Instantiate Soft ISP if this is enabled for the given driver and no converter is used. + */ + if (!converter_ && pipe->swIspEnabled()) { +- swIsp_ = std::make_unique(pipe, sensor_->controls()); ++ swIsp_ = std::make_unique(pipe, sensor_.get()); + if (!swIsp_->isValid()) { + LOG(SimplePipeline, Warning) + << "Failed to create software ISP, disabling software debayering"; +diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp +index 9b49be41..ea4d96e4 100644 +--- a/src/libcamera/software_isp/software_isp.cpp ++++ b/src/libcamera/software_isp/software_isp.cpp +@@ -60,9 +60,9 @@ LOG_DEFINE_CATEGORY(SoftwareIsp) + /** + * \brief Constructs SoftwareIsp object + * \param[in] pipe The pipeline handler in use +- * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor ++ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline handler + */ +-SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls) ++SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor) + : debayer_(nullptr), + debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 }, + dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System) +@@ -97,10 +97,10 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorCont + return; + } + +- int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" }, ++ int ret = ipa_->init(IPASettings{ "No cfg file", sensor->model() }, + debayer_->getStatsFD(), + sharedParams_.fd(), +- sensorControls); ++ sensor->controls()); + if (ret) { + LOG(SoftwareIsp, Error) << "IPA init failed"; + debayer_.reset(); +-- +2.43.2 + 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 deleted file mode 100644 index cdbd0b9922..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index 768ec07119..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0020-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch +++ /dev/null @@ -1,237 +0,0 @@ -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/0020-ov01a1s-HACK.patch b/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch new file mode 100644 index 0000000000..343f04c850 --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch @@ -0,0 +1,95 @@ +From 2bde6e420571c6dc0ff25246620b4c987987f6be Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 19 Dec 2023 15:45:51 +0100 +Subject: [PATCH 20/21] 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 | 4 ++++ + 3 files changed, 18 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 3be3cdfe..d6599805 100644 +--- a/src/libcamera/software_isp/debayer_cpu.cpp ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -23,6 +23,7 @@ + + namespace libcamera { + ++extern bool is_ov01a1s; + /** + * \class DebayerCpu + * \brief Class for debayering on the CPU +@@ -262,6 +263,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputFormat); + ++ 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)) { +@@ -330,7 +334,11 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputFormat); + ++ if (is_ov01a1s) ++ bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB; ++ + xShift_ = 0; ++ + swapRedBlueGains_ = false; + + switch (outputFormat) { +diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp +index be310f56..cda1894a 100644 +--- a/src/libcamera/software_isp/swstats_cpu.cpp ++++ b/src/libcamera/software_isp/swstats_cpu.cpp +@@ -19,6 +19,7 @@ + + namespace libcamera { + ++extern bool is_ov01a1s; + /** + * \class SwStatsCpu + * \brief Class for gathering statistics on the CPU +@@ -271,6 +272,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg) + BayerFormat bayerFormat = + BayerFormat::fromPixelFormat(inputCfg.pixelFormat); + ++ if (is_ov01a1s) ++ bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB; ++ + if (bayerFormat.packing == BayerFormat::Packing::None && + setupStandardBayerOrder(bayerFormat.order) == 0) { + switch (bayerFormat.bitDepth) { +-- +2.43.2 + diff --git a/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch b/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch new file mode 100644 index 0000000000..a3af38c93c --- /dev/null +++ b/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch @@ -0,0 +1,42 @@ +From a21bb26dcfcc00425f031421b87576f9c81e4824 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 24 Jan 2024 20:44:29 +0100 +Subject: [PATCH 21/21] libcamera: debayer_cpu: Make the minimum size 1280x720 + +pipewire + firefox default to what looks like 640x480 if we export +the entire supported cropping range. Hardcode 720p as minsize for now. + +Signed-off-by: Hans de Goede +--- + src/libcamera/software_isp/debayer_cpu.cpp | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp +index d6599805..5a06b191 100644 +--- a/src/libcamera/software_isp/debayer_cpu.cpp ++++ b/src/libcamera/software_isp/debayer_cpu.cpp +@@ -790,10 +790,17 @@ SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize) + return {}; + } + +- return SizeRange(Size(pattern_size.width, pattern_size.height), +- Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1), +- (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)), +- pattern_size.width, pattern_size.height); ++ /* ++ * pipewire + firefox default to what looks like 640x480 ++ * if we export the entire supported cropping range. ++ * Hardcode 720p as minsize for now. Minsize should be ++ * Size(pattern_size.width, pattern_size.height) ++ */ ++ unsigned int w = (inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1); ++ unsigned int h = (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1); ++ return SizeRange(Size(std::min(w, 1280u), std::min(h, 720u)), ++ Size(w, h), ++ pattern_size.width, pattern_size.height); + } + + } /* namespace libcamera */ +-- +2.43.2 + 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 deleted file mode 100644 index 44985a94e1..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch +++ /dev/null @@ -1,131 +0,0 @@ -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 deleted file mode 100644 index 4054a9563f..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch +++ /dev/null @@ -1,315 +0,0 @@ -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 deleted file mode 100644 index ec49917699..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch +++ /dev/null @@ -1,130 +0,0 @@ -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 deleted file mode 100644 index 3b558e06d0..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0024-ov01a1s-HACK.patch +++ /dev/null @@ -1,95 +0,0 @@ -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 deleted file mode 100644 index 1f267353c5..0000000000 --- a/users/flokli/ipu6-softisp/libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch +++ /dev/null @@ -1,40 +0,0 @@ -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