diff options
Diffstat (limited to 'users/flokli/ipu6-softisp')
24 files changed, 159 insertions, 22346 deletions
diff --git a/users/flokli/ipu6-softisp/config.nix b/users/flokli/ipu6-softisp/config.nix index 95cb3e5c2563..0e47c88fbbad 100644 --- a/users/flokli/ipu6-softisp/config.nix +++ b/users/flokli/ipu6-softisp/config.nix @@ -12,27 +12,9 @@ let # 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-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 + ./libcamera/0001-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch + ./libcamera/0002-ov01a1s-HACK.patch + ./libcamera/0003-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch ]; }); diff --git a/users/flokli/ipu6-softisp/default.nix b/users/flokli/ipu6-softisp/default.nix index 66a2f04a5133..ab129d1bc85e 100644 --- a/users/flokli/ipu6-softisp/default.nix +++ b/users/flokli/ipu6-softisp/default.nix @@ -11,8 +11,8 @@ let systemFor = sys: (depot.ops.nixos.nixosFor sys).system; in -depot.nix.readTree.drvTargets rec { - testSystem = systemFor ({ modulesPath, pkgs, ... }: { +(depot.nix.readTree.drvTargets rec { + testSystem = (systemFor ({ modulesPath, pkgs, ... }: { imports = [ # Import the module, this is something a user would do in their config. ./config.nix @@ -39,7 +39,10 @@ depot.nix.readTree.drvTargets rec { # Shut off the warning. system.stateVersion = "24.05"; - }); + })) // { + # 2024-07-28 aspen: The patches no longer cleanly apply on upstream + meta.broken = true; + }; # Make sure the firmware requested by the driver is present in our firmware. # We do have a .zst suffix here, but that's fine, since request_firmware does @@ -54,4 +57,4 @@ depot.nix.readTree.drvTargets rec { # all good, succeed build touch $out ''; -} +}) diff --git a/users/flokli/ipu6-softisp/kernel/softisp.patch b/users/flokli/ipu6-softisp/kernel/softisp.patch index 8731ed914c1d..e1395bbb135f 100644 --- a/users/flokli/ipu6-softisp/kernel/softisp.patch +++ b/users/flokli/ipu6-softisp/kernel/softisp.patch @@ -1,16249 +1,7 @@ -From 037f05a9772f3243907bb826e913971ee20e9487 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus <sakari.ailus@linux.intel.com> -Date: Tue, 8 Aug 2023 10:55:31 +0300 -Subject: [PATCH 01/33] media: mc: Add INTERNAL pad flag - -Internal source pads will be used as routing endpoints in V4L2 -[GS]_ROUTING IOCTLs, to indicate that the stream begins in the entity. - -Also prevent creating links to pads that have been flagged as internal. - -Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> ---- - Documentation/userspace-api/media/glossary.rst | 6 ++++++ - Documentation/userspace-api/media/mediactl/media-types.rst | 6 ++++++ - drivers/media/mc/mc-entity.c | 6 +++++- - include/uapi/linux/media.h | 1 + - 4 files changed, 18 insertions(+), 1 deletion(-) - -diff --git a/Documentation/userspace-api/media/glossary.rst b/Documentation/userspace-api/media/glossary.rst -index 96a360edbf3b..f7b99a4527c7 100644 ---- a/Documentation/userspace-api/media/glossary.rst -+++ b/Documentation/userspace-api/media/glossary.rst -@@ -173,6 +173,12 @@ Glossary - An integrated circuit that integrates all components of a computer - or other electronic systems. - -+_media-glossary-stream: -+ Stream -+ A distinct flow of data (image data or metadata) over a media pipeline -+ from source to sink. A source may be e.g. an image sensor and a sink -+ e.g. a memory buffer. -+ - V4L2 API - **V4L2 userspace API** - -diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst -index 0ffeece1e0c8..28941da27790 100644 ---- a/Documentation/userspace-api/media/mediactl/media-types.rst -+++ b/Documentation/userspace-api/media/mediactl/media-types.rst -@@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements - .. _MEDIA-PAD-FL-SINK: - .. _MEDIA-PAD-FL-SOURCE: - .. _MEDIA-PAD-FL-MUST-CONNECT: -+.. _MEDIA-PAD-FL-INTERNAL: - - .. flat-table:: Media pad flags - :header-rows: 0 -@@ -382,6 +383,11 @@ Types and flags used to represent the media graph elements - when this flag isn't set; the absence of the flag doesn't imply - there is none. - -+ * - ``MEDIA_PAD_FL_INTERNAL`` -+ - The internal flag indicates an internal pad that has no external -+ connections. Such a pad shall not be connected with a link. The -+ internal flag indicates that the :ref:``stream -+ <media-glossary-stream>`` either starts or ends in the entity. - - One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE`` - must be set for every pad. -diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c -index 543a392f8635..1fc80fd3e5e3 100644 ---- a/drivers/media/mc/mc-entity.c -+++ b/drivers/media/mc/mc-entity.c -@@ -1075,7 +1075,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type, - - for (i = 0; i < entity->num_pads; i++) { - if ((entity->pads[i].flags & -- (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type) -+ (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE | -+ MEDIA_PAD_FL_INTERNAL)) != pad_type) - continue; - - if (entity->pads[i].sig_type == sig_type) -@@ -1098,6 +1099,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad, - return -EINVAL; - if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE))) - return -EINVAL; -+ if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE && -+ source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL)) -+ return -EINVAL; - if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) - return -EINVAL; - -diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h -index 1c80b1d6bbaf..80cfd12a43fc 100644 ---- a/include/uapi/linux/media.h -+++ b/include/uapi/linux/media.h -@@ -208,6 +208,7 @@ struct media_entity_desc { - #define MEDIA_PAD_FL_SINK (1U << 0) - #define MEDIA_PAD_FL_SOURCE (1U << 1) - #define MEDIA_PAD_FL_MUST_CONNECT (1U << 2) -+#define MEDIA_PAD_FL_INTERNAL (1U << 3) - - struct media_pad_desc { - __u32 entity; /* entity ID */ --- -2.43.2 - - -From 5f0cdae874f1c0237936c2c12a9fc019b93de4c9 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus <sakari.ailus@linux.intel.com> -Date: Tue, 8 Aug 2023 10:55:32 +0300 -Subject: [PATCH 02/33] media: uapi: Add generic serial metadata mbus formats - -Add generic serial metadata mbus formats. These formats describe data -width and packing but not the content itself. The reason for specifying -such formats is that the formats as such are fairly device specific but -they are still handled by CSI-2 receiver drivers that should not be aware -of device specific formats. What makes generic metadata formats possible -is that these formats are parsed by software only, after capturing the -data to system memory. - -Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> ---- - .../media/v4l/subdev-formats.rst | 257 ++++++++++++++++++ - include/uapi/linux/media-bus-format.h | 9 + - 2 files changed, 266 insertions(+) - -diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst -index eb3cd20b0cf2..7d107873cddd 100644 ---- a/Documentation/userspace-api/media/v4l/subdev-formats.rst -+++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst -@@ -8306,3 +8306,260 @@ The following table lists the existing metadata formats. - both sides of the link and the bus format is a fixed - metadata format that is not configurable from userspace. - Width and height will be set to 0 for this format. -+ -+Generic Serial Metadata Formats -+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -+ -+Generic serial metadata formats are used on serial busses where the actual data -+content is more or less device specific but the data is transmitted and received -+by multiple devices that do not process the data in any way, simply writing -+it to system memory for processing in software at the end of the pipeline. -+ -+The more specific variant describing the actual data is used on the internal -+source pad of the originating sub-device. -+ -+"b" in an array cell signifies a byte of data, followed by the number of byte -+and finally the bit number in subscript. "p" indicates a padding bit. -+ -+.. _media-bus-format-generic-meta: -+ -+.. cssclass: longtable -+ -+.. flat-table:: Generic Serial Metadata Formats -+ :header-rows: 2 -+ :stub-columns: 0 -+ -+ * - Identifier -+ - Code -+ - -+ - :cspan:`23` Data organization -+ * - -+ - -+ - Bit -+ - 23 -+ - 22 -+ - 21 -+ - 20 -+ - 19 -+ - 18 -+ - 17 -+ - 16 -+ - 15 -+ - 14 -+ - 13 -+ - 12 -+ - 11 -+ - 10 -+ - 9 -+ - 8 -+ - 7 -+ - 6 -+ - 5 -+ - 4 -+ - 3 -+ - 2 -+ - 1 -+ - 0 -+ * .. _MEDIA-BUS-FMT-META-8: -+ -+ - MEDIA_BUS_FMT_META_8 -+ - 0x8001 -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - b0\ :sub:`7` -+ - b0\ :sub:`6` -+ - b0\ :sub:`5` -+ - b0\ :sub:`4` -+ - b0\ :sub:`3` -+ - b0\ :sub:`2` -+ - b0\ :sub:`1` -+ - b0\ :sub:`0` -+ * .. _MEDIA-BUS-FMT-META-10: -+ -+ - MEDIA_BUS_FMT_META_10 -+ - 0x8002 -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - b0\ :sub:`7` -+ - b0\ :sub:`6` -+ - b0\ :sub:`5` -+ - b0\ :sub:`4` -+ - b0\ :sub:`3` -+ - b0\ :sub:`2` -+ - b0\ :sub:`1` -+ - b0\ :sub:`0` -+ - p -+ - p -+ * .. _MEDIA-BUS-FMT-META-12: -+ -+ - MEDIA_BUS_FMT_META_12 -+ - 0x8003 -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - b0\ :sub:`7` -+ - b0\ :sub:`6` -+ - b0\ :sub:`5` -+ - b0\ :sub:`4` -+ - b0\ :sub:`3` -+ - b0\ :sub:`2` -+ - b0\ :sub:`1` -+ - b0\ :sub:`0` -+ - p -+ - p -+ - p -+ - p -+ * .. _MEDIA-BUS-FMT-META-14: -+ -+ - MEDIA_BUS_FMT_META_14 -+ - 0x8004 -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - b0\ :sub:`7` -+ - b0\ :sub:`6` -+ - b0\ :sub:`5` -+ - b0\ :sub:`4` -+ - b0\ :sub:`3` -+ - b0\ :sub:`2` -+ - b0\ :sub:`1` -+ - b0\ :sub:`0` -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ * .. _MEDIA-BUS-FMT-META-16: -+ -+ - MEDIA_BUS_FMT_META_16 -+ - 0x8005 -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - -+ - b0\ :sub:`7` -+ - b0\ :sub:`6` -+ - b0\ :sub:`5` -+ - b0\ :sub:`4` -+ - b0\ :sub:`3` -+ - b0\ :sub:`2` -+ - b0\ :sub:`1` -+ - b0\ :sub:`0` -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ * .. _MEDIA-BUS-FMT-META-20: -+ -+ - MEDIA_BUS_FMT_META_20 -+ - 0x8007 -+ - -+ - -+ - -+ - -+ - -+ - b0\ :sub:`7` -+ - b0\ :sub:`6` -+ - b0\ :sub:`5` -+ - b0\ :sub:`4` -+ - b0\ :sub:`3` -+ - b0\ :sub:`2` -+ - b0\ :sub:`1` -+ - b0\ :sub:`0` -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ * .. _MEDIA-BUS-FMT-META-24: -+ -+ - MEDIA_BUS_FMT_META_24 -+ - 0x8009 -+ - -+ - b0\ :sub:`7` -+ - b0\ :sub:`6` -+ - b0\ :sub:`5` -+ - b0\ :sub:`4` -+ - b0\ :sub:`3` -+ - b0\ :sub:`2` -+ - b0\ :sub:`1` -+ - b0\ :sub:`0` -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -+ - p -diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h -index f05f747e444d..d4c1d991014b 100644 ---- a/include/uapi/linux/media-bus-format.h -+++ b/include/uapi/linux/media-bus-format.h -@@ -174,4 +174,13 @@ - */ - #define MEDIA_BUS_FMT_METADATA_FIXED 0x7001 - -+/* Generic line based metadata formats for serial buses. Next is 0x8008. */ -+#define MEDIA_BUS_FMT_META_8 0x8001 -+#define MEDIA_BUS_FMT_META_10 0x8002 -+#define MEDIA_BUS_FMT_META_12 0x8003 -+#define MEDIA_BUS_FMT_META_14 0x8004 -+#define MEDIA_BUS_FMT_META_16 0x8005 -+#define MEDIA_BUS_FMT_META_20 0x8006 -+#define MEDIA_BUS_FMT_META_24 0x8007 -+ - #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ --- -2.43.2 - - -From 8af4eeaee34159605ec86b57fa638a82fd968f31 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus <sakari.ailus@linux.intel.com> -Date: Tue, 8 Aug 2023 10:55:33 +0300 -Subject: [PATCH 03/33] media: uapi: Document which mbus format fields are - valid for metadata - -Now that metadata mbus formats have been added, it is necessary to define -which fields in struct v4l2_mbus_format are applicable to them (not many). - -Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> ---- - include/uapi/linux/v4l2-mediabus.h | 18 ++++++++++++------ - 1 file changed, 12 insertions(+), 6 deletions(-) - -diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h -index 6b07b73473b5..3cadb3b58b85 100644 ---- a/include/uapi/linux/v4l2-mediabus.h -+++ b/include/uapi/linux/v4l2-mediabus.h -@@ -19,12 +19,18 @@ - * @width: image width - * @height: image height - * @code: data format code (from enum v4l2_mbus_pixelcode) -- * @field: used interlacing type (from enum v4l2_field) -- * @colorspace: colorspace of the data (from enum v4l2_colorspace) -- * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding) -- * @hsv_enc: HSV encoding of the data (from enum v4l2_hsv_encoding) -- * @quantization: quantization of the data (from enum v4l2_quantization) -- * @xfer_func: transfer function of the data (from enum v4l2_xfer_func) -+ * @field: used interlacing type (from enum v4l2_field), not applicable -+ * to metadata mbus codes -+ * @colorspace: colorspace of the data (from enum v4l2_colorspace), zero on -+ * metadata mbus codes -+ * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding), zero -+ * on metadata mbus codes -+ * @hsv_enc: HSV encoding of the data (from enum v4l2_hsv_encoding), zero on -+ * metadata mbus codes -+ * @quantization: quantization of the data (from enum v4l2_quantization), zero -+ * on metadata mbus codes -+ * @xfer_func: transfer function of the data (from enum v4l2_xfer_func), zero -+ * on metadata mbus codes - * @flags: flags (V4L2_MBUS_FRAMEFMT_*) - * @reserved: reserved bytes that can be later used - */ --- -2.43.2 - - -From ed5884d40def9adfa77841427e52733746158a77 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus <sakari.ailus@linux.intel.com> -Date: Tue, 8 Aug 2023 10:55:34 +0300 -Subject: [PATCH 04/33] media: uapi: Add a macro to tell whether an mbus code - is metadata - -Add a macro to tell whether a given mbus code is metadata. - -Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> ---- - include/uapi/linux/media-bus-format.h | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h -index d4c1d991014b..6fcd7d276bc6 100644 ---- a/include/uapi/linux/media-bus-format.h -+++ b/include/uapi/linux/media-bus-format.h -@@ -183,4 +183,7 @@ - #define MEDIA_BUS_FMT_META_20 0x8006 - #define MEDIA_BUS_FMT_META_24 0x8007 - -+#define MEDIA_BUS_FMT_IS_META(code) \ -+ ((code) & 0xf000 == 0x7000 || (code) & 0xf000 == 0x8000) -+ - #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ --- -2.43.2 - - -From c0e682a815c5baf012c8963968385c197e7e0943 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus <sakari.ailus@linux.intel.com> -Date: Tue, 8 Aug 2023 10:55:35 +0300 -Subject: [PATCH 05/33] media: uapi: Add generic 8-bit metadata format - definitions - -Generic 8-bit metadata formats define the in-memory data layout but not -the format of the data itself. The reasoning for having such formats is to -allow CSI-2 receiver drivers to receive and DMA drivers to write the data -to memory without knowing a large number of device specific formats. - -These formats may be used only in conjunction of a Media controller -pipeline where the internal pad of the source sub-device defines the -specific format of the data (using an mbus code). - -Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> ---- - .../userspace-api/media/v4l/meta-formats.rst | 1 + - .../media/v4l/metafmt-generic.rst | 331 ++++++++++++++++++ - drivers/media/v4l2-core/v4l2-ioctl.c | 8 + - include/uapi/linux/videodev2.h | 9 + - 4 files changed, 349 insertions(+) - create mode 100644 Documentation/userspace-api/media/v4l/metafmt-generic.rst - -diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst -index 0bb61fc5bc00..919f595576b9 100644 ---- a/Documentation/userspace-api/media/v4l/meta-formats.rst -+++ b/Documentation/userspace-api/media/v4l/meta-formats.rst -@@ -19,3 +19,4 @@ These formats are used for the :ref:`metadata` interface only. - metafmt-vsp1-hgo - metafmt-vsp1-hgt - metafmt-vivid -+ metafmt-generic -diff --git a/Documentation/userspace-api/media/v4l/metafmt-generic.rst b/Documentation/userspace-api/media/v4l/metafmt-generic.rst -new file mode 100644 -index 000000000000..a27bfc721edf ---- /dev/null -+++ b/Documentation/userspace-api/media/v4l/metafmt-generic.rst -@@ -0,0 +1,331 @@ -+.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later -+ -+************************************************************************************************************************************************************************************************************************************************************************************************************************** -+V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O'), V4L2_META_FMT_GENERIC_CSI2_2_24 ('MC2O') -+************************************************************************************************************************************************************************************************************************************************************************************************************************** -+ -+ -+Generic line-based metadata formats -+ -+ -+Description -+=========== -+ -+These generic line-based metadata formats define the memory layout of the data -+without defining the format or meaning of the metadata itself. These formats may -+only be used with a Media controller pipeline where the more specific format is -+defined in an :ref:`internal source pad <MEDIA-PAD-FL-INTERNAL>` of the source -+sub-device. See also :ref:`source routes <v4l2-subdev-source-routes>`. -+ -+.. _v4l2-meta-fmt-generic-8: -+ -+V4L2_META_FMT_GENERIC_8 -+----------------------- -+ -+The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format. -+ -+This format is also used on CSI-2 on both 8 bits per sample as well as on -+16 bits per sample when two bytes of metadata are packed into one sample. -+ -+**Byte Order Of V4L2_META_FMT_GENERIC_8.** -+Each cell is one byte. "M" denotes a byte of metadata. -+ -+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}| -+ -+.. flat-table:: -+ :header-rows: 0 -+ :stub-columns: 0 -+ :widths: 12 8 8 8 8 -+ -+ * - start + 0: -+ - M\ :sub:`00` -+ - M\ :sub:`10` -+ - M\ :sub:`20` -+ - M\ :sub:`30` -+ * - start + 4: -+ - M\ :sub:`01` -+ - M\ :sub:`11` -+ - M\ :sub:`21` -+ - M\ :sub:`31` -+ -+.. _v4l2-meta-fmt-generic-csi2-10: -+ -+V4L2_META_FMT_GENERIC_CSI2_10 -+----------------------------- -+ -+V4L2_META_FMT_GENERIC_CSI2_10 contains packed 8-bit generic metadata, 10 bits -+for each 8 bits of data. Every four bytes of metadata is followed by a single -+byte of padding. The way the data is stored follows the CSI-2 specification. -+ -+This format is also used on CSI-2 on 20 bits per sample format that packs two -+bytes of metadata into one sample. -+ -+This format is little endian. -+ -+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.** -+Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding. -+ -+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}| -+ -+.. flat-table:: -+ :header-rows: 0 -+ :stub-columns: 0 -+ :widths: 12 8 8 8 8 8 -+ -+ * - start + 0: -+ - M\ :sub:`00` -+ - M\ :sub:`10` -+ - M\ :sub:`20` -+ - M\ :sub:`30` -+ - p -+ * - start + 5: -+ - M\ :sub:`01` -+ - M\ :sub:`11` -+ - M\ :sub:`21` -+ - M\ :sub:`31` -+ - p -+ -+.. _v4l2-meta-fmt-generic-csi2-12: -+ -+V4L2_META_FMT_GENERIC_CSI2_12 -+----------------------------- -+ -+V4L2_META_FMT_GENERIC_CSI2_12 contains packed 8-bit generic metadata, 12 bits -+for each 8 bits of data. Every four bytes of metadata is followed by two bytes -+of padding. The way the data is stored follows the CSI-2 specification. -+ -+This format is little endian. -+ -+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.** -+Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding. -+ -+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}| -+ -+.. flat-table:: -+ :header-rows: 0 -+ :stub-columns: 0 -+ :widths: 12 8 8 8 8 8 8 -+ -+ * - start + 0: -+ - M\ :sub:`00` -+ - M\ :sub:`10` -+ - M\ :sub:`20` -+ - M\ :sub:`30` -+ - p -+ - p -+ * - start + 6: -+ - M\ :sub:`01` -+ - M\ :sub:`11` -+ - M\ :sub:`21` -+ - M\ :sub:`31` -+ - p -+ - p -+ -+.. _v4l2-meta-fmt-generic-csi2-14: -+ -+V4L2_META_FMT_GENERIC_CSI2_14 -+----------------------------- -+ -+V4L2_META_FMT_GENERIC_CSI2_14 contains packed 8-bit generic metadata, 14 bits -+for each 8 bits of data. Every four bytes of metadata is followed by three -+bytes of padding. The way the data is stored follows the CSI-2 specification. -+ -+This format is little endian. -+ -+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.** -+Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding. -+ -+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}| -+ -+.. flat-table:: -+ :header-rows: 0 -+ :stub-columns: 0 -+ :widths: 12 8 8 8 8 8 8 8 -+ -+ * - start + 0: -+ - M\ :sub:`00` -+ - M\ :sub:`10` -+ - M\ :sub:`20` -+ - M\ :sub:`30` -+ - p -+ - p -+ - p -+ * - start + 7: -+ - M\ :sub:`01` -+ - M\ :sub:`11` -+ - M\ :sub:`21` -+ - M\ :sub:`31` -+ - p -+ - p -+ - p -+ -+.. _v4l2-meta-fmt-generic-csi2-16: -+ -+V4L2_META_FMT_GENERIC_CSI2_16 -+----------------------------- -+ -+V4L2_META_FMT_GENERIC_CSI2_16 contains packed 8-bit generic metadata, 16 bits -+for each 8 bits of data. Every byte of metadata is followed by one byte of -+padding. The way the data is stored follows the CSI-2 specification. -+ -+This format is little endian. -+ -+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.** -+Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding. -+ -+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}| -+ -+.. flat-table:: -+ :header-rows: 0 -+ :stub-columns: 0 -+ :widths: 12 8 8 8 8 8 8 8 8 -+ -+ * - start + 0: -+ - M\ :sub:`00` -+ - p -+ - M\ :sub:`10` -+ - p -+ - M\ :sub:`20` -+ - p -+ - M\ :sub:`30` -+ - p -+ * - start + 8: -+ - M\ :sub:`01` -+ - p -+ - M\ :sub:`11` -+ - p -+ - M\ :sub:`21` -+ - p -+ - M\ :sub:`31` -+ - p -+ -+.. _v4l2-meta-fmt-generic-csi2-20: -+ -+V4L2_META_FMT_GENERIC_CSI2_20 -+----------------------------- -+ -+V4L2_META_FMT_GENERIC_CSI2_20 contains packed 8-bit generic metadata, 20 bits -+for each 8 bits of data. Every byte of metadata is followed by alternating one -+and two bytes of padding. The way the data is stored follows the CSI-2 -+specification. -+ -+This format is little endian. -+ -+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.** -+Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding. -+ -+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}| -+ -+.. flat-table:: -+ :header-rows: 0 -+ :stub-columns: 0 -+ :widths: 12 8 8 8 8 8 8 8 8 8 8 -+ -+ * - start + 0: -+ - M\ :sub:`00` -+ - p -+ - M\ :sub:`10` -+ - p -+ - p -+ - M\ :sub:`20` -+ - p -+ - M\ :sub:`30` -+ - p -+ - p -+ * - start + 10: -+ - M\ :sub:`01` -+ - p -+ - M\ :sub:`11` -+ - p -+ - p -+ - M\ :sub:`21` -+ - p -+ - M\ :sub:`31` -+ - p -+ - p -+ -+.. _v4l2-meta-fmt-generic-csi2-24: -+ -+V4L2_META_FMT_GENERIC_CSI2_24 -+----------------------------- -+ -+V4L2_META_FMT_GENERIC_CSI2_24 contains packed 8-bit generic metadata, 24 bits -+for each 8 bits of data. Every byte of metadata is followed by two bytes of -+padding. The way the data is stored follows the CSI-2 specification. -+ -+This format is little endian. -+ -+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.** -+Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding. -+ -+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}| -+ -+.. flat-table:: -+ :header-rows: 0 -+ :stub-columns: 0 -+ :widths: 12 8 8 8 8 8 8 8 8 8 8 8 8 -+ -+ * - start + 0: -+ - M\ :sub:`00` -+ - p -+ - p -+ - M\ :sub:`10` -+ - p -+ - p -+ - M\ :sub:`20` -+ - p -+ - p -+ - M\ :sub:`30` -+ - p -+ - p -+ * - start + 12: -+ - M\ :sub:`01` -+ - p -+ - p -+ - M\ :sub:`11` -+ - p -+ - p -+ - M\ :sub:`21` -+ - p -+ - p -+ - M\ :sub:`31` -+ - p -+ - p -+ -+.. _v4l2-meta-fmt-generic-csi2-2-24: -+ -+V4L2_META_FMT_GENERIC_CSI2_2_24 -+------------------------------- -+ -+V4L2_META_FMT_GENERIC_CSI2_2_24 contains packed 8-bit generic metadata, 24 bits -+for each two times 8 bits of data. Every two bytes of metadata are followed by -+one byte of padding. The way the data is stored follows the CSI-2 -+specification. -+ -+This format is little endian. -+ -+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_2_24.** -+Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding. -+ -+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{1.2cm}|p{.8cm}| -+ -+.. flat-table:: -+ :header-rows: 0 -+ :stub-columns: 0 -+ :widths: 12 8 8 8 8 8 8 -+ -+ * - start + 0: -+ - M\ :sub:`00` -+ - M\ :sub:`10` -+ - p -+ - M\ :sub:`20` -+ - M\ :sub:`30` -+ - p -+ * - start + 6: -+ - M\ :sub:`01` -+ - M\ :sub:`11` -+ - p -+ - M\ :sub:`21` -+ - M\ :sub:`31` -+ - p -+ -diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c -index 33076af4dfdb..4eb3db1773e1 100644 ---- a/drivers/media/v4l2-core/v4l2-ioctl.c -+++ b/drivers/media/v4l2-core/v4l2-ioctl.c -@@ -1452,6 +1452,14 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) - case V4L2_PIX_FMT_Y210: descr = "10-bit YUYV Packed"; break; - case V4L2_PIX_FMT_Y212: descr = "12-bit YUYV Packed"; break; - case V4L2_PIX_FMT_Y216: descr = "16-bit YUYV Packed"; break; -+ case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break; -+ case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8b Generic Meta, 10b CSI-2"; break; -+ case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8b Generic Meta, 12b CSI-2"; break; -+ case V4L2_META_FMT_GENERIC_CSI2_14: descr = "8b Generic Meta, 14b CSI-2"; break; -+ case V4L2_META_FMT_GENERIC_CSI2_16: descr = "8b Generic Meta, 16b CSI-2"; break; -+ case V4L2_META_FMT_GENERIC_CSI2_20: descr = "8b Generic Meta, 20b CSI-2"; break; -+ case V4L2_META_FMT_GENERIC_CSI2_24: descr = "8b Generic Meta, 24b CSI-2"; break; -+ case V4L2_META_FMT_GENERIC_CSI2_2_24: descr = "2x8b Generic Meta, 24b CSI-2"; break; - - default: - /* Compressed formats */ -diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h -index 68e7ac178cc2..2c4e03d47789 100644 ---- a/include/uapi/linux/videodev2.h -+++ b/include/uapi/linux/videodev2.h -@@ -839,6 +839,15 @@ struct v4l2_pix_format { - #define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */ - #define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */ - -+#define V4L2_META_FMT_GENERIC_8 v4l2_fourcc('M', 'E', 'T', '8') /* Generic 8-bit metadata */ -+#define V4L2_META_FMT_GENERIC_CSI2_10 v4l2_fourcc('M', 'C', '1', 'A') /* 10-bit CSI-2 packed 8-bit metadata */ -+#define V4L2_META_FMT_GENERIC_CSI2_12 v4l2_fourcc('M', 'C', '1', 'C') /* 12-bit CSI-2 packed 8-bit metadata */ -+#define V4L2_META_FMT_GENERIC_CSI2_14 v4l2_fourcc('M', 'C', '1', 'E') /* 14-bit CSI-2 packed 8-bit metadata */ -+#define V4L2_META_FMT_GENERIC_CSI2_16 v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */ -+#define V4L2_META_FMT_GENERIC_CSI2_20 v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */ -+#define V4L2_META_FMT_GENERIC_CSI2_24 v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */ -+#define V4L2_META_FMT_GENERIC_CSI2_2_24 v4l2_fourcc('M', 'C', '2', 'O') /* 2 bytes of 8-bit metadata, 24-bit CSI-2 packed */ -+ - /* priv field value to indicates that subsequent fields are valid. */ - #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe - --- -2.43.2 - - -From 453627c23062ff0aa01e0e46e3b7922ddf82f998 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus <sakari.ailus@linux.intel.com> -Date: Tue, 8 Aug 2023 10:55:36 +0300 -Subject: [PATCH 06/33] media: v4l: Support line-based metadata capture - -many camera sensors, among other devices, transmit embedded data and image -data for each CSI-2 frame. This embedded data typically contains register -configuration of the sensor that has been used to capture the image data -of the same frame. - -The embedded data is received by the CSI-2 receiver and has the same -properties as the image data, including that it is line based: it has -width, height and bytesperline (stride). - -Add these fields to struct v4l2_meta_format and document them. - -Also add V4L2_FMT_FLAG_META_LINE_BASED to tell a given format is -line-based i.e. these fields of struct v4l2_meta_format are valid for it. - -Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> ---- - .../userspace-api/media/v4l/dev-meta.rst | 15 +++++++++++++++ - .../userspace-api/media/v4l/vidioc-enum-fmt.rst | 7 +++++++ - .../media/videodev2.h.rst.exceptions | 1 + - drivers/media/v4l2-core/v4l2-ioctl.c | 5 +++-- - include/uapi/linux/videodev2.h | 10 ++++++++++ - 5 files changed, 36 insertions(+), 2 deletions(-) - -diff --git a/Documentation/userspace-api/media/v4l/dev-meta.rst b/Documentation/userspace-api/media/v4l/dev-meta.rst -index 0e7e1ee1471a..4b24bae6e171 100644 ---- a/Documentation/userspace-api/media/v4l/dev-meta.rst -+++ b/Documentation/userspace-api/media/v4l/dev-meta.rst -@@ -65,3 +65,18 @@ to 0. - - ``buffersize`` - - Maximum buffer size in bytes required for data. The value is set by the - driver. -+ * - __u32 -+ - ``width`` -+ - Width of a line of metadata in samples. Valid when :c:type`v4l2_fmtdesc` -+ flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See -+ :c:func:`VIDIOC_ENUM_FMT`. -+ * - __u32 -+ - ``height`` -+ - Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag -+ ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See -+ :c:func:`VIDIOC_ENUM_FMT`. -+ * - __u32 -+ - ``bytesperline`` -+ - Offset in bytes between the beginning of two consecutive lines. Valid -+ when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is -+ set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`. -diff --git a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst -index 000c154b0f98..6d7664345a4e 100644 ---- a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst -+++ b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst -@@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently: - The application can ask to configure the quantization of the capture - device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with - :ref:`V4L2_PIX_FMT_FLAG_SET_CSC <v4l2-pix-fmt-flag-set-csc>` set. -+ * - ``V4L2_FMT_FLAG_META_LINE_BASED`` -+ - 0x0200 -+ - The metadata format is line-based. In this case the ``width``, -+ ``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are -+ valid. The buffer consists of ``height`` lines, each having ``width`` -+ bytes of data and offset between the beginning of each two consecutive -+ lines is ``bytesperline``. - - Return Value - ============ -diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions -index 3e58aac4ef0b..bdc628e8c1d6 100644 ---- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions -+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions -@@ -215,6 +215,7 @@ replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags - replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags - replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags - replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags -+replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags - - # V4L2 timecode types - replace define V4L2_TC_TYPE_24FPS timecode-type -diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c -index 4eb3db1773e1..1fcec1515bcc 100644 ---- a/drivers/media/v4l2-core/v4l2-ioctl.c -+++ b/drivers/media/v4l2-core/v4l2-ioctl.c -@@ -343,8 +343,9 @@ static void v4l_print_format(const void *arg, bool write_only) - case V4L2_BUF_TYPE_META_OUTPUT: - meta = &p->fmt.meta; - pixelformat = meta->dataformat; -- pr_cont(", dataformat=%p4cc, buffersize=%u\n", -- &pixelformat, meta->buffersize); -+ pr_cont(", dataformat=%p4cc, buffersize=%u, width=%u, height=%u, bytesperline=%u\n", -+ &pixelformat, meta->buffersize, meta->width, -+ meta->height, meta->bytesperline); - break; - } - } -diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h -index 2c4e03d47789..48fb44772098 100644 ---- a/include/uapi/linux/videodev2.h -+++ b/include/uapi/linux/videodev2.h -@@ -878,6 +878,7 @@ struct v4l2_fmtdesc { - #define V4L2_FMT_FLAG_CSC_YCBCR_ENC 0x0080 - #define V4L2_FMT_FLAG_CSC_HSV_ENC V4L2_FMT_FLAG_CSC_YCBCR_ENC - #define V4L2_FMT_FLAG_CSC_QUANTIZATION 0x0100 -+#define V4L2_FMT_FLAG_META_LINE_BASED 0x0200 - - /* Frame Size and frame rate enumeration */ - /* -@@ -2424,10 +2425,19 @@ struct v4l2_sdr_format { - * struct v4l2_meta_format - metadata format definition - * @dataformat: little endian four character code (fourcc) - * @buffersize: maximum size in bytes required for data -+ * @width: number of bytes of data per line (valid for line based -+ * formats only, see format documentation) -+ * @height: number of lines of data per buffer (valid for line based -+ * formats only) -+ * @bytesperline: offset between the beginnings of two adjacent lines in -+ * bytes (valid for line based formats only) - */ - struct v4l2_meta_format { - __u32 dataformat; - __u32 buffersize; -+ __u32 width; -+ __u32 height; -+ __u32 bytesperline; - } __attribute__ ((packed)); - - /** --- -2.43.2 - - -From 090bb3894bda739ff06e8a431815210b731056b7 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus <sakari.ailus@linux.intel.com> -Date: Tue, 8 Aug 2023 10:55:37 +0300 -Subject: [PATCH 07/33] media: Add media bus codes for MIPI CCS embedded data - -Add new MIPI CCS embedded data media bus formats. - -Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> ---- - .../media/v4l/subdev-formats.rst | 32 +++++++++++++++++++ - include/uapi/linux/media-bus-format.h | 10 +++++- - 2 files changed, 41 insertions(+), 1 deletion(-) - -diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst -index 7d107873cddd..7afb057a09c5 100644 ---- a/Documentation/userspace-api/media/v4l/subdev-formats.rst -+++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst -@@ -8563,3 +8563,35 @@ and finally the bit number in subscript. "p" indicates a padding bit. - - p - - p - - p -+ -+MIPI CCS Embedded Data Formats -+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -+ -+`MIPI CCS <https://www.mipi.org/specifications/camera-command-set>`_ defines an -+metadata format for sensor embedded data, which is used to store the register -+configuration used for capturing a given frame. The format is defined in the CCS -+specification. -+ -+The bit depth of the CCS embedded data matches the pixel data bit depth -+configured on the sensor. The formats used and their corresponding generic -+formats are listed in the table below. -+ -+.. flat-table: CCS embedded data mbus formats and corresponding generic formats -+ :header-rows: 1 -+ -+ * - CCS embedded data mbus format -+ - Generic metadata format -+ * - MEDIA_BUS_FMT_CCS_EMBEDDED_8 -+ - MEDIA_BUS_FMT_META_8 -+ * - MEDIA_BUS_FMT_CCS_EMBEDDED_10 -+ - MEDIA_BUS_FMT_META_10 -+ * - MEDIA_BUS_FMT_CCS_EMBEDDED_12 -+ - MEDIA_BUS_FMT_META_12 -+ * - MEDIA_BUS_FMT_CCS_EMBEDDED_14 -+ - MEDIA_BUS_FMT_META_14 -+ * - MEDIA_BUS_FMT_CCS_EMBEDDED_16 -+ - MEDIA_BUS_FMT_META_16 -+ * - MEDIA_BUS_FMT_CCS_EMBEDDED_20 -+ - MEDIA_BUS_FMT_META_20 -+ * - MEDIA_BUS_FMT_CCS_EMBEDDED_24 -+ - MEDIA_BUS_FMT_META_24 -diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h -index 6fcd7d276bc6..d5014ef3c43a 100644 ---- a/include/uapi/linux/media-bus-format.h -+++ b/include/uapi/linux/media-bus-format.h -@@ -183,7 +183,15 @@ - #define MEDIA_BUS_FMT_META_20 0x8006 - #define MEDIA_BUS_FMT_META_24 0x8007 - -+/* Specific metadata formats. Next is 0x9008. */ -+#define MEDIA_BUS_FMT_CCS_EMBEDDED_8 0x9001 -+#define MEDIA_BUS_FMT_CCS_EMBEDDED_10 0x9002 -+#define MEDIA_BUS_FMT_CCS_EMBEDDED_12 0x9003 -+#define MEDIA_BUS_FMT_CCS_EMBEDDED_14 0x9004 -+#define MEDIA_BUS_FMT_CCS_EMBEDDED_16 0x9005 -+#define MEDIA_BUS_FMT_CCS_EMBEDDED_20 0x9006 -+#define MEDIA_BUS_FMT_CCS_EMBEDDED_24 0x9007 -+ - #define MEDIA_BUS_FMT_IS_META(code) \ - ((code) & 0xf000 == 0x7000 || (code) & 0xf000 == 0x8000) -- - #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ --- -2.43.2 - - -From 44f8084f055969874d2216ba4e6e225046931e73 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:15 +0800 -Subject: [PATCH 08/33] media: intel/ipu6: add Intel IPU6 PCI device driver - -Intel Image Processing Unit 6th Gen includes input and processing systems -but the hardware presents itself as a single PCI device in system. - -IPU6 PCI device driver basically does PCI configurations and load -the firmware binary, initialises IPU virtual bus, and sets up platform -specific variants to support multiple IPU6 devices in single device -driver. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - .../media/pci/intel/ipu6/ipu6-platform-regs.h | 179 ++++ - drivers/media/pci/intel/ipu6/ipu6.c | 966 ++++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6.h | 356 +++++++ - 3 files changed, 1501 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-regs.h - create mode 100644 drivers/media/pci/intel/ipu6/ipu6.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h -new file mode 100644 -index 000000000000..cae26009c9fc ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h -@@ -0,0 +1,179 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2018 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_PLATFORM_REGS_H -+#define IPU6_PLATFORM_REGS_H -+ -+#include <linux/bits.h> -+ -+/* -+ * IPU6 uses uniform address within IPU6, therefore all subsystem registers -+ * locates in one single space starts from 0 but in different sctions with -+ * different addresses, the subsystem offsets are defined to 0 as the -+ * register definition will have the address offset to 0. -+ */ -+#define IPU6_UNIFIED_OFFSET 0 -+ -+#define IPU6_ISYS_IOMMU0_OFFSET 0x2e0000 -+#define IPU6_ISYS_IOMMU1_OFFSET 0x2e0500 -+#define IPU6_ISYS_IOMMUI_OFFSET 0x2e0a00 -+ -+#define IPU6_PSYS_IOMMU0_OFFSET 0x1b0000 -+#define IPU6_PSYS_IOMMU1_OFFSET 0x1b0700 -+#define IPU6_PSYS_IOMMU1R_OFFSET 0x1b0e00 -+#define IPU6_PSYS_IOMMUI_OFFSET 0x1b1500 -+ -+/* the offset from IOMMU base register */ -+#define IPU6_MMU_L1_STREAM_ID_REG_OFFSET 0x0c -+#define IPU6_MMU_L2_STREAM_ID_REG_OFFSET 0x4c -+#define IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET 0x8c -+ -+#define IPU6_MMU_INFO_OFFSET 0x8 -+ -+#define IPU6_ISYS_SPC_OFFSET 0x210000 -+ -+#define IPU6SE_PSYS_SPC_OFFSET 0x110000 -+#define IPU6_PSYS_SPC_OFFSET 0x118000 -+ -+#define IPU6_ISYS_DMEM_OFFSET 0x200000 -+#define IPU6_PSYS_DMEM_OFFSET 0x100000 -+ -+#define IPU6_REG_ISYS_UNISPART_IRQ_EDGE 0x27c000 -+#define IPU6_REG_ISYS_UNISPART_IRQ_MASK 0x27c004 -+#define IPU6_REG_ISYS_UNISPART_IRQ_STATUS 0x27c008 -+#define IPU6_REG_ISYS_UNISPART_IRQ_CLEAR 0x27c00c -+#define IPU6_REG_ISYS_UNISPART_IRQ_ENABLE 0x27c010 -+#define IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE 0x27c014 -+#define IPU6_REG_ISYS_UNISPART_SW_IRQ_REG 0x27c414 -+#define IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG 0x27c418 -+#define IPU6_ISYS_UNISPART_IRQ_CSI0 BIT(2) -+#define IPU6_ISYS_UNISPART_IRQ_CSI1 BIT(3) -+#define IPU6_ISYS_UNISPART_IRQ_SW BIT(22) -+ -+#define IPU6_REG_ISYS_ISL_TOP_IRQ_EDGE 0x2b0200 -+#define IPU6_REG_ISYS_ISL_TOP_IRQ_MASK 0x2b0204 -+#define IPU6_REG_ISYS_ISL_TOP_IRQ_STATUS 0x2b0208 -+#define IPU6_REG_ISYS_ISL_TOP_IRQ_CLEAR 0x2b020c -+#define IPU6_REG_ISYS_ISL_TOP_IRQ_ENABLE 0x2b0210 -+#define IPU6_REG_ISYS_ISL_TOP_IRQ_LEVEL_NOT_PULSE 0x2b0214 -+ -+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_EDGE 0x2d2100 -+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_MASK 0x2d2104 -+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_STATUS 0x2d2108 -+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_CLEAR 0x2d210c -+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_ENABLE 0x2d2110 -+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_LEVEL_NOT_PULSE 0x2d2114 -+ -+/* CDC Burst collector thresholds for isys - 3 FIFOs i = 0..2 */ -+#define IPU6_REG_ISYS_CDC_THRESHOLD(i) (0x27c400 + ((i) * 4)) -+ -+#define IPU6_CSI_IRQ_NUM_PER_PIPE 4 -+#define IPU6SE_ISYS_CSI_PORT_NUM 4 -+#define IPU6_ISYS_CSI_PORT_NUM 8 -+ -+#define IPU6_ISYS_CSI_PORT_IRQ(irq_num) BIT(irq_num) -+ -+/* PKG DIR OFFSET in IMR in secure mode */ -+#define IPU6_PKG_DIR_IMR_OFFSET 0x40 -+ -+#define IPU6_ISYS_REG_SPC_STATUS_CTRL 0x0 -+ -+#define IPU6_ISYS_SPC_STATUS_START BIT(1) -+#define IPU6_ISYS_SPC_STATUS_RUN BIT(3) -+#define IPU6_ISYS_SPC_STATUS_READY BIT(5) -+#define IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12) -+#define IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH BIT(13) -+ -+#define IPU6_PSYS_REG_SPC_STATUS_CTRL 0x0 -+#define IPU6_PSYS_REG_SPC_START_PC 0x4 -+#define IPU6_PSYS_REG_SPC_ICACHE_BASE 0x10 -+#define IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER 0x14 -+ -+#define IPU6_PSYS_SPC_STATUS_START BIT(1) -+#define IPU6_PSYS_SPC_STATUS_RUN BIT(3) -+#define IPU6_PSYS_SPC_STATUS_READY BIT(5) -+#define IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12) -+#define IPU6_PSYS_SPC_STATUS_ICACHE_PREFETCH BIT(13) -+ -+#define IPU6_PSYS_REG_SPP0_STATUS_CTRL 0x20000 -+ -+#define IPU6_INFO_ENABLE_SNOOP BIT(0) -+#define IPU6_INFO_DEC_FORCE_FLUSH BIT(1) -+#define IPU6_INFO_DEC_PASS_THROUGH BIT(2) -+#define IPU6_INFO_ZLW BIT(3) -+#define IPU6_INFO_REQUEST_DESTINATION_IOSF BIT(9) -+#define IPU6_INFO_IMR_BASE BIT(10) -+#define IPU6_INFO_IMR_DESTINED BIT(11) -+ -+#define IPU6_INFO_REQUEST_DESTINATION_PRIMARY IPU6_INFO_REQUEST_DESTINATION_IOSF -+ -+/* -+ * s2m_pixel_soc_pixel_remapping is dedicated for the enabling of the -+ * pixel s2m remp ability.Remap here means that s2m rearange the order -+ * of the pixels in each 4 pixels group. -+ * For examle, mirroring remping means that if input's 4 first pixels -+ * are 1 2 3 4 then in output we should see 4 3 2 1 in this 4 first pixels. -+ * 0xE4 is from s2m MAS document. It means no remapping. -+ */ -+#define S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4 -+/* -+ * csi_be_soc_pixel_remapping is for the enabling of the pixel remapping. -+ * This remapping is exactly like the stream2mmio remapping. -+ */ -+#define CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4 -+ -+#define IPU6_REG_DMA_TOP_AB_GROUP1_BASE_ADDR 0x1ae000 -+#define IPU6_REG_DMA_TOP_AB_GROUP2_BASE_ADDR 0x1af000 -+#define IPU6_REG_DMA_TOP_AB_RING_MIN_OFFSET(n) (0x4 + (n) * 0xc) -+#define IPU6_REG_DMA_TOP_AB_RING_MAX_OFFSET(n) (0x8 + (n) * 0xc) -+#define IPU6_REG_DMA_TOP_AB_RING_ACCESS_OFFSET(n) (0xc + (n) * 0xc) -+ -+enum ipu6_device_ab_group1_target_id { -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R0_SPC_DMEM, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R1_SPC_DMEM, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R2_SPC_DMEM, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R3_SPC_STATUS_REG, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R4_SPC_MASTER_BASE_ADDR, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R5_SPC_PC_STALL, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R6_SPC_EQ, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R7_SPC_RESERVED, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R8_SPC_RESERVED, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R9_SPP0, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R10_SPP1, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R11_CENTRAL_R1, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R12_IRQ, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R13_CENTRAL_R2, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R14_DMA, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R15_DMA, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R16_GP, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R17_ZLW_INSERTER, -+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R18_AB, -+}; -+ -+enum nci_ab_access_mode { -+ NCI_AB_ACCESS_MODE_RW, /* read & write */ -+ NCI_AB_ACCESS_MODE_RO, /* read only */ -+ NCI_AB_ACCESS_MODE_WO, /* write only */ -+ NCI_AB_ACCESS_MODE_NA, /* No access at all */ -+}; -+ -+/* IRQ-related registers in PSYS */ -+#define IPU6_REG_PSYS_GPDEV_IRQ_EDGE 0x1aa200 -+#define IPU6_REG_PSYS_GPDEV_IRQ_MASK 0x1aa204 -+#define IPU6_REG_PSYS_GPDEV_IRQ_STATUS 0x1aa208 -+#define IPU6_REG_PSYS_GPDEV_IRQ_CLEAR 0x1aa20c -+#define IPU6_REG_PSYS_GPDEV_IRQ_ENABLE 0x1aa210 -+#define IPU6_REG_PSYS_GPDEV_IRQ_LEVEL_NOT_PULSE 0x1aa214 -+/* There are 8 FW interrupts, n = 0..7 */ -+#define IPU6_PSYS_GPDEV_FWIRQ0 5 -+#define IPU6_PSYS_GPDEV_FWIRQ1 6 -+#define IPU6_PSYS_GPDEV_FWIRQ2 7 -+#define IPU6_PSYS_GPDEV_FWIRQ3 8 -+#define IPU6_PSYS_GPDEV_FWIRQ4 9 -+#define IPU6_PSYS_GPDEV_FWIRQ5 10 -+#define IPU6_PSYS_GPDEV_FWIRQ6 11 -+#define IPU6_PSYS_GPDEV_FWIRQ7 12 -+#define IPU6_PSYS_GPDEV_IRQ_FWIRQ(n) BIT(n) -+#define IPU6_REG_PSYS_GPDEV_FWIRQ(n) (4 * (n) + 0x1aa100) -+ -+#endif /* IPU6_PLATFORM_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c -new file mode 100644 -index 000000000000..abd40a783729 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6.c -@@ -0,0 +1,966 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/bitfield.h> -+#include <linux/bits.h> -+#include <linux/dma-mapping.h> -+#include <linux/err.h> -+#include <linux/firmware.h> -+#include <linux/kernel.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+#include <linux/list.h> -+#include <linux/module.h> -+#include <linux/pci-ats.h> -+#include <linux/pm_runtime.h> -+#include <linux/property.h> -+#include <linux/scatterlist.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+ -+#include <media/ipu-bridge.h> -+ -+#include "ipu6.h" -+#include "ipu6-bus.h" -+#include "ipu6-buttress.h" -+#include "ipu6-cpd.h" -+#include "ipu6-isys.h" -+#include "ipu6-mmu.h" -+#include "ipu6-platform-buttress-regs.h" -+#include "ipu6-platform-isys-csi2-reg.h" -+#include "ipu6-platform-regs.h" -+ -+#define IPU6_PCI_BAR 0 -+ -+struct ipu6_cell_program { -+ u32 magic_number; -+ -+ u32 blob_offset; -+ u32 blob_size; -+ -+ u32 start[3]; -+ -+ u32 icache_source; -+ u32 icache_target; -+ u32 icache_size; -+ -+ u32 pmem_source; -+ u32 pmem_target; -+ u32 pmem_size; -+ -+ u32 data_source; -+ u32 data_target; -+ u32 data_size; -+ -+ u32 bss_target; -+ u32 bss_size; -+ -+ u32 cell_id; -+ u32 regs_addr; -+ -+ u32 cell_pmem_data_bus_address; -+ u32 cell_dmem_data_bus_address; -+ u32 cell_pmem_control_bus_address; -+ u32 cell_dmem_control_bus_address; -+ -+ u32 next; -+ u32 dummy[2]; -+}; -+ -+static u32 ipu6se_csi_offsets[] = { -+ IPU6_CSI_PORT_A_ADDR_OFFSET, -+ IPU6_CSI_PORT_B_ADDR_OFFSET, -+ IPU6_CSI_PORT_C_ADDR_OFFSET, -+ IPU6_CSI_PORT_D_ADDR_OFFSET -+}; -+ -+/* -+ * IPU6 on TGL support maximum 8 csi2 ports -+ * IPU6SE on JSL and IPU6EP on ADL support maximum 4 csi2 ports -+ * IPU6EP on MTL support maximum 6 csi2 ports -+ */ -+static u32 ipu6_tgl_csi_offsets[] = { -+ IPU6_CSI_PORT_A_ADDR_OFFSET, -+ IPU6_CSI_PORT_B_ADDR_OFFSET, -+ IPU6_CSI_PORT_C_ADDR_OFFSET, -+ IPU6_CSI_PORT_D_ADDR_OFFSET, -+ IPU6_CSI_PORT_E_ADDR_OFFSET, -+ IPU6_CSI_PORT_F_ADDR_OFFSET, -+ IPU6_CSI_PORT_G_ADDR_OFFSET, -+ IPU6_CSI_PORT_H_ADDR_OFFSET -+}; -+ -+static u32 ipu6ep_mtl_csi_offsets[] = { -+ IPU6_CSI_PORT_A_ADDR_OFFSET, -+ IPU6_CSI_PORT_B_ADDR_OFFSET, -+ IPU6_CSI_PORT_C_ADDR_OFFSET, -+ IPU6_CSI_PORT_D_ADDR_OFFSET, -+ IPU6_CSI_PORT_E_ADDR_OFFSET, -+ IPU6_CSI_PORT_F_ADDR_OFFSET -+}; -+ -+static u32 ipu6_csi_offsets[] = { -+ IPU6_CSI_PORT_A_ADDR_OFFSET, -+ IPU6_CSI_PORT_B_ADDR_OFFSET, -+ IPU6_CSI_PORT_C_ADDR_OFFSET, -+ IPU6_CSI_PORT_D_ADDR_OFFSET -+}; -+ -+static struct ipu6_isys_internal_pdata isys_ipdata = { -+ .hw_variant = { -+ .offset = IPU6_UNIFIED_OFFSET, -+ .nr_mmus = 3, -+ .mmu_hw = { -+ { -+ .offset = IPU6_ISYS_IOMMU0_OFFSET, -+ .info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF, -+ .nr_l1streams = 16, -+ .l1_block_sz = { -+ 3, 8, 2, 2, 2, 2, 2, 2, 1, 1, -+ 1, 1, 1, 1, 1, 1 -+ }, -+ .nr_l2streams = 16, -+ .l2_block_sz = { -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2 -+ }, -+ .insert_read_before_invalidate = false, -+ .l1_stream_id_reg_offset = -+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET, -+ .l2_stream_id_reg_offset = -+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET, -+ }, -+ { -+ .offset = IPU6_ISYS_IOMMU1_OFFSET, -+ .info_bits = 0, -+ .nr_l1streams = 16, -+ .l1_block_sz = { -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 1, 1, 4 -+ }, -+ .nr_l2streams = 16, -+ .l2_block_sz = { -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2 -+ }, -+ .insert_read_before_invalidate = false, -+ .l1_stream_id_reg_offset = -+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET, -+ .l2_stream_id_reg_offset = -+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET, -+ }, -+ { -+ .offset = IPU6_ISYS_IOMMUI_OFFSET, -+ .info_bits = 0, -+ .nr_l1streams = 0, -+ .nr_l2streams = 0, -+ .insert_read_before_invalidate = false, -+ }, -+ }, -+ .cdc_fifos = 3, -+ .cdc_fifo_threshold = {6, 8, 2}, -+ .dmem_offset = IPU6_ISYS_DMEM_OFFSET, -+ .spc_offset = IPU6_ISYS_SPC_OFFSET, -+ }, -+ .isys_dma_overshoot = IPU6_ISYS_OVERALLOC_MIN, -+}; -+ -+static struct ipu6_psys_internal_pdata psys_ipdata = { -+ .hw_variant = { -+ .offset = IPU6_UNIFIED_OFFSET, -+ .nr_mmus = 4, -+ .mmu_hw = { -+ { -+ .offset = IPU6_PSYS_IOMMU0_OFFSET, -+ .info_bits = -+ IPU6_INFO_REQUEST_DESTINATION_IOSF, -+ .nr_l1streams = 16, -+ .l1_block_sz = { -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2 -+ }, -+ .nr_l2streams = 16, -+ .l2_block_sz = { -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2 -+ }, -+ .insert_read_before_invalidate = false, -+ .l1_stream_id_reg_offset = -+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET, -+ .l2_stream_id_reg_offset = -+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET, -+ }, -+ { -+ .offset = IPU6_PSYS_IOMMU1_OFFSET, -+ .info_bits = 0, -+ .nr_l1streams = 32, -+ .l1_block_sz = { -+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 10, -+ 5, 4, 14, 6, 4, 14, 6, 4, 8, -+ 4, 2, 1, 1, 1, 1, 14 -+ }, -+ .nr_l2streams = 32, -+ .l2_block_sz = { -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2 -+ }, -+ .insert_read_before_invalidate = false, -+ .l1_stream_id_reg_offset = -+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET, -+ .l2_stream_id_reg_offset = -+ IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET, -+ }, -+ { -+ .offset = IPU6_PSYS_IOMMU1R_OFFSET, -+ .info_bits = 0, -+ .nr_l1streams = 16, -+ .l1_block_sz = { -+ 1, 4, 4, 4, 4, 16, 8, 4, 32, -+ 16, 16, 2, 2, 2, 1, 12 -+ }, -+ .nr_l2streams = 16, -+ .l2_block_sz = { -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2 -+ }, -+ .insert_read_before_invalidate = false, -+ .l1_stream_id_reg_offset = -+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET, -+ .l2_stream_id_reg_offset = -+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET, -+ }, -+ { -+ .offset = IPU6_PSYS_IOMMUI_OFFSET, -+ .info_bits = 0, -+ .nr_l1streams = 0, -+ .nr_l2streams = 0, -+ .insert_read_before_invalidate = false, -+ }, -+ }, -+ .dmem_offset = IPU6_PSYS_DMEM_OFFSET, -+ }, -+}; -+ -+static const struct ipu6_buttress_ctrl isys_buttress_ctrl = { -+ .ratio = IPU6_IS_FREQ_CTL_DEFAULT_RATIO, -+ .qos_floor = IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO, -+ .freq_ctl = IPU6_BUTTRESS_REG_IS_FREQ_CTL, -+ .pwr_sts_shift = IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -+ .pwr_sts_mask = IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK, -+ .pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE, -+}; -+ -+static const struct ipu6_buttress_ctrl psys_buttress_ctrl = { -+ .ratio = IPU6_PS_FREQ_CTL_DEFAULT_RATIO, -+ .qos_floor = IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO, -+ .freq_ctl = IPU6_BUTTRESS_REG_PS_FREQ_CTL, -+ .pwr_sts_shift = IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -+ .pwr_sts_mask = IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK, -+ .pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE, -+}; -+ -+static void -+ipu6_pkg_dir_configure_spc(struct ipu6_device *isp, -+ const struct ipu6_hw_variants *hw_variant, -+ int pkg_dir_idx, void __iomem *base, -+ u64 *pkg_dir, dma_addr_t pkg_dir_vied_address) -+{ -+ struct ipu6_cell_program *prog; -+ void __iomem *spc_base; -+ u32 server_fw_addr; -+ dma_addr_t dma_addr; -+ u32 pg_offset; -+ -+ server_fw_addr = lower_32_bits(*(pkg_dir + (pkg_dir_idx + 1) * 2)); -+ if (pkg_dir_idx == IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX) -+ dma_addr = sg_dma_address(isp->isys->fw_sgt.sgl); -+ else -+ dma_addr = sg_dma_address(isp->psys->fw_sgt.sgl); -+ -+ pg_offset = server_fw_addr - dma_addr; -+ prog = (struct ipu6_cell_program *)((u64)isp->cpd_fw->data + pg_offset); -+ spc_base = base + prog->regs_addr; -+ if (spc_base != (base + hw_variant->spc_offset)) -+ dev_warn(&isp->pdev->dev, -+ "SPC reg addr %p not matching value from CPD %p\n", -+ base + hw_variant->spc_offset, spc_base); -+ writel(server_fw_addr + prog->blob_offset + -+ prog->icache_source, spc_base + IPU6_PSYS_REG_SPC_ICACHE_BASE); -+ writel(IPU6_INFO_REQUEST_DESTINATION_IOSF, -+ spc_base + IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER); -+ writel(prog->start[1], spc_base + IPU6_PSYS_REG_SPC_START_PC); -+ writel(pkg_dir_vied_address, base + hw_variant->dmem_offset); -+} -+ -+void ipu6_configure_spc(struct ipu6_device *isp, -+ const struct ipu6_hw_variants *hw_variant, -+ int pkg_dir_idx, void __iomem *base, u64 *pkg_dir, -+ dma_addr_t pkg_dir_dma_addr) -+{ -+ void __iomem *dmem_base = base + hw_variant->dmem_offset; -+ void __iomem *spc_regs_base = base + hw_variant->spc_offset; -+ u32 val; -+ -+ val = readl(spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL); -+ val |= IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE; -+ writel(val, spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL); -+ -+ if (isp->secure_mode) -+ writel(IPU6_PKG_DIR_IMR_OFFSET, dmem_base); -+ else -+ ipu6_pkg_dir_configure_spc(isp, hw_variant, pkg_dir_idx, base, -+ pkg_dir, pkg_dir_dma_addr); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6); -+ -+static void ipu6_internal_pdata_init(struct ipu6_device *isp) -+{ -+ u8 hw_ver = isp->hw_ver; -+ -+ isys_ipdata.num_parallel_streams = IPU6_ISYS_NUM_STREAMS; -+ isys_ipdata.sram_gran_shift = IPU6_SRAM_GRANULARITY_SHIFT; -+ isys_ipdata.sram_gran_size = IPU6_SRAM_GRANULARITY_SIZE; -+ isys_ipdata.max_sram_size = IPU6_MAX_SRAM_SIZE; -+ isys_ipdata.sensor_type_start = IPU6_FW_ISYS_SENSOR_TYPE_START; -+ isys_ipdata.sensor_type_end = IPU6_FW_ISYS_SENSOR_TYPE_END; -+ isys_ipdata.max_streams = IPU6_ISYS_NUM_STREAMS; -+ isys_ipdata.max_send_queues = IPU6_N_MAX_SEND_QUEUES; -+ isys_ipdata.max_sram_blocks = IPU6_NOF_SRAM_BLOCKS_MAX; -+ isys_ipdata.max_devq_size = IPU6_DEV_SEND_QUEUE_SIZE; -+ isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6_csi_offsets); -+ isys_ipdata.csi2.offsets = ipu6_csi_offsets; -+ isys_ipdata.csi2.irq_mask = IPU6_CSI_RX_ERROR_IRQ_MASK; -+ isys_ipdata.csi2.ctrl0_irq_edge = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE; -+ isys_ipdata.csi2.ctrl0_irq_clear = -+ IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR; -+ isys_ipdata.csi2.ctrl0_irq_mask = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK; -+ isys_ipdata.csi2.ctrl0_irq_enable = -+ IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE; -+ isys_ipdata.csi2.ctrl0_irq_status = -+ IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS; -+ isys_ipdata.csi2.ctrl0_irq_lnp = -+ IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE; -+ isys_ipdata.enhanced_iwake = is_ipu6ep_mtl(hw_ver) || is_ipu6ep(hw_ver); -+ psys_ipdata.hw_variant.spc_offset = IPU6_PSYS_SPC_OFFSET; -+ isys_ipdata.csi2.fw_access_port_ofs = CSI_REG_HUB_FW_ACCESS_PORT_OFS; -+ -+ if (is_ipu6ep(hw_ver)) { -+ isys_ipdata.ltr = IPU6EP_LTR_VALUE; -+ isys_ipdata.memopen_threshold = IPU6EP_MIN_MEMOPEN_TH; -+ } -+ -+ if (is_ipu6_tgl(hw_ver)) { -+ isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6_tgl_csi_offsets); -+ isys_ipdata.csi2.offsets = ipu6_tgl_csi_offsets; -+ } -+ -+ if (is_ipu6ep_mtl(hw_ver)) { -+ isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6ep_mtl_csi_offsets); -+ isys_ipdata.csi2.offsets = ipu6ep_mtl_csi_offsets; -+ -+ isys_ipdata.csi2.ctrl0_irq_edge = -+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE; -+ isys_ipdata.csi2.ctrl0_irq_clear = -+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR; -+ isys_ipdata.csi2.ctrl0_irq_mask = -+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK; -+ isys_ipdata.csi2.ctrl0_irq_enable = -+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE; -+ isys_ipdata.csi2.ctrl0_irq_lnp = -+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE; -+ isys_ipdata.csi2.ctrl0_irq_status = -+ IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS; -+ isys_ipdata.csi2.fw_access_port_ofs = -+ CSI_REG_HUB_FW_ACCESS_PORT_V6OFS; -+ isys_ipdata.ltr = IPU6EP_MTL_LTR_VALUE; -+ isys_ipdata.memopen_threshold = IPU6EP_MTL_MIN_MEMOPEN_TH; -+ } -+ -+ if (is_ipu6se(hw_ver)) { -+ isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6se_csi_offsets); -+ isys_ipdata.csi2.irq_mask = IPU6SE_CSI_RX_ERROR_IRQ_MASK; -+ isys_ipdata.csi2.offsets = ipu6se_csi_offsets; -+ isys_ipdata.num_parallel_streams = IPU6SE_ISYS_NUM_STREAMS; -+ isys_ipdata.sram_gran_shift = IPU6SE_SRAM_GRANULARITY_SHIFT; -+ isys_ipdata.sram_gran_size = IPU6SE_SRAM_GRANULARITY_SIZE; -+ isys_ipdata.max_sram_size = IPU6SE_MAX_SRAM_SIZE; -+ isys_ipdata.sensor_type_start = -+ IPU6SE_FW_ISYS_SENSOR_TYPE_START; -+ isys_ipdata.sensor_type_end = IPU6SE_FW_ISYS_SENSOR_TYPE_END; -+ isys_ipdata.max_streams = IPU6SE_ISYS_NUM_STREAMS; -+ isys_ipdata.max_send_queues = IPU6SE_N_MAX_SEND_QUEUES; -+ isys_ipdata.max_sram_blocks = IPU6SE_NOF_SRAM_BLOCKS_MAX; -+ isys_ipdata.max_devq_size = IPU6SE_DEV_SEND_QUEUE_SIZE; -+ psys_ipdata.hw_variant.spc_offset = IPU6SE_PSYS_SPC_OFFSET; -+ } -+} -+ -+static int ipu6_isys_check_fwnode_graph(struct fwnode_handle *fwnode) -+{ -+ struct fwnode_handle *endpoint; -+ -+ if (IS_ERR_OR_NULL(fwnode)) -+ return -EINVAL; -+ -+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); -+ if (endpoint) { -+ fwnode_handle_put(endpoint); -+ return 0; -+ } -+ -+ return ipu6_isys_check_fwnode_graph(fwnode->secondary); -+} -+ -+static struct ipu6_bus_device * -+ipu6_isys_init(struct pci_dev *pdev, struct device *parent, -+ struct ipu6_buttress_ctrl *ctrl, void __iomem *base, -+ const struct ipu6_isys_internal_pdata *ipdata) -+{ -+ struct device *dev = &pdev->dev; -+ struct fwnode_handle *fwnode = dev_fwnode(dev); -+ struct ipu6_bus_device *isys_adev; -+ struct ipu6_isys_pdata *pdata; -+ int ret; -+ -+ /* check fwnode at first, fallback into bridge if no fwnode graph */ -+ ret = ipu6_isys_check_fwnode_graph(fwnode); -+ if (ret) { -+ if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) { -+ dev_err(dev, -+ "fwnode graph has no endpoints connection\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb); -+ if (ret) { -+ dev_err_probe(dev, ret, "IPU6 bridge init failed\n"); -+ return ERR_PTR(ret); -+ } -+ } -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ pdata->base = base; -+ pdata->ipdata = ipdata; -+ -+ isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl, -+ IPU6_ISYS_NAME); -+ if (IS_ERR(isys_adev)) { -+ dev_err_probe(dev, PTR_ERR(isys_adev), -+ "ipu6_bus_initialize_device isys failed\n"); -+ kfree(pdata); -+ return ERR_CAST(isys_adev); -+ } -+ -+ isys_adev->mmu = ipu6_mmu_init(dev, base, ISYS_MMID, -+ &ipdata->hw_variant); -+ if (IS_ERR(isys_adev->mmu)) { -+ dev_err_probe(dev, PTR_ERR(isys_adev), -+ "ipu6_mmu_init(isys_adev->mmu) failed\n"); -+ put_device(&isys_adev->auxdev.dev); -+ kfree(pdata); -+ return ERR_CAST(isys_adev->mmu); -+ } -+ -+ isys_adev->mmu->dev = &isys_adev->auxdev.dev; -+ -+ ret = ipu6_bus_add_device(isys_adev); -+ if (ret) { -+ kfree(pdata); -+ return ERR_PTR(ret); -+ } -+ -+ return isys_adev; -+} -+ -+static struct ipu6_bus_device * -+ipu6_psys_init(struct pci_dev *pdev, struct device *parent, -+ struct ipu6_buttress_ctrl *ctrl, void __iomem *base, -+ const struct ipu6_psys_internal_pdata *ipdata) -+{ -+ struct ipu6_bus_device *psys_adev; -+ struct ipu6_psys_pdata *pdata; -+ int ret; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ pdata->base = base; -+ pdata->ipdata = ipdata; -+ -+ psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl, -+ IPU6_PSYS_NAME); -+ if (IS_ERR(psys_adev)) { -+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev), -+ "ipu6_bus_initialize_device psys failed\n"); -+ kfree(pdata); -+ return ERR_CAST(psys_adev); -+ } -+ -+ psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, PSYS_MMID, -+ &ipdata->hw_variant); -+ if (IS_ERR(psys_adev->mmu)) { -+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev), -+ "ipu6_mmu_init(psys_adev->mmu) failed\n"); -+ put_device(&psys_adev->auxdev.dev); -+ kfree(pdata); -+ return ERR_CAST(psys_adev->mmu); -+ } -+ -+ psys_adev->mmu->dev = &psys_adev->auxdev.dev; -+ -+ ret = ipu6_bus_add_device(psys_adev); -+ if (ret) { -+ kfree(pdata); -+ return ERR_PTR(ret); -+ } -+ -+ return psys_adev; -+} -+ -+static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver) -+{ -+ int ret; -+ -+ /* disable IPU6 PCI ATS on mtl ES2 */ -+ if (is_ipu6ep_mtl(hw_ver) && boot_cpu_data.x86_stepping == 0x2 && -+ pci_ats_supported(dev)) -+ pci_disable_ats(dev); -+ -+ /* No PCI msi capability for IPU6EP */ -+ if (is_ipu6ep(hw_ver) || is_ipu6ep_mtl(hw_ver)) { -+ /* likely do nothing as msi not enabled by default */ -+ pci_disable_msi(dev); -+ return 0; -+ } -+ -+ ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_MSI); -+ if (ret < 0) -+ return dev_err_probe(&dev->dev, ret, "Request msi failed"); -+ -+ return 0; -+} -+ -+static void ipu6_configure_vc_mechanism(struct ipu6_device *isp) -+{ -+ u32 val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL); -+ -+ if (IPU6_BTRS_ARB_STALL_MODE_VC0 == IPU6_BTRS_ARB_MODE_TYPE_STALL) -+ val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0; -+ else -+ val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0; -+ -+ if (IPU6_BTRS_ARB_STALL_MODE_VC1 == IPU6_BTRS_ARB_MODE_TYPE_STALL) -+ val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1; -+ else -+ val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1; -+ -+ writel(val, isp->base + BUTTRESS_REG_BTRS_CTRL); -+} -+ -+static int request_cpd_fw(const struct firmware **firmware_p, const char *name, -+ struct device *device) -+{ -+ const struct firmware *fw; -+ struct firmware *dst; -+ int ret = 0; -+ -+ ret = request_firmware(&fw, name, device); -+ if (ret) -+ return ret; -+ -+ if (is_vmalloc_addr(fw->data)) { -+ *firmware_p = fw; -+ return 0; -+ } -+ -+ dst = kzalloc(sizeof(*dst), GFP_KERNEL); -+ if (!dst) { -+ ret = -ENOMEM; -+ goto release_firmware; -+ } -+ -+ dst->size = fw->size; -+ dst->data = vmalloc(fw->size); -+ if (!dst->data) { -+ kfree(dst); -+ ret = -ENOMEM; -+ goto release_firmware; -+ } -+ -+ memcpy((void *)dst->data, fw->data, fw->size); -+ *firmware_p = dst; -+ -+release_firmware: -+ release_firmware(fw); -+ -+ return ret; -+} -+ -+static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -+{ -+ struct ipu6_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL; -+ struct device *dev = &pdev->dev; -+ void __iomem *isys_base = NULL; -+ void __iomem *psys_base = NULL; -+ struct ipu6_device *isp; -+ phys_addr_t phys; -+ u32 val, version, sku_id; -+ int ret; -+ -+ isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); -+ if (!isp) -+ return -ENOMEM; -+ -+ isp->pdev = pdev; -+ INIT_LIST_HEAD(&isp->devices); -+ -+ ret = pcim_enable_device(pdev); -+ if (ret) -+ return dev_err_probe(dev, ret, "Enable PCI device failed\n"); -+ -+ phys = pci_resource_start(pdev, IPU6_PCI_BAR); -+ dev_dbg(dev, "IPU6 PCI bar[%u] = %pa\n", IPU6_PCI_BAR, &phys); -+ -+ ret = pcim_iomap_regions(pdev, 1 << IPU6_PCI_BAR, pci_name(pdev)); -+ if (ret) -+ return dev_err_probe(dev, ret, "Failed to I/O mem remappinp\n"); -+ -+ isp->base = pcim_iomap_table(pdev)[IPU6_PCI_BAR]; -+ pci_set_drvdata(pdev, isp); -+ pci_set_master(pdev); -+ -+ isp->cpd_metadata_cmpnt_size = sizeof(struct ipu6_cpd_metadata_cmpnt); -+ switch (id->device) { -+ case PCI_DEVICE_ID_INTEL_IPU6: -+ isp->hw_ver = IPU6_VER_6; -+ isp->cpd_fw_name = IPU6_FIRMWARE_NAME; -+ break; -+ case PCI_DEVICE_ID_INTEL_IPU6SE: -+ isp->hw_ver = IPU6_VER_6SE; -+ isp->cpd_fw_name = IPU6SE_FIRMWARE_NAME; -+ isp->cpd_metadata_cmpnt_size = -+ sizeof(struct ipu6se_cpd_metadata_cmpnt); -+ break; -+ case PCI_DEVICE_ID_INTEL_IPU6EP_ADLP: -+ case PCI_DEVICE_ID_INTEL_IPU6EP_ADLN: -+ case PCI_DEVICE_ID_INTEL_IPU6EP_RPLP: -+ isp->hw_ver = IPU6_VER_6EP; -+ isp->cpd_fw_name = IPU6EP_FIRMWARE_NAME; -+ break; -+ case PCI_DEVICE_ID_INTEL_IPU6EP_MTL: -+ isp->hw_ver = IPU6_VER_6EP_MTL; -+ isp->cpd_fw_name = IPU6EPMTL_FIRMWARE_NAME; -+ break; -+ default: -+ return dev_err_probe(dev, -ENODEV, -+ "Unsupported IPU6 device %x\n", -+ id->device); -+ } -+ -+ ipu6_internal_pdata_init(isp); -+ -+ isys_base = isp->base + isys_ipdata.hw_variant.offset; -+ psys_base = isp->base + psys_ipdata.hw_variant.offset; -+ -+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(39)); -+ if (ret) -+ return dev_err_probe(dev, ret, "Failed to set DMA mask\n"); -+ -+ ret = dma_set_max_seg_size(dev, UINT_MAX); -+ if (ret) -+ return dev_err_probe(dev, ret, "Failed to set max_seg_size\n"); -+ -+ ret = ipu6_pci_config_setup(pdev, isp->hw_ver); -+ if (ret) -+ return ret; -+ -+ ret = ipu6_buttress_init(isp); -+ if (ret) -+ return ret; -+ -+ ret = request_cpd_fw(&isp->cpd_fw, isp->cpd_fw_name, dev); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "Requesting signed firmware %s failed\n", -+ isp->cpd_fw_name); -+ goto buttress_exit; -+ } -+ -+ ret = ipu6_cpd_validate_cpd_file(isp, isp->cpd_fw->data, -+ isp->cpd_fw->size); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "Failed to validate cpd\n"); -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ isys_ctrl = devm_kmemdup(dev, &isys_buttress_ctrl, -+ sizeof(isys_buttress_ctrl), GFP_KERNEL); -+ if (!isys_ctrl) { -+ ret = -ENOMEM; -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ isp->isys = ipu6_isys_init(pdev, dev, isys_ctrl, isys_base, -+ &isys_ipdata); -+ if (IS_ERR(isp->isys)) { -+ ret = PTR_ERR(isp->isys); -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ psys_ctrl = devm_kmemdup(dev, &psys_buttress_ctrl, -+ sizeof(psys_buttress_ctrl), GFP_KERNEL); -+ if (!psys_ctrl) { -+ ret = -ENOMEM; -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ isp->psys = ipu6_psys_init(pdev, &isp->isys->auxdev.dev, psys_ctrl, -+ psys_base, &psys_ipdata); -+ if (IS_ERR(isp->psys)) { -+ ret = PTR_ERR(isp->psys); -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev); -+ if (ret < 0) -+ goto out_ipu6_bus_del_devices; -+ -+ ret = ipu6_mmu_hw_init(isp->psys->mmu); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "Failed to set MMU hardware\n"); -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ ret = ipu6_buttress_map_fw_image(isp->psys, isp->cpd_fw, -+ &isp->psys->fw_sgt); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, "failed to map fw image\n"); -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ ret = ipu6_cpd_create_pkg_dir(isp->psys, isp->cpd_fw->data); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "failed to create pkg dir\n"); -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ ret = devm_request_threaded_irq(dev, pdev->irq, ipu6_buttress_isr, -+ ipu6_buttress_isr_threaded, -+ IRQF_SHARED, IPU6_NAME, isp); -+ if (ret) { -+ dev_err_probe(dev, ret, "Requesting irq failed\n"); -+ goto out_ipu6_bus_del_devices; -+ } -+ -+ ret = ipu6_buttress_authenticate(isp); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "FW authentication failed\n"); -+ goto out_free_irq; -+ } -+ -+ ipu6_mmu_hw_cleanup(isp->psys->mmu); -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ -+ /* Configure the arbitration mechanisms for VC requests */ -+ ipu6_configure_vc_mechanism(isp); -+ -+ val = readl(isp->base + BUTTRESS_REG_SKU); -+ sku_id = FIELD_GET(GENMASK(6, 4), val); -+ version = FIELD_GET(GENMASK(3, 0), val); -+ dev_info(dev, "IPU%u-v%u[%x] hardware version %d\n", version, sku_id, -+ pdev->device, isp->hw_ver); -+ -+ pm_runtime_put_noidle(dev); -+ pm_runtime_allow(dev); -+ -+ isp->bus_ready_to_probe = true; -+ -+ return 0; -+ -+out_free_irq: -+ devm_free_irq(dev, pdev->irq, isp); -+out_ipu6_bus_del_devices: -+ if (isp->psys) { -+ ipu6_cpd_free_pkg_dir(isp->psys); -+ ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt); -+ } -+ if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) -+ ipu6_mmu_cleanup(isp->psys->mmu); -+ if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) -+ ipu6_mmu_cleanup(isp->isys->mmu); -+ ipu6_bus_del_devices(pdev); -+ release_firmware(isp->cpd_fw); -+buttress_exit: -+ ipu6_buttress_exit(isp); -+ -+ return ret; -+} -+ -+static void ipu6_pci_remove(struct pci_dev *pdev) -+{ -+ struct ipu6_device *isp = pci_get_drvdata(pdev); -+ struct ipu6_mmu *isys_mmu = isp->isys->mmu; -+ struct ipu6_mmu *psys_mmu = isp->psys->mmu; -+ -+ devm_free_irq(&pdev->dev, pdev->irq, isp); -+ ipu6_cpd_free_pkg_dir(isp->psys); -+ -+ ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt); -+ ipu6_buttress_exit(isp); -+ -+ ipu6_bus_del_devices(pdev); -+ -+ pm_runtime_forbid(&pdev->dev); -+ pm_runtime_get_noresume(&pdev->dev); -+ -+ pci_release_regions(pdev); -+ pci_disable_device(pdev); -+ -+ release_firmware(isp->cpd_fw); -+ -+ ipu6_mmu_cleanup(psys_mmu); -+ ipu6_mmu_cleanup(isys_mmu); -+} -+ -+static void ipu6_pci_reset_prepare(struct pci_dev *pdev) -+{ -+ struct ipu6_device *isp = pci_get_drvdata(pdev); -+ -+ pm_runtime_forbid(&isp->pdev->dev); -+} -+ -+static void ipu6_pci_reset_done(struct pci_dev *pdev) -+{ -+ struct ipu6_device *isp = pci_get_drvdata(pdev); -+ -+ ipu6_buttress_restore(isp); -+ if (isp->secure_mode) -+ ipu6_buttress_reset_authentication(isp); -+ -+ isp->need_ipc_reset = true; -+ pm_runtime_allow(&isp->pdev->dev); -+} -+ -+/* -+ * PCI base driver code requires driver to provide these to enable -+ * PCI device level PM state transitions (D0<->D3) -+ */ -+static int ipu6_suspend(struct device *dev) -+{ -+ return 0; -+} -+ -+static int ipu6_resume(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct ipu6_device *isp = pci_get_drvdata(pdev); -+ struct ipu6_buttress *b = &isp->buttress; -+ int ret; -+ -+ /* Configure the arbitration mechanisms for VC requests */ -+ ipu6_configure_vc_mechanism(isp); -+ -+ isp->secure_mode = ipu6_buttress_get_secure_mode(isp); -+ dev_info(dev, "IPU6 in %s mode\n", -+ isp->secure_mode ? "secure" : "non-secure"); -+ -+ ipu6_buttress_restore(isp); -+ -+ ret = ipu6_buttress_ipc_reset(isp, &b->cse); -+ if (ret) -+ dev_err(&isp->pdev->dev, "IPC reset protocol failed!\n"); -+ -+ ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev); -+ if (ret < 0) { -+ dev_err(&isp->psys->auxdev.dev, "Failed to get runtime PM\n"); -+ return 0; -+ } -+ -+ ret = ipu6_buttress_authenticate(isp); -+ if (ret) -+ dev_err(&isp->pdev->dev, "FW authentication failed(%d)\n", ret); -+ -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ -+ return 0; -+} -+ -+static int ipu6_runtime_resume(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct ipu6_device *isp = pci_get_drvdata(pdev); -+ int ret; -+ -+ ipu6_configure_vc_mechanism(isp); -+ ipu6_buttress_restore(isp); -+ -+ if (isp->need_ipc_reset) { -+ struct ipu6_buttress *b = &isp->buttress; -+ -+ isp->need_ipc_reset = false; -+ ret = ipu6_buttress_ipc_reset(isp, &b->cse); -+ if (ret) -+ dev_err(&isp->pdev->dev, "IPC reset protocol failed\n"); -+ } -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops ipu6_pm_ops = { -+ SET_SYSTEM_SLEEP_PM_OPS(&ipu6_suspend, &ipu6_resume) -+ SET_RUNTIME_PM_OPS(&ipu6_suspend, &ipu6_runtime_resume, NULL) -+}; -+ -+static const struct pci_device_id ipu6_pci_tbl[] = { -+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6) }, -+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6SE) }, -+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_ADLP) }, -+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_ADLN) }, -+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_RPLP) }, -+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_MTL) }, -+ { } -+}; -+MODULE_DEVICE_TABLE(pci, ipu6_pci_tbl); -+ -+static const struct pci_error_handlers pci_err_handlers = { -+ .reset_prepare = ipu6_pci_reset_prepare, -+ .reset_done = ipu6_pci_reset_done, -+}; -+ -+static struct pci_driver ipu6_pci_driver = { -+ .name = IPU6_NAME, -+ .id_table = ipu6_pci_tbl, -+ .probe = ipu6_pci_probe, -+ .remove = ipu6_pci_remove, -+ .driver = { -+ .pm = pm_ptr(&ipu6_pm_ops), -+ }, -+ .err_handler = &pci_err_handlers, -+}; -+ -+module_pci_driver(ipu6_pci_driver); -+ -+MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); -+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); -+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); -+MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); -+MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>"); -+MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>"); -+MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel IPU6 PCI driver"); -diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h -new file mode 100644 -index 000000000000..04e7e7e61ca5 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6.h -@@ -0,0 +1,356 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_H -+#define IPU6_H -+ -+#include <linux/list.h> -+#include <linux/pci.h> -+#include <linux/types.h> -+ -+#include "ipu6-buttress.h" -+ -+struct firmware; -+struct pci_dev; -+struct ipu6_bus_device; -+ -+#define PCI_DEVICE_ID_INTEL_IPU6 0x9a19 -+#define PCI_DEVICE_ID_INTEL_IPU6SE 0x4e19 -+#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLP 0x465d -+#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLN 0x462e -+#define PCI_DEVICE_ID_INTEL_IPU6EP_RPLP 0xa75d -+#define PCI_DEVICE_ID_INTEL_IPU6EP_MTL 0x7d19 -+ -+#define IPU6_NAME "intel-ipu6" -+#define IPU6_MEDIA_DEV_MODEL_NAME "ipu6" -+ -+#define IPU6SE_FIRMWARE_NAME "intel/ipu6se_fw.bin" -+#define IPU6EP_FIRMWARE_NAME "intel/ipu6ep_fw.bin" -+#define IPU6_FIRMWARE_NAME "intel/ipu6_fw.bin" -+#define IPU6EPMTL_FIRMWARE_NAME "intel/ipu6epmtl_fw.bin" -+ -+enum ipu6_version { -+ IPU6_VER_INVALID = 0, -+ IPU6_VER_6 = 1, -+ IPU6_VER_6SE = 3, -+ IPU6_VER_6EP = 5, -+ IPU6_VER_6EP_MTL = 6, -+}; -+ -+/* -+ * IPU6 - TGL -+ * IPU6SE - JSL -+ * IPU6EP - ADL/RPL -+ * IPU6EP_MTL - MTL -+ */ -+static inline bool is_ipu6se(u8 hw_ver) -+{ -+ return hw_ver == IPU6_VER_6SE; -+} -+ -+static inline bool is_ipu6ep(u8 hw_ver) -+{ -+ return hw_ver == IPU6_VER_6EP; -+} -+ -+static inline bool is_ipu6ep_mtl(u8 hw_ver) -+{ -+ return hw_ver == IPU6_VER_6EP_MTL; -+} -+ -+static inline bool is_ipu6_tgl(u8 hw_ver) -+{ -+ return hw_ver == IPU6_VER_6; -+} -+ -+/* -+ * ISYS DMA can overshoot. For higher resolutions over allocation is one line -+ * but it must be at minimum 1024 bytes. Value could be different in -+ * different versions / generations thus provide it via platform data. -+ */ -+#define IPU6_ISYS_OVERALLOC_MIN 1024 -+ -+/* Physical pages in GDA is 128, page size is 2K for IPU6, 1K for others */ -+#define IPU6_DEVICE_GDA_NR_PAGES 128 -+ -+/* Virtualization factor to calculate the available virtual pages */ -+#define IPU6_DEVICE_GDA_VIRT_FACTOR 32 -+ -+#define NR_OF_MMU_RESOURCES 2 -+ -+struct ipu6_device { -+ struct pci_dev *pdev; -+ struct list_head devices; -+ struct ipu6_bus_device *isys; -+ struct ipu6_bus_device *psys; -+ struct ipu6_buttress buttress; -+ -+ const struct firmware *cpd_fw; -+ const char *cpd_fw_name; -+ u32 cpd_metadata_cmpnt_size; -+ -+ void __iomem *base; -+ bool need_ipc_reset; -+ bool secure_mode; -+ u8 hw_ver; -+ bool bus_ready_to_probe; -+}; -+ -+#define IPU6_ISYS_NAME "isys" -+#define IPU6_PSYS_NAME "psys" -+ -+#define IPU6_MMU_MAX_DEVICES 4 -+#define IPU6_MMU_ADDR_BITS 32 -+/* The firmware is accessible within the first 2 GiB only in non-secure mode. */ -+#define IPU6_MMU_ADDR_BITS_NON_SECURE 31 -+ -+#define IPU6_MMU_MAX_TLB_L1_STREAMS 32 -+#define IPU6_MMU_MAX_TLB_L2_STREAMS 32 -+#define IPU6_MAX_LI_BLOCK_ADDR 128 -+#define IPU6_MAX_L2_BLOCK_ADDR 64 -+ -+#define IPU6_ISYS_MAX_CSI2_LEGACY_PORTS 4 -+#define IPU6_ISYS_MAX_CSI2_COMBO_PORTS 2 -+ -+#define IPU6_MAX_FRAME_COUNTER 0xff -+ -+#define IPU6SE_ISYS_NUM_STREAMS IPU6SE_NONSECURE_STREAM_ID_MAX -+#define IPU6_ISYS_NUM_STREAMS IPU6_NONSECURE_STREAM_ID_MAX -+ -+/* -+ * To maximize the IOSF utlization, IPU6 need to send requests in bursts. -+ * At the DMA interface with the buttress, there are CDC FIFOs with burst -+ * collection capability. CDC FIFO burst collectors have a configurable -+ * threshold and is configured based on the outcome of performance measurements. -+ * -+ * isys has 3 ports with IOSF interface for VC0, VC1 and VC2 -+ * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2 -+ * -+ * Threshold values are pre-defined and are arrived at after performance -+ * evaluations on a type of IPU6 -+ */ -+#define IPU6_MAX_VC_IOSF_PORTS 4 -+ -+/* -+ * IPU6 must configure correct arbitration mechanism related to the IOSF VC -+ * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on -+ * stall and 1 means stall until the request is completed. -+ */ -+#define IPU6_BTRS_ARB_MODE_TYPE_REARB 0 -+#define IPU6_BTRS_ARB_MODE_TYPE_STALL 1 -+ -+/* Currently chosen arbitration mechanism for VC0 */ -+#define IPU6_BTRS_ARB_STALL_MODE_VC0 \ -+ IPU6_BTRS_ARB_MODE_TYPE_REARB -+ -+/* Currently chosen arbitration mechanism for VC1 */ -+#define IPU6_BTRS_ARB_STALL_MODE_VC1 \ -+ IPU6_BTRS_ARB_MODE_TYPE_REARB -+ -+/* -+ * MMU Invalidation HW bug workaround by ZLW mechanism -+ * -+ * Old IPU6 MMUV2 has a bug in the invalidation mechanism which might result in -+ * wrong translation or replication of the translation. This will cause data -+ * corruption. So we cannot directly use the MMU V2 invalidation registers -+ * to invalidate the MMU. Instead, whenever an invalidate is called, we need to -+ * clear the TLB by evicting all the valid translations by filling it with trash -+ * buffer (which is guaranteed not to be used by any other processes). ZLW is -+ * used to fill the L1 and L2 caches with the trash buffer translations. ZLW -+ * or Zero length write, is pre-fetch mechanism to pre-fetch the pages in -+ * advance to the L1 and L2 caches without triggering any memory operations. -+ * -+ * In MMU V2, L1 -> 16 streams and 64 blocks, maximum 16 blocks per stream -+ * One L1 block has 16 entries, hence points to 16 * 4K pages -+ * L2 -> 16 streams and 32 blocks. 2 blocks per streams -+ * One L2 block maps to 1024 L1 entries, hence points to 4MB address range -+ * 2 blocks per L2 stream means, 1 stream points to 8MB range -+ * -+ * As we need to clear the caches and 8MB being the biggest cache size, we need -+ * to have trash buffer which points to 8MB address range. As these trash -+ * buffers are not used for any memory transactions, we need only the least -+ * amount of physical memory. So we reserve 8MB IOVA address range but only -+ * one page is reserved from physical memory. Each of this 8MB IOVA address -+ * range is then mapped to the same physical memory page. -+ */ -+/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ -+#define IPU6_MMUV2_L2_RANGE (1024 * PAGE_SIZE) -+/* Max L2 blocks per stream */ -+#define IPU6_MMUV2_MAX_L2_BLOCKS 2 -+/* Max L1 blocks per stream */ -+#define IPU6_MMUV2_MAX_L1_BLOCKS 16 -+#define IPU6_MMUV2_TRASH_RANGE (IPU6_MMUV2_L2_RANGE * IPU6_MMUV2_MAX_L2_BLOCKS) -+/* Entries per L1 block */ -+#define MMUV2_ENTRIES_PER_L1_BLOCK 16 -+#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE) -+#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU6_MMUV2_L2_RANGE -+ -+/* -+ * In some of the IPU6 MMUs, there is provision to configure L1 and L2 page -+ * table caches. Both these L1 and L2 caches are divided into multiple sections -+ * called streams. There is maximum 16 streams for both caches. Each of these -+ * sections are subdivided into multiple blocks. When nr_l1streams = 0 and -+ * nr_l2streams = 0, means the MMU is of type MMU_V1 and do not support -+ * L1/L2 page table caches. -+ * -+ * L1 stream per block sizes are configurable and varies per usecase. -+ * L2 has constant block sizes - 2 blocks per stream. -+ * -+ * MMU1 support pre-fetching of the pages to have less cache lookup misses. To -+ * enable the pre-fetching, MMU1 AT (Address Translator) device registers -+ * need to be configured. -+ * -+ * There are four types of memory accesses which requires ZLW configuration. -+ * ZLW(Zero Length Write) is a mechanism to enable VT-d pre-fetching on IOMMU. -+ * -+ * 1. Sequential Access or 1D mode -+ * Set ZLW_EN -> 1 -+ * set ZLW_PAGE_CROSS_1D -> 1 -+ * Set ZLW_N to "N" pages so that ZLW will be inserte N pages ahead where -+ * N is pre-defined and hardcoded in the platform data -+ * Set ZLW_2D -> 0 -+ * -+ * 2. ZLW 2D mode -+ * Set ZLW_EN -> 1 -+ * set ZLW_PAGE_CROSS_1D -> 1, -+ * Set ZLW_N -> 0 -+ * Set ZLW_2D -> 1 -+ * -+ * 3. ZLW Enable (no 1D or 2D mode) -+ * Set ZLW_EN -> 1 -+ * set ZLW_PAGE_CROSS_1D -> 0, -+ * Set ZLW_N -> 0 -+ * Set ZLW_2D -> 0 -+ * -+ * 4. ZLW disable -+ * Set ZLW_EN -> 0 -+ * set ZLW_PAGE_CROSS_1D -> 0, -+ * Set ZLW_N -> 0 -+ * Set ZLW_2D -> 0 -+ * -+ * To configure the ZLW for the above memory access, four registers are -+ * available. Hence to track these four settings, we have the following entries -+ * in the struct ipu6_mmu_hw. Each of these entries are per stream and -+ * available only for the L1 streams. -+ * -+ * a. l1_zlw_en -> To track zlw enabled per stream (ZLW_EN) -+ * b. l1_zlw_1d_mode -> Track 1D mode per stream. ZLW inserted at page boundary -+ * c. l1_ins_zlw_ahead_pages -> to track how advance the ZLW need to be inserted -+ * Insert ZLW request N pages ahead address. -+ * d. l1_zlw_2d_mode -> To track 2D mode per stream (ZLW_2D) -+ * -+ * -+ * Currently L1/L2 streams, blocks, AT ZLW configurations etc. are pre-defined -+ * as per the usecase specific calculations. Any change to this pre-defined -+ * table has to happen in sync with IPU6 FW. -+ */ -+struct ipu6_mmu_hw { -+ union { -+ unsigned long offset; -+ void __iomem *base; -+ }; -+ u32 info_bits; -+ u8 nr_l1streams; -+ /* -+ * L1 has variable blocks per stream - total of 64 blocks and maximum of -+ * 16 blocks per stream. Configurable by using the block start address -+ * per stream. Block start address is calculated from the block size -+ */ -+ u8 l1_block_sz[IPU6_MMU_MAX_TLB_L1_STREAMS]; -+ /* Is ZLW is enabled in each stream */ -+ bool l1_zlw_en[IPU6_MMU_MAX_TLB_L1_STREAMS]; -+ bool l1_zlw_1d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS]; -+ u8 l1_ins_zlw_ahead_pages[IPU6_MMU_MAX_TLB_L1_STREAMS]; -+ bool l1_zlw_2d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS]; -+ -+ u32 l1_stream_id_reg_offset; -+ u32 l2_stream_id_reg_offset; -+ -+ u8 nr_l2streams; -+ /* -+ * L2 has fixed 2 blocks per stream. Block address is calculated -+ * from the block size -+ */ -+ u8 l2_block_sz[IPU6_MMU_MAX_TLB_L2_STREAMS]; -+ /* flag to track if WA is needed for successive invalidate HW bug */ -+ bool insert_read_before_invalidate; -+}; -+ -+struct ipu6_mmu_pdata { -+ u32 nr_mmus; -+ struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES]; -+ int mmid; -+}; -+ -+struct ipu6_isys_csi2_pdata { -+ void __iomem *base; -+}; -+ -+struct ipu6_isys_internal_csi2_pdata { -+ u32 nports; -+ u32 irq_mask; -+ u32 *offsets; -+ u32 ctrl0_irq_edge; -+ u32 ctrl0_irq_clear; -+ u32 ctrl0_irq_mask; -+ u32 ctrl0_irq_enable; -+ u32 ctrl0_irq_lnp; -+ u32 ctrl0_irq_status; -+ u32 fw_access_port_ofs; -+}; -+ -+struct ipu6_isys_internal_tpg_pdata { -+ u32 ntpgs; -+ u32 *offsets; -+ u32 *sels; -+}; -+ -+struct ipu6_hw_variants { -+ unsigned long offset; -+ u32 nr_mmus; -+ struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES]; -+ u8 cdc_fifos; -+ u8 cdc_fifo_threshold[IPU6_MAX_VC_IOSF_PORTS]; -+ u32 dmem_offset; -+ u32 spc_offset; -+}; -+ -+struct ipu6_isys_internal_pdata { -+ struct ipu6_isys_internal_csi2_pdata csi2; -+ struct ipu6_hw_variants hw_variant; -+ u32 num_parallel_streams; -+ u32 isys_dma_overshoot; -+ u32 sram_gran_shift; -+ u32 sram_gran_size; -+ u32 max_sram_size; -+ u32 max_streams; -+ u32 max_send_queues; -+ u32 max_sram_blocks; -+ u32 max_devq_size; -+ u32 sensor_type_start; -+ u32 sensor_type_end; -+ u32 ltr; -+ u32 memopen_threshold; -+ bool enhanced_iwake; -+}; -+ -+struct ipu6_isys_pdata { -+ void __iomem *base; -+ const struct ipu6_isys_internal_pdata *ipdata; -+}; -+ -+struct ipu6_psys_internal_pdata { -+ struct ipu6_hw_variants hw_variant; -+}; -+ -+struct ipu6_psys_pdata { -+ void __iomem *base; -+ const struct ipu6_psys_internal_pdata *ipdata; -+}; -+ -+int ipu6_fw_authenticate(void *data, u64 val); -+void ipu6_configure_spc(struct ipu6_device *isp, -+ const struct ipu6_hw_variants *hw_variant, -+ int pkg_dir_idx, void __iomem *base, u64 *pkg_dir, -+ dma_addr_t pkg_dir_dma_addr); -+#endif /* IPU6_H */ --- -2.43.2 - - -From f52c1b80222269f99d52b0af5937995e22c9ed6d Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:16 +0800 -Subject: [PATCH 09/33] media: intel/ipu6: add IPU auxiliary devices - -Even the IPU input system and processing system are in a single PCI -device, each system has its own power sequence, the processing system -power up depends on the input system power up. - -Besides, input system and processing system have their own MMU -hardware for IPU DMA address mapping. - -Register the IS/PS devices on auxiliary bus and attach power domain -to implement the power sequence dependency. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-bus.c | 165 ++++++++++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-bus.h | 58 +++++++++ - 2 files changed, 223 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-bus.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-bus.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.c b/drivers/media/pci/intel/ipu6/ipu6-bus.c -new file mode 100644 -index 000000000000..e81b9a6518a1 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-bus.c -@@ -0,0 +1,165 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/auxiliary_bus.h> -+#include <linux/device.h> -+#include <linux/dma-mapping.h> -+#include <linux/err.h> -+#include <linux/list.h> -+#include <linux/mutex.h> -+#include <linux/pci.h> -+#include <linux/pm_domain.h> -+#include <linux/pm_runtime.h> -+#include <linux/slab.h> -+ -+#include "ipu6.h" -+#include "ipu6-bus.h" -+#include "ipu6-buttress.h" -+#include "ipu6-dma.h" -+ -+static int bus_pm_runtime_suspend(struct device *dev) -+{ -+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); -+ int ret; -+ -+ ret = pm_generic_runtime_suspend(dev); -+ if (ret) -+ return ret; -+ -+ ret = ipu6_buttress_power(dev, adev->ctrl, false); -+ if (!ret) -+ return 0; -+ -+ dev_err(dev, "power down failed!\n"); -+ -+ /* Powering down failed, attempt to resume device now */ -+ ret = pm_generic_runtime_resume(dev); -+ if (!ret) -+ return -EBUSY; -+ -+ return -EIO; -+} -+ -+static int bus_pm_runtime_resume(struct device *dev) -+{ -+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); -+ int ret; -+ -+ ret = ipu6_buttress_power(dev, adev->ctrl, true); -+ if (ret) -+ return ret; -+ -+ ret = pm_generic_runtime_resume(dev); -+ if (ret) -+ goto out_err; -+ -+ return 0; -+ -+out_err: -+ ipu6_buttress_power(dev, adev->ctrl, false); -+ -+ return -EBUSY; -+} -+ -+static struct dev_pm_domain ipu6_bus_pm_domain = { -+ .ops = { -+ .runtime_suspend = bus_pm_runtime_suspend, -+ .runtime_resume = bus_pm_runtime_resume, -+ }, -+}; -+ -+static DEFINE_MUTEX(ipu6_bus_mutex); -+ -+static void ipu6_bus_release(struct device *dev) -+{ -+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); -+ -+ kfree(adev->pdata); -+ kfree(adev); -+} -+ -+struct ipu6_bus_device * -+ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -+ void *pdata, struct ipu6_buttress_ctrl *ctrl, -+ char *name) -+{ -+ struct auxiliary_device *auxdev; -+ struct ipu6_bus_device *adev; -+ struct ipu6_device *isp = pci_get_drvdata(pdev); -+ int ret; -+ -+ adev = kzalloc(sizeof(*adev), GFP_KERNEL); -+ if (!adev) -+ return ERR_PTR(-ENOMEM); -+ -+ adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS : -+ IPU6_MMU_ADDR_BITS_NON_SECURE); -+ adev->isp = isp; -+ adev->ctrl = ctrl; -+ adev->pdata = pdata; -+ auxdev = &adev->auxdev; -+ auxdev->name = name; -+ auxdev->id = (pci_domain_nr(pdev->bus) << 16) | -+ PCI_DEVID(pdev->bus->number, pdev->devfn); -+ -+ auxdev->dev.parent = parent; -+ auxdev->dev.release = ipu6_bus_release; -+ auxdev->dev.dma_ops = &ipu6_dma_ops; -+ auxdev->dev.dma_mask = &adev->dma_mask; -+ auxdev->dev.dma_parms = pdev->dev.dma_parms; -+ auxdev->dev.coherent_dma_mask = adev->dma_mask; -+ -+ ret = auxiliary_device_init(auxdev); -+ if (ret < 0) { -+ dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", -+ ret); -+ kfree(adev); -+ return ERR_PTR(ret); -+ } -+ -+ dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain); -+ -+ pm_runtime_forbid(&adev->auxdev.dev); -+ pm_runtime_enable(&adev->auxdev.dev); -+ -+ return adev; -+} -+ -+int ipu6_bus_add_device(struct ipu6_bus_device *adev) -+{ -+ struct auxiliary_device *auxdev = &adev->auxdev; -+ int ret; -+ -+ ret = auxiliary_device_add(auxdev); -+ if (ret) { -+ auxiliary_device_uninit(auxdev); -+ return ret; -+ } -+ -+ mutex_lock(&ipu6_bus_mutex); -+ list_add(&adev->list, &adev->isp->devices); -+ mutex_unlock(&ipu6_bus_mutex); -+ -+ pm_runtime_allow(&auxdev->dev); -+ -+ return 0; -+} -+ -+void ipu6_bus_del_devices(struct pci_dev *pdev) -+{ -+ struct ipu6_device *isp = pci_get_drvdata(pdev); -+ struct ipu6_bus_device *adev, *save; -+ -+ mutex_lock(&ipu6_bus_mutex); -+ -+ list_for_each_entry_safe(adev, save, &isp->devices, list) { -+ pm_runtime_disable(&adev->auxdev.dev); -+ list_del(&adev->list); -+ auxiliary_device_delete(&adev->auxdev); -+ auxiliary_device_uninit(&adev->auxdev); -+ } -+ -+ mutex_unlock(&ipu6_bus_mutex); -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h -new file mode 100644 -index 000000000000..d46181354836 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h -@@ -0,0 +1,58 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_BUS_H -+#define IPU6_BUS_H -+ -+#include <linux/auxiliary_bus.h> -+#include <linux/container_of.h> -+#include <linux/device.h> -+#include <linux/irqreturn.h> -+#include <linux/list.h> -+#include <linux/scatterlist.h> -+#include <linux/types.h> -+ -+struct firmware; -+struct pci_dev; -+ -+#define IPU6_BUS_NAME IPU6_NAME "-bus" -+ -+struct ipu6_buttress_ctrl; -+ -+struct ipu6_bus_device { -+ struct auxiliary_device auxdev; -+ struct auxiliary_driver *auxdrv; -+ const struct ipu6_auxdrv_data *auxdrv_data; -+ struct list_head list; -+ void *pdata; -+ struct ipu6_mmu *mmu; -+ struct ipu6_device *isp; -+ struct ipu6_buttress_ctrl *ctrl; -+ u64 dma_mask; -+ const struct firmware *fw; -+ struct sg_table fw_sgt; -+ u64 *pkg_dir; -+ dma_addr_t pkg_dir_dma_addr; -+ unsigned int pkg_dir_size; -+}; -+ -+struct ipu6_auxdrv_data { -+ irqreturn_t (*isr)(struct ipu6_bus_device *adev); -+ irqreturn_t (*isr_threaded)(struct ipu6_bus_device *adev); -+ bool wake_isr_thread; -+}; -+ -+#define to_ipu6_bus_device(_dev) \ -+ container_of(to_auxiliary_dev(_dev), struct ipu6_bus_device, auxdev) -+#define auxdev_to_adev(_auxdev) \ -+ container_of(_auxdev, struct ipu6_bus_device, auxdev) -+#define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev) -+ -+struct ipu6_bus_device * -+ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -+ void *pdata, struct ipu6_buttress_ctrl *ctrl, -+ char *name); -+int ipu6_bus_add_device(struct ipu6_bus_device *adev); -+void ipu6_bus_del_devices(struct pci_dev *pdev); -+ -+#endif --- -2.43.2 - - -From a74d85716ec13ff2f55997c73c9f06367174d7a6 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:17 +0800 -Subject: [PATCH 10/33] media: intel/ipu6: add IPU6 buttress interface driver - -The IPU6 buttress is the interface between IPU device (input system -and processing system) with rest of the SoC. It contains overall IPU -hardware control registers, these control registers are used as the -interfaces with the Intel Converged Security Engine and Punit to do -firmware authentication and power management. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-buttress.c | 912 ++++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-buttress.h | 102 ++ - .../intel/ipu6/ipu6-platform-buttress-regs.h | 232 +++++ - 3 files changed, 1246 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-buttress.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-buttress.h - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c -new file mode 100644 -index 000000000000..2f73302812f3 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c -@@ -0,0 +1,912 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/bitfield.h> -+#include <linux/bits.h> -+#include <linux/completion.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/dma-mapping.h> -+#include <linux/firmware.h> -+#include <linux/interrupt.h> -+#include <linux/iopoll.h> -+#include <linux/math64.h> -+#include <linux/mm.h> -+#include <linux/mutex.h> -+#include <linux/pci.h> -+#include <linux/pfn.h> -+#include <linux/pm_runtime.h> -+#include <linux/scatterlist.h> -+#include <linux/slab.h> -+#include <linux/time64.h> -+ -+#include "ipu6.h" -+#include "ipu6-bus.h" -+#include "ipu6-buttress.h" -+#include "ipu6-platform-buttress-regs.h" -+ -+#define BOOTLOADER_STATUS_OFFSET 0x15c -+ -+#define BOOTLOADER_MAGIC_KEY 0xb00710ad -+ -+#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 -+#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 -+#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE -+ -+#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10 -+ -+#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC) -+ -+#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC) -+#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC) -+#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC) -+ -+#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC -+#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC -+#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC) -+#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC) -+ -+#define BUTTRESS_IPC_RESET_RETRY 2000 -+#define BUTTRESS_CSE_IPC_RESET_RETRY 4 -+#define BUTTRESS_IPC_CMD_SEND_RETRY 1 -+ -+#define BUTTRESS_MAX_CONSECUTIVE_IRQS 100 -+ -+static const u32 ipu6_adev_irq_mask[2] = { -+ BUTTRESS_ISR_IS_IRQ, -+ BUTTRESS_ISR_PS_IRQ -+}; -+ -+int ipu6_buttress_ipc_reset(struct ipu6_device *isp, -+ struct ipu6_buttress_ipc *ipc) -+{ -+ unsigned int retries = BUTTRESS_IPC_RESET_RETRY; -+ struct ipu6_buttress *b = &isp->buttress; -+ u32 val = 0, csr_in_clr; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(&isp->pdev->dev, "Skip IPC reset for non-secure mode"); -+ return 0; -+ } -+ -+ mutex_lock(&b->ipc_mutex); -+ -+ /* Clear-by-1 CSR (all bits), corresponding internal states. */ -+ val = readl(isp->base + ipc->csr_in); -+ writel(val, isp->base + ipc->csr_in); -+ -+ /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ -+ writel(ENTRY, isp->base + ipc->csr_out); -+ /* -+ * Clear-by-1 all CSR bits EXCEPT following -+ * bits: -+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * C. Possibly custom bits, depending on -+ * their role. -+ */ -+ csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | -+ BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | -+ BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; -+ -+ do { -+ usleep_range(400, 500); -+ val = readl(isp->base + ipc->csr_in); -+ switch (val) { -+ case ENTRY | EXIT: -+ case ENTRY | EXIT | QUERY: -+ /* -+ * 1) Clear-by-1 CSR bits -+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2). -+ * 2) Set peer CSR bit -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -+ */ -+ writel(ENTRY | EXIT, isp->base + ipc->csr_in); -+ writel(QUERY, isp->base + ipc->csr_out); -+ break; -+ case ENTRY: -+ case ENTRY | QUERY: -+ /* -+ * 1) Clear-by-1 CSR bits -+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). -+ * 2) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ */ -+ writel(ENTRY | QUERY, isp->base + ipc->csr_in); -+ writel(ENTRY, isp->base + ipc->csr_out); -+ break; -+ case EXIT: -+ case EXIT | QUERY: -+ /* -+ * Clear-by-1 CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * 1) Clear incoming doorbell. -+ * 2) Clear-by-1 all CSR bits EXCEPT following -+ * bits: -+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * C. Possibly custom bits, depending on -+ * their role. -+ * 3) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ */ -+ writel(EXIT, isp->base + ipc->csr_in); -+ writel(0, isp->base + ipc->db0_in); -+ writel(csr_in_clr, isp->base + ipc->csr_in); -+ writel(EXIT, isp->base + ipc->csr_out); -+ -+ /* -+ * Read csr_in again to make sure if RST_PHASE2 is done. -+ * If csr_in is QUERY, it should be handled again. -+ */ -+ usleep_range(200, 300); -+ val = readl(isp->base + ipc->csr_in); -+ if (val & QUERY) { -+ dev_dbg(&isp->pdev->dev, -+ "RST_PHASE2 retry csr_in = %x\n", val); -+ break; -+ } -+ mutex_unlock(&b->ipc_mutex); -+ return 0; -+ case QUERY: -+ /* -+ * 1) Clear-by-1 CSR bit -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -+ * 2) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1 -+ */ -+ writel(QUERY, isp->base + ipc->csr_in); -+ writel(ENTRY, isp->base + ipc->csr_out); -+ break; -+ default: -+ dev_warn_ratelimited(&isp->pdev->dev, -+ "Unexpected CSR 0x%x\n", val); -+ break; -+ } -+ } while (retries--); -+ -+ mutex_unlock(&b->ipc_mutex); -+ dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n"); -+ -+ return -ETIMEDOUT; -+} -+ -+static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp, -+ struct ipu6_buttress_ipc *ipc) -+{ -+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, -+ isp->base + ipc->csr_out); -+} -+ -+static int -+ipu6_buttress_ipc_validity_open(struct ipu6_device *isp, -+ struct ipu6_buttress_ipc *ipc) -+{ -+ unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; -+ void __iomem *addr; -+ int ret; -+ u32 val; -+ -+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, -+ isp->base + ipc->csr_out); -+ -+ addr = isp->base + ipc->csr_in; -+ ret = readl_poll_timeout(addr, val, val & mask, 200, -+ BUTTRESS_IPC_VALIDITY_TIMEOUT_US); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); -+ ipu6_buttress_ipc_validity_close(isp, ipc); -+ } -+ -+ return ret; -+} -+ -+static void ipu6_buttress_ipc_recv(struct ipu6_device *isp, -+ struct ipu6_buttress_ipc *ipc, u32 *ipc_msg) -+{ -+ if (ipc_msg) -+ *ipc_msg = readl(isp->base + ipc->data0_in); -+ writel(0, isp->base + ipc->db0_in); -+} -+ -+static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp, -+ enum ipu6_buttress_ipc_domain ipc_domain, -+ struct ipu6_ipc_buttress_bulk_msg *msgs, -+ u32 size) -+{ -+ unsigned long tx_timeout_jiffies, rx_timeout_jiffies; -+ unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY; -+ struct ipu6_buttress *b = &isp->buttress; -+ struct ipu6_buttress_ipc *ipc; -+ u32 val; -+ int ret; -+ int tout; -+ -+ ipc = ipc_domain == IPU6_BUTTRESS_IPC_CSE ? &b->cse : &b->ish; -+ -+ mutex_lock(&b->ipc_mutex); -+ -+ ret = ipu6_buttress_ipc_validity_open(isp, ipc); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "IPC validity open failed\n"); -+ goto out; -+ } -+ -+ tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS); -+ rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS); -+ -+ for (i = 0; i < size; i++) { -+ reinit_completion(&ipc->send_complete); -+ if (msgs[i].require_resp) -+ reinit_completion(&ipc->recv_complete); -+ -+ dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n", -+ msgs[i].cmd); -+ writel(msgs[i].cmd, isp->base + ipc->data0_out); -+ val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size; -+ writel(val, isp->base + ipc->db0_out); -+ -+ tout = wait_for_completion_timeout(&ipc->send_complete, -+ tx_timeout_jiffies); -+ if (!tout) { -+ dev_err(&isp->pdev->dev, "send IPC response timeout\n"); -+ if (!retry--) { -+ ret = -ETIMEDOUT; -+ goto out; -+ } -+ -+ /* Try again if CSE is not responding on first try */ -+ writel(0, isp->base + ipc->db0_out); -+ i--; -+ continue; -+ } -+ -+ retry = BUTTRESS_IPC_CMD_SEND_RETRY; -+ -+ if (!msgs[i].require_resp) -+ continue; -+ -+ tout = wait_for_completion_timeout(&ipc->recv_complete, -+ rx_timeout_jiffies); -+ if (!tout) { -+ dev_err(&isp->pdev->dev, "recv IPC response timeout\n"); -+ ret = -ETIMEDOUT; -+ goto out; -+ } -+ -+ if (ipc->nack_mask && -+ (ipc->recv_data & ipc->nack_mask) == ipc->nack) { -+ dev_err(&isp->pdev->dev, -+ "IPC NACK for cmd 0x%x\n", msgs[i].cmd); -+ ret = -EIO; -+ goto out; -+ } -+ -+ if (ipc->recv_data != msgs[i].expected_resp) { -+ dev_err(&isp->pdev->dev, -+ "expected resp: 0x%x, IPC response: 0x%x ", -+ msgs[i].expected_resp, ipc->recv_data); -+ ret = -EIO; -+ goto out; -+ } -+ } -+ -+ dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n"); -+ -+out: -+ ipu6_buttress_ipc_validity_close(isp, ipc); -+ mutex_unlock(&b->ipc_mutex); -+ return ret; -+} -+ -+static int -+ipu6_buttress_ipc_send(struct ipu6_device *isp, -+ enum ipu6_buttress_ipc_domain ipc_domain, -+ u32 ipc_msg, u32 size, bool require_resp, -+ u32 expected_resp) -+{ -+ struct ipu6_ipc_buttress_bulk_msg msg = { -+ .cmd = ipc_msg, -+ .cmd_size = size, -+ .require_resp = require_resp, -+ .expected_resp = expected_resp, -+ }; -+ -+ return ipu6_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1); -+} -+ -+static irqreturn_t ipu6_buttress_call_isr(struct ipu6_bus_device *adev) -+{ -+ irqreturn_t ret = IRQ_WAKE_THREAD; -+ -+ if (!adev || !adev->auxdrv || !adev->auxdrv_data) -+ return IRQ_NONE; -+ -+ if (adev->auxdrv_data->isr) -+ ret = adev->auxdrv_data->isr(adev); -+ -+ if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded) -+ ret = IRQ_NONE; -+ -+ return ret; -+} -+ -+irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr) -+{ -+ struct ipu6_device *isp = isp_ptr; -+ struct ipu6_bus_device *adev[] = { isp->isys, isp->psys }; -+ struct ipu6_buttress *b = &isp->buttress; -+ u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS; -+ irqreturn_t ret = IRQ_NONE; -+ u32 disable_irqs = 0; -+ u32 irq_status; -+ u32 i, count = 0; -+ -+ pm_runtime_get_noresume(&isp->pdev->dev); -+ -+ irq_status = readl(isp->base + reg_irq_sts); -+ if (!irq_status) { -+ pm_runtime_put_noidle(&isp->pdev->dev); -+ return IRQ_NONE; -+ } -+ -+ do { -+ writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR); -+ -+ for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) { -+ irqreturn_t r = ipu6_buttress_call_isr(adev[i]); -+ -+ if (!(irq_status & ipu6_adev_irq_mask[i])) -+ continue; -+ -+ if (r == IRQ_WAKE_THREAD) { -+ ret = IRQ_WAKE_THREAD; -+ disable_irqs |= ipu6_adev_irq_mask[i]; -+ } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { -+ ret = IRQ_HANDLED; -+ } -+ } -+ -+ if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE) -+ ret = IRQ_HANDLED; -+ -+ if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) { -+ dev_dbg(&isp->pdev->dev, -+ "BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n"); -+ ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); -+ complete(&b->cse.recv_complete); -+ } -+ -+ if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) { -+ dev_dbg(&isp->pdev->dev, -+ "BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n"); -+ ipu6_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data); -+ complete(&b->ish.recv_complete); -+ } -+ -+ if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) { -+ dev_dbg(&isp->pdev->dev, -+ "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); -+ complete(&b->cse.send_complete); -+ } -+ -+ if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) { -+ dev_dbg(&isp->pdev->dev, -+ "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); -+ complete(&b->ish.send_complete); -+ } -+ -+ if (irq_status & BUTTRESS_ISR_SAI_VIOLATION && -+ ipu6_buttress_get_secure_mode(isp)) -+ dev_err(&isp->pdev->dev, -+ "BUTTRESS_ISR_SAI_VIOLATION\n"); -+ -+ if (irq_status & (BUTTRESS_ISR_IS_FATAL_MEM_ERR | -+ BUTTRESS_ISR_PS_FATAL_MEM_ERR)) -+ dev_err(&isp->pdev->dev, -+ "BUTTRESS_ISR_FATAL_MEM_ERR\n"); -+ -+ if (irq_status & BUTTRESS_ISR_UFI_ERROR) -+ dev_err(&isp->pdev->dev, "BUTTRESS_ISR_UFI_ERROR\n"); -+ -+ if (++count == BUTTRESS_MAX_CONSECUTIVE_IRQS) { -+ dev_err(&isp->pdev->dev, "too many consecutive IRQs\n"); -+ ret = IRQ_NONE; -+ break; -+ } -+ -+ irq_status = readl(isp->base + reg_irq_sts); -+ } while (irq_status); -+ -+ if (disable_irqs) -+ writel(BUTTRESS_IRQS & ~disable_irqs, -+ isp->base + BUTTRESS_REG_ISR_ENABLE); -+ -+ pm_runtime_put(&isp->pdev->dev); -+ -+ return ret; -+} -+ -+irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr) -+{ -+ struct ipu6_device *isp = isp_ptr; -+ struct ipu6_bus_device *adev[] = { isp->isys, isp->psys }; -+ const struct ipu6_auxdrv_data *drv_data = NULL; -+ irqreturn_t ret = IRQ_NONE; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) { -+ drv_data = adev[i]->auxdrv_data; -+ if (!drv_data) -+ continue; -+ -+ if (drv_data->wake_isr_thread && -+ drv_data->isr_threaded(adev[i]) == IRQ_HANDLED) -+ ret = IRQ_HANDLED; -+ } -+ -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); -+ -+ return ret; -+} -+ -+int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl, -+ bool on) -+{ -+ struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp; -+ u32 pwr_sts, val; -+ int ret; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ -+ if (!on) { -+ val = 0; -+ pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -+ } else { -+ val = BUTTRESS_FREQ_CTL_START | -+ FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK, -+ ctrl->ratio) | -+ FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK, -+ ctrl->qos_floor) | -+ BUTTRESS_FREQ_CTL_ICCMAX_LEVEL; -+ -+ pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -+ } -+ -+ writel(val, isp->base + ctrl->freq_ctl); -+ -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, -+ val, (val & ctrl->pwr_sts_mask) == pwr_sts, -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) -+ dev_err(&isp->pdev->dev, -+ "Change power status timeout with 0x%x\n", val); -+ -+ ctrl->started = !ret && on; -+ -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp) -+{ -+ u32 val; -+ -+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -+ -+ return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; -+} -+ -+bool ipu6_buttress_auth_done(struct ipu6_device *isp) -+{ -+ u32 val; -+ -+ if (!isp->secure_mode) -+ return true; -+ -+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -+ val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val); -+ -+ return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6); -+ -+int ipu6_buttress_reset_authentication(struct ipu6_device *isp) -+{ -+ int ret; -+ u32 val; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n"); -+ return 0; -+ } -+ -+ writel(BUTTRESS_FW_RESET_CTL_START, isp->base + -+ BUTTRESS_REG_FW_RESET_CTL); -+ -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, -+ val & BUTTRESS_FW_RESET_CTL_DONE, 500, -+ BUTTRESS_CSE_FWRESET_TIMEOUT_US); -+ if (ret) { -+ dev_err(&isp->pdev->dev, -+ "Time out while resetting authentication state\n"); -+ return ret; -+ } -+ -+ dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n"); -+ writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); -+ /* leave some time for HW restore */ -+ usleep_range(800, 1000); -+ -+ return 0; -+} -+ -+int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys, -+ const struct firmware *fw, struct sg_table *sgt) -+{ -+ struct page **pages; -+ const void *addr; -+ unsigned long n_pages; -+ unsigned int i; -+ int ret; -+ -+ n_pages = PHYS_PFN(PAGE_ALIGN(fw->size)); -+ -+ pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); -+ if (!pages) -+ return -ENOMEM; -+ -+ addr = fw->data; -+ for (i = 0; i < n_pages; i++) { -+ struct page *p = vmalloc_to_page(addr); -+ -+ if (!p) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ pages[i] = p; -+ addr += PAGE_SIZE; -+ } -+ -+ ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size, -+ GFP_KERNEL); -+ if (ret) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ ret = dma_map_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0); -+ if (ret < 0) { -+ ret = -ENOMEM; -+ sg_free_table(sgt); -+ goto out; -+ } -+ -+ dma_sync_sgtable_for_device(&sys->auxdev.dev, sgt, DMA_TO_DEVICE); -+ -+out: -+ kfree(pages); -+ -+ return ret; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6); -+ -+void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys, -+ struct sg_table *sgt) -+{ -+ dma_unmap_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0); -+ sg_free_table(sgt); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6); -+ -+int ipu6_buttress_authenticate(struct ipu6_device *isp) -+{ -+ struct ipu6_buttress *b = &isp->buttress; -+ struct ipu6_psys_pdata *psys_pdata; -+ u32 data, mask, done, fail; -+ int ret; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n"); -+ return 0; -+ } -+ -+ psys_pdata = isp->psys->pdata; -+ -+ mutex_lock(&b->auth_mutex); -+ -+ if (ipu6_buttress_auth_done(isp)) { -+ ret = 0; -+ goto out_unlock; -+ } -+ -+ /* -+ * Write address of FIT table to FW_SOURCE register -+ * Let's use fw address. I.e. not using FIT table yet -+ */ -+ data = lower_32_bits(isp->psys->pkg_dir_dma_addr); -+ writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO); -+ -+ data = upper_32_bits(isp->psys->pkg_dir_dma_addr); -+ writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI); -+ -+ /* -+ * Write boot_load into IU2CSEDATA0 -+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -+ */ -+ dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n"); -+ -+ ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE, -+ BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, -+ 1, true, -+ BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "CSE boot_load failed\n"); -+ goto out_unlock; -+ } -+ -+ mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; -+ done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; -+ fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -+ ((data & mask) == done || -+ (data & mask) == fail), 500, -+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "CSE boot_load timeout\n"); -+ goto out_unlock; -+ } -+ -+ if ((data & mask) == fail) { -+ dev_err(&isp->pdev->dev, "CSE auth failed\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET, -+ data, data == BOOTLOADER_MAGIC_KEY, 500, -+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n", -+ data); -+ goto out_unlock; -+ } -+ -+ /* -+ * Write authenticate_run into IU2CSEDATA0 -+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -+ */ -+ dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n"); -+ ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE, -+ BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN, -+ 1, true, -+ BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n"); -+ goto out_unlock; -+ } -+ -+ done = BUTTRESS_SECURITY_CTL_AUTH_DONE; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -+ ((data & mask) == done || -+ (data & mask) == fail), 500, -+ BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "CSE authenticate timeout\n"); -+ goto out_unlock; -+ } -+ -+ if ((data & mask) == fail) { -+ dev_err(&isp->pdev->dev, "CSE boot_load failed\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ dev_info(&isp->pdev->dev, "CSE authenticate_run done\n"); -+ -+out_unlock: -+ mutex_unlock(&b->auth_mutex); -+ -+ return ret; -+} -+ -+static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp) -+{ -+ u32 val, mask, done; -+ int ret; -+ -+ mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK; -+ -+ writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC, -+ isp->base + BUTTRESS_REG_FABRIC_CMD); -+ -+ val = readl(isp->base + BUTTRESS_REG_PWR_STATE); -+ val = FIELD_GET(mask, val); -+ if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) { -+ dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); -+ return -EINVAL; -+ } -+ -+ done = BUTTRESS_PWR_STATE_HH_STATE_DONE; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val, -+ FIELD_GET(mask, val) == done, 500, -+ BUTTRESS_TSC_SYNC_TIMEOUT_US); -+ if (ret) -+ dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); -+ -+ return ret; -+} -+ -+int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -+ u32 val; -+ int ret; -+ -+ ret = ipu6_buttress_send_tsc_request(isp); -+ if (ret != -ETIMEDOUT) -+ return ret; -+ -+ val = readl(isp->base + BUTTRESS_REG_TSW_CTL); -+ val = val | BUTTRESS_TSW_CTL_SOFT_RESET; -+ writel(val, isp->base + BUTTRESS_REG_TSW_CTL); -+ val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET; -+ writel(val, isp->base + BUTTRESS_REG_TSW_CTL); -+ } -+ -+ dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); -+ -+ return -ETIMEDOUT; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6); -+ -+void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val) -+{ -+ u32 tsc_hi_1, tsc_hi_2, tsc_lo; -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI); -+ tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); -+ tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI); -+ if (tsc_hi_1 == tsc_hi_2) { -+ *val = (u64)tsc_hi_1 << 32 | tsc_lo; -+ } else { -+ /* Check if TSC has rolled over */ -+ if (tsc_lo & BIT(31)) -+ *val = (u64)tsc_hi_1 << 32 | tsc_lo; -+ else -+ *val = (u64)tsc_hi_2 << 32 | tsc_lo; -+ } -+ local_irq_restore(flags); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6); -+ -+u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp) -+{ -+ u64 ns = ticks * 10000; -+ -+ /* -+ * converting TSC tick count to ns is calculated by: -+ * Example (TSC clock frequency is 19.2MHz): -+ * ns = ticks * 1000 000 000 / 19.2Mhz -+ * = ticks * 1000 000 000 / 19200000Hz -+ * = ticks * 10000 / 192 ns -+ */ -+ return div_u64(ns, isp->buttress.ref_clk); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6); -+ -+void ipu6_buttress_restore(struct ipu6_device *isp) -+{ -+ struct ipu6_buttress *b = &isp->buttress; -+ -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); -+ writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT); -+} -+ -+int ipu6_buttress_init(struct ipu6_device *isp) -+{ -+ int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; -+ struct ipu6_buttress *b = &isp->buttress; -+ u32 val; -+ -+ mutex_init(&b->power_mutex); -+ mutex_init(&b->auth_mutex); -+ mutex_init(&b->cons_mutex); -+ mutex_init(&b->ipc_mutex); -+ init_completion(&b->ish.send_complete); -+ init_completion(&b->cse.send_complete); -+ init_completion(&b->ish.recv_complete); -+ init_completion(&b->cse.recv_complete); -+ -+ b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; -+ b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; -+ b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; -+ b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; -+ b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; -+ b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; -+ b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; -+ b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; -+ -+ /* no ISH on IPU6 */ -+ memset(&b->ish, 0, sizeof(b->ish)); -+ INIT_LIST_HEAD(&b->constraints); -+ -+ isp->secure_mode = ipu6_buttress_get_secure_mode(isp); -+ dev_info(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n", -+ isp->secure_mode ? "secure" : "non-secure", -+ readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH), -+ readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); -+ -+ b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT); -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); -+ -+ /* get ref_clk frequency by reading the indication in btrs control */ -+ val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL); -+ val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val); -+ -+ switch (val) { -+ case 0x0: -+ b->ref_clk = 240; -+ break; -+ case 0x1: -+ b->ref_clk = 192; -+ break; -+ case 0x2: -+ b->ref_clk = 384; -+ break; -+ default: -+ dev_warn(&isp->pdev->dev, -+ "Unsupported ref clock, use 19.2Mhz by default.\n"); -+ b->ref_clk = 192; -+ break; -+ } -+ -+ /* Retry couple of times in case of CSE initialization is delayed */ -+ do { -+ ret = ipu6_buttress_ipc_reset(isp, &b->cse); -+ if (ret) { -+ dev_warn(&isp->pdev->dev, -+ "IPC reset protocol failed, retrying\n"); -+ } else { -+ dev_dbg(&isp->pdev->dev, "IPC reset done\n"); -+ return 0; -+ } -+ } while (ipc_reset_retry--); -+ -+ dev_err(&isp->pdev->dev, "IPC reset protocol failed\n"); -+ -+ mutex_destroy(&b->power_mutex); -+ mutex_destroy(&b->auth_mutex); -+ mutex_destroy(&b->cons_mutex); -+ mutex_destroy(&b->ipc_mutex); -+ -+ return ret; -+} -+ -+void ipu6_buttress_exit(struct ipu6_device *isp) -+{ -+ struct ipu6_buttress *b = &isp->buttress; -+ -+ writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE); -+ -+ mutex_destroy(&b->power_mutex); -+ mutex_destroy(&b->auth_mutex); -+ mutex_destroy(&b->cons_mutex); -+ mutex_destroy(&b->ipc_mutex); -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.h b/drivers/media/pci/intel/ipu6/ipu6-buttress.h -new file mode 100644 -index 000000000000..558e1d70f4af ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.h -@@ -0,0 +1,102 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_BUTTRESS_H -+#define IPU6_BUTTRESS_H -+ -+#include <linux/completion.h> -+#include <linux/irqreturn.h> -+#include <linux/list.h> -+#include <linux/mutex.h> -+ -+struct device; -+struct firmware; -+struct ipu6_device; -+struct ipu6_bus_device; -+ -+#define BUTTRESS_PS_FREQ_STEP 25U -+#define BUTTRESS_MIN_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 8) -+#define BUTTRESS_MAX_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 32) -+ -+#define BUTTRESS_IS_FREQ_STEP 25U -+#define BUTTRESS_MIN_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 8) -+#define BUTTRESS_MAX_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 22) -+ -+struct ipu6_buttress_ctrl { -+ u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; -+ unsigned int ratio; -+ unsigned int qos_floor; -+ bool started; -+}; -+ -+struct ipu6_buttress_ipc { -+ struct completion send_complete; -+ struct completion recv_complete; -+ u32 nack; -+ u32 nack_mask; -+ u32 recv_data; -+ u32 csr_out; -+ u32 csr_in; -+ u32 db0_in; -+ u32 db0_out; -+ u32 data0_out; -+ u32 data0_in; -+}; -+ -+struct ipu6_buttress { -+ struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; -+ struct ipu6_buttress_ipc cse; -+ struct ipu6_buttress_ipc ish; -+ struct list_head constraints; -+ u32 wdt_cached_value; -+ bool force_suspend; -+ u32 ref_clk; -+}; -+ -+struct ipu6_buttress_sensor_clk_freq { -+ unsigned int rate; -+ unsigned int val; -+}; -+ -+enum ipu6_buttress_ipc_domain { -+ IPU6_BUTTRESS_IPC_CSE, -+ IPU6_BUTTRESS_IPC_ISH, -+}; -+ -+struct ipu6_buttress_constraint { -+ struct list_head list; -+ unsigned int min_freq; -+}; -+ -+struct ipu6_ipc_buttress_bulk_msg { -+ u32 cmd; -+ u32 expected_resp; -+ bool require_resp; -+ u8 cmd_size; -+}; -+ -+int ipu6_buttress_ipc_reset(struct ipu6_device *isp, -+ struct ipu6_buttress_ipc *ipc); -+int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys, -+ const struct firmware *fw, -+ struct sg_table *sgt); -+void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys, -+ struct sg_table *sgt); -+int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl, -+ bool on); -+bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp); -+int ipu6_buttress_authenticate(struct ipu6_device *isp); -+int ipu6_buttress_reset_authentication(struct ipu6_device *isp); -+bool ipu6_buttress_auth_done(struct ipu6_device *isp); -+int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp); -+void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val); -+u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp); -+ -+irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr); -+irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr); -+int ipu6_buttress_init(struct ipu6_device *isp); -+void ipu6_buttress_exit(struct ipu6_device *isp); -+void ipu6_buttress_csi_port_config(struct ipu6_device *isp, -+ u32 legacy, u32 combo); -+void ipu6_buttress_restore(struct ipu6_device *isp); -+#endif /* IPU6_BUTTRESS_H */ -diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h -new file mode 100644 -index 000000000000..87239af96502 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h -@@ -0,0 +1,232 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2023 Intel Corporation */ -+ -+#ifndef IPU6_PLATFORM_BUTTRESS_REGS_H -+#define IPU6_PLATFORM_BUTTRESS_REGS_H -+ -+#include <linux/bits.h> -+ -+/* IS_WORKPOINT_REQ */ -+#define IPU6_BUTTRESS_REG_IS_FREQ_CTL 0x34 -+/* PS_WORKPOINT_REQ */ -+#define IPU6_BUTTRESS_REG_PS_FREQ_CTL 0x38 -+ -+#define IPU6_IS_FREQ_MAX 533 -+#define IPU6_IS_FREQ_MIN 200 -+#define IPU6_PS_FREQ_MAX 450 -+#define IPU6_IS_FREQ_RATIO_BASE 25 -+#define IPU6_PS_FREQ_RATIO_BASE 25 -+ -+/* should be tuned for real silicon */ -+#define IPU6_IS_FREQ_CTL_DEFAULT_RATIO 0x08 -+#define IPU6SE_IS_FREQ_CTL_DEFAULT_RATIO 0x0a -+#define IPU6_PS_FREQ_CTL_DEFAULT_RATIO 0x0d -+ -+#define IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO 0x10 -+#define IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO 0x0708 -+ -+#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3 -+#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK GENMASK(4, 3) -+ -+#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6 -+#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK GENMASK(7, 6) -+ -+#define IPU6_BUTTRESS_PWR_STATE_DN_DONE 0x0 -+#define IPU6_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 -+#define IPU6_BUTTRESS_PWR_STATE_DN_PROCESS 0x2 -+#define IPU6_BUTTRESS_PWR_STATE_UP_DONE 0x3 -+ -+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_0 0x270 -+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_1 0x274 -+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_2 0x278 -+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_3 0x27c -+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_4 0x280 -+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_5 0x284 -+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_6 0x288 -+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_7 0x28c -+ -+#define BUTTRESS_REG_WDT 0x8 -+#define BUTTRESS_REG_BTRS_CTRL 0xc -+#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0 BIT(0) -+#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1 BIT(1) -+#define BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND GENMASK(9, 8) -+ -+#define BUTTRESS_REG_FW_RESET_CTL 0x30 -+#define BUTTRESS_FW_RESET_CTL_START BIT(0) -+#define BUTTRESS_FW_RESET_CTL_DONE BIT(1) -+ -+#define BUTTRESS_REG_IS_FREQ_CTL 0x34 -+#define BUTTRESS_REG_PS_FREQ_CTL 0x38 -+ -+#define BUTTRESS_FREQ_CTL_START BIT(31) -+#define BUTTRESS_FREQ_CTL_ICCMAX_LEVEL GENMASK(19, 16) -+#define BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK GENMASK(15, 8) -+#define BUTTRESS_FREQ_CTL_RATIO_MASK GENMASK(7, 0) -+ -+#define BUTTRESS_REG_PWR_STATE 0x5c -+ -+#define BUTTRESS_PWR_STATE_RESET 0x0 -+#define BUTTRESS_PWR_STATE_PWR_ON_DONE 0x1 -+#define BUTTRESS_PWR_STATE_PWR_RDY 0x3 -+#define BUTTRESS_PWR_STATE_PWR_IDLE 0x4 -+ -+#define BUTTRESS_PWR_STATE_HH_STATUS_MASK GENMASK(12, 11) -+ -+enum { -+ BUTTRESS_PWR_STATE_HH_STATE_IDLE, -+ BUTTRESS_PWR_STATE_HH_STATE_IN_PRGS, -+ BUTTRESS_PWR_STATE_HH_STATE_DONE, -+ BUTTRESS_PWR_STATE_HH_STATE_ERR, -+}; -+ -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK GENMASK(23, 19) -+ -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE 0x0 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PLL_CMP 0x1 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK 0x2 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PG_ACK 0x3 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_ASSRT_CYCLES 0x4 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES1 0x5 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES2 0x6 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DEASSRT_CYCLES 0x7 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_FUSE_WR_CMP 0x8 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_BRK_POINT 0x9 -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY 0xa -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_HALT_HALTED 0xb -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DURATION_CNT3 0xc -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK_PD 0xd -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_PD_BRK_POINT 0xe -+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PD_PG_ACK0 0xf -+ -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK GENMASK(28, 24) -+ -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE 0x0 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_IP_RDY 0x1 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_PRE_CNT_EXH 0x2 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_VGI_PWRGOOD 0x3 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_POST_CNT_EXH 0x4 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WR_PLL_RATIO 0x5 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_CMP 0x6 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_CLKACK 0x7 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_ASSRT_CYCLES 0x8 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES1 0x9 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES2 0xa -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_DEASSRT_CYCLES 0xb -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PU_BRK_PNT 0xc -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_FUSE_ACCPT 0xd -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP 0xf -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_4_HALTED 0x10 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RESET_CNT3 0x11 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_CLKACK 0x12 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_OFF_IND 0x13 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PH4 0x14 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PLL_CMP 0x15 -+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_CLKACK 0x16 -+ -+#define BUTTRESS_REG_SECURITY_CTL 0x300 -+#define BUTTRESS_REG_SKU 0x314 -+#define BUTTRESS_REG_SECURITY_TOUCH 0x318 -+#define BUTTRESS_REG_CAMERA_MASK 0x84 -+ -+#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16) -+#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0) -+ -+#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0) -+#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1) -+#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3) -+ -+#define BUTTRESS_REG_FW_SOURCE_BASE_LO 0x78 -+#define BUTTRESS_REG_FW_SOURCE_BASE_HI 0x7C -+#define BUTTRESS_REG_FW_SOURCE_SIZE 0x80 -+ -+#define BUTTRESS_REG_ISR_STATUS 0x90 -+#define BUTTRESS_REG_ISR_ENABLED_STATUS 0x94 -+#define BUTTRESS_REG_ISR_ENABLE 0x98 -+#define BUTTRESS_REG_ISR_CLEAR 0x9C -+ -+#define BUTTRESS_ISR_IS_IRQ BIT(0) -+#define BUTTRESS_ISR_PS_IRQ BIT(1) -+#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE BIT(2) -+#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH BIT(3) -+#define BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING BIT(4) -+#define BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING BIT(5) -+#define BUTTRESS_ISR_CSE_CSR_SET BIT(6) -+#define BUTTRESS_ISR_ISH_CSR_SET BIT(7) -+#define BUTTRESS_ISR_SPURIOUS_CMP BIT(8) -+#define BUTTRESS_ISR_WATCHDOG_EXPIRED BIT(9) -+#define BUTTRESS_ISR_PUNIT_2_IUNIT_IRQ BIT(10) -+#define BUTTRESS_ISR_SAI_VIOLATION BIT(11) -+#define BUTTRESS_ISR_HW_ASSERTION BIT(12) -+#define BUTTRESS_ISR_IS_CORRECTABLE_MEM_ERR BIT(13) -+#define BUTTRESS_ISR_IS_FATAL_MEM_ERR BIT(14) -+#define BUTTRESS_ISR_IS_NON_FATAL_MEM_ERR BIT(15) -+#define BUTTRESS_ISR_PS_CORRECTABLE_MEM_ERR BIT(16) -+#define BUTTRESS_ISR_PS_FATAL_MEM_ERR BIT(17) -+#define BUTTRESS_ISR_PS_NON_FATAL_MEM_ERR BIT(18) -+#define BUTTRESS_ISR_PS_FAST_THROTTLE BIT(19) -+#define BUTTRESS_ISR_UFI_ERROR BIT(20) -+ -+#define BUTTRESS_REG_IU2CSEDB0 0x100 -+ -+#define BUTTRESS_IU2CSEDB0_BUSY BIT(31) -+#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2 -+ -+#define BUTTRESS_REG_IU2CSEDATA0 0x104 -+ -+#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1 -+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2 -+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3 -+#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16 -+ -+#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0) -+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1) -+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2) -+#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4) -+ -+#define BUTTRESS_REG_IU2CSECSR 0x108 -+ -+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5) -+ -+#define BUTTRESS_REG_CSE2IUDB0 0x304 -+#define BUTTRESS_REG_CSE2IUCSR 0x30C -+#define BUTTRESS_REG_CSE2IUDATA0 0x308 -+ -+/* 0x20 == NACK, 0xf == unknown command */ -+#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20 -+#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK GENMASK(15, 0) -+ -+#define BUTTRESS_REG_ISH2IUCSR 0x50 -+#define BUTTRESS_REG_ISH2IUDB0 0x54 -+#define BUTTRESS_REG_ISH2IUDATA0 0x58 -+ -+#define BUTTRESS_REG_IU2ISHDB0 0x10C -+#define BUTTRESS_REG_IU2ISHDATA0 0x110 -+#define BUTTRESS_REG_IU2ISHDATA1 0x114 -+#define BUTTRESS_REG_IU2ISHCSR 0x118 -+ -+#define BUTTRESS_REG_FABRIC_CMD 0x88 -+ -+#define BUTTRESS_FABRIC_CMD_START_TSC_SYNC BIT(0) -+#define BUTTRESS_FABRIC_CMD_IS_DRAIN BIT(4) -+ -+#define BUTTRESS_REG_TSW_CTL 0x120 -+#define BUTTRESS_TSW_CTL_SOFT_RESET BIT(8) -+ -+#define BUTTRESS_REG_TSC_LO 0x164 -+#define BUTTRESS_REG_TSC_HI 0x168 -+ -+#define BUTTRESS_IRQS (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \ -+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \ -+ BUTTRESS_ISR_IS_IRQ | BUTTRESS_ISR_PS_IRQ) -+ -+#define BUTTRESS_EVENT (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \ -+ BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | \ -+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \ -+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH | \ -+ BUTTRESS_ISR_SAI_VIOLATION) -+#endif /* IPU6_PLATFORM_BUTTRESS_REGS_H */ --- -2.43.2 - - -From 12bd5bdd53a7c829cc5cd61a6887f89ffb036f8f Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:18 +0800 -Subject: [PATCH 11/33] media: intel/ipu6: CPD parsing for get firmware - components - -For IPU6, firmware is generated and released as signed -Code Partition Directory (CPD) format file, which is aligned with -the SPI flash code partition definition. CPD format include CPD -header, manifest, metadata and module data. Driver can parse them -according to the CPD layout to acquire each component. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-cpd.c | 362 ++++++++++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-cpd.h | 105 +++++++ - 2 files changed, 467 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-cpd.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-cpd.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.c b/drivers/media/pci/intel/ipu6/ipu6-cpd.c -new file mode 100644 -index 000000000000..b0ffd04c4cd3 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.c -@@ -0,0 +1,362 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/bitfield.h> -+#include <linux/bits.h> -+#include <linux/err.h> -+#include <linux/dma-mapping.h> -+#include <linux/gfp_types.h> -+#include <linux/math64.h> -+#include <linux/sizes.h> -+#include <linux/types.h> -+ -+#include "ipu6.h" -+#include "ipu6-bus.h" -+#include "ipu6-cpd.h" -+ -+/* 15 entries + header*/ -+#define MAX_PKG_DIR_ENT_CNT 16 -+/* 2 qword per entry/header */ -+#define PKG_DIR_ENT_LEN 2 -+/* PKG_DIR size in bytes */ -+#define PKG_DIR_SIZE ((MAX_PKG_DIR_ENT_CNT) * \ -+ (PKG_DIR_ENT_LEN) * sizeof(u64)) -+/* _IUPKDR_ */ -+#define PKG_DIR_HDR_MARK 0x5f4955504b44525fULL -+ -+/* $CPD */ -+#define CPD_HDR_MARK 0x44504324 -+ -+#define MAX_MANIFEST_SIZE (SZ_2K * sizeof(u32)) -+#define MAX_METADATA_SIZE SZ_64K -+ -+#define MAX_COMPONENT_ID 127 -+#define MAX_COMPONENT_VERSION 0xffff -+ -+#define MANIFEST_IDX 0 -+#define METADATA_IDX 1 -+#define MODULEDATA_IDX 2 -+/* -+ * PKG_DIR Entry (type == id) -+ * 63:56 55 54:48 47:32 31:24 23:0 -+ * Rsvd Rsvd Type Version Rsvd Size -+ */ -+#define PKG_DIR_SIZE_MASK GENMASK(23, 0) -+#define PKG_DIR_VERSION_MASK GENMASK(47, 32) -+#define PKG_DIR_TYPE_MASK GENMASK(54, 48) -+ -+static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd, -+ u8 idx) -+{ -+ const struct ipu6_cpd_hdr *cpd_hdr = cpd; -+ const struct ipu6_cpd_ent *ent; -+ -+ ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len); -+ return ent + idx; -+} -+ -+#define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX) -+#define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX) -+#define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX) -+ -+static const struct ipu6_cpd_metadata_cmpnt_hdr * -+ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata, -+ unsigned int metadata_size, u8 idx) -+{ -+ size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn); -+ size_t cmpnt_count = metadata_size - extn_size; -+ -+ cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size); -+ -+ if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) { -+ dev_err(&isp->pdev->dev, "Component index out of range (%d)\n", -+ idx); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size; -+} -+ -+static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp, -+ const void *metadata, -+ unsigned int metadata_size, u8 idx) -+{ -+ const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt; -+ -+ cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx); -+ if (IS_ERR(cmpnt)) -+ return PTR_ERR(cmpnt); -+ -+ return cmpnt->ver; -+} -+ -+static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp, -+ const void *metadata, -+ unsigned int metadata_size, u8 idx) -+{ -+ const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt; -+ -+ cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx); -+ if (IS_ERR(cmpnt)) -+ return PTR_ERR(cmpnt); -+ -+ return cmpnt->id; -+} -+ -+static int ipu6_cpd_parse_module_data(struct ipu6_device *isp, -+ const void *module_data, -+ unsigned int module_data_size, -+ dma_addr_t dma_addr_module_data, -+ u64 *pkg_dir, const void *metadata, -+ unsigned int metadata_size) -+{ -+ const struct ipu6_cpd_module_data_hdr *module_data_hdr; -+ const struct ipu6_cpd_hdr *dir_hdr; -+ const struct ipu6_cpd_ent *dir_ent; -+ unsigned int i; -+ u8 len; -+ -+ if (!module_data) -+ return -EINVAL; -+ -+ module_data_hdr = module_data; -+ dir_hdr = module_data + module_data_hdr->hdr_len; -+ len = dir_hdr->hdr_len; -+ dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len); -+ -+ pkg_dir[0] = PKG_DIR_HDR_MARK; -+ /* pkg_dir entry count = component count + pkg_dir header */ -+ pkg_dir[1] = dir_hdr->ent_cnt + 1; -+ -+ for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) { -+ u64 *p = &pkg_dir[PKG_DIR_ENT_LEN * (1 + i)]; -+ int ver, id; -+ -+ *p++ = dma_addr_module_data + dir_ent->offset; -+ id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata, -+ metadata_size, i); -+ if (id < 0 || id > MAX_COMPONENT_ID) { -+ dev_err(&isp->pdev->dev, "Invalid CPD component id\n"); -+ return -EINVAL; -+ } -+ -+ ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata, -+ metadata_size, i); -+ if (ver < 0 || ver > MAX_COMPONENT_VERSION) { -+ dev_err(&isp->pdev->dev, -+ "Invalid CPD component version\n"); -+ return -EINVAL; -+ } -+ -+ *p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) | -+ FIELD_PREP(PKG_DIR_TYPE_MASK, id) | -+ FIELD_PREP(PKG_DIR_VERSION_MASK, ver); -+ } -+ -+ return 0; -+} -+ -+int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src) -+{ -+ dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl); -+ const struct ipu6_cpd_ent *ent, *man_ent, *met_ent; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu6_device *isp = adev->isp; -+ unsigned int man_sz, met_sz; -+ void *pkg_dir_pos; -+ int ret; -+ -+ man_ent = ipu6_cpd_get_manifest(src); -+ man_sz = man_ent->len; -+ -+ met_ent = ipu6_cpd_get_metadata(src); -+ met_sz = met_ent->len; -+ -+ adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz; -+ adev->pkg_dir = dma_alloc_attrs(dev, adev->pkg_dir_size, -+ &adev->pkg_dir_dma_addr, GFP_KERNEL, 0); -+ if (!adev->pkg_dir) -+ return -ENOMEM; -+ -+ /* -+ * pkg_dir entry/header: -+ * qword | 63:56 | 55 | 54:48 | 47:32 | 31:24 | 23:0 -+ * N Address/Offset/"_IUPKDR_" -+ * N + 1 | rsvd | rsvd | type | ver | rsvd | size -+ * -+ * We can ignore other fields that size in N + 1 qword as they -+ * are 0 anyway. Just setting size for now. -+ */ -+ -+ ent = ipu6_cpd_get_moduledata(src); -+ -+ ret = ipu6_cpd_parse_module_data(isp, src + ent->offset, -+ ent->len, dma_addr_src + ent->offset, -+ adev->pkg_dir, src + met_ent->offset, -+ met_ent->len); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "Failed to parse module data\n"); -+ dma_free_attrs(dev, adev->pkg_dir_size, -+ adev->pkg_dir, adev->pkg_dir_dma_addr, 0); -+ return ret; -+ } -+ -+ /* Copy manifest after pkg_dir */ -+ pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT; -+ memcpy(pkg_dir_pos, src + man_ent->offset, man_sz); -+ -+ /* Copy metadata after manifest */ -+ pkg_dir_pos += man_sz; -+ memcpy(pkg_dir_pos, src + met_ent->offset, met_sz); -+ -+ dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr, -+ 0, adev->pkg_dir_size, DMA_TO_DEVICE); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6); -+ -+void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev) -+{ -+ dma_free_attrs(&adev->auxdev.dev, adev->pkg_dir_size, adev->pkg_dir, -+ adev->pkg_dir_dma_addr, 0); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6); -+ -+static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd, -+ unsigned long cpd_size, -+ unsigned long data_size) -+{ -+ const struct ipu6_cpd_hdr *cpd_hdr = cpd; -+ const struct ipu6_cpd_ent *ent; -+ unsigned int i; -+ u8 len; -+ -+ len = cpd_hdr->hdr_len; -+ -+ /* Ensure cpd hdr is within moduledata */ -+ if (cpd_size < len) { -+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n"); -+ return -EINVAL; -+ } -+ -+ /* Sanity check for CPD header */ -+ if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { -+ dev_err(&isp->pdev->dev, "Invalid CPD header\n"); -+ return -EINVAL; -+ } -+ -+ /* Ensure that all entries are within moduledata */ -+ ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len); -+ for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) { -+ if (data_size < ent->offset || -+ data_size - ent->offset < ent->len) { -+ dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i); -+ return -EINVAL; -+ } -+ } -+ -+ return 0; -+} -+ -+static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp, -+ const void *moduledata, -+ u32 moduledata_size) -+{ -+ const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata; -+ int ret; -+ -+ /* Ensure moduledata hdr is within moduledata */ -+ if (moduledata_size < sizeof(*mod_hdr) || -+ moduledata_size < mod_hdr->hdr_len) { -+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n"); -+ return -EINVAL; -+ } -+ -+ dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date); -+ ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len, -+ moduledata_size - mod_hdr->hdr_len, -+ moduledata_size); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int ipu6_cpd_validate_metadata(struct ipu6_device *isp, -+ const void *metadata, u32 meta_size) -+{ -+ const struct ipu6_cpd_metadata_extn *extn = metadata; -+ -+ /* Sanity check for metadata size */ -+ if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) { -+ dev_err(&isp->pdev->dev, "Invalid CPD metadata\n"); -+ return -EINVAL; -+ } -+ -+ /* Validate extension and image types */ -+ if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT || -+ extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) { -+ dev_err(&isp->pdev->dev, -+ "Invalid CPD metadata descriptor img_type (%d)\n", -+ extn->img_type); -+ return -EINVAL; -+ } -+ -+ /* Validate metadata size multiple of metadata components */ -+ if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) { -+ dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, -+ unsigned long cpd_file_size) -+{ -+ const struct ipu6_cpd_hdr *hdr = cpd_file; -+ const struct ipu6_cpd_ent *ent; -+ int ret; -+ -+ ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size, -+ cpd_file_size); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "Invalid CPD in file\n"); -+ return ret; -+ } -+ -+ /* Check for CPD file marker */ -+ if (hdr->hdr_mark != CPD_HDR_MARK) { -+ dev_err(&isp->pdev->dev, "Invalid CPD header\n"); -+ return -EINVAL; -+ } -+ -+ /* Sanity check for manifest size */ -+ ent = ipu6_cpd_get_manifest(cpd_file); -+ if (ent->len > MAX_MANIFEST_SIZE) { -+ dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n"); -+ return -EINVAL; -+ } -+ -+ /* Validate metadata */ -+ ent = ipu6_cpd_get_metadata(cpd_file); -+ ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "Invalid CPD metadata\n"); -+ return ret; -+ } -+ -+ /* Validate moduledata */ -+ ent = ipu6_cpd_get_moduledata(cpd_file); -+ ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset, -+ ent->len); -+ if (ret) -+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n"); -+ -+ return ret; -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.h b/drivers/media/pci/intel/ipu6/ipu6-cpd.h -new file mode 100644 -index 000000000000..37465d507386 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.h -@@ -0,0 +1,105 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2015 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_CPD_H -+#define IPU6_CPD_H -+ -+struct ipu6_device; -+struct ipu6_bus_device; -+ -+#define IPU6_CPD_SIZE_OF_FW_ARCH_VERSION 7 -+#define IPU6_CPD_SIZE_OF_SYSTEM_VERSION 11 -+#define IPU6_CPD_SIZE_OF_COMPONENT_NAME 12 -+ -+#define IPU6_CPD_METADATA_EXTN_TYPE_IUNIT 0x10 -+ -+#define IPU6_CPD_METADATA_IMAGE_TYPE_RESERVED 0 -+#define IPU6_CPD_METADATA_IMAGE_TYPE_BOOTLOADER 1 -+#define IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE 2 -+ -+#define IPU6_CPD_PKG_DIR_PSYS_SERVER_IDX 0 -+#define IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX 1 -+ -+#define IPU6_CPD_PKG_DIR_CLIENT_PG_TYPE 3 -+ -+#define IPU6_CPD_METADATA_HASH_KEY_SIZE 48 -+#define IPU6SE_CPD_METADATA_HASH_KEY_SIZE 32 -+ -+struct ipu6_cpd_module_data_hdr { -+ u32 hdr_len; -+ u32 endian; -+ u32 fw_pkg_date; -+ u32 hive_sdk_date; -+ u32 compiler_date; -+ u32 target_platform_type; -+ u8 sys_ver[IPU6_CPD_SIZE_OF_SYSTEM_VERSION]; -+ u8 fw_arch_ver[IPU6_CPD_SIZE_OF_FW_ARCH_VERSION]; -+ u8 rsvd[2]; -+} __packed; -+ -+/* -+ * ipu6_cpd_hdr structure updated as the chksum and -+ * sub_partition_name is unused on host side -+ * CSE layout version 1.6 for IPU6SE (hdr_len = 0x10) -+ * CSE layout version 1.7 for IPU6 (hdr_len = 0x14) -+ */ -+struct ipu6_cpd_hdr { -+ u32 hdr_mark; -+ u32 ent_cnt; -+ u8 hdr_ver; -+ u8 ent_ver; -+ u8 hdr_len; -+} __packed; -+ -+struct ipu6_cpd_ent { -+ u8 name[IPU6_CPD_SIZE_OF_COMPONENT_NAME]; -+ u32 offset; -+ u32 len; -+ u8 rsvd[4]; -+} __packed; -+ -+struct ipu6_cpd_metadata_cmpnt_hdr { -+ u32 id; -+ u32 size; -+ u32 ver; -+} __packed; -+ -+struct ipu6_cpd_metadata_cmpnt { -+ struct ipu6_cpd_metadata_cmpnt_hdr hdr; -+ u8 sha2_hash[IPU6_CPD_METADATA_HASH_KEY_SIZE]; -+ u32 entry_point; -+ u32 icache_base_offs; -+ u8 attrs[16]; -+} __packed; -+ -+struct ipu6se_cpd_metadata_cmpnt { -+ struct ipu6_cpd_metadata_cmpnt_hdr hdr; -+ u8 sha2_hash[IPU6SE_CPD_METADATA_HASH_KEY_SIZE]; -+ u32 entry_point; -+ u32 icache_base_offs; -+ u8 attrs[16]; -+} __packed; -+ -+struct ipu6_cpd_metadata_extn { -+ u32 extn_type; -+ u32 len; -+ u32 img_type; -+ u8 rsvd[16]; -+} __packed; -+ -+struct ipu6_cpd_client_pkg_hdr { -+ u32 prog_list_offs; -+ u32 prog_list_size; -+ u32 prog_desc_offs; -+ u32 prog_desc_size; -+ u32 pg_manifest_offs; -+ u32 pg_manifest_size; -+ u32 prog_bin_offs; -+ u32 prog_bin_size; -+} __packed; -+ -+int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src); -+void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev); -+int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, -+ unsigned long cpd_file_size); -+#endif /* IPU6_CPD_H */ --- -2.43.2 - - -From 86b4cd540a9cb10de7a521882495f5eba6cc919f Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:19 +0800 -Subject: [PATCH 12/33] media: intel/ipu6: add IPU6 DMA mapping API and MMU - table - -he Intel IPU6 has an internal microcontroller (scalar processor, SP) which -is used to execute the firmware. The SP can access IPU internal memory and -map system DRAM to its an internal 32-bit virtual address space. - -This patch adds a driver for the IPU MMU and a DMA mapping implementation -using the internal MMU. The system IOMMU may be used besides the IPU MMU. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-dma.c | 502 ++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-dma.h | 19 + - drivers/media/pci/intel/ipu6/ipu6-mmu.c | 845 ++++++++++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-mmu.h | 73 ++ - 4 files changed, 1439 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-dma.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-dma.h - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-mmu.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-mmu.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.c b/drivers/media/pci/intel/ipu6/ipu6-dma.c -new file mode 100644 -index 000000000000..3d77c6e5a45e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-dma.c -@@ -0,0 +1,502 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/cacheflush.h> -+#include <linux/dma-mapping.h> -+#include <linux/iova.h> -+#include <linux/list.h> -+#include <linux/mm.h> -+#include <linux/vmalloc.h> -+#include <linux/scatterlist.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+ -+#include "ipu6.h" -+#include "ipu6-bus.h" -+#include "ipu6-dma.h" -+#include "ipu6-mmu.h" -+ -+struct vm_info { -+ struct list_head list; -+ struct page **pages; -+ dma_addr_t ipu6_iova; -+ void *vaddr; -+ unsigned long size; -+}; -+ -+static struct vm_info *get_vm_info(struct ipu6_mmu *mmu, dma_addr_t iova) -+{ -+ struct vm_info *info, *save; -+ -+ list_for_each_entry_safe(info, save, &mmu->vma_list, list) { -+ if (iova >= info->ipu6_iova && -+ iova < (info->ipu6_iova + info->size)) -+ return info; -+ } -+ -+ return NULL; -+} -+ -+static void __dma_clear_buffer(struct page *page, size_t size, -+ unsigned long attrs) -+{ -+ void *ptr; -+ -+ if (!page) -+ return; -+ /* -+ * Ensure that the allocated pages are zeroed, and that any data -+ * lurking in the kernel direct-mapped region is invalidated. -+ */ -+ ptr = page_address(page); -+ memset(ptr, 0, size); -+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) -+ clflush_cache_range(ptr, size); -+} -+ -+static struct page **__dma_alloc_buffer(struct device *dev, size_t size, -+ gfp_t gfp, unsigned long attrs) -+{ -+ int count = PHYS_PFN(size); -+ int array_size = count * sizeof(struct page *); -+ struct page **pages; -+ int i = 0; -+ -+ pages = kvzalloc(array_size, GFP_KERNEL); -+ if (!pages) -+ return NULL; -+ -+ gfp |= __GFP_NOWARN; -+ -+ while (count) { -+ int j, order = __fls(count); -+ -+ pages[i] = alloc_pages(gfp, order); -+ while (!pages[i] && order) -+ pages[i] = alloc_pages(gfp, --order); -+ if (!pages[i]) -+ goto error; -+ -+ if (order) { -+ split_page(pages[i], order); -+ j = 1 << order; -+ while (j--) -+ pages[i + j] = pages[i] + j; -+ } -+ -+ __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs); -+ i += 1 << order; -+ count -= 1 << order; -+ } -+ -+ return pages; -+error: -+ while (i--) -+ if (pages[i]) -+ __free_pages(pages[i], 0); -+ kvfree(pages); -+ return NULL; -+} -+ -+static void __dma_free_buffer(struct device *dev, struct page **pages, -+ size_t size, unsigned long attrs) -+{ -+ int count = PHYS_PFN(size); -+ unsigned int i; -+ -+ for (i = 0; i < count && pages[i]; i++) { -+ __dma_clear_buffer(pages[i], PAGE_SIZE, attrs); -+ __free_pages(pages[i], 0); -+ } -+ -+ kvfree(pages); -+} -+ -+static void ipu6_dma_sync_single_for_cpu(struct device *dev, -+ dma_addr_t dma_handle, -+ size_t size, -+ enum dma_data_direction dir) -+{ -+ void *vaddr; -+ u32 offset; -+ struct vm_info *info; -+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; -+ -+ info = get_vm_info(mmu, dma_handle); -+ if (WARN_ON(!info)) -+ return; -+ -+ offset = dma_handle - info->ipu6_iova; -+ if (WARN_ON(size > (info->size - offset))) -+ return; -+ -+ vaddr = info->vaddr + offset; -+ clflush_cache_range(vaddr, size); -+} -+ -+static void ipu6_dma_sync_sg_for_cpu(struct device *dev, -+ struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir) -+{ -+ struct scatterlist *sg; -+ int i; -+ -+ for_each_sg(sglist, sg, nents, i) -+ clflush_cache_range(page_to_virt(sg_page(sg)), sg->length); -+} -+ -+static void *ipu6_dma_alloc(struct device *dev, size_t size, -+ dma_addr_t *dma_handle, gfp_t gfp, -+ unsigned long attrs) -+{ -+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; -+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev; -+ dma_addr_t pci_dma_addr, ipu6_iova; -+ struct vm_info *info; -+ unsigned long count; -+ struct page **pages; -+ struct iova *iova; -+ unsigned int i; -+ int ret; -+ -+ info = kzalloc(sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return NULL; -+ -+ size = PAGE_ALIGN(size); -+ count = PHYS_PFN(size); -+ -+ iova = alloc_iova(&mmu->dmap->iovad, count, -+ PHYS_PFN(dma_get_mask(dev)), 0); -+ if (!iova) -+ goto out_kfree; -+ -+ pages = __dma_alloc_buffer(dev, size, gfp, attrs); -+ if (!pages) -+ goto out_free_iova; -+ -+ dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n", -+ size, iova->pfn_lo, iova->pfn_hi); -+ for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { -+ pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0, -+ PAGE_SIZE, DMA_BIDIRECTIONAL, -+ attrs); -+ dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n", -+ &pci_dma_addr); -+ if (dma_mapping_error(&pdev->dev, pci_dma_addr)) { -+ dev_err(dev, "pci_dma_mapping for page[%d] failed", i); -+ goto out_unmap; -+ } -+ -+ ret = ipu6_mmu_map(mmu->dmap->mmu_info, -+ PFN_PHYS(iova->pfn_lo + i), pci_dma_addr, -+ PAGE_SIZE); -+ if (ret) { -+ dev_err(dev, "ipu6_mmu_map for pci_dma[%d] %pad failed", -+ i, &pci_dma_addr); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, -+ PAGE_SIZE, DMA_BIDIRECTIONAL, -+ attrs); -+ goto out_unmap; -+ } -+ } -+ -+ info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); -+ if (!info->vaddr) -+ goto out_unmap; -+ -+ *dma_handle = PFN_PHYS(iova->pfn_lo); -+ -+ info->pages = pages; -+ info->ipu6_iova = *dma_handle; -+ info->size = size; -+ list_add(&info->list, &mmu->vma_list); -+ -+ return info->vaddr; -+ -+out_unmap: -+ while (i--) { -+ ipu6_iova = PFN_PHYS(iova->pfn_lo + i); -+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ ipu6_iova); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -+ DMA_BIDIRECTIONAL, attrs); -+ -+ ipu6_mmu_unmap(mmu->dmap->mmu_info, ipu6_iova, PAGE_SIZE); -+ } -+ -+ __dma_free_buffer(dev, pages, size, attrs); -+ -+out_free_iova: -+ __free_iova(&mmu->dmap->iovad, iova); -+out_kfree: -+ kfree(info); -+ -+ return NULL; -+} -+ -+static void ipu6_dma_free(struct device *dev, size_t size, void *vaddr, -+ dma_addr_t dma_handle, -+ unsigned long attrs) -+{ -+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; -+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev; -+ struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); -+ dma_addr_t pci_dma_addr, ipu6_iova; -+ struct vm_info *info; -+ struct page **pages; -+ unsigned int i; -+ -+ if (WARN_ON(!iova)) -+ return; -+ -+ info = get_vm_info(mmu, dma_handle); -+ if (WARN_ON(!info)) -+ return; -+ -+ if (WARN_ON(!info->vaddr)) -+ return; -+ -+ if (WARN_ON(!info->pages)) -+ return; -+ -+ list_del(&info->list); -+ -+ size = PAGE_ALIGN(size); -+ -+ pages = info->pages; -+ -+ vunmap(vaddr); -+ -+ for (i = 0; i < PHYS_PFN(size); i++) { -+ ipu6_iova = PFN_PHYS(iova->pfn_lo + i); -+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ ipu6_iova); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -+ DMA_BIDIRECTIONAL, attrs); -+ } -+ -+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ -+ __dma_free_buffer(dev, pages, size, attrs); -+ -+ mmu->tlb_invalidate(mmu); -+ -+ __free_iova(&mmu->dmap->iovad, iova); -+ -+ kfree(info); -+} -+ -+static int ipu6_dma_mmap(struct device *dev, struct vm_area_struct *vma, -+ void *addr, dma_addr_t iova, size_t size, -+ unsigned long attrs) -+{ -+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; -+ size_t count = PHYS_PFN(PAGE_ALIGN(size)); -+ struct vm_info *info; -+ size_t i; -+ int ret; -+ -+ info = get_vm_info(mmu, iova); -+ if (!info) -+ return -EFAULT; -+ -+ if (!info->vaddr) -+ return -EFAULT; -+ -+ if (vma->vm_start & ~PAGE_MASK) -+ return -EINVAL; -+ -+ if (size > info->size) -+ return -EFAULT; -+ -+ for (i = 0; i < count; i++) { -+ ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i), -+ info->pages[i]); -+ if (ret < 0) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void ipu6_dma_unmap_sg(struct device *dev, -+ struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs) -+{ -+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev; -+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; -+ struct iova *iova = find_iova(&mmu->dmap->iovad, -+ PHYS_PFN(sg_dma_address(sglist))); -+ int i, npages, count; -+ struct scatterlist *sg; -+ dma_addr_t pci_dma_addr; -+ -+ if (!nents) -+ return; -+ -+ if (WARN_ON(!iova)) -+ return; -+ -+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) -+ ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL); -+ -+ /* get the nents as orig_nents given by caller */ -+ count = 0; -+ npages = iova_size(iova); -+ for_each_sg(sglist, sg, nents, i) { -+ if (sg_dma_len(sg) == 0 || -+ sg_dma_address(sg) == DMA_MAPPING_ERROR) -+ break; -+ -+ npages -= PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg))); -+ count++; -+ if (npages <= 0) -+ break; -+ } -+ -+ /* -+ * Before IPU6 mmu unmap, return the pci dma address back to sg -+ * assume the nents is less than orig_nents as the least granule -+ * is 1 SZ_4K page -+ */ -+ dev_dbg(dev, "trying to unmap concatenated %u ents\n", count); -+ for_each_sg(sglist, sg, count, i) { -+ dev_dbg(dev, "ipu unmap sg[%d] %pad\n", i, &sg_dma_address(sg)); -+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ sg_dma_address(sg)); -+ dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n", -+ &pci_dma_addr, i); -+ sg_dma_address(sg) = pci_dma_addr; -+ } -+ -+ dev_dbg(dev, "ipu6_mmu_unmap low pfn %lu high pfn %lu\n", -+ iova->pfn_lo, iova->pfn_hi); -+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ -+ mmu->tlb_invalidate(mmu); -+ -+ dma_unmap_sg_attrs(&pdev->dev, sglist, nents, dir, attrs); -+ -+ __free_iova(&mmu->dmap->iovad, iova); -+} -+ -+static int ipu6_dma_map_sg(struct device *dev, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs) -+{ -+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; -+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev; -+ struct scatterlist *sg; -+ struct iova *iova; -+ size_t npages = 0; -+ unsigned long iova_addr; -+ int i, count; -+ -+ for_each_sg(sglist, sg, nents, i) { -+ if (sg->offset) { -+ dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n", -+ i, sg->offset); -+ return -EFAULT; -+ } -+ } -+ -+ dev_dbg(dev, "pci_dma_map_sg trying to map %d ents\n", nents); -+ count = dma_map_sg_attrs(&pdev->dev, sglist, nents, dir, attrs); -+ if (count <= 0) { -+ dev_err(dev, "pci_dma_map_sg %d ents failed\n", nents); -+ return 0; -+ } -+ -+ dev_dbg(dev, "pci_dma_map_sg %d ents mapped\n", count); -+ -+ for_each_sg(sglist, sg, count, i) -+ npages += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg))); -+ -+ iova = alloc_iova(&mmu->dmap->iovad, npages, -+ PHYS_PFN(dma_get_mask(dev)), 0); -+ if (!iova) -+ return 0; -+ -+ dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, -+ iova->pfn_hi); -+ -+ iova_addr = iova->pfn_lo; -+ for_each_sg(sglist, sg, count, i) { -+ int ret; -+ -+ dev_dbg(dev, "mapping entry %d: iova 0x%llx phy %pad size %d\n", -+ i, PFN_PHYS(iova_addr), &sg_dma_address(sg), -+ sg_dma_len(sg)); -+ -+ ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -+ sg_dma_address(sg), -+ PAGE_ALIGN(sg_dma_len(sg))); -+ if (ret) -+ goto out_fail; -+ -+ sg_dma_address(sg) = PFN_PHYS(iova_addr); -+ -+ iova_addr += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg))); -+ } -+ -+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) -+ ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL); -+ -+ return count; -+ -+out_fail: -+ ipu6_dma_unmap_sg(dev, sglist, i, dir, attrs); -+ -+ return 0; -+} -+ -+/* -+ * Create scatter-list for the already allocated DMA buffer -+ */ -+static int ipu6_dma_get_sgtable(struct device *dev, struct sg_table *sgt, -+ void *cpu_addr, dma_addr_t handle, size_t size, -+ unsigned long attrs) -+{ -+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu; -+ struct vm_info *info; -+ int n_pages; -+ int ret = 0; -+ -+ info = get_vm_info(mmu, handle); -+ if (!info) -+ return -EFAULT; -+ -+ if (!info->vaddr) -+ return -EFAULT; -+ -+ if (WARN_ON(!info->pages)) -+ return -ENOMEM; -+ -+ n_pages = PHYS_PFN(PAGE_ALIGN(size)); -+ -+ ret = sg_alloc_table_from_pages(sgt, info->pages, n_pages, 0, size, -+ GFP_KERNEL); -+ if (ret) -+ dev_warn(dev, "IPU6 get sgt table failed\n"); -+ -+ return ret; -+} -+ -+const struct dma_map_ops ipu6_dma_ops = { -+ .alloc = ipu6_dma_alloc, -+ .free = ipu6_dma_free, -+ .mmap = ipu6_dma_mmap, -+ .map_sg = ipu6_dma_map_sg, -+ .unmap_sg = ipu6_dma_unmap_sg, -+ .sync_single_for_cpu = ipu6_dma_sync_single_for_cpu, -+ .sync_single_for_device = ipu6_dma_sync_single_for_cpu, -+ .sync_sg_for_cpu = ipu6_dma_sync_sg_for_cpu, -+ .sync_sg_for_device = ipu6_dma_sync_sg_for_cpu, -+ .get_sgtable = ipu6_dma_get_sgtable, -+}; -diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.h b/drivers/media/pci/intel/ipu6/ipu6-dma.h -new file mode 100644 -index 000000000000..c75ad2462368 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-dma.h -@@ -0,0 +1,19 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_DMA_H -+#define IPU6_DMA_H -+ -+#include <linux/dma-map-ops.h> -+#include <linux/iova.h> -+ -+struct ipu6_mmu_info; -+ -+struct ipu6_dma_mapping { -+ struct ipu6_mmu_info *mmu_info; -+ struct iova_domain iovad; -+}; -+ -+extern const struct dma_map_ops ipu6_dma_ops; -+ -+#endif /* IPU6_DMA_H */ -diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/drivers/media/pci/intel/ipu6/ipu6-mmu.c -new file mode 100644 -index 000000000000..dc16d45187a8 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.c -@@ -0,0 +1,845 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+#include <asm/barrier.h> -+ -+#include <linux/align.h> -+#include <linux/atomic.h> -+#include <linux/bitops.h> -+#include <linux/bits.h> -+#include <linux/bug.h> -+#include <linux/cacheflush.h> -+#include <linux/dma-mapping.h> -+#include <linux/err.h> -+#include <linux/gfp.h> -+#include <linux/io.h> -+#include <linux/iova.h> -+#include <linux/math.h> -+#include <linux/minmax.h> -+#include <linux/mm.h> -+#include <linux/pfn.h> -+#include <linux/slab.h> -+#include <linux/spinlock.h> -+#include <linux/types.h> -+ -+#include "ipu6.h" -+#include "ipu6-dma.h" -+#include "ipu6-mmu.h" -+#include "ipu6-platform-regs.h" -+ -+#define ISP_PAGE_SHIFT 12 -+#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) -+#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1)) -+ -+#define ISP_L1PT_SHIFT 22 -+#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) -+ -+#define ISP_L2PT_SHIFT 12 -+#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) -+ -+#define ISP_L1PT_PTES 1024 -+#define ISP_L2PT_PTES 1024 -+ -+#define ISP_PADDR_SHIFT 12 -+ -+#define REG_TLB_INVALIDATE 0x0000 -+ -+#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ -+#define REG_INFO 0x0008 -+ -+#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) -+ -+static void tlb_invalidate(struct ipu6_mmu *mmu) -+{ -+ unsigned long flags; -+ unsigned int i; -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ if (!mmu->ready) { -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+ return; -+ } -+ -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ /* -+ * To avoid the HW bug induced dead lock in some of the IPU6 -+ * MMUs on successive invalidate calls, we need to first do a -+ * read to the page table base before writing the invalidate -+ * register. MMUs which need to implement this WA, will have -+ * the insert_read_before_invalidate flags set as true. -+ * Disregard the return value of the read. -+ */ -+ if (mmu->mmu_hw[i].insert_read_before_invalidate) -+ readl(mmu->mmu_hw[i].base + REG_L1_PHYS); -+ -+ writel(0xffffffff, mmu->mmu_hw[i].base + -+ REG_TLB_INVALIDATE); -+ /* -+ * The TLB invalidation is a "single cycle" (IOMMU clock cycles) -+ * When the actual MMIO write reaches the IPU6 TLB Invalidate -+ * register, wmb() will force the TLB invalidate out if the CPU -+ * attempts to update the IOMMU page table (or sooner). -+ */ -+ wmb(); -+ } -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+} -+ -+#ifdef DEBUG -+static void page_table_dump(struct ipu6_mmu_info *mmu_info) -+{ -+ u32 l1_idx; -+ -+ dev_dbg(mmu_info->dev, "begin IOMMU page table dump\n"); -+ -+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { -+ u32 l2_idx; -+ u32 iova = (phys_addr_t)l1_idx << ISP_L1PT_SHIFT; -+ -+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) -+ continue; -+ dev_dbg(mmu_info->dev, -+ "l1 entry %u; iovas 0x%8.8x-0x%8.8x, at %pa\n", -+ l1_idx, iova, iova + ISP_PAGE_SIZE, -+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx])); -+ -+ for (l2_idx = 0; l2_idx < ISP_L2PT_PTES; l2_idx++) { -+ u32 *l2_pt = mmu_info->l2_pts[l1_idx]; -+ u32 iova2 = iova + (l2_idx << ISP_L2PT_SHIFT); -+ -+ if (l2_pt[l2_idx] == mmu_info->dummy_page_pteval) -+ continue; -+ -+ dev_dbg(mmu_info->dev, -+ "\tl2 entry %u; iova 0x%8.8x, phys %pa\n", -+ l2_idx, iova2, -+ TBL_PHYS_ADDR(l2_pt[l2_idx])); -+ } -+ } -+ -+ dev_dbg(mmu_info->dev, "end IOMMU page table dump\n"); -+} -+#endif /* DEBUG */ -+ -+static dma_addr_t map_single(struct ipu6_mmu_info *mmu_info, void *ptr) -+{ -+ dma_addr_t dma; -+ -+ dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL); -+ if (dma_mapping_error(mmu_info->dev, dma)) -+ return 0; -+ -+ return dma; -+} -+ -+static int get_dummy_page(struct ipu6_mmu_info *mmu_info) -+{ -+ void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ -+ if (!pt) -+ return -ENOMEM; -+ -+ dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt); -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map dummy page\n"); -+ goto err_free_page; -+ } -+ -+ mmu_info->dummy_page = pt; -+ mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT; -+ -+ return 0; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return -ENOMEM; -+} -+ -+static void free_dummy_page(struct ipu6_mmu_info *mmu_info) -+{ -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->dummy_page_pteval), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_page); -+} -+ -+static int alloc_dummy_l2_pt(struct ipu6_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ unsigned int i; -+ -+ if (!pt) -+ return -ENOMEM; -+ -+ dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt); -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map l2pt page\n"); -+ goto err_free_page; -+ } -+ -+ for (i = 0; i < ISP_L2PT_PTES; i++) -+ pt[i] = mmu_info->dummy_page_pteval; -+ -+ mmu_info->dummy_l2_pt = pt; -+ mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT; -+ -+ return 0; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return -ENOMEM; -+} -+ -+static void free_dummy_l2_pt(struct ipu6_mmu_info *mmu_info) -+{ -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_l2_pt); -+} -+ -+static u32 *alloc_l1_pt(struct ipu6_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ unsigned int i; -+ -+ if (!pt) -+ return NULL; -+ -+ dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt); -+ -+ for (i = 0; i < ISP_L1PT_PTES; i++) -+ pt[i] = mmu_info->dummy_l2_pteval; -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map l1pt page\n"); -+ goto err_free_page; -+ } -+ -+ mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT; -+ dev_dbg(mmu_info->dev, "l1 pt %p mapped at %llx\n", pt, dma); -+ -+ return pt; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return NULL; -+} -+ -+static u32 *alloc_l2_pt(struct ipu6_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ unsigned int i; -+ -+ if (!pt) -+ return NULL; -+ -+ dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); -+ -+ for (i = 0; i < ISP_L1PT_PTES; i++) -+ pt[i] = mmu_info->dummy_page_pteval; -+ -+ return pt; -+} -+ -+static int l2_map(struct ipu6_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ u32 l1_idx = iova >> ISP_L1PT_SHIFT; -+ u32 iova_start = iova; -+ u32 *l2_pt, *l2_virt; -+ unsigned int l2_idx; -+ unsigned long flags; -+ dma_addr_t dma; -+ u32 l1_entry; -+ -+ dev_dbg(mmu_info->dev, -+ "mapping l2 page table for l1 index %u (iova %8.8x)\n", -+ l1_idx, (u32)iova); -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ l1_entry = mmu_info->l1_pt[l1_idx]; -+ if (l1_entry == mmu_info->dummy_l2_pteval) { -+ l2_virt = mmu_info->l2_pts[l1_idx]; -+ if (likely(!l2_virt)) { -+ l2_virt = alloc_l2_pt(mmu_info); -+ if (!l2_virt) { -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ return -ENOMEM; -+ } -+ } -+ -+ dma = map_single(mmu_info, l2_virt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map l2pt page\n"); -+ free_page((unsigned long)l2_virt); -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ return -EINVAL; -+ } -+ -+ l1_entry = dma >> ISP_PADDR_SHIFT; -+ -+ dev_dbg(mmu_info->dev, "page for l1_idx %u %p allocated\n", -+ l1_idx, l2_virt); -+ mmu_info->l1_pt[l1_idx] = l1_entry; -+ mmu_info->l2_pts[l1_idx] = l2_virt; -+ clflush_cache_range((void *)&mmu_info->l1_pt[l1_idx], -+ sizeof(mmu_info->l1_pt[l1_idx])); -+ } -+ -+ l2_pt = mmu_info->l2_pts[l1_idx]; -+ -+ dev_dbg(mmu_info->dev, "l2_pt at %p with dma 0x%x\n", l2_pt, l1_entry); -+ -+ paddr = ALIGN(paddr, ISP_PAGE_SIZE); -+ -+ l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -+ -+ dev_dbg(mmu_info->dev, "l2_idx %u, phys 0x%8.8x\n", l2_idx, -+ l2_pt[l2_idx]); -+ if (l2_pt[l2_idx] != mmu_info->dummy_page_pteval) { -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ return -EINVAL; -+ } -+ -+ l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT; -+ -+ clflush_cache_range((void *)&l2_pt[l2_idx], sizeof(l2_pt[l2_idx])); -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ -+ dev_dbg(mmu_info->dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx, -+ l2_pt[l2_idx]); -+ -+ return 0; -+} -+ -+static int __ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ u32 iova_start = round_down(iova, ISP_PAGE_SIZE); -+ u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE); -+ -+ dev_dbg(mmu_info->dev, -+ "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr 0x%10.10llx\n", -+ iova_start, iova_end, size, paddr); -+ -+ return l2_map(mmu_info, iova_start, paddr, size); -+} -+ -+static size_t l2_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t dummy, size_t size) -+{ -+ u32 l1_idx = iova >> ISP_L1PT_SHIFT; -+ u32 iova_start = iova; -+ unsigned int l2_idx; -+ size_t unmapped = 0; -+ unsigned long flags; -+ u32 *l2_pt; -+ -+ dev_dbg(mmu_info->dev, "unmapping l2 page table for l1 index %u (iova 0x%8.8lx)\n", -+ l1_idx, iova); -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) { -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ dev_err(mmu_info->dev, -+ "unmap iova 0x%8.8lx l1 idx %u which was not mapped\n", -+ iova, l1_idx); -+ return 0; -+ } -+ -+ for (l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -+ (iova_start & ISP_L1PT_MASK) + (l2_idx << ISP_PAGE_SHIFT) -+ < iova_start + size && l2_idx < ISP_L2PT_PTES; l2_idx++) { -+ l2_pt = mmu_info->l2_pts[l1_idx]; -+ dev_dbg(mmu_info->dev, -+ "unmap l2 index %u with pteval 0x%10.10llx\n", -+ l2_idx, TBL_PHYS_ADDR(l2_pt[l2_idx])); -+ l2_pt[l2_idx] = mmu_info->dummy_page_pteval; -+ -+ clflush_cache_range((void *)&l2_pt[l2_idx], -+ sizeof(l2_pt[l2_idx])); -+ unmapped++; -+ } -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ -+ return unmapped << ISP_PAGE_SHIFT; -+} -+ -+static size_t __ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, -+ unsigned long iova, size_t size) -+{ -+ return l2_unmap(mmu_info, iova, 0, size); -+} -+ -+static int allocate_trash_buffer(struct ipu6_mmu *mmu) -+{ -+ unsigned int n_pages = PHYS_PFN(PAGE_ALIGN(IPU6_MMUV2_TRASH_RANGE)); -+ struct iova *iova; -+ unsigned int i; -+ dma_addr_t dma; -+ unsigned long iova_addr; -+ int ret; -+ -+ /* Allocate 8MB in iova range */ -+ iova = alloc_iova(&mmu->dmap->iovad, n_pages, -+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -+ if (!iova) { -+ dev_err(mmu->dev, "cannot allocate iova range for trash\n"); -+ return -ENOMEM; -+ } -+ -+ dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) { -+ dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n"); -+ ret = -ENOMEM; -+ goto out_free_iova; -+ } -+ -+ mmu->pci_trash_page = dma; -+ -+ /* -+ * Map the 8MB iova address range to the same physical trash page -+ * mmu->trash_page which is already reserved at the probe -+ */ -+ iova_addr = iova->pfn_lo; -+ for (i = 0; i < n_pages; i++) { -+ ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -+ mmu->pci_trash_page, PAGE_SIZE); -+ if (ret) { -+ dev_err(mmu->dev, -+ "mapping trash buffer range failed\n"); -+ goto out_unmap; -+ } -+ -+ iova_addr++; -+ } -+ -+ mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo); -+ dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n", -+ mmu->mmid, (unsigned int)mmu->iova_trash_page); -+ return 0; -+ -+out_unmap: -+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+out_free_iova: -+ __free_iova(&mmu->dmap->iovad, iova); -+ return ret; -+} -+ -+int ipu6_mmu_hw_init(struct ipu6_mmu *mmu) -+{ -+ struct ipu6_mmu_info *mmu_info; -+ unsigned long flags; -+ unsigned int i; -+ -+ mmu_info = mmu->dmap->mmu_info; -+ -+ /* Initialise the each MMU HW block */ -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ struct ipu6_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -+ unsigned int j; -+ u16 block_addr; -+ -+ /* Write page table address per MMU */ -+ writel((phys_addr_t)mmu_info->l1_pt_dma, -+ mmu->mmu_hw[i].base + REG_L1_PHYS); -+ -+ /* Set info bits per MMU */ -+ writel(mmu->mmu_hw[i].info_bits, -+ mmu->mmu_hw[i].base + REG_INFO); -+ -+ /* Configure MMU TLB stream configuration for L1 */ -+ for (j = 0, block_addr = 0; j < mmu_hw->nr_l1streams; -+ block_addr += mmu->mmu_hw[i].l1_block_sz[j], j++) { -+ if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) { -+ dev_err(mmu->dev, "invalid L1 configuration\n"); -+ return -EINVAL; -+ } -+ -+ /* Write block start address for each streams */ -+ writel(block_addr, mmu_hw->base + -+ mmu_hw->l1_stream_id_reg_offset + 4 * j); -+ } -+ -+ /* Configure MMU TLB stream configuration for L2 */ -+ for (j = 0, block_addr = 0; j < mmu_hw->nr_l2streams; -+ block_addr += mmu->mmu_hw[i].l2_block_sz[j], j++) { -+ if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) { -+ dev_err(mmu->dev, "invalid L2 configuration\n"); -+ return -EINVAL; -+ } -+ -+ writel(block_addr, mmu_hw->base + -+ mmu_hw->l2_stream_id_reg_offset + 4 * j); -+ } -+ } -+ -+ if (!mmu->trash_page) { -+ int ret; -+ -+ mmu->trash_page = alloc_page(GFP_KERNEL); -+ if (!mmu->trash_page) { -+ dev_err(mmu->dev, "insufficient memory for trash buffer\n"); -+ return -ENOMEM; -+ } -+ -+ ret = allocate_trash_buffer(mmu); -+ if (ret) { -+ __free_page(mmu->trash_page); -+ mmu->trash_page = NULL; -+ dev_err(mmu->dev, "trash buffer allocation failed\n"); -+ return ret; -+ } -+ } -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ mmu->ready = true; -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6); -+ -+static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp) -+{ -+ struct ipu6_mmu_info *mmu_info; -+ int ret; -+ -+ mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL); -+ if (!mmu_info) -+ return NULL; -+ -+ mmu_info->aperture_start = 0; -+ mmu_info->aperture_end = DMA_BIT_MASK(isp->secure_mode ? -+ IPU6_MMU_ADDR_BITS : -+ IPU6_MMU_ADDR_BITS_NON_SECURE); -+ mmu_info->pgsize_bitmap = SZ_4K; -+ mmu_info->dev = &isp->pdev->dev; -+ -+ ret = get_dummy_page(mmu_info); -+ if (ret) -+ goto err_free_info; -+ -+ ret = alloc_dummy_l2_pt(mmu_info); -+ if (ret) -+ goto err_free_dummy_page; -+ -+ mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts)); -+ if (!mmu_info->l2_pts) -+ goto err_free_dummy_l2_pt; -+ -+ /* -+ * We always map the L1 page table (a single page as well as -+ * the L2 page tables). -+ */ -+ mmu_info->l1_pt = alloc_l1_pt(mmu_info); -+ if (!mmu_info->l1_pt) -+ goto err_free_l2_pts; -+ -+ spin_lock_init(&mmu_info->lock); -+ -+ dev_dbg(mmu_info->dev, "domain initialised\n"); -+ -+ return mmu_info; -+ -+err_free_l2_pts: -+ vfree(mmu_info->l2_pts); -+err_free_dummy_l2_pt: -+ free_dummy_l2_pt(mmu_info); -+err_free_dummy_page: -+ free_dummy_page(mmu_info); -+err_free_info: -+ kfree(mmu_info); -+ -+ return NULL; -+} -+ -+void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ mmu->ready = false; -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6); -+ -+static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp) -+{ -+ struct ipu6_dma_mapping *dmap; -+ -+ dmap = kzalloc(sizeof(*dmap), GFP_KERNEL); -+ if (!dmap) -+ return NULL; -+ -+ dmap->mmu_info = ipu6_mmu_alloc(isp); -+ if (!dmap->mmu_info) { -+ kfree(dmap); -+ return NULL; -+ } -+ -+ init_iova_domain(&dmap->iovad, SZ_4K, 1); -+ dmap->mmu_info->dmap = dmap; -+ -+ dev_dbg(&isp->pdev->dev, "alloc mapping\n"); -+ -+ iova_cache_get(); -+ -+ return dmap; -+} -+ -+phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info, -+ dma_addr_t iova) -+{ -+ phys_addr_t phy_addr; -+ unsigned long flags; -+ u32 *l2_pt; -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT]; -+ phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT]; -+ phy_addr <<= ISP_PAGE_SHIFT; -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ -+ return phy_addr; -+} -+ -+static size_t ipu6_mmu_pgsize(unsigned long pgsize_bitmap, -+ unsigned long addr_merge, size_t size) -+{ -+ unsigned int pgsize_idx; -+ size_t pgsize; -+ -+ /* Max page size that still fits into 'size' */ -+ pgsize_idx = __fls(size); -+ -+ if (likely(addr_merge)) { -+ /* Max page size allowed by address */ -+ unsigned int align_pgsize_idx = __ffs(addr_merge); -+ -+ pgsize_idx = min(pgsize_idx, align_pgsize_idx); -+ } -+ -+ pgsize = (1UL << (pgsize_idx + 1)) - 1; -+ pgsize &= pgsize_bitmap; -+ -+ WARN_ON(!pgsize); -+ -+ /* pick the biggest page */ -+ pgsize_idx = __fls(pgsize); -+ pgsize = 1UL << pgsize_idx; -+ -+ return pgsize; -+} -+ -+size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova, -+ size_t size) -+{ -+ size_t unmapped_page, unmapped = 0; -+ unsigned int min_pagesz; -+ -+ /* find out the minimum page size supported */ -+ min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap); -+ -+ /* -+ * The virtual address and the size of the mapping must be -+ * aligned (at least) to the size of the smallest page supported -+ * by the hardware -+ */ -+ if (!IS_ALIGNED(iova | size, min_pagesz)) { -+ dev_err(NULL, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", -+ iova, size, min_pagesz); -+ return -EINVAL; -+ } -+ -+ /* -+ * Keep iterating until we either unmap 'size' bytes (or more) -+ * or we hit an area that isn't mapped. -+ */ -+ while (unmapped < size) { -+ size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap, -+ iova, size - unmapped); -+ -+ unmapped_page = __ipu6_mmu_unmap(mmu_info, iova, pgsize); -+ if (!unmapped_page) -+ break; -+ -+ dev_dbg(mmu_info->dev, "unmapped: iova 0x%lx size 0x%zx\n", -+ iova, unmapped_page); -+ -+ iova += unmapped_page; -+ unmapped += unmapped_page; -+ } -+ -+ return unmapped; -+} -+ -+int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ unsigned long orig_iova = iova; -+ unsigned int min_pagesz; -+ size_t orig_size = size; -+ int ret = 0; -+ -+ if (mmu_info->pgsize_bitmap == 0UL) -+ return -ENODEV; -+ -+ /* find out the minimum page size supported */ -+ min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap); -+ -+ /* -+ * both the virtual address and the physical one, as well as -+ * the size of the mapping, must be aligned (at least) to the -+ * size of the smallest page supported by the hardware -+ */ -+ if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) { -+ dev_err(mmu_info->dev, -+ "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n", -+ iova, &paddr, size, min_pagesz); -+ return -EINVAL; -+ } -+ -+ dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n", -+ iova, &paddr, size); -+ -+ while (size) { -+ size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap, -+ iova | paddr, size); -+ -+ dev_dbg(mmu_info->dev, -+ "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n", -+ iova, &paddr, pgsize); -+ -+ ret = __ipu6_mmu_map(mmu_info, iova, paddr, pgsize); -+ if (ret) -+ break; -+ -+ iova += pgsize; -+ paddr += pgsize; -+ size -= pgsize; -+ } -+ -+ /* unroll mapping in case something went wrong */ -+ if (ret) -+ ipu6_mmu_unmap(mmu_info, orig_iova, orig_size - size); -+ -+ return ret; -+} -+ -+static void ipu6_mmu_destroy(struct ipu6_mmu *mmu) -+{ -+ struct ipu6_dma_mapping *dmap = mmu->dmap; -+ struct ipu6_mmu_info *mmu_info = dmap->mmu_info; -+ struct iova *iova; -+ u32 l1_idx; -+ -+ if (mmu->iova_trash_page) { -+ iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page)); -+ if (iova) { -+ /* unmap and free the trash buffer iova */ -+ ipu6_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ __free_iova(&dmap->iovad, iova); -+ } else { -+ dev_err(mmu->dev, "trash buffer iova not found.\n"); -+ } -+ -+ mmu->iova_trash_page = 0; -+ dma_unmap_page(mmu_info->dev, mmu->pci_trash_page, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ mmu->pci_trash_page = 0; -+ __free_page(mmu->trash_page); -+ } -+ -+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { -+ if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) { -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->l2_pts[l1_idx]); -+ } -+ } -+ -+ vfree(mmu_info->l2_pts); -+ free_dummy_page(mmu_info); -+ dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_l2_pt); -+ free_page((unsigned long)mmu_info->l1_pt); -+ kfree(mmu_info); -+} -+ -+struct ipu6_mmu *ipu6_mmu_init(struct device *dev, -+ void __iomem *base, int mmid, -+ const struct ipu6_hw_variants *hw) -+{ -+ struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev)); -+ struct ipu6_mmu_pdata *pdata; -+ struct ipu6_mmu *mmu; -+ unsigned int i; -+ -+ if (hw->nr_mmus > IPU6_MMU_MAX_DEVICES) -+ return ERR_PTR(-EINVAL); -+ -+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ for (i = 0; i < hw->nr_mmus; i++) { -+ struct ipu6_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; -+ const struct ipu6_mmu_hw *src_mmu = &hw->mmu_hw[i]; -+ -+ if (src_mmu->nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS || -+ src_mmu->nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS) -+ return ERR_PTR(-EINVAL); -+ -+ *pdata_mmu = *src_mmu; -+ pdata_mmu->base = base + src_mmu->offset; -+ } -+ -+ mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL); -+ if (!mmu) -+ return ERR_PTR(-ENOMEM); -+ -+ mmu->mmid = mmid; -+ mmu->mmu_hw = pdata->mmu_hw; -+ mmu->nr_mmus = hw->nr_mmus; -+ mmu->tlb_invalidate = tlb_invalidate; -+ mmu->ready = false; -+ INIT_LIST_HEAD(&mmu->vma_list); -+ spin_lock_init(&mmu->ready_lock); -+ -+ mmu->dmap = alloc_dma_mapping(isp); -+ if (!mmu->dmap) { -+ dev_err(dev, "can't alloc dma mapping\n"); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ return mmu; -+} -+ -+void ipu6_mmu_cleanup(struct ipu6_mmu *mmu) -+{ -+ struct ipu6_dma_mapping *dmap = mmu->dmap; -+ -+ ipu6_mmu_destroy(mmu); -+ mmu->dmap = NULL; -+ iova_cache_put(); -+ put_iova_domain(&dmap->iovad); -+ kfree(dmap); -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.h b/drivers/media/pci/intel/ipu6/ipu6-mmu.h -new file mode 100644 -index 000000000000..95df7931a2e5 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.h -@@ -0,0 +1,73 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_MMU_H -+#define IPU6_MMU_H -+ -+#define ISYS_MMID 1 -+#define PSYS_MMID 0 -+ -+#include <linux/list.h> -+#include <linux/spinlock_types.h> -+#include <linux/types.h> -+ -+struct device; -+struct page; -+struct ipu6_hw_variants; -+ -+struct ipu6_mmu_info { -+ struct device *dev; -+ -+ u32 *l1_pt; -+ u32 l1_pt_dma; -+ u32 **l2_pts; -+ -+ u32 *dummy_l2_pt; -+ u32 dummy_l2_pteval; -+ void *dummy_page; -+ u32 dummy_page_pteval; -+ -+ dma_addr_t aperture_start; -+ dma_addr_t aperture_end; -+ unsigned long pgsize_bitmap; -+ -+ spinlock_t lock; /* Serialize access to users */ -+ struct ipu6_dma_mapping *dmap; -+}; -+ -+struct ipu6_mmu { -+ struct list_head node; -+ -+ struct ipu6_mmu_hw *mmu_hw; -+ unsigned int nr_mmus; -+ unsigned int mmid; -+ -+ phys_addr_t pgtbl; -+ struct device *dev; -+ -+ struct ipu6_dma_mapping *dmap; -+ struct list_head vma_list; -+ -+ struct page *trash_page; -+ dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */ -+ dma_addr_t iova_trash_page; /* IOVA for IPU6 child nodes to use */ -+ -+ bool ready; -+ spinlock_t ready_lock; /* Serialize access to bool ready */ -+ -+ void (*tlb_invalidate)(struct ipu6_mmu *mmu); -+}; -+ -+struct ipu6_mmu *ipu6_mmu_init(struct device *dev, -+ void __iomem *base, int mmid, -+ const struct ipu6_hw_variants *hw); -+void ipu6_mmu_cleanup(struct ipu6_mmu *mmu); -+int ipu6_mmu_hw_init(struct ipu6_mmu *mmu); -+void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu); -+int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size); -+size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova, -+ size_t size); -+phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info, -+ dma_addr_t iova); -+#endif --- -2.43.2 - - -From a4363013580d8a552e8084faedbb98bd2482c057 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:20 +0800 -Subject: [PATCH 13/33] media: intel/ipu6: add syscom interfaces between - firmware and driver - -Syscom is an inter-process(or) communication mechanism between an IPU -and host. Syscom uses message queues for message exchange between IPU -and host. Each message queue has its consumer and producer, host queue -messages to firmware as the producer and then firmware to dequeue the -messages as consumer and vice versa. IPU and host use shared registers -or memory to reside the read and write indices which are updated by -consumer and producer. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-fw-com.c | 413 +++++++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-fw-com.h | 47 +++ - 2 files changed, 460 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-com.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-com.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-com.c b/drivers/media/pci/intel/ipu6/ipu6-fw-com.c -new file mode 100644 -index 000000000000..0f893f44e04c ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-com.c -@@ -0,0 +1,413 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/device.h> -+#include <linux/dma-mapping.h> -+#include <linux/io.h> -+#include <linux/math.h> -+#include <linux/overflow.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+ -+#include "ipu6-bus.h" -+#include "ipu6-fw-com.h" -+ -+/* -+ * FWCOM layer is a shared resource between FW and driver. It consist -+ * of token queues to both send and receive directions. Queue is simply -+ * an array of structures with read and write indexes to the queue. -+ * There are 1...n queues to both directions. Queues locates in -+ * system RAM and are mapped to ISP MMU so that both CPU and ISP can -+ * see the same buffer. Indexes are located in ISP DMEM so that FW code -+ * can poll those with very low latency and cost. CPU access to indexes is -+ * more costly but that happens only at message sending time and -+ * interrupt triggered message handling. CPU doesn't need to poll indexes. -+ * wr_reg / rd_reg are offsets to those dmem location. They are not -+ * the indexes itself. -+ */ -+ -+/* Shared structure between driver and FW - do not modify */ -+struct ipu6_fw_sys_queue { -+ u64 host_address; -+ u32 vied_address; -+ u32 size; -+ u32 token_size; -+ u32 wr_reg; /* reg number in subsystem's regmem */ -+ u32 rd_reg; -+ u32 _align; -+} __packed; -+ -+struct ipu6_fw_sys_queue_res { -+ u64 host_address; -+ u32 vied_address; -+ u32 reg; -+} __packed; -+ -+enum syscom_state { -+ /* Program load or explicit host setting should init to this */ -+ SYSCOM_STATE_UNINIT = 0x57a7e000, -+ /* SP Syscom sets this when it is ready for use */ -+ SYSCOM_STATE_READY = 0x57a7e001, -+ /* SP Syscom sets this when no more syscom accesses will happen */ -+ SYSCOM_STATE_INACTIVE = 0x57a7e002, -+}; -+ -+enum syscom_cmd { -+ /* Program load or explicit host setting should init to this */ -+ SYSCOM_COMMAND_UNINIT = 0x57a7f000, -+ /* Host Syscom requests syscom to become inactive */ -+ SYSCOM_COMMAND_INACTIVE = 0x57a7f001, -+}; -+ -+/* firmware config: data that sent from the host to SP via DDR */ -+/* Cell copies data into a context */ -+ -+struct ipu6_fw_syscom_config { -+ u32 firmware_address; -+ -+ u32 num_input_queues; -+ u32 num_output_queues; -+ -+ /* ISP pointers to an array of ipu6_fw_sys_queue structures */ -+ u32 input_queue; -+ u32 output_queue; -+ -+ /* ISYS / PSYS private data */ -+ u32 specific_addr; -+ u32 specific_size; -+}; -+ -+struct ipu6_fw_com_context { -+ struct ipu6_bus_device *adev; -+ void __iomem *dmem_addr; -+ int (*cell_ready)(struct ipu6_bus_device *adev); -+ void (*cell_start)(struct ipu6_bus_device *adev); -+ -+ void *dma_buffer; -+ dma_addr_t dma_addr; -+ unsigned int dma_size; -+ unsigned long attrs; -+ -+ struct ipu6_fw_sys_queue *input_queue; /* array of host to SP queues */ -+ struct ipu6_fw_sys_queue *output_queue; /* array of SP to host */ -+ -+ u32 config_vied_addr; -+ -+ unsigned int buttress_boot_offset; -+ void __iomem *base_addr; -+}; -+ -+#define FW_COM_WR_REG 0 -+#define FW_COM_RD_REG 4 -+ -+#define REGMEM_OFFSET 0 -+#define TUNIT_MAGIC_PATTERN 0x5a5a5a5a -+ -+enum regmem_id { -+ /* pass pkg_dir address to SPC in non-secure mode */ -+ PKG_DIR_ADDR_REG = 0, -+ /* Tunit CFG blob for secure - provided by host.*/ -+ TUNIT_CFG_DWR_REG = 1, -+ /* syscom commands - modified by the host */ -+ SYSCOM_COMMAND_REG = 2, -+ /* Store interrupt status - updated by SP */ -+ SYSCOM_IRQ_REG = 3, -+ /* first syscom queue pointer register */ -+ SYSCOM_QPR_BASE_REG = 4 -+}; -+ -+#define BUTTRESS_FW_BOOT_PARAMS_0 0x4000 -+#define BUTTRESS_FW_BOOT_PARAM_REG(base, offset, id) \ -+ ((base) + BUTTRESS_FW_BOOT_PARAMS_0 + ((offset) + (id)) * 4) -+ -+enum buttress_syscom_id { -+ /* pass syscom configuration to SPC */ -+ SYSCOM_CONFIG_ID = 0, -+ /* syscom state - modified by SP */ -+ SYSCOM_STATE_ID = 1, -+ /* syscom vtl0 addr mask */ -+ SYSCOM_VTL0_ADDR_MASK_ID = 2, -+ SYSCOM_ID_MAX -+}; -+ -+static void ipu6_sys_queue_init(struct ipu6_fw_sys_queue *q, unsigned int size, -+ unsigned int token_size, -+ struct ipu6_fw_sys_queue_res *res) -+{ -+ unsigned int buf_size = (size + 1) * token_size; -+ -+ q->size = size + 1; -+ q->token_size = token_size; -+ -+ /* acquire the shared buffer space */ -+ q->host_address = res->host_address; -+ res->host_address += buf_size; -+ q->vied_address = res->vied_address; -+ res->vied_address += buf_size; -+ -+ /* acquire the shared read and writer pointers */ -+ q->wr_reg = res->reg; -+ res->reg++; -+ q->rd_reg = res->reg; -+ res->reg++; -+} -+ -+void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg, -+ struct ipu6_bus_device *adev, void __iomem *base) -+{ -+ size_t conf_size, inq_size, outq_size, specific_size; -+ struct ipu6_fw_syscom_config *config_host_addr; -+ unsigned int sizeinput = 0, sizeoutput = 0; -+ struct ipu6_fw_sys_queue_res res; -+ struct ipu6_fw_com_context *ctx; -+ struct device *dev = &adev->auxdev.dev; -+ size_t sizeall, offset; -+ unsigned long attrs = 0; -+ void *specific_host_addr; -+ unsigned int i; -+ -+ if (!cfg || !cfg->cell_start || !cfg->cell_ready) -+ return NULL; -+ -+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return NULL; -+ ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET; -+ ctx->adev = adev; -+ ctx->cell_start = cfg->cell_start; -+ ctx->cell_ready = cfg->cell_ready; -+ ctx->buttress_boot_offset = cfg->buttress_boot_offset; -+ ctx->base_addr = base; -+ -+ /* -+ * Allocate DMA mapped memory. Allocate one big chunk. -+ */ -+ /* Base cfg for FW */ -+ conf_size = roundup(sizeof(struct ipu6_fw_syscom_config), 8); -+ /* Descriptions of the queues */ -+ inq_size = size_mul(cfg->num_input_queues, -+ sizeof(struct ipu6_fw_sys_queue)); -+ outq_size = size_mul(cfg->num_output_queues, -+ sizeof(struct ipu6_fw_sys_queue)); -+ /* FW specific information structure */ -+ specific_size = roundup(cfg->specific_size, 8); -+ -+ sizeall = conf_size + inq_size + outq_size + specific_size; -+ -+ for (i = 0; i < cfg->num_input_queues; i++) -+ sizeinput += size_mul(cfg->input[i].queue_size + 1, -+ cfg->input[i].token_size); -+ -+ for (i = 0; i < cfg->num_output_queues; i++) -+ sizeoutput += size_mul(cfg->output[i].queue_size + 1, -+ cfg->output[i].token_size); -+ -+ sizeall += sizeinput + sizeoutput; -+ -+ ctx->dma_buffer = dma_alloc_attrs(dev, sizeall, &ctx->dma_addr, -+ GFP_KERNEL, attrs); -+ ctx->attrs = attrs; -+ if (!ctx->dma_buffer) { -+ dev_err(dev, "failed to allocate dma memory\n"); -+ kfree(ctx); -+ return NULL; -+ } -+ -+ ctx->dma_size = sizeall; -+ -+ config_host_addr = ctx->dma_buffer; -+ ctx->config_vied_addr = ctx->dma_addr; -+ -+ offset = conf_size; -+ ctx->input_queue = ctx->dma_buffer + offset; -+ config_host_addr->input_queue = ctx->dma_addr + offset; -+ config_host_addr->num_input_queues = cfg->num_input_queues; -+ -+ offset += inq_size; -+ ctx->output_queue = ctx->dma_buffer + offset; -+ config_host_addr->output_queue = ctx->dma_addr + offset; -+ config_host_addr->num_output_queues = cfg->num_output_queues; -+ -+ /* copy firmware specific data */ -+ offset += outq_size; -+ specific_host_addr = ctx->dma_buffer + offset; -+ config_host_addr->specific_addr = ctx->dma_addr + offset; -+ config_host_addr->specific_size = cfg->specific_size; -+ if (cfg->specific_addr && cfg->specific_size) -+ memcpy(specific_host_addr, cfg->specific_addr, -+ cfg->specific_size); -+ -+ /* initialize input queues */ -+ offset += specific_size; -+ res.reg = SYSCOM_QPR_BASE_REG; -+ res.host_address = (u64)(ctx->dma_buffer + offset); -+ res.vied_address = ctx->dma_addr + offset; -+ for (i = 0; i < cfg->num_input_queues; i++) -+ ipu6_sys_queue_init(ctx->input_queue + i, -+ cfg->input[i].queue_size, -+ cfg->input[i].token_size, &res); -+ -+ /* initialize output queues */ -+ offset += sizeinput; -+ res.host_address = (u64)(ctx->dma_buffer + offset); -+ res.vied_address = ctx->dma_addr + offset; -+ for (i = 0; i < cfg->num_output_queues; i++) { -+ ipu6_sys_queue_init(ctx->output_queue + i, -+ cfg->output[i].queue_size, -+ cfg->output[i].token_size, &res); -+ } -+ -+ return ctx; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6); -+ -+int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx) -+{ -+ /* write magic pattern to disable the tunit trace */ -+ writel(TUNIT_MAGIC_PATTERN, ctx->dmem_addr + TUNIT_CFG_DWR_REG * 4); -+ /* Check if SP is in valid state */ -+ if (!ctx->cell_ready(ctx->adev)) -+ return -EIO; -+ -+ /* store syscom uninitialized command */ -+ writel(SYSCOM_COMMAND_UNINIT, ctx->dmem_addr + SYSCOM_COMMAND_REG * 4); -+ -+ /* store syscom uninitialized state */ -+ writel(SYSCOM_STATE_UNINIT, -+ BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr, -+ ctx->buttress_boot_offset, -+ SYSCOM_STATE_ID)); -+ -+ /* store firmware configuration address */ -+ writel(ctx->config_vied_addr, -+ BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr, -+ ctx->buttress_boot_offset, -+ SYSCOM_CONFIG_ID)); -+ ctx->cell_start(ctx->adev); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6); -+ -+int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx) -+{ -+ int state; -+ -+ state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr, -+ ctx->buttress_boot_offset, -+ SYSCOM_STATE_ID)); -+ if (state != SYSCOM_STATE_READY) -+ return -EBUSY; -+ -+ /* set close request flag */ -+ writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr + -+ SYSCOM_COMMAND_REG * 4); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6); -+ -+int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force) -+{ -+ /* check if release is forced, an verify cell state if it is not */ -+ if (!force && !ctx->cell_ready(ctx->adev)) -+ return -EBUSY; -+ -+ dma_free_attrs(&ctx->adev->auxdev.dev, ctx->dma_size, -+ ctx->dma_buffer, ctx->dma_addr, ctx->attrs); -+ kfree(ctx); -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6); -+ -+bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx) -+{ -+ int state; -+ -+ state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr, -+ ctx->buttress_boot_offset, -+ SYSCOM_STATE_ID)); -+ -+ return state == SYSCOM_STATE_READY; -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6); -+ -+void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr) -+{ -+ struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr]; -+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; -+ unsigned int wr, rd; -+ unsigned int packets; -+ unsigned int index; -+ -+ wr = readl(q_dmem + FW_COM_WR_REG); -+ rd = readl(q_dmem + FW_COM_RD_REG); -+ -+ if (WARN_ON_ONCE(wr >= q->size || rd >= q->size)) -+ return NULL; -+ -+ if (wr < rd) -+ packets = rd - wr - 1; -+ else -+ packets = q->size - (wr - rd + 1); -+ -+ if (!packets) -+ return NULL; -+ -+ index = readl(q_dmem + FW_COM_WR_REG); -+ -+ return (void *)(q->host_address + index * q->token_size); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6); -+ -+void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr) -+{ -+ struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr]; -+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; -+ unsigned int wr = readl(q_dmem + FW_COM_WR_REG) + 1; -+ -+ if (wr >= q->size) -+ wr = 0; -+ -+ writel(wr, q_dmem + FW_COM_WR_REG); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6); -+ -+void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr) -+{ -+ struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr]; -+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; -+ unsigned int wr, rd; -+ unsigned int packets; -+ -+ wr = readl(q_dmem + FW_COM_WR_REG); -+ rd = readl(q_dmem + FW_COM_RD_REG); -+ -+ if (WARN_ON_ONCE(wr >= q->size || rd >= q->size)) -+ return NULL; -+ -+ if (wr < rd) -+ wr += q->size; -+ -+ packets = wr - rd; -+ if (!packets) -+ return NULL; -+ -+ return (void *)(q->host_address + rd * q->token_size); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6); -+ -+void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr) -+{ -+ struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr]; -+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; -+ unsigned int rd = readl(q_dmem + FW_COM_RD_REG) + 1; -+ -+ if (rd >= q->size) -+ rd = 0; -+ -+ writel(rd, q_dmem + FW_COM_RD_REG); -+} -+EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6); -diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-com.h b/drivers/media/pci/intel/ipu6/ipu6-fw-com.h -new file mode 100644 -index 000000000000..660c406b3ac9 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-com.h -@@ -0,0 +1,47 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_FW_COM_H -+#define IPU6_FW_COM_H -+ -+struct ipu6_fw_com_context; -+struct ipu6_bus_device; -+ -+struct ipu6_fw_syscom_queue_config { -+ unsigned int queue_size; /* tokens per queue */ -+ unsigned int token_size; /* bytes per token */ -+}; -+ -+#define SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET 0 -+ -+struct ipu6_fw_com_cfg { -+ unsigned int num_input_queues; -+ unsigned int num_output_queues; -+ struct ipu6_fw_syscom_queue_config *input; -+ struct ipu6_fw_syscom_queue_config *output; -+ -+ unsigned int dmem_addr; -+ -+ /* firmware-specific configuration data */ -+ void *specific_addr; -+ unsigned int specific_size; -+ int (*cell_ready)(struct ipu6_bus_device *adev); -+ void (*cell_start)(struct ipu6_bus_device *adev); -+ -+ unsigned int buttress_boot_offset; -+}; -+ -+void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg, -+ struct ipu6_bus_device *adev, void __iomem *base); -+ -+int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx); -+bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx); -+int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx); -+int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force); -+ -+void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr); -+void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr); -+void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr); -+void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr); -+ -+#endif --- -2.43.2 - - -From e690b8a96eb4fbe1d948ad80047a9af7c601b761 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:21 +0800 -Subject: [PATCH 14/33] media: intel/ipu6: input system ABI between firmware - and driver - -Implement the input system firmware ABIs between the firmware and -driver - include stream configuration, control command, capture -request and response, etc. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-fw-isys.c | 487 +++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-fw-isys.h | 573 ++++++++++++++++++++ - 2 files changed, 1060 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-isys.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-isys.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c -new file mode 100644 -index 000000000000..e06c1c955d38 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c -@@ -0,0 +1,487 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/cacheflush.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/io.h> -+#include <linux/spinlock.h> -+#include <linux/types.h> -+ -+#include "ipu6-bus.h" -+#include "ipu6-fw-com.h" -+#include "ipu6-isys.h" -+#include "ipu6-platform-isys-csi2-reg.h" -+#include "ipu6-platform-regs.h" -+ -+static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = { -+ "STREAM_OPEN", -+ "STREAM_START", -+ "STREAM_START_AND_CAPTURE", -+ "STREAM_CAPTURE", -+ "STREAM_STOP", -+ "STREAM_FLUSH", -+ "STREAM_CLOSE" -+}; -+ -+static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu6_fw_isys_proxy_resp_info_abi *resp; -+ int ret; -+ -+ resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES); -+ if (!resp) -+ return 1; -+ -+ dev_dbg(dev, "Proxy response: id %u, error %u, details %u\n", -+ resp->request_id, resp->error_info.error, -+ resp->error_info.error_details); -+ -+ ret = req_id == resp->request_id ? 0 : -EIO; -+ -+ ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES); -+ -+ return ret; -+} -+ -+int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, -+ unsigned int req_id, -+ unsigned int index, -+ unsigned int offset, u32 value) -+{ -+ struct ipu6_fw_com_context *ctx = isys->fwcom; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu6_fw_proxy_send_queue_token *token; -+ unsigned int timeout = 1000; -+ int ret; -+ -+ dev_dbg(dev, -+ "proxy send: req_id 0x%x, index %d, offset 0x%x, value 0x%x\n", -+ req_id, index, offset, value); -+ -+ token = ipu6_send_get_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES); -+ if (!token) -+ return -EBUSY; -+ -+ token->request_id = req_id; -+ token->region_index = index; -+ token->offset = offset; -+ token->value = value; -+ ipu6_send_put_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES); -+ -+ do { -+ usleep_range(100, 110); -+ ret = handle_proxy_response(isys, req_id); -+ if (!ret) -+ break; -+ if (ret == -EIO) { -+ dev_err(dev, "Proxy respond with unexpected id\n"); -+ break; -+ } -+ timeout--; -+ } while (ret && timeout); -+ -+ if (!timeout) -+ dev_err(dev, "Proxy response timed out\n"); -+ -+ return ret; -+} -+ -+int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys, -+ const unsigned int stream_handle, -+ void *cpu_mapped_buf, -+ dma_addr_t dma_mapped_buf, -+ size_t size, u16 send_type) -+{ -+ struct ipu6_fw_com_context *ctx = isys->fwcom; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu6_fw_send_queue_token *token; -+ -+ if (send_type >= N_IPU6_FW_ISYS_SEND_TYPE) -+ return -EINVAL; -+ -+ dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]); -+ -+ /* -+ * Time to flush cache in case we have some payload. Not all messages -+ * have that -+ */ -+ if (cpu_mapped_buf) -+ clflush_cache_range(cpu_mapped_buf, size); -+ -+ token = ipu6_send_get_token(ctx, -+ stream_handle + IPU6_BASE_MSG_SEND_QUEUES); -+ if (!token) -+ return -EBUSY; -+ -+ token->payload = dma_mapped_buf; -+ token->buf_handle = (unsigned long)cpu_mapped_buf; -+ token->send_type = send_type; -+ -+ ipu6_send_put_token(ctx, stream_handle + IPU6_BASE_MSG_SEND_QUEUES); -+ -+ return 0; -+} -+ -+int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys, -+ const unsigned int stream_handle, u16 send_type) -+{ -+ return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, -+ send_type); -+} -+ -+int ipu6_fw_isys_close(struct ipu6_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int retry = IPU6_ISYS_CLOSE_RETRY; -+ unsigned long flags; -+ void *fwcom; -+ int ret; -+ -+ /* -+ * Stop the isys fw. Actual close takes -+ * some time as the FW must stop its actions including code fetch -+ * to SP icache. -+ * spinlock to wait the interrupt handler to be finished -+ */ -+ spin_lock_irqsave(&isys->power_lock, flags); -+ ret = ipu6_fw_com_close(isys->fwcom); -+ fwcom = isys->fwcom; -+ isys->fwcom = NULL; -+ spin_unlock_irqrestore(&isys->power_lock, flags); -+ if (ret) -+ dev_err(dev, "Device close failure: %d\n", ret); -+ -+ /* release probably fails if the close failed. Let's try still */ -+ do { -+ usleep_range(400, 500); -+ ret = ipu6_fw_com_release(fwcom, 0); -+ retry--; -+ } while (ret && retry); -+ -+ if (ret) { -+ dev_err(dev, "Device release time out %d\n", ret); -+ spin_lock_irqsave(&isys->power_lock, flags); -+ isys->fwcom = fwcom; -+ spin_unlock_irqrestore(&isys->power_lock, flags); -+ } -+ -+ return ret; -+} -+ -+void ipu6_fw_isys_cleanup(struct ipu6_isys *isys) -+{ -+ int ret; -+ -+ ret = ipu6_fw_com_release(isys->fwcom, 1); -+ if (ret < 0) -+ dev_warn(&isys->adev->auxdev.dev, -+ "Device busy, fw_com release failed."); -+ isys->fwcom = NULL; -+} -+ -+static void start_sp(struct ipu6_bus_device *adev) -+{ -+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); -+ void __iomem *spc_regs_base = isys->pdata->base + -+ isys->pdata->ipdata->hw_variant.spc_offset; -+ u32 val = IPU6_ISYS_SPC_STATUS_START | -+ IPU6_ISYS_SPC_STATUS_RUN | -+ IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE; -+ -+ val |= isys->icache_prefetch ? IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH : 0; -+ -+ writel(val, spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL); -+} -+ -+static int query_sp(struct ipu6_bus_device *adev) -+{ -+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); -+ void __iomem *spc_regs_base = isys->pdata->base + -+ isys->pdata->ipdata->hw_variant.spc_offset; -+ u32 val; -+ -+ val = readl(spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL); -+ /* return true when READY == 1, START == 0 */ -+ val &= IPU6_ISYS_SPC_STATUS_READY | IPU6_ISYS_SPC_STATUS_START; -+ -+ return val == IPU6_ISYS_SPC_STATUS_READY; -+} -+ -+static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys, -+ struct ipu6_fw_com_cfg *fwcom, -+ unsigned int num_streams) -+{ -+ unsigned int max_send_queues, max_sram_blocks, max_devq_size; -+ struct ipu6_fw_syscom_queue_config *input_queue_cfg; -+ struct ipu6_fw_syscom_queue_config *output_queue_cfg; -+ struct device *dev = &isys->adev->auxdev.dev; -+ int type_proxy = IPU6_FW_ISYS_QUEUE_TYPE_PROXY; -+ int type_dev = IPU6_FW_ISYS_QUEUE_TYPE_DEV; -+ int type_msg = IPU6_FW_ISYS_QUEUE_TYPE_MSG; -+ int base_dev_send = IPU6_BASE_DEV_SEND_QUEUES; -+ int base_msg_send = IPU6_BASE_MSG_SEND_QUEUES; -+ int base_msg_recv = IPU6_BASE_MSG_RECV_QUEUES; -+ struct ipu6_fw_isys_fw_config *isys_fw_cfg; -+ u32 num_in_message_queues; -+ unsigned int max_streams; -+ unsigned int size; -+ unsigned int i; -+ -+ max_streams = isys->pdata->ipdata->max_streams; -+ max_send_queues = isys->pdata->ipdata->max_send_queues; -+ max_sram_blocks = isys->pdata->ipdata->max_sram_blocks; -+ max_devq_size = isys->pdata->ipdata->max_devq_size; -+ num_in_message_queues = clamp(num_streams, 1U, max_streams); -+ isys_fw_cfg = devm_kzalloc(dev, sizeof(*isys_fw_cfg), GFP_KERNEL); -+ if (!isys_fw_cfg) -+ return -ENOMEM; -+ -+ isys_fw_cfg->num_send_queues[type_proxy] = IPU6_N_MAX_PROXY_SEND_QUEUES; -+ isys_fw_cfg->num_send_queues[type_dev] = IPU6_N_MAX_DEV_SEND_QUEUES; -+ isys_fw_cfg->num_send_queues[type_msg] = num_in_message_queues; -+ isys_fw_cfg->num_recv_queues[type_proxy] = IPU6_N_MAX_PROXY_RECV_QUEUES; -+ /* Common msg/dev return queue */ -+ isys_fw_cfg->num_recv_queues[type_dev] = 0; -+ isys_fw_cfg->num_recv_queues[type_msg] = 1; -+ -+ size = sizeof(*input_queue_cfg) * max_send_queues; -+ input_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL); -+ if (!input_queue_cfg) -+ return -ENOMEM; -+ -+ size = sizeof(*output_queue_cfg) * IPU6_N_MAX_RECV_QUEUES; -+ output_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL); -+ if (!output_queue_cfg) -+ return -ENOMEM; -+ -+ fwcom->input = input_queue_cfg; -+ fwcom->output = output_queue_cfg; -+ -+ fwcom->num_input_queues = isys_fw_cfg->num_send_queues[type_proxy] + -+ isys_fw_cfg->num_send_queues[type_dev] + -+ isys_fw_cfg->num_send_queues[type_msg]; -+ -+ fwcom->num_output_queues = isys_fw_cfg->num_recv_queues[type_proxy] + -+ isys_fw_cfg->num_recv_queues[type_dev] + -+ isys_fw_cfg->num_recv_queues[type_msg]; -+ -+ /* SRAM partitioning. Equal partitioning is set. */ -+ for (i = 0; i < max_sram_blocks; i++) { -+ if (i < num_in_message_queues) -+ isys_fw_cfg->buffer_partition.num_gda_pages[i] = -+ (IPU6_DEVICE_GDA_NR_PAGES * -+ IPU6_DEVICE_GDA_VIRT_FACTOR) / -+ num_in_message_queues; -+ else -+ isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0; -+ } -+ -+ /* FW assumes proxy interface at fwcom queue 0 */ -+ for (i = 0; i < isys_fw_cfg->num_send_queues[type_proxy]; i++) { -+ input_queue_cfg[i].token_size = -+ sizeof(struct ipu6_fw_proxy_send_queue_token); -+ input_queue_cfg[i].queue_size = IPU6_ISYS_SIZE_PROXY_SEND_QUEUE; -+ } -+ -+ for (i = 0; i < isys_fw_cfg->num_send_queues[type_dev]; i++) { -+ input_queue_cfg[base_dev_send + i].token_size = -+ sizeof(struct ipu6_fw_send_queue_token); -+ input_queue_cfg[base_dev_send + i].queue_size = max_devq_size; -+ } -+ -+ for (i = 0; i < isys_fw_cfg->num_send_queues[type_msg]; i++) { -+ input_queue_cfg[base_msg_send + i].token_size = -+ sizeof(struct ipu6_fw_send_queue_token); -+ input_queue_cfg[base_msg_send + i].queue_size = -+ IPU6_ISYS_SIZE_SEND_QUEUE; -+ } -+ -+ for (i = 0; i < isys_fw_cfg->num_recv_queues[type_proxy]; i++) { -+ output_queue_cfg[i].token_size = -+ sizeof(struct ipu6_fw_proxy_resp_queue_token); -+ output_queue_cfg[i].queue_size = -+ IPU6_ISYS_SIZE_PROXY_RECV_QUEUE; -+ } -+ /* There is no recv DEV queue */ -+ for (i = 0; i < isys_fw_cfg->num_recv_queues[type_msg]; i++) { -+ output_queue_cfg[base_msg_recv + i].token_size = -+ sizeof(struct ipu6_fw_resp_queue_token); -+ output_queue_cfg[base_msg_recv + i].queue_size = -+ IPU6_ISYS_SIZE_RECV_QUEUE; -+ } -+ -+ fwcom->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset; -+ fwcom->specific_addr = isys_fw_cfg; -+ fwcom->specific_size = sizeof(*isys_fw_cfg); -+ -+ return 0; -+} -+ -+int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int retry = IPU6_ISYS_OPEN_RETRY; -+ struct ipu6_fw_com_cfg fwcom = { -+ .cell_start = start_sp, -+ .cell_ready = query_sp, -+ .buttress_boot_offset = SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET, -+ }; -+ int ret; -+ -+ ipu6_isys_fwcom_cfg_init(isys, &fwcom, num_streams); -+ -+ isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev, -+ isys->pdata->base); -+ if (!isys->fwcom) { -+ dev_err(dev, "isys fw com prepare failed\n"); -+ return -EIO; -+ } -+ -+ ret = ipu6_fw_com_open(isys->fwcom); -+ if (ret) { -+ dev_err(dev, "isys fw com open failed %d\n", ret); -+ return ret; -+ } -+ -+ do { -+ usleep_range(400, 500); -+ if (ipu6_fw_com_ready(isys->fwcom)) -+ break; -+ retry--; -+ } while (retry > 0); -+ -+ if (!retry) { -+ dev_err(dev, "isys port open ready failed %d\n", ret); -+ ipu6_fw_isys_close(isys); -+ ret = -EIO; -+ } -+ -+ return ret; -+} -+ -+struct ipu6_fw_isys_resp_info_abi * -+ipu6_fw_isys_get_resp(void *context, unsigned int queue) -+{ -+ return ipu6_recv_get_token(context, queue); -+} -+ -+void ipu6_fw_isys_put_resp(void *context, unsigned int queue) -+{ -+ ipu6_recv_put_token(context, queue); -+} -+ -+void ipu6_fw_isys_dump_stream_cfg(struct device *dev, -+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg) -+{ -+ unsigned int i; -+ -+ dev_dbg(dev, "-----------------------------------------------------\n"); -+ dev_dbg(dev, "IPU6_FW_ISYS_STREAM_CFG_DATA\n"); -+ -+ dev_dbg(dev, "compfmt = %d\n", cfg->vc); -+ dev_dbg(dev, "src = %d\n", cfg->src); -+ dev_dbg(dev, "vc = %d\n", cfg->vc); -+ dev_dbg(dev, "isl_use = %d\n", cfg->isl_use); -+ dev_dbg(dev, "sensor_type = %d\n", cfg->sensor_type); -+ -+ dev_dbg(dev, "send_irq_sof_discarded = %d\n", -+ cfg->send_irq_sof_discarded); -+ dev_dbg(dev, "send_irq_eof_discarded = %d\n", -+ cfg->send_irq_eof_discarded); -+ dev_dbg(dev, "send_resp_sof_discarded = %d\n", -+ cfg->send_resp_sof_discarded); -+ dev_dbg(dev, "send_resp_eof_discarded = %d\n", -+ cfg->send_resp_eof_discarded); -+ -+ dev_dbg(dev, "crop:\n"); -+ dev_dbg(dev, "\t.left_top = [%d, %d]\n", cfg->crop.left_offset, -+ cfg->crop.top_offset); -+ dev_dbg(dev, "\t.right_bottom = [%d, %d]\n", cfg->crop.right_offset, -+ cfg->crop.bottom_offset); -+ -+ dev_dbg(dev, "nof_input_pins = %d\n", cfg->nof_input_pins); -+ for (i = 0; i < cfg->nof_input_pins; i++) { -+ dev_dbg(dev, "input pin[%d]:\n", i); -+ dev_dbg(dev, "\t.dt = 0x%0x\n", cfg->input_pins[i].dt); -+ dev_dbg(dev, "\t.mipi_store_mode = %d\n", -+ cfg->input_pins[i].mipi_store_mode); -+ dev_dbg(dev, "\t.bits_per_pix = %d\n", -+ cfg->input_pins[i].bits_per_pix); -+ dev_dbg(dev, "\t.mapped_dt = 0x%0x\n", -+ cfg->input_pins[i].mapped_dt); -+ dev_dbg(dev, "\t.input_res = %dx%d\n", -+ cfg->input_pins[i].input_res.width, -+ cfg->input_pins[i].input_res.height); -+ dev_dbg(dev, "\t.mipi_decompression = %d\n", -+ cfg->input_pins[i].mipi_decompression); -+ dev_dbg(dev, "\t.capture_mode = %d\n", -+ cfg->input_pins[i].capture_mode); -+ } -+ -+ dev_dbg(dev, "nof_output_pins = %d\n", cfg->nof_output_pins); -+ for (i = 0; i < cfg->nof_output_pins; i++) { -+ dev_dbg(dev, "output_pin[%d]:\n", i); -+ dev_dbg(dev, "\t.input_pin_id = %d\n", -+ cfg->output_pins[i].input_pin_id); -+ dev_dbg(dev, "\t.output_res = %dx%d\n", -+ cfg->output_pins[i].output_res.width, -+ cfg->output_pins[i].output_res.height); -+ dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride); -+ dev_dbg(dev, "\t.pt = %d\n", cfg->output_pins[i].pt); -+ dev_dbg(dev, "\t.payload_buf_size = %d\n", -+ cfg->output_pins[i].payload_buf_size); -+ dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft); -+ dev_dbg(dev, "\t.watermark_in_lines = %d\n", -+ cfg->output_pins[i].watermark_in_lines); -+ dev_dbg(dev, "\t.send_irq = %d\n", -+ cfg->output_pins[i].send_irq); -+ dev_dbg(dev, "\t.reserve_compression = %d\n", -+ cfg->output_pins[i].reserve_compression); -+ dev_dbg(dev, "\t.snoopable = %d\n", -+ cfg->output_pins[i].snoopable); -+ dev_dbg(dev, "\t.error_handling_enable = %d\n", -+ cfg->output_pins[i].error_handling_enable); -+ dev_dbg(dev, "\t.sensor_type = %d\n", -+ cfg->output_pins[i].sensor_type); -+ } -+ dev_dbg(dev, "-----------------------------------------------------\n"); -+} -+ -+void -+ipu6_fw_isys_dump_frame_buff_set(struct device *dev, -+ struct ipu6_fw_isys_frame_buff_set_abi *buf, -+ unsigned int outputs) -+{ -+ unsigned int i; -+ -+ dev_dbg(dev, "-----------------------------------------------------\n"); -+ dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n"); -+ -+ for (i = 0; i < outputs; i++) { -+ dev_dbg(dev, "output_pin[%d]:\n", i); -+ dev_dbg(dev, "\t.out_buf_id = %llu\n", -+ buf->output_pins[i].out_buf_id); -+ dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); -+ dev_dbg(dev, "\t.compress = %d\n", -+ buf->output_pins[i].compress); -+ } -+ -+ dev_dbg(dev, "send_irq_sof = 0x%x\n", buf->send_irq_sof); -+ dev_dbg(dev, "send_irq_eof = 0x%x\n", buf->send_irq_eof); -+ dev_dbg(dev, "send_resp_sof = 0x%x\n", buf->send_resp_sof); -+ dev_dbg(dev, "send_resp_eof = 0x%x\n", buf->send_resp_eof); -+ dev_dbg(dev, "send_irq_capture_ack = 0x%x\n", -+ buf->send_irq_capture_ack); -+ dev_dbg(dev, "send_irq_capture_done = 0x%x\n", -+ buf->send_irq_capture_done); -+ dev_dbg(dev, "send_resp_capture_ack = 0x%x\n", -+ buf->send_resp_capture_ack); -+ dev_dbg(dev, "send_resp_capture_done = 0x%x\n", -+ buf->send_resp_capture_done); -+ -+ dev_dbg(dev, "-----------------------------------------------------\n"); -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h -new file mode 100644 -index 000000000000..a7ffa0e22bf0 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h -@@ -0,0 +1,573 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_FW_ISYS_H -+#define IPU6_FW_ISYS_H -+ -+#include <linux/types.h> -+ -+struct device; -+struct ipu6_isys; -+ -+/* Max number of Input/Output Pins */ -+#define IPU6_MAX_IPINS 4 -+ -+#define IPU6_MAX_OPINS ((IPU6_MAX_IPINS) + 1) -+ -+#define IPU6_STREAM_ID_MAX 16 -+#define IPU6_NONSECURE_STREAM_ID_MAX 12 -+#define IPU6_DEV_SEND_QUEUE_SIZE (IPU6_STREAM_ID_MAX) -+#define IPU6_NOF_SRAM_BLOCKS_MAX (IPU6_STREAM_ID_MAX) -+#define IPU6_N_MAX_MSG_SEND_QUEUES (IPU6_STREAM_ID_MAX) -+#define IPU6SE_STREAM_ID_MAX 8 -+#define IPU6SE_NONSECURE_STREAM_ID_MAX 4 -+#define IPU6SE_DEV_SEND_QUEUE_SIZE (IPU6SE_STREAM_ID_MAX) -+#define IPU6SE_NOF_SRAM_BLOCKS_MAX (IPU6SE_STREAM_ID_MAX) -+#define IPU6SE_N_MAX_MSG_SEND_QUEUES (IPU6SE_STREAM_ID_MAX) -+ -+/* Single return queue for all streams/commands type */ -+#define IPU6_N_MAX_MSG_RECV_QUEUES 1 -+/* Single device queue for high priority commands (bypass in-order queue) */ -+#define IPU6_N_MAX_DEV_SEND_QUEUES 1 -+/* Single dedicated send queue for proxy interface */ -+#define IPU6_N_MAX_PROXY_SEND_QUEUES 1 -+/* Single dedicated recv queue for proxy interface */ -+#define IPU6_N_MAX_PROXY_RECV_QUEUES 1 -+/* Send queues layout */ -+#define IPU6_BASE_PROXY_SEND_QUEUES 0 -+#define IPU6_BASE_DEV_SEND_QUEUES \ -+ (IPU6_BASE_PROXY_SEND_QUEUES + IPU6_N_MAX_PROXY_SEND_QUEUES) -+#define IPU6_BASE_MSG_SEND_QUEUES \ -+ (IPU6_BASE_DEV_SEND_QUEUES + IPU6_N_MAX_DEV_SEND_QUEUES) -+/* Recv queues layout */ -+#define IPU6_BASE_PROXY_RECV_QUEUES 0 -+#define IPU6_BASE_MSG_RECV_QUEUES \ -+ (IPU6_BASE_PROXY_RECV_QUEUES + IPU6_N_MAX_PROXY_RECV_QUEUES) -+#define IPU6_N_MAX_RECV_QUEUES \ -+ (IPU6_BASE_MSG_RECV_QUEUES + IPU6_N_MAX_MSG_RECV_QUEUES) -+ -+#define IPU6_N_MAX_SEND_QUEUES \ -+ (IPU6_BASE_MSG_SEND_QUEUES + IPU6_N_MAX_MSG_SEND_QUEUES) -+#define IPU6SE_N_MAX_SEND_QUEUES \ -+ (IPU6_BASE_MSG_SEND_QUEUES + IPU6SE_N_MAX_MSG_SEND_QUEUES) -+ -+/* Max number of supported input pins routed in ISL */ -+#define IPU6_MAX_IPINS_IN_ISL 2 -+ -+/* Max number of planes for frame formats supported by the FW */ -+#define IPU6_PIN_PLANES_MAX 4 -+ -+#define IPU6_FW_ISYS_SENSOR_TYPE_START 14 -+#define IPU6_FW_ISYS_SENSOR_TYPE_END 19 -+#define IPU6SE_FW_ISYS_SENSOR_TYPE_START 6 -+#define IPU6SE_FW_ISYS_SENSOR_TYPE_END 11 -+/* -+ * Device close takes some time from last ack message to actual stopping -+ * of the SP processor. As long as the SP processor runs we can't proceed with -+ * clean up of resources. -+ */ -+#define IPU6_ISYS_OPEN_RETRY 2000 -+#define IPU6_ISYS_CLOSE_RETRY 2000 -+#define IPU6_FW_CALL_TIMEOUT_JIFFIES msecs_to_jiffies(2000) -+ -+enum ipu6_fw_isys_resp_type { -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, -+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, -+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_WATERMARK, -+ IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, -+ IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, -+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_SKIPPED, -+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED, -+ IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED, -+ IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED, -+ IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, -+ N_IPU6_FW_ISYS_RESP_TYPE -+}; -+ -+enum ipu6_fw_isys_send_type { -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN = 0, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_START, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_STOP, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE, -+ N_IPU6_FW_ISYS_SEND_TYPE -+}; -+ -+enum ipu6_fw_isys_queue_type { -+ IPU6_FW_ISYS_QUEUE_TYPE_PROXY = 0, -+ IPU6_FW_ISYS_QUEUE_TYPE_DEV, -+ IPU6_FW_ISYS_QUEUE_TYPE_MSG, -+ N_IPU6_FW_ISYS_QUEUE_TYPE -+}; -+ -+enum ipu6_fw_isys_stream_source { -+ IPU6_FW_ISYS_STREAM_SRC_PORT_0 = 0, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_1, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_2, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_3, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_4, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_5, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_6, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_7, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_8, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_9, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_10, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_11, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_12, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_13, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_14, -+ IPU6_FW_ISYS_STREAM_SRC_PORT_15, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_2, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_3, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_4, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_5, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_6, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_7, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_8, -+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_9, -+ N_IPU6_FW_ISYS_STREAM_SRC -+}; -+ -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 IPU6_FW_ISYS_STREAM_SRC_PORT_0 -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT1 IPU6_FW_ISYS_STREAM_SRC_PORT_1 -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT2 IPU6_FW_ISYS_STREAM_SRC_PORT_2 -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT3 IPU6_FW_ISYS_STREAM_SRC_PORT_3 -+ -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTA IPU6_FW_ISYS_STREAM_SRC_PORT_4 -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTB IPU6_FW_ISYS_STREAM_SRC_PORT_5 -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 \ -+ IPU6_FW_ISYS_STREAM_SRC_PORT_6 -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 \ -+ IPU6_FW_ISYS_STREAM_SRC_PORT_7 -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 \ -+ IPU6_FW_ISYS_STREAM_SRC_PORT_8 -+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 \ -+ IPU6_FW_ISYS_STREAM_SRC_PORT_9 -+ -+#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0 -+#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT1 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1 -+ -+/** -+ * enum ipu6_fw_isys_mipi_vc: MIPI csi2 spec -+ * supports up to 4 virtual per physical channel -+ */ -+enum ipu6_fw_isys_mipi_vc { -+ IPU6_FW_ISYS_MIPI_VC_0 = 0, -+ IPU6_FW_ISYS_MIPI_VC_1, -+ IPU6_FW_ISYS_MIPI_VC_2, -+ IPU6_FW_ISYS_MIPI_VC_3, -+ N_IPU6_FW_ISYS_MIPI_VC -+}; -+ -+enum ipu6_fw_isys_frame_format_type { -+ IPU6_FW_ISYS_FRAME_FORMAT_NV11 = 0, /* 12 bit YUV 411, Y, UV plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_NV12, /* 12 bit YUV 420, Y, UV plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_NV12_16, /* 16 bit YUV 420, Y, UV plane */ -+ /* 12 bit YUV 420, Intel proprietary tiled format */ -+ IPU6_FW_ISYS_FRAME_FORMAT_NV12_TILEY, -+ -+ IPU6_FW_ISYS_FRAME_FORMAT_NV16, /* 16 bit YUV 422, Y, UV plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_NV21, /* 12 bit YUV 420, Y, VU plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_NV61, /* 16 bit YUV 422, Y, VU plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YV12, /* 12 bit YUV 420, Y, V, U plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YV16, /* 16 bit YUV 422, Y, V, U plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420, /* 12 bit YUV 420, Y, U, V plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_10, /* yuv420, 10 bits per subpixel */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_12, /* yuv420, 12 bits per subpixel */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_14, /* yuv420, 14 bits per subpixel */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_16, /* yuv420, 16 bits per subpixel */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV422, /* 16 bit YUV 422, Y, U, V plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV422_16, /* yuv422, 16 bits per subpixel */ -+ IPU6_FW_ISYS_FRAME_FORMAT_UYVY, /* 16 bit YUV 422, UYVY interleaved */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUYV, /* 16 bit YUV 422, YUYV interleaved */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV444, /* 24 bit YUV 444, Y, U, V plane */ -+ /* Internal format, 2 y lines followed by a uvinterleaved line */ -+ IPU6_FW_ISYS_FRAME_FORMAT_YUV_LINE, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8, /* RAW8, 1 plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */ -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */ -+ /** -+ * 16 bit RGB, 1 plane. Each 3 sub pixels are packed into one 16 bit -+ * value, 5 bits for R, 6 bits for G and 5 bits for B. -+ */ -+ IPU6_FW_ISYS_FRAME_FORMAT_RGB565, -+ IPU6_FW_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */ -+ IPU6_FW_ISYS_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, A=Alpha */ -+ IPU6_FW_ISYS_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */ -+ IPU6_FW_ISYS_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. */ -+ N_IPU6_FW_ISYS_FRAME_FORMAT -+}; -+ -+#define IPU6_FW_ISYS_FRAME_FORMAT_RAW (IPU6_FW_ISYS_FRAME_FORMAT_RAW16) -+ -+enum ipu6_fw_isys_pin_type { -+ /* captured as MIPI packets */ -+ IPU6_FW_ISYS_PIN_TYPE_MIPI = 0, -+ /* captured through the SoC path */ -+ IPU6_FW_ISYS_PIN_TYPE_RAW_SOC = 3, -+}; -+ -+/** -+ * enum ipu6_fw_isys_mipi_store_mode. Describes if long MIPI packets reach -+ * MIPI SRAM with the long packet header or -+ * if not, then only option is to capture it with pin type MIPI. -+ */ -+enum ipu6_fw_isys_mipi_store_mode { -+ IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL = 0, -+ IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER, -+ N_IPU6_FW_ISYS_MIPI_STORE_MODE -+}; -+ -+enum ipu6_fw_isys_capture_mode { -+ IPU6_FW_ISYS_CAPTURE_MODE_REGULAR = 0, -+ IPU6_FW_ISYS_CAPTURE_MODE_BURST, -+ N_IPU6_FW_ISYS_CAPTURE_MODE, -+}; -+ -+enum ipu6_fw_isys_sensor_mode { -+ IPU6_FW_ISYS_SENSOR_MODE_NORMAL = 0, -+ IPU6_FW_ISYS_SENSOR_MODE_TOBII, -+ N_IPU6_FW_ISYS_SENSOR_MODE, -+}; -+ -+enum ipu6_fw_isys_error { -+ IPU6_FW_ISYS_ERROR_NONE = 0, -+ IPU6_FW_ISYS_ERROR_FW_INTERNAL_CONSISTENCY, -+ IPU6_FW_ISYS_ERROR_HW_CONSISTENCY, -+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE, -+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION, -+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION, -+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION, -+ IPU6_FW_ISYS_ERROR_INSUFFICIENT_RESOURCES, -+ IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO, -+ IPU6_FW_ISYS_ERROR_HW_REPORTED_SIG2CIO, -+ IPU6_FW_ISYS_ERROR_SENSOR_FW_SYNC, -+ IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION, -+ IPU6_FW_ISYS_ERROR_RESPONSE_QUEUE_FULL, -+ N_IPU6_FW_ISYS_ERROR -+}; -+ -+enum ipu6_fw_proxy_error { -+ IPU6_FW_PROXY_ERROR_NONE = 0, -+ IPU6_FW_PROXY_ERROR_INVALID_WRITE_REGION, -+ IPU6_FW_PROXY_ERROR_INVALID_WRITE_OFFSET, -+ N_IPU6_FW_PROXY_ERROR -+}; -+ -+/* firmware ABI structure below are aligned in firmware, no need pack */ -+struct ipu6_fw_isys_buffer_partition_abi { -+ u32 num_gda_pages[IPU6_STREAM_ID_MAX]; -+}; -+ -+struct ipu6_fw_isys_fw_config { -+ struct ipu6_fw_isys_buffer_partition_abi buffer_partition; -+ u32 num_send_queues[N_IPU6_FW_ISYS_QUEUE_TYPE]; -+ u32 num_recv_queues[N_IPU6_FW_ISYS_QUEUE_TYPE]; -+}; -+ -+/** -+ * struct ipu6_fw_isys_resolution_abi: Generic resolution structure. -+ */ -+struct ipu6_fw_isys_resolution_abi { -+ u32 width; -+ u32 height; -+}; -+ -+/** -+ * struct ipu6_fw_isys_output_pin_payload_abi -+ * @out_buf_id: Points to output pin buffer - buffer identifier -+ * @addr: Points to output pin buffer - CSS Virtual Address -+ * @compress: Request frame compression (1), or not (0) -+ */ -+struct ipu6_fw_isys_output_pin_payload_abi { -+ u64 out_buf_id; -+ u32 addr; -+ u32 compress; -+}; -+ -+/** -+ * struct ipu6_fw_isys_output_pin_info_abi -+ * @output_res: output pin resolution -+ * @stride: output stride in Bytes (not valid for statistics) -+ * @watermark_in_lines: pin watermark level in lines -+ * @payload_buf_size: minimum size in Bytes of all buffers that will be -+ * supplied for capture on this pin -+ * @send_irq: assert if pin event should trigger irq -+ * @pt: pin type -real format "enum ipu6_fw_isys_pin_type" -+ * @ft: frame format type -real format "enum ipu6_fw_isys_frame_format_type" -+ * @input_pin_id: related input pin id -+ * @reserve_compression: reserve compression resources for pin -+ */ -+struct ipu6_fw_isys_output_pin_info_abi { -+ struct ipu6_fw_isys_resolution_abi output_res; -+ u32 stride; -+ u32 watermark_in_lines; -+ u32 payload_buf_size; -+ u32 ts_offsets[IPU6_PIN_PLANES_MAX]; -+ u32 s2m_pixel_soc_pixel_remapping; -+ u32 csi_be_soc_pixel_remapping; -+ u8 send_irq; -+ u8 input_pin_id; -+ u8 pt; -+ u8 ft; -+ u8 reserved; -+ u8 reserve_compression; -+ u8 snoopable; -+ u8 error_handling_enable; -+ u32 sensor_type; -+}; -+ -+/** -+ * struct ipu6_fw_isys_input_pin_info_abi -+ * @input_res: input resolution -+ * @dt: mipi data type ((enum ipu6_fw_isys_mipi_data_type) -+ * @mipi_store_mode: defines if legacy long packet header will be stored or -+ * discarded if discarded, output pin type for this -+ * input pin can only be MIPI -+ * (enum ipu6_fw_isys_mipi_store_mode) -+ * @bits_per_pix: native bits per pixel -+ * @mapped_dt: actual data type from sensor -+ * @mipi_decompression: defines which compression will be in mipi backend -+ * @crop_first_and_last_lines Control whether to crop the -+ * first and last line of the -+ * input image. Crop done by HW -+ * device. -+ * @capture_mode: mode of capture, regular or burst, default value is regular -+ */ -+struct ipu6_fw_isys_input_pin_info_abi { -+ struct ipu6_fw_isys_resolution_abi input_res; -+ u8 dt; -+ u8 mipi_store_mode; -+ u8 bits_per_pix; -+ u8 mapped_dt; -+ u8 mipi_decompression; -+ u8 crop_first_and_last_lines; -+ u8 capture_mode; -+ u8 reserved; -+}; -+ -+/** -+ * struct ipu6_fw_isys_cropping_abi - cropping coordinates -+ */ -+struct ipu6_fw_isys_cropping_abi { -+ s32 top_offset; -+ s32 left_offset; -+ s32 bottom_offset; -+ s32 right_offset; -+}; -+ -+/** -+ * struct ipu6_fw_isys_stream_cfg_data_abi -+ * ISYS stream configuration data structure -+ * @crop: for extended use and is not used in FW currently -+ * @input_pins: input pin descriptors -+ * @output_pins: output pin descriptors -+ * @compfmt: de-compression setting for User Defined Data -+ * @nof_input_pins: number of input pins -+ * @nof_output_pins: number of output pins -+ * @send_irq_sof_discarded: send irq on discarded frame sof response -+ * - if '1' it will override the send_resp_sof_discarded -+ * and send the response -+ * - if '0' the send_resp_sof_discarded will determine -+ * whether to send the response -+ * @send_irq_eof_discarded: send irq on discarded frame eof response -+ * - if '1' it will override the send_resp_eof_discarded -+ * and send the response -+ * - if '0' the send_resp_eof_discarded will determine -+ * whether to send the response -+ * @send_resp_sof_discarded: send response for discarded frame sof detected, -+ * used only when send_irq_sof_discarded is '0' -+ * @send_resp_eof_discarded: send response for discarded frame eof detected, -+ * used only when send_irq_eof_discarded is '0' -+ * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1 -+ * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel) -+ * @isl_use: indicates whether stream requires ISL and how -+ * @sensor_type: type of connected sensor, tobii or others, default is 0 -+ */ -+struct ipu6_fw_isys_stream_cfg_data_abi { -+ struct ipu6_fw_isys_cropping_abi crop; -+ struct ipu6_fw_isys_input_pin_info_abi input_pins[IPU6_MAX_IPINS]; -+ struct ipu6_fw_isys_output_pin_info_abi output_pins[IPU6_MAX_OPINS]; -+ u32 compfmt; -+ u8 nof_input_pins; -+ u8 nof_output_pins; -+ u8 send_irq_sof_discarded; -+ u8 send_irq_eof_discarded; -+ u8 send_resp_sof_discarded; -+ u8 send_resp_eof_discarded; -+ u8 src; -+ u8 vc; -+ u8 isl_use; -+ u8 sensor_type; -+ u8 reserved; -+ u8 reserved2; -+}; -+ -+/** -+ * struct ipu6_fw_isys_frame_buff_set - frame buffer set -+ * @output_pins: output pin addresses -+ * @send_irq_sof: send irq on frame sof response -+ * - if '1' it will override the send_resp_sof and -+ * send the response -+ * - if '0' the send_resp_sof will determine whether to -+ * send the response -+ * @send_irq_eof: send irq on frame eof response -+ * - if '1' it will override the send_resp_eof and -+ * send the response -+ * - if '0' the send_resp_eof will determine whether to -+ * send the response -+ * @send_resp_sof: send response for frame sof detected, -+ * used only when send_irq_sof is '0' -+ * @send_resp_eof: send response for frame eof detected, -+ * used only when send_irq_eof is '0' -+ * @send_resp_capture_ack: send response for capture ack event -+ * @send_resp_capture_done: send response for capture done event -+ */ -+struct ipu6_fw_isys_frame_buff_set_abi { -+ struct ipu6_fw_isys_output_pin_payload_abi output_pins[IPU6_MAX_OPINS]; -+ u8 send_irq_sof; -+ u8 send_irq_eof; -+ u8 send_irq_capture_ack; -+ u8 send_irq_capture_done; -+ u8 send_resp_sof; -+ u8 send_resp_eof; -+ u8 send_resp_capture_ack; -+ u8 send_resp_capture_done; -+ u8 reserved[8]; -+}; -+ -+/** -+ * struct ipu6_fw_isys_error_info_abi -+ * @error: error code if something went wrong -+ * @error_details: depending on error code, it may contain additional error info -+ */ -+struct ipu6_fw_isys_error_info_abi { -+ u32 error; -+ u32 error_details; -+}; -+ -+/** -+ * struct ipu6_fw_isys_resp_info_comm -+ * @pin: this var is only valid for pin event related responses, -+ * contains pin addresses -+ * @error_info: error information from the FW -+ * @timestamp: Time information for event if available -+ * @stream_handle: stream id the response corresponds to -+ * @type: response type (enum ipu6_fw_isys_resp_type) -+ * @pin_id: pin id that the pin payload corresponds to -+ */ -+struct ipu6_fw_isys_resp_info_abi { -+ u64 buf_id; -+ struct ipu6_fw_isys_output_pin_payload_abi pin; -+ struct ipu6_fw_isys_error_info_abi error_info; -+ u32 timestamp[2]; -+ u8 stream_handle; -+ u8 type; -+ u8 pin_id; -+ u8 reserved; -+ u32 reserved2; -+}; -+ -+/** -+ * struct ipu6_fw_isys_proxy_error_info_comm -+ * @proxy_error: error code if something went wrong -+ * @proxy_error_details: depending on error code, it may contain additional -+ * error info -+ */ -+struct ipu6_fw_isys_proxy_error_info_abi { -+ u32 error; -+ u32 error_details; -+}; -+ -+struct ipu6_fw_isys_proxy_resp_info_abi { -+ u32 request_id; -+ struct ipu6_fw_isys_proxy_error_info_abi error_info; -+}; -+ -+/** -+ * struct ipu6_fw_proxy_write_queue_token -+ * @request_id: update id for the specific proxy write request -+ * @region_index: Region id for the proxy write request -+ * @offset: Offset of the write request according to the base address -+ * of the region -+ * @value: Value that is requested to be written with the proxy write request -+ */ -+struct ipu6_fw_proxy_write_queue_token { -+ u32 request_id; -+ u32 region_index; -+ u32 offset; -+ u32 value; -+}; -+ -+/** -+ * struct ipu6_fw_resp_queue_token -+ */ -+struct ipu6_fw_resp_queue_token { -+ struct ipu6_fw_isys_resp_info_abi resp_info; -+}; -+ -+/** -+ * struct ipu6_fw_send_queue_token -+ */ -+struct ipu6_fw_send_queue_token { -+ u64 buf_handle; -+ u32 payload; -+ u16 send_type; -+ u16 stream_id; -+}; -+ -+/** -+ * struct ipu6_fw_proxy_resp_queue_token -+ */ -+struct ipu6_fw_proxy_resp_queue_token { -+ struct ipu6_fw_isys_proxy_resp_info_abi proxy_resp_info; -+}; -+ -+/** -+ * struct ipu6_fw_proxy_send_queue_token -+ */ -+struct ipu6_fw_proxy_send_queue_token { -+ u32 request_id; -+ u32 region_index; -+ u32 offset; -+ u32 value; -+}; -+ -+void -+ipu6_fw_isys_dump_stream_cfg(struct device *dev, -+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg); -+void -+ipu6_fw_isys_dump_frame_buff_set(struct device *dev, -+ struct ipu6_fw_isys_frame_buff_set_abi *buf, -+ unsigned int outputs); -+int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams); -+int ipu6_fw_isys_close(struct ipu6_isys *isys); -+int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys, -+ const unsigned int stream_handle, u16 send_type); -+int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys, -+ const unsigned int stream_handle, -+ void *cpu_mapped_buf, dma_addr_t dma_mapped_buf, -+ size_t size, u16 send_type); -+int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, -+ unsigned int req_id, -+ unsigned int index, -+ unsigned int offset, u32 value); -+void ipu6_fw_isys_cleanup(struct ipu6_isys *isys); -+struct ipu6_fw_isys_resp_info_abi * -+ipu6_fw_isys_get_resp(void *context, unsigned int queue); -+void ipu6_fw_isys_put_resp(void *context, unsigned int queue); -+#endif --- -2.43.2 - - -From e69a245fa4db99e5983170aab73f962dc99d3149 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:22 +0800 -Subject: [PATCH 15/33] media: intel/ipu6: add IPU6 CSI2 receiver v4l2 - sub-device - -Input system CSI2 receiver is exposed as a v4l2 sub-device. -Each CSI2 sub-device represent one single CSI2 hardware port -which be linked with external sub-device such camera sensor -by linked with ISYS CSI2's sink pad. CSI2 source pad is linked -to the sink pad of video capture device. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c | 666 ++++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h | 81 +++ - .../media/pci/intel/ipu6/ipu6-isys-subdev.c | 381 ++++++++++ - .../media/pci/intel/ipu6/ipu6-isys-subdev.h | 61 ++ - .../intel/ipu6/ipu6-platform-isys-csi2-reg.h | 189 +++++ - 5 files changed, 1378 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c -new file mode 100644 -index 000000000000..ac9fa3e0d7ab ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c -@@ -0,0 +1,666 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/atomic.h> -+#include <linux/bitfield.h> -+#include <linux/bits.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/err.h> -+#include <linux/io.h> -+#include <linux/minmax.h> -+#include <linux/sprintf.h> -+ -+#include <media/media-entity.h> -+#include <media/v4l2-ctrls.h> -+#include <media/v4l2-device.h> -+#include <media/v4l2-event.h> -+#include <media/v4l2-subdev.h> -+ -+#include "ipu6-bus.h" -+#include "ipu6-isys.h" -+#include "ipu6-isys-csi2.h" -+#include "ipu6-isys-subdev.h" -+#include "ipu6-platform-isys-csi2-reg.h" -+ -+static const u32 csi2_supported_codes[] = { -+ MEDIA_BUS_FMT_RGB565_1X16, -+ MEDIA_BUS_FMT_RGB888_1X24, -+ MEDIA_BUS_FMT_UYVY8_1X16, -+ MEDIA_BUS_FMT_YUYV8_1X16, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ 0 -+}; -+ -+/* -+ * Strings corresponding to CSI-2 receiver errors are here. -+ * Corresponding macros are defined in the header file. -+ */ -+static const struct ipu6_csi2_error dphy_rx_errors[] = { -+ { "Single packet header error corrected", true }, -+ { "Multiple packet header errors detected", true }, -+ { "Payload checksum (CRC) error", true }, -+ { "Transfer FIFO overflow", false }, -+ { "Reserved short packet data type detected", true }, -+ { "Reserved long packet data type detected", true }, -+ { "Incomplete long packet detected", false }, -+ { "Frame sync error", false }, -+ { "Line sync error", false }, -+ { "DPHY recoverable synchronization error", true }, -+ { "DPHY fatal error", false }, -+ { "DPHY elastic FIFO overflow", false }, -+ { "Inter-frame short packet discarded", true }, -+ { "Inter-frame long packet discarded", true }, -+ { "MIPI pktgen overflow", false }, -+ { "MIPI pktgen data loss", false }, -+ { "FIFO overflow", false }, -+ { "Lane deskew", false }, -+ { "SOT sync error", false }, -+ { "HSIDLE detected", false } -+}; -+ -+s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2) -+{ -+ struct media_pad *src_pad; -+ struct v4l2_subdev *ext_sd; -+ struct device *dev; -+ -+ if (!csi2) -+ return -EINVAL; -+ -+ dev = &csi2->isys->adev->auxdev.dev; -+ src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -+ if (IS_ERR_OR_NULL(src_pad)) { -+ dev_err(dev, "can't get source pad of %s\n", csi2->asd.sd.name); -+ return -ENOLINK; -+ } -+ -+ ext_sd = media_entity_to_v4l2_subdev(src_pad->entity); -+ if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name)) -+ return -ENODEV; -+ -+ return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0); -+} -+ -+static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -+ struct v4l2_event_subscription *sub) -+{ -+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); -+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n", -+ sub->type, sub->id); -+ -+ switch (sub->type) { -+ case V4L2_EVENT_FRAME_SYNC: -+ return v4l2_event_subscribe(fh, sub, 10, NULL); -+ case V4L2_EVENT_CTRL: -+ return v4l2_ctrl_subscribe_event(fh, sub); -+ default: -+ return -EINVAL; -+ } -+} -+ -+static const struct v4l2_subdev_core_ops csi2_sd_core_ops = { -+ .subscribe_event = csi2_subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+/* -+ * The input system CSI2+ receiver has several -+ * parameters affecting the receiver timings. These depend -+ * on the MIPI bus frequency F in Hz (sensor transmitter rate) -+ * as follows: -+ * register value = (A/1e9 + B * UI) / COUNT_ACC -+ * where -+ * UI = 1 / (2 * F) in seconds -+ * COUNT_ACC = counter accuracy in seconds -+ * COUNT_ACC = 0.125 ns = 1 / 8 ns, ACCINV = 8. -+ * -+ * A and B are coefficients from the table below, -+ * depending whether the register minimum or maximum value is -+ * calculated. -+ * Minimum Maximum -+ * Clock lane A B A B -+ * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0 -+ * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16 -+ * Data lanes -+ * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4 -+ * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6 -+ * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4 -+ * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6 -+ * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4 -+ * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6 -+ * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4 -+ * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6 -+ * -+ * We use the minimum values of both A and B. -+ */ -+ -+#define DIV_SHIFT 8 -+#define CSI2_ACCINV 8 -+ -+static u32 calc_timing(s32 a, s32 b, s64 link_freq, s32 accinv) -+{ -+ return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT) -+ / (s32)(link_freq >> DIV_SHIFT)); -+} -+ -+static int -+ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2, -+ struct ipu6_isys_csi2_timing *timing, s32 accinv) -+{ -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ s64 link_freq; -+ -+ link_freq = ipu6_isys_csi2_get_link_freq(csi2); -+ if (link_freq < 0) -+ return link_freq; -+ -+ timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A, -+ CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B, -+ link_freq, accinv); -+ timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A, -+ CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B, -+ link_freq, accinv); -+ timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A, -+ CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B, -+ link_freq, accinv); -+ timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A, -+ CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B, -+ link_freq, accinv); -+ -+ dev_dbg(dev, "ctermen %u csettle %u dtermen %u dsettle %u\n", -+ timing->ctermen, timing->csettle, -+ timing->dtermen, timing->dsettle); -+ -+ return 0; -+} -+ -+void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2) -+{ -+ u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); -+ struct ipu6_isys *isys = csi2->isys; -+ u32 mask; -+ -+ mask = isys->pdata->ipdata->csi2.irq_mask; -+ writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); -+ csi2->receiver_errors |= irq & mask; -+} -+ -+void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2) -+{ -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ const struct ipu6_csi2_error *errors; -+ u32 status; -+ u32 i; -+ -+ /* register errors once more in case of interrupts are disabled */ -+ ipu6_isys_register_errors(csi2); -+ status = csi2->receiver_errors; -+ csi2->receiver_errors = 0; -+ errors = dphy_rx_errors; -+ -+ for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) { -+ if (status & BIT(i)) -+ dev_err_ratelimited(dev, "csi2-%i error: %s\n", -+ csi2->port, errors[i].error_string); -+ } -+} -+ -+static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd, -+ const struct ipu6_isys_csi2_timing *timing, -+ unsigned int nlanes, int enable) -+{ -+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); -+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); -+ struct ipu6_isys *isys = csi2->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu6_isys_csi2_config cfg; -+ unsigned int nports; -+ int ret = 0; -+ u32 mask = 0; -+ u32 i; -+ -+ dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off", -+ csi2->port, nlanes); -+ -+ cfg.port = csi2->port; -+ cfg.nlanes = nlanes; -+ -+ mask = isys->pdata->ipdata->csi2.irq_mask; -+ nports = isys->pdata->ipdata->csi2.nports; -+ -+ if (!enable) { -+ writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE); -+ writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE); -+ -+ writel(0, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); -+ writel(mask, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); -+ writel(0, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); -+ writel(0xffffffff, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); -+ -+ isys->phy_set_power(isys, &cfg, timing, false); -+ -+ writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT -+ (isys->pdata->ipdata->csi2.fw_access_port_ofs, -+ csi2->port)); -+ writel(0, isys->pdata->base + -+ CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port)); -+ -+ return ret; -+ } -+ -+ /* reset port reset */ -+ writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST); -+ usleep_range(100, 200); -+ writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST); -+ -+ /* enable port clock */ -+ for (i = 0; i < nports; i++) { -+ writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i)); -+ writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT -+ (isys->pdata->ipdata->csi2.fw_access_port_ofs, i)); -+ } -+ -+ /* enable all error related irq */ -+ writel(mask, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); -+ writel(mask, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_MASK_OFFSET); -+ writel(mask, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); -+ writel(mask, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET); -+ writel(mask, -+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + -+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); -+ -+ /* -+ * Using event from firmware instead of irq to handle CSI2 sync event -+ * which can reduce system wakeups. If CSI2 sync irq enabled, we need -+ * disable the firmware CSI2 sync event to avoid duplicate handling. -+ */ -+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); -+ writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_MASK_OFFSET); -+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); -+ writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET); -+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); -+ -+ /* configure to enable FE and PPI2CSI */ -+ writel(0, csi2->base + CSI_REG_CSI_FE_MODE); -+ writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL); -+ writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID, -+ csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL); -+ writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1), -+ csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF); -+ -+ writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE); -+ writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE); -+ -+ ret = isys->phy_set_power(isys, &cfg, timing, true); -+ if (ret) -+ dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port, -+ ret); -+ -+ return ret; -+} -+ -+static int set_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); -+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ struct ipu6_isys_csi2_timing timing = { }; -+ unsigned int nlanes; -+ int ret; -+ -+ dev_dbg(dev, "csi2 stream %s callback\n", enable ? "on" : "off"); -+ -+ if (!enable) { -+ csi2->stream_count--; -+ if (csi2->stream_count) -+ return 0; -+ -+ ipu6_isys_csi2_set_stream(sd, &timing, 0, enable); -+ return 0; -+ } -+ -+ if (csi2->stream_count) { -+ csi2->stream_count++; -+ return 0; -+ } -+ -+ nlanes = csi2->nlanes; -+ -+ ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV); -+ if (ret) -+ return ret; -+ -+ ret = ipu6_isys_csi2_set_stream(sd, &timing, nlanes, enable); -+ if (ret) -+ return ret; -+ -+ csi2->stream_count++; -+ -+ return 0; -+} -+ -+static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); -+ struct device *dev = &asd->isys->adev->auxdev.dev; -+ struct v4l2_mbus_framefmt *sink_ffmt; -+ struct v4l2_mbus_framefmt *src_ffmt; -+ struct v4l2_rect *crop; -+ -+ if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) -+ return -EINVAL; -+ -+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -+ sel->pad, -+ sel->stream); -+ if (!sink_ffmt) -+ return -EINVAL; -+ -+ src_ffmt = v4l2_subdev_state_get_stream_format(state, sel->pad, -+ sel->stream); -+ if (!src_ffmt) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream); -+ if (!crop) -+ return -EINVAL; -+ -+ /* Only vertical cropping is supported */ -+ sel->r.left = 0; -+ sel->r.width = sink_ffmt->width; -+ /* Non-bayer formats can't be single line cropped */ -+ if (!ipu6_isys_is_bayer_format(sink_ffmt->code)) -+ sel->r.top &= ~1; -+ sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT, -+ sink_ffmt->height - sel->r.top); -+ *crop = sel->r; -+ -+ /* update source pad format */ -+ src_ffmt->width = sel->r.width; -+ src_ffmt->height = sel->r.height; -+ if (ipu6_isys_is_bayer_format(sink_ffmt->code)) -+ src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code, -+ sel->r.left, -+ sel->r.top); -+ dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n", -+ sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height, -+ src_ffmt->code); -+ -+ return 0; -+} -+ -+static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct v4l2_mbus_framefmt *sink_ffmt; -+ struct v4l2_rect *crop; -+ int ret = 0; -+ -+ if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK) -+ return -EINVAL; -+ -+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -+ sel->pad, -+ sel->stream); -+ if (!sink_ffmt) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream); -+ if (!crop) -+ return -EINVAL; -+ -+ switch (sel->target) { -+ case V4L2_SEL_TGT_CROP_DEFAULT: -+ case V4L2_SEL_TGT_CROP_BOUNDS: -+ sel->r.left = 0; -+ sel->r.top = 0; -+ sel->r.width = sink_ffmt->width; -+ sel->r.height = sink_ffmt->height; -+ break; -+ case V4L2_SEL_TGT_CROP: -+ sel->r = *crop; -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static const struct v4l2_subdev_video_ops csi2_sd_video_ops = { -+ .s_stream = set_stream, -+}; -+ -+static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { -+ .init_cfg = ipu6_isys_subdev_init_cfg, -+ .get_fmt = v4l2_subdev_get_fmt, -+ .set_fmt = ipu6_isys_subdev_set_fmt, -+ .get_selection = ipu6_isys_csi2_get_sel, -+ .set_selection = ipu6_isys_csi2_set_sel, -+ .enum_mbus_code = ipu6_isys_subdev_enum_mbus_code, -+ .set_routing = ipu6_isys_subdev_set_routing, -+}; -+ -+static const struct v4l2_subdev_ops csi2_sd_ops = { -+ .core = &csi2_sd_core_ops, -+ .video = &csi2_sd_video_ops, -+ .pad = &csi2_sd_pad_ops, -+}; -+ -+static const struct media_entity_operations csi2_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+ .has_pad_interdep = v4l2_subdev_has_pad_interdep, -+}; -+ -+void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2) -+{ -+ if (!csi2->isys) -+ return; -+ -+ v4l2_device_unregister_subdev(&csi2->asd.sd); -+ v4l2_subdev_cleanup(&csi2->asd.sd); -+ ipu6_isys_subdev_cleanup(&csi2->asd); -+ csi2->isys = NULL; -+} -+ -+int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, -+ struct ipu6_isys *isys, -+ void __iomem *base, unsigned int index) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ csi2->isys = isys; -+ csi2->base = base; -+ csi2->port = index; -+ -+ csi2->asd.sd.entity.ops = &csi2_entity_ops; -+ csi2->asd.isys = isys; -+ ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, -+ NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS); -+ if (ret) -+ goto fail; -+ -+ csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index; -+ csi2->asd.supported_codes = csi2_supported_codes; -+ snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), -+ IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index); -+ v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); -+ ret = v4l2_subdev_init_finalize(&csi2->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to init v4l2 subdev\n"); -+ goto fail; -+ } -+ -+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to register v4l2 subdev\n"); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ ipu6_isys_csi2_cleanup(csi2); -+ -+ return ret; -+} -+ -+void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream) -+{ -+ struct video_device *vdev = stream->asd->sd.devnode; -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd); -+ struct v4l2_event ev = { -+ .type = V4L2_EVENT_FRAME_SYNC, -+ }; -+ -+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -+ v4l2_event_queue(vdev, &ev); -+ -+ dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n", -+ csi2->port, ev.u.frame_sync.frame_sequence, stream->vc); -+} -+ -+void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream) -+{ -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd); -+ u32 frame_sequence = atomic_read(&stream->sequence); -+ -+ dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", -+ csi2->port, frame_sequence); -+} -+ -+int ipu6_isys_csi2_get_remote_desc(u32 source_stream, -+ struct ipu6_isys_csi2 *csi2, -+ struct media_entity *source_entity, -+ struct v4l2_mbus_frame_desc_entry *entry, -+ int *nr_queues) -+{ -+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ struct v4l2_mbus_frame_desc desc; -+ struct v4l2_subdev *source; -+ struct media_pad *pad; -+ unsigned int i; -+ int count = 0; -+ int ret; -+ -+ source = media_entity_to_v4l2_subdev(source_entity); -+ if (!source) -+ return -EPIPE; -+ -+ pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]); -+ if (!pad) -+ return -EPIPE; -+ -+ ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); -+ if (ret) -+ return ret; -+ -+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -+ dev_err(dev, "Unsupported frame descriptor type\n"); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < desc.num_entries; i++) { -+ if (source_stream == desc.entry[i].stream) { -+ desc_entry = &desc.entry[i]; -+ break; -+ } -+ } -+ -+ if (!desc_entry) { -+ dev_err(dev, "Failed to find stream %u from remote subdev\n", -+ source_stream); -+ return -EINVAL; -+ } -+ -+ if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) { -+ dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); -+ return -EINVAL; -+ } -+ -+ *entry = *desc_entry; -+ for (i = 0; i < desc.num_entries; i++) { -+ if (entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc) -+ count++; -+ } -+ -+ *nr_queues = count; -+ return 0; -+} -+ -+void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status) -+{ -+ struct ipu6_isys_stream *stream = av->stream; -+ struct v4l2_subdev *sd = &stream->asd->sd; -+ struct v4l2_subdev_state *state; -+ struct media_pad *r_pad; -+ unsigned int i; -+ u32 r_stream; -+ -+ r_pad = media_pad_remote_pad_first(&av->pad); -+ r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ -+ for (i = 0; i < state->stream_configs.num_configs; i++) { -+ struct v4l2_subdev_stream_config *cfg = -+ &state->stream_configs.configs[i]; -+ -+ if (cfg->pad == r_pad->index && r_stream == cfg->stream) { -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "%s: pad:%u, stream:%u, status:%u\n", -+ sd->entity.name, r_pad->index, r_stream, -+ status); -+ cfg->enabled = status; -+ } -+ } -+ -+ v4l2_subdev_unlock_state(state); -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h -new file mode 100644 -index 000000000000..d4765bae6112 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h -@@ -0,0 +1,81 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_ISYS_CSI2_H -+#define IPU6_ISYS_CSI2_H -+ -+#include <linux/container_of.h> -+ -+#include "ipu6-isys-subdev.h" -+ -+struct media_entity; -+struct v4l2_mbus_frame_desc_entry; -+ -+struct ipu6_isys_video; -+struct ipu6_isys; -+struct ipu6_isys_csi2_pdata; -+struct ipu6_isys_stream; -+ -+#define NR_OF_CSI2_VC 16 -+#define INVALID_VC_ID -1 -+#define NR_OF_CSI2_SINK_PADS 1 -+#define CSI2_PAD_SINK 0 -+#define NR_OF_CSI2_SRC_PADS 8 -+#define CSI2_PAD_SRC 1 -+#define NR_OF_CSI2_PADS (NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SRC_PADS) -+ -+#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A 0 -+#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B 0 -+#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A 95 -+#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B -8 -+ -+#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A 0 -+#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B 0 -+#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A 85 -+#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B -2 -+ -+struct ipu6_isys_csi2 { -+ struct ipu6_isys_subdev asd; -+ struct ipu6_isys_csi2_pdata *pdata; -+ struct ipu6_isys *isys; -+ -+ void __iomem *base; -+ u32 receiver_errors; -+ unsigned int nlanes; -+ unsigned int port; -+ unsigned int stream_count; -+}; -+ -+struct ipu6_isys_csi2_timing { -+ u32 ctermen; -+ u32 csettle; -+ u32 dtermen; -+ u32 dsettle; -+}; -+ -+struct ipu6_csi2_error { -+ const char *error_string; -+ bool is_info_only; -+}; -+ -+#define ipu6_isys_subdev_to_csi2(__sd) \ -+ container_of(__sd, struct ipu6_isys_csi2, asd) -+ -+#define to_ipu6_isys_csi2(__asd) container_of(__asd, struct ipu6_isys_csi2, asd) -+ -+s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2); -+int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, struct ipu6_isys *isys, -+ void __iomem *base, unsigned int index); -+void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2); -+void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream); -+void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream); -+void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2); -+void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2); -+int ipu6_isys_csi2_get_remote_desc(u32 source_stream, -+ struct ipu6_isys_csi2 *csi2, -+ struct media_entity *source_entity, -+ struct v4l2_mbus_frame_desc_entry *entry, -+ int *nr_queues); -+void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status); -+ -+#endif /* IPU6_ISYS_CSI2_H */ -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c -new file mode 100644 -index 000000000000..510c5ca34f9f ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c -@@ -0,0 +1,381 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/bug.h> -+#include <linux/device.h> -+#include <linux/minmax.h> -+ -+#include <media/media-entity.h> -+#include <media/mipi-csi2.h> -+#include <media/v4l2-ctrls.h> -+#include <media/v4l2-subdev.h> -+ -+#include "ipu6-bus.h" -+#include "ipu6-isys.h" -+#include "ipu6-isys-subdev.h" -+ -+unsigned int ipu6_isys_mbus_code_to_bpp(u32 code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return 24; -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return 16; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return 12; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return 10; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return 8; -+ default: -+ WARN_ON(1); -+ return 8; -+ } -+} -+ -+unsigned int ipu6_isys_mbus_code_to_mipi(u32 code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return MIPI_CSI2_DT_RGB565; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return MIPI_CSI2_DT_RGB888; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return MIPI_CSI2_DT_YUV422_8B; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return MIPI_CSI2_DT_RAW12; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return MIPI_CSI2_DT_RAW10; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return MIPI_CSI2_DT_RAW8; -+ default: -+ /* return unavailable MIPI data type - 0x3f */ -+ WARN_ON(1); -+ return 0x3f; -+ } -+} -+ -+bool ipu6_isys_is_bayer_format(u32 code) -+{ -+ switch (ipu6_isys_mbus_code_to_mipi(code)) { -+ case MIPI_CSI2_DT_RAW8: -+ case MIPI_CSI2_DT_RAW10: -+ case MIPI_CSI2_DT_RAW12: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y) -+{ -+ static const u32 code_map[] = { -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SBGGR12_1X12 -+ }; -+ u32 i; -+ -+ for (i = 0; i < ARRAY_SIZE(code_map); i++) -+ if (code_map[i] == code) -+ break; -+ -+ if (WARN_ON(i == ARRAY_SIZE(code_map))) -+ return code; -+ -+ return code_map[i ^ (((y & 1) << 1) | (x & 1))]; -+} -+ -+int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_format *format) -+{ -+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); -+ struct v4l2_mbus_framefmt *fmt; -+ struct v4l2_rect *crop; -+ u32 code = asd->supported_codes[0]; -+ u32 other_pad, other_stream; -+ unsigned int i; -+ int ret; -+ -+ /* No transcoding, source and sink formats must match. */ -+ if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && -+ sd->entity.num_pads > 1) -+ return v4l2_subdev_get_fmt(sd, state, format); -+ -+ format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH, -+ IPU6_ISYS_MAX_WIDTH); -+ format->format.height = clamp(format->format.height, -+ IPU6_ISYS_MIN_HEIGHT, -+ IPU6_ISYS_MAX_HEIGHT); -+ -+ for (i = 0; asd->supported_codes[i]; i++) { -+ if (asd->supported_codes[i] == format->format.code) { -+ code = asd->supported_codes[i]; -+ break; -+ } -+ } -+ format->format.code = code; -+ format->format.field = V4L2_FIELD_NONE; -+ -+ /* Store the format and propagate it to the source pad. */ -+ fmt = v4l2_subdev_state_get_stream_format(state, format->pad, -+ format->stream); -+ if (!fmt) -+ return -EINVAL; -+ -+ *fmt = format->format; -+ -+ if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) -+ return 0; -+ -+ /* propagate format to following source pad */ -+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, -+ format->stream); -+ if (!fmt) -+ return -EINVAL; -+ -+ *fmt = format->format; -+ -+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, -+ format->pad, -+ format->stream, -+ &other_pad, -+ &other_stream); -+ if (ret) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_stream_crop(state, other_pad, -+ other_stream); -+ /* reset crop */ -+ crop->left = 0; -+ crop->top = 0; -+ crop->width = fmt->width; -+ crop->height = fmt->height; -+ -+ return 0; -+} -+ -+int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); -+ const u32 *supported_codes = asd->supported_codes; -+ u32 index; -+ -+ for (index = 0; supported_codes[index]; index++) { -+ if (index == code->index) { -+ code->code = supported_codes[index]; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+static int subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_krouting *routing) -+{ -+ static const struct v4l2_mbus_framefmt format = { -+ .width = 4096, -+ .height = 3072, -+ .code = MEDIA_BUS_FMT_SGRBG10_1X10, -+ .field = V4L2_FIELD_NONE, -+ }; -+ int ret; -+ -+ ret = v4l2_subdev_routing_validate(sd, routing, -+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); -+ if (ret) -+ return ret; -+ -+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); -+} -+ -+int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -+ struct v4l2_mbus_framefmt *format) -+{ -+ struct v4l2_mbus_framefmt *fmt; -+ struct v4l2_subdev_state *state; -+ -+ if (!sd || !format) -+ return -EINVAL; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ fmt = v4l2_subdev_state_get_stream_format(state, pad, stream); -+ if (fmt) -+ *format = *fmt; -+ v4l2_subdev_unlock_state(state); -+ -+ return fmt ? 0 : -EINVAL; -+} -+ -+int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, -+ struct v4l2_rect *crop) -+{ -+ struct v4l2_subdev_state *state; -+ struct v4l2_rect *rect; -+ -+ if (!sd || !crop) -+ return -EINVAL; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ rect = v4l2_subdev_state_get_stream_crop(state, pad, stream); -+ if (rect) -+ *crop = *rect; -+ v4l2_subdev_unlock_state(state); -+ -+ return rect ? 0 : -EINVAL; -+} -+ -+u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) -+{ -+ struct v4l2_subdev_state *state; -+ struct v4l2_subdev_route *routes; -+ unsigned int i; -+ u32 source_stream = 0; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ if (!state) -+ return 0; -+ -+ routes = state->routing.routes; -+ for (i = 0; i < state->routing.num_routes; i++) { -+ if (routes[i].source_pad == pad) { -+ source_stream = routes[i].source_stream; -+ break; -+ } -+ } -+ -+ v4l2_subdev_unlock_state(state); -+ -+ return source_stream; -+} -+ -+int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct v4l2_subdev_route route = { -+ .sink_pad = 0, -+ .sink_stream = 0, -+ .source_pad = 1, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }; -+ struct v4l2_subdev_krouting routing = { -+ .num_routes = 1, -+ .routes = &route, -+ }; -+ -+ return subdev_set_routing(sd, state, &routing); -+} -+ -+int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing) -+{ -+ return subdev_set_routing(sd, state, routing); -+} -+ -+int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd, -+ const struct v4l2_subdev_ops *ops, -+ unsigned int nr_ctrls, -+ unsigned int num_sink_pads, -+ unsigned int num_source_pads) -+{ -+ unsigned int num_pads = num_sink_pads + num_source_pads; -+ unsigned int i; -+ int ret; -+ -+ v4l2_subdev_init(&asd->sd, ops); -+ -+ asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | -+ V4L2_SUBDEV_FL_HAS_EVENTS | -+ V4L2_SUBDEV_FL_STREAMS; -+ asd->sd.owner = THIS_MODULE; -+ asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; -+ -+ asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, -+ sizeof(*asd->pad), GFP_KERNEL); -+ -+ if (!asd->pad) -+ return -ENOMEM; -+ -+ for (i = 0; i < num_sink_pads; i++) -+ asd->pad[i].flags = MEDIA_PAD_FL_SINK | -+ MEDIA_PAD_FL_MUST_CONNECT; -+ -+ for (i = num_sink_pads; i < num_pads; i++) -+ asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; -+ -+ ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); -+ if (ret) -+ return ret; -+ -+ if (asd->ctrl_init) { -+ ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); -+ if (ret) -+ goto out_media_entity_cleanup; -+ -+ asd->ctrl_init(&asd->sd); -+ if (asd->ctrl_handler.error) { -+ ret = asd->ctrl_handler.error; -+ goto out_v4l2_ctrl_handler_free; -+ } -+ -+ asd->sd.ctrl_handler = &asd->ctrl_handler; -+ } -+ -+ asd->source = -1; -+ -+ return 0; -+ -+out_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(&asd->ctrl_handler); -+ -+out_media_entity_cleanup: -+ media_entity_cleanup(&asd->sd.entity); -+ -+ return ret; -+} -+ -+void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd) -+{ -+ media_entity_cleanup(&asd->sd.entity); -+ v4l2_ctrl_handler_free(&asd->ctrl_handler); -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h -new file mode 100644 -index 000000000000..adea2a55761d ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h -@@ -0,0 +1,61 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_ISYS_SUBDEV_H -+#define IPU6_ISYS_SUBDEV_H -+ -+#include <linux/container_of.h> -+ -+#include <media/media-entity.h> -+#include <media/v4l2-ctrls.h> -+#include <media/v4l2-subdev.h> -+ -+struct ipu6_isys; -+ -+struct ipu6_isys_subdev { -+ struct v4l2_subdev sd; -+ struct ipu6_isys *isys; -+ u32 const *supported_codes; -+ struct media_pad *pad; -+ struct v4l2_ctrl_handler ctrl_handler; -+ void (*ctrl_init)(struct v4l2_subdev *sd); -+ int source; /* SSI stream source; -1 if unset */ -+}; -+ -+#define to_ipu6_isys_subdev(__sd) \ -+ container_of(__sd, struct ipu6_isys_subdev, sd) -+ -+unsigned int ipu6_isys_mbus_code_to_bpp(u32 code); -+unsigned int ipu6_isys_mbus_code_to_mipi(u32 code); -+bool ipu6_isys_is_bayer_format(u32 code); -+u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y); -+ -+int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_format *fmt); -+int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_mbus_code_enum -+ *code); -+int ipu6_isys_subdev_link_validate(struct v4l2_subdev *sd, -+ struct media_link *link, -+ struct v4l2_subdev_format *source_fmt, -+ struct v4l2_subdev_format *sink_fmt); -+u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); -+int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -+ struct v4l2_mbus_framefmt *format); -+int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, -+ struct v4l2_rect *crop); -+int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state); -+int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing); -+int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd, -+ const struct v4l2_subdev_ops *ops, -+ unsigned int nr_ctrls, -+ unsigned int num_sink_pads, -+ unsigned int num_source_pads); -+void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd); -+#endif /* IPU6_ISYS_SUBDEV_H */ -diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h b/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h -new file mode 100644 -index 000000000000..2034e1109d98 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h -@@ -0,0 +1,189 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2023 Intel Corporation */ -+ -+#ifndef IPU6_PLATFORM_ISYS_CSI2_REG_H -+#define IPU6_PLATFORM_ISYS_CSI2_REG_H -+ -+#include <linux/bits.h> -+ -+#define CSI_REG_BASE 0x220000 -+#define CSI_REG_BASE_PORT(id) ((id) * 0x1000) -+ -+#define IPU6_CSI_PORT_A_ADDR_OFFSET \ -+ (CSI_REG_BASE + CSI_REG_BASE_PORT(0)) -+#define IPU6_CSI_PORT_B_ADDR_OFFSET \ -+ (CSI_REG_BASE + CSI_REG_BASE_PORT(1)) -+#define IPU6_CSI_PORT_C_ADDR_OFFSET \ -+ (CSI_REG_BASE + CSI_REG_BASE_PORT(2)) -+#define IPU6_CSI_PORT_D_ADDR_OFFSET \ -+ (CSI_REG_BASE + CSI_REG_BASE_PORT(3)) -+#define IPU6_CSI_PORT_E_ADDR_OFFSET \ -+ (CSI_REG_BASE + CSI_REG_BASE_PORT(4)) -+#define IPU6_CSI_PORT_F_ADDR_OFFSET \ -+ (CSI_REG_BASE + CSI_REG_BASE_PORT(5)) -+#define IPU6_CSI_PORT_G_ADDR_OFFSET \ -+ (CSI_REG_BASE + CSI_REG_BASE_PORT(6)) -+#define IPU6_CSI_PORT_H_ADDR_OFFSET \ -+ (CSI_REG_BASE + CSI_REG_BASE_PORT(7)) -+ -+/* CSI Port Genral Purpose Registers */ -+#define CSI_REG_PORT_GPREG_SRST 0x0 -+#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4 -+#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8 -+ -+/* -+ * Port IRQs mapping events: -+ * IRQ0 - CSI_FE event -+ * IRQ1 - CSI_SYNC -+ * IRQ2 - S2M_SIDS0TO7 -+ * IRQ3 - S2M_SIDS8TO15 -+ */ -+#define CSI_PORT_REG_BASE_IRQ_CSI 0x80 -+#define CSI_PORT_REG_BASE_IRQ_CSI_SYNC 0xA0 -+#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS0TOS7 0xC0 -+#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS8TOS15 0xE0 -+ -+#define CSI_PORT_REG_BASE_IRQ_EDGE_OFFSET 0x0 -+#define CSI_PORT_REG_BASE_IRQ_MASK_OFFSET 0x4 -+#define CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET 0x8 -+#define CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET 0xc -+#define CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET 0x10 -+#define CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET 0x14 -+ -+#define IPU6SE_CSI_RX_ERROR_IRQ_MASK GENMASK(18, 0) -+#define IPU6_CSI_RX_ERROR_IRQ_MASK GENMASK(19, 0) -+ -+#define CSI_RX_NUM_ERRORS_IN_IRQ 20 -+#define CSI_RX_NUM_IRQ 32 -+ -+#define IPU_CSI_RX_IRQ_FS_VC(chn) (1 << ((chn) * 2)) -+#define IPU_CSI_RX_IRQ_FE_VC(chn) (2 << ((chn) * 2)) -+ -+/* PPI2CSI */ -+#define CSI_REG_PPI2CSI_ENABLE 0x200 -+#define CSI_REG_PPI2CSI_CONFIG_PPI_INTF 0x204 -+#define PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK GENMASK(4, 3) -+#define CSI_REG_PPI2CSI_CONFIG_CSI_FEATURE 0x208 -+ -+enum CSI_PPI2CSI_CTRL { -+ CSI_PPI2CSI_DISABLE = 0, -+ CSI_PPI2CSI_ENABLE = 1, -+}; -+ -+/* CSI_FE */ -+#define CSI_REG_CSI_FE_ENABLE 0x280 -+#define CSI_REG_CSI_FE_MODE 0x284 -+#define CSI_REG_CSI_FE_MUX_CTRL 0x288 -+#define CSI_REG_CSI_FE_SYNC_CNTR_SEL 0x290 -+ -+enum CSI_FE_ENABLE_TYPE { -+ CSI_FE_DISABLE = 0, -+ CSI_FE_ENABLE = 1, -+}; -+ -+enum CSI_FE_MODE_TYPE { -+ CSI_FE_DPHY_MODE = 0, -+ CSI_FE_CPHY_MODE = 1, -+}; -+ -+enum CSI_FE_INPUT_SELECTOR { -+ CSI_SENSOR_INPUT = 0, -+ CSI_MIPIGEN_INPUT = 1, -+}; -+ -+enum CSI_FE_SYNC_CNTR_SEL_TYPE { -+ CSI_CNTR_SENSOR_LINE_ID = BIT(0), -+ CSI_CNTR_INT_LINE_PKT_ID = ~CSI_CNTR_SENSOR_LINE_ID, -+ CSI_CNTR_SENSOR_FRAME_ID = BIT(1), -+ CSI_CNTR_INT_FRAME_PKT_ID = ~CSI_CNTR_SENSOR_FRAME_ID, -+}; -+ -+/* CSI HUB General Purpose Registers */ -+#define CSI_REG_HUB_GPREG_SRST (CSI_REG_BASE + 0x18000) -+#define CSI_REG_HUB_GPREG_SLV_REG_SRST (CSI_REG_BASE + 0x18004) -+ -+#define CSI_REG_HUB_DRV_ACCESS_PORT(id) (CSI_REG_BASE + 0x18018 + (id) * 4) -+#define CSI_REG_HUB_FW_ACCESS_PORT_OFS 0x17000 -+#define CSI_REG_HUB_FW_ACCESS_PORT_V6OFS 0x16000 -+#define CSI_REG_HUB_FW_ACCESS_PORT(ofs, id) \ -+ (CSI_REG_BASE + (ofs) + (id) * 4) -+ -+enum CSI_PORT_CLK_GATING_SWITCH { -+ CSI_PORT_CLK_GATING_OFF = 0, -+ CSI_PORT_CLK_GATING_ON = 1, -+}; -+ -+#define CSI_REG_BASE_HUB_IRQ 0x18200 -+ -+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE 0x238200 -+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK 0x238204 -+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS 0x238208 -+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR 0x23820c -+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE 0x238210 -+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE 0x238214 -+ -+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE 0x238220 -+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK 0x238224 -+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS 0x238228 -+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR 0x23822c -+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE 0x238230 -+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE 0x238234 -+ -+/* MTL IPU6V6 irq ctrl0 & ctrl1 */ -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE 0x238700 -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK 0x238704 -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS 0x238708 -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR 0x23870c -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE 0x238710 -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE 0x238714 -+ -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE 0x238720 -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK 0x238724 -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS 0x238728 -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR 0x23872c -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE 0x238730 -+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE 0x238734 -+ -+/* -+ * 3:0 CSI_PORT.irq_out[3:0] CSI_PORT_CTRL0 IRQ outputs (4bits) -+ * [0] CSI_PORT.IRQ_CTRL0_csi -+ * [1] CSI_PORT.IRQ_CTRL1_csi_sync -+ * [2] CSI_PORT.IRQ_CTRL2_s2m_sids0to7 -+ * [3] CSI_PORT.IRQ_CTRL3_s2m_sids8to15 -+ */ -+#define IPU6_ISYS_UNISPART_IRQ_CSI2(port) \ -+ (0x3 << ((port) * IPU6_CSI_IRQ_NUM_PER_PIPE)) -+ -+/* -+ * ipu6se support 2 front ends, 2 port per front end, 4 ports 0..3 -+ * sip0 - 0, 1 -+ * sip1 - 2, 3 -+ * 0 and 2 support 4 data lanes, 1 and 3 support 2 data lanes -+ * all offset are base from isys base address -+ */ -+ -+#define CSI2_HUB_GPREG_SIP_SRST(sip) (0x238038 + (sip) * 4) -+#define CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip) (0x238050 + (sip) * 4) -+ -+#define CSI2_HUB_GPREG_DPHY_TIMER_INCR 0x238040 -+#define CSI2_HUB_GPREG_HPLL_FREQ 0x238044 -+#define CSI2_HUB_GPREG_IS_CLK_RATIO 0x238048 -+#define CSI2_HUB_GPREG_HPLL_FREQ_ISCLK_RATE_OVERRIDE 0x23804c -+#define CSI2_HUB_GPREG_PORT_CLKGATING_DISABLE 0x238058 -+#define CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL 0x23805c -+#define CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL 0x238088 -+#define CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL 0x2380a4 -+#define CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL 0x2380d0 -+ -+#define CSI2_SIP_TOP_CSI_RX_BASE(sip) (0x23805c + (sip) * 0x48) -+#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port) (0x23805c + ((port) / 2) * 0x48) -+#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) (0x238088 + ((port) / 2) * 0x48) -+ -+/* offset from port base */ -+#define CSI2_SIP_TOP_CSI_RX_PORT_CONTROL 0x0 -+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE 0x4 -+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE 0x8 -+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(lane) (0xc + (lane) * 8) -+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(lane) (0x10 + (lane) * 8) -+ -+#endif /* IPU6_ISYS_CSI2_REG_H */ --- -2.43.2 - - -From 71d3da5e8f1ea094e26030a12ceed4553cbe182a Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:23 +0800 -Subject: [PATCH 16/33] media: intel/ipu6: add the CSI2 DPHY implementation - -IPU6 CSI2 DPHY hardware varies on different platforms, current -IPU6 has three DPHY hardware instance which maybe used on tigerlake, -alderlake, metorlake and jasperlake. MCD DPHY is shipped on tigerlake -and alderlake, DWC DPHY is shipped on metorlake. - -Each PHY has its own register space, input system driver call the -DPHY callback which was set at isys_probe(). - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - .../media/pci/intel/ipu6/ipu6-isys-dwc-phy.c | 536 +++++++++++++ - .../media/pci/intel/ipu6/ipu6-isys-jsl-phy.c | 242 ++++++ - .../media/pci/intel/ipu6/ipu6-isys-mcd-phy.c | 720 ++++++++++++++++++ - 3 files changed, 1498 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c -new file mode 100644 -index 000000000000..a4bb5ff51d4e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c -@@ -0,0 +1,536 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/bitfield.h> -+#include <linux/bits.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/iopoll.h> -+#include <linux/math64.h> -+ -+#include "ipu6-bus.h" -+#include "ipu6-isys.h" -+#include "ipu6-platform-isys-csi2-reg.h" -+ -+#define IPU6_DWC_DPHY_BASE(i) (0x238038 + 0x34 * (i)) -+#define IPU6_DWC_DPHY_RSTZ 0x00 -+#define IPU6_DWC_DPHY_SHUTDOWNZ 0x04 -+#define IPU6_DWC_DPHY_HSFREQRANGE 0x08 -+#define IPU6_DWC_DPHY_CFGCLKFREQRANGE 0x0c -+#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE 0x10 -+#define IPU6_DWC_DPHY_TEST_IFC_REQ 0x14 -+#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION 0x18 -+#define IPU6_DWC_DPHY_DFT_CTRL0 0x28 -+#define IPU6_DWC_DPHY_DFT_CTRL1 0x2c -+#define IPU6_DWC_DPHY_DFT_CTRL2 0x30 -+ -+/* -+ * test IFC request definition: -+ * - req: 0 for read, 1 for write -+ * - 12 bits address -+ * - 8bits data (will ignore for read) -+ * --24----16------4-----0 -+ * --|-data-|-addr-|-req-| -+ */ -+#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \ -+ FIELD_PREP(GENMASK(15, 4), addr) | \ -+ FIELD_PREP(GENMASK(1, 0), req)) -+ -+#define TEST_IFC_REQ_READ 0 -+#define TEST_IFC_REQ_WRITE 1 -+#define TEST_IFC_REQ_RESET 2 -+ -+#define TEST_IFC_ACCESS_MODE_FSM 0 -+#define TEST_IFC_ACCESS_MODE_IFC_CTL 1 -+ -+enum phy_fsm_state { -+ PHY_FSM_STATE_POWERON = 0, -+ PHY_FSM_STATE_BGPON = 1, -+ PHY_FSM_STATE_CAL_TYPE = 2, -+ PHY_FSM_STATE_BURNIN_CAL = 3, -+ PHY_FSM_STATE_TERMCAL = 4, -+ PHY_FSM_STATE_OFFSETCAL = 5, -+ PHY_FSM_STATE_OFFSET_LANE = 6, -+ PHY_FSM_STATE_IDLE = 7, -+ PHY_FSM_STATE_ULP = 8, -+ PHY_FSM_STATE_DDLTUNNING = 9, -+ PHY_FSM_STATE_SKEW_BACKWARD = 10, -+ PHY_FSM_STATE_INVALID, -+}; -+ -+static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr, -+ u32 data) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id); -+ -+ dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base, -+ data); -+ writel(data, base + addr); -+} -+ -+static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id); -+ u32 data; -+ -+ data = readl(base + addr); -+ dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base, -+ data); -+ -+ return data; -+} -+ -+static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr, -+ u32 data, u8 shift, u8 width) -+{ -+ u32 temp; -+ u32 mask; -+ -+ mask = (1 << width) - 1; -+ temp = dwc_dphy_read(isys, phy_id, addr); -+ temp &= ~(mask << shift); -+ temp |= (data & mask) << shift; -+ dwc_dphy_write(isys, phy_id, addr, temp); -+} -+ -+static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id, -+ u32 addr, u8 shift, u8 width) -+{ -+ u32 val; -+ -+ val = dwc_dphy_read(isys, phy_id, addr) >> shift; -+ return val & ((1 << width) - 1); -+} -+ -+#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC) -+static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr, -+ u32 *val) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id); -+ void __iomem *reg; -+ u32 completion; -+ int ret; -+ -+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ, -+ IFC_REQ(TEST_IFC_REQ_READ, addr, 0)); -+ reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION; -+ ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)), -+ 10, DWC_DPHY_TIMEOUT); -+ if (ret) { -+ dev_err(dev, "DWC ifc request read timeout\n"); -+ return ret; -+ } -+ -+ *val = completion >> 8 & 0xff; -+ *val = FIELD_GET(GENMASK(15, 8), completion); -+ dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val); -+ -+ return 0; -+} -+ -+static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr, -+ u32 data) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id); -+ void __iomem *reg; -+ u32 completion; -+ int ret; -+ -+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ, -+ IFC_REQ(TEST_IFC_REQ_WRITE, addr, data)); -+ completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION); -+ reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION; -+ ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)), -+ 10, DWC_DPHY_TIMEOUT); -+ if (ret) -+ dev_err(dev, "DWC ifc request write timeout\n"); -+ -+ return ret; -+} -+ -+static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id, -+ u32 addr, u32 data, u8 shift, u8 width) -+{ -+ u32 temp, mask; -+ int ret; -+ -+ ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp); -+ if (ret) -+ return; -+ -+ mask = (1 << width) - 1; -+ temp &= ~(mask << shift); -+ temp |= (data & mask) << shift; -+ dwc_dphy_ifc_write(isys, phy_id, addr, temp); -+} -+ -+static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr, -+ u8 shift, u8 width) -+{ -+ int ret; -+ u32 val; -+ -+ ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val); -+ if (ret) -+ return 0; -+ -+ return ((val >> shift) & ((1 << width) - 1)); -+} -+ -+static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 fsm_state; -+ int ret; -+ -+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1); -+ usleep_range(10, 20); -+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1); -+ -+ ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state, -+ (fsm_state == PHY_FSM_STATE_IDLE || -+ fsm_state == PHY_FSM_STATE_ULP), -+ 100, DWC_DPHY_TIMEOUT, false, isys, -+ phy_id, 0x1e, 0, 4); -+ -+ if (ret) -+ dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id, -+ fsm_state); -+ -+ return ret; -+} -+ -+struct dwc_dphy_freq_range { -+ u8 hsfreq; -+ u16 min; -+ u16 max; -+ u16 default_mbps; -+ u16 osc_freq_target; -+}; -+ -+#define DPHY_FREQ_RANGE_NUM (63) -+#define DPHY_FREQ_RANGE_INVALID_INDEX (0xff) -+static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = { -+ {0x00, 80, 97, 80, 335}, -+ {0x10, 80, 107, 90, 335}, -+ {0x20, 84, 118, 100, 335}, -+ {0x30, 93, 128, 110, 335}, -+ {0x01, 103, 139, 120, 335}, -+ {0x11, 112, 149, 130, 335}, -+ {0x21, 122, 160, 140, 335}, -+ {0x31, 131, 170, 150, 335}, -+ {0x02, 141, 181, 160, 335}, -+ {0x12, 150, 191, 170, 335}, -+ {0x22, 160, 202, 180, 335}, -+ {0x32, 169, 212, 190, 335}, -+ {0x03, 183, 228, 205, 335}, -+ {0x13, 198, 244, 220, 335}, -+ {0x23, 212, 259, 235, 335}, -+ {0x33, 226, 275, 250, 335}, -+ {0x04, 250, 301, 275, 335}, -+ {0x14, 274, 328, 300, 335}, -+ {0x25, 297, 354, 325, 335}, -+ {0x35, 321, 380, 350, 335}, -+ {0x05, 369, 433, 400, 335}, -+ {0x16, 416, 485, 450, 335}, -+ {0x26, 464, 538, 500, 335}, -+ {0x37, 511, 590, 550, 335}, -+ {0x07, 559, 643, 600, 335}, -+ {0x18, 606, 695, 650, 335}, -+ {0x28, 654, 748, 700, 335}, -+ {0x39, 701, 800, 750, 335}, -+ {0x09, 749, 853, 800, 335}, -+ {0x19, 796, 905, 850, 335}, -+ {0x29, 844, 958, 900, 335}, -+ {0x3a, 891, 1010, 950, 335}, -+ {0x0a, 939, 1063, 1000, 335}, -+ {0x1a, 986, 1115, 1050, 335}, -+ {0x2a, 1034, 1168, 1100, 335}, -+ {0x3b, 1081, 1220, 1150, 335}, -+ {0x0b, 1129, 1273, 1200, 335}, -+ {0x1b, 1176, 1325, 1250, 335}, -+ {0x2b, 1224, 1378, 1300, 335}, -+ {0x3c, 1271, 1430, 1350, 335}, -+ {0x0c, 1319, 1483, 1400, 335}, -+ {0x1c, 1366, 1535, 1450, 335}, -+ {0x2c, 1414, 1588, 1500, 335}, -+ {0x3d, 1461, 1640, 1550, 208}, -+ {0x0d, 1509, 1693, 1600, 214}, -+ {0x1d, 1556, 1745, 1650, 221}, -+ {0x2e, 1604, 1798, 1700, 228}, -+ {0x3e, 1651, 1850, 1750, 234}, -+ {0x0e, 1699, 1903, 1800, 241}, -+ {0x1e, 1746, 1955, 1850, 248}, -+ {0x2f, 1794, 2008, 1900, 255}, -+ {0x3f, 1841, 2060, 1950, 261}, -+ {0x0f, 1889, 2113, 2000, 268}, -+ {0x40, 1936, 2165, 2050, 275}, -+ {0x41, 1984, 2218, 2100, 281}, -+ {0x42, 2031, 2270, 2150, 288}, -+ {0x43, 2079, 2323, 2200, 294}, -+ {0x44, 2126, 2375, 2250, 302}, -+ {0x45, 2174, 2428, 2300, 308}, -+ {0x46, 2221, 2480, 2350, 315}, -+ {0x47, 2269, 2500, 2400, 321}, -+ {0x48, 2316, 2500, 2450, 328}, -+ {0x49, 2364, 2500, 2500, 335} -+}; -+ -+static u16 get_hsfreq_by_mbps(u32 mbps) -+{ -+ unsigned int i = DPHY_FREQ_RANGE_NUM; -+ -+ while (i--) { -+ if (freqranges[i].default_mbps == mbps || -+ (mbps >= freqranges[i].min && mbps <= freqranges[i].max)) -+ return i; -+ } -+ -+ return DPHY_FREQ_RANGE_INVALID_INDEX; -+} -+ -+static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys, -+ u32 phy_id, u32 mbps) -+{ -+ struct ipu6_bus_device *adev = isys->adev; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu6_device *isp = adev->isp; -+ u32 cfg_clk_freqrange; -+ u32 osc_freq_target; -+ u32 index; -+ -+ dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps); -+ -+ index = get_hsfreq_by_mbps(mbps); -+ if (index == DPHY_FREQ_RANGE_INVALID_INDEX) { -+ dev_err(dev, "link freq not found for mbps %u", mbps); -+ return -EINVAL; -+ } -+ -+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE, -+ freqranges[index].hsfreq, 0, 7); -+ -+ /* Force termination Calibration */ -+ if (isys->phy_termcal_val) { -+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1); -+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2); -+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, -+ isys->phy_termcal_val, 4, 4); -+ } -+ -+ /* -+ * Enable override to configure the DDL target oscillation -+ * frequency on bit 0 of register 0xe4 -+ */ -+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1); -+ /* -+ * configure registers 0xe2, 0xe3 with the -+ * appropriate DDL target oscillation frequency -+ * 0x1cc(460) -+ */ -+ osc_freq_target = freqranges[index].osc_freq_target; -+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2, -+ osc_freq_target & 0xff, 0, 8); -+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3, -+ (osc_freq_target >> 8) & 0xf, 0, 4); -+ -+ if (mbps < 1500) { -+ /* deskew_polarity_rw, for < 1.5Gbps */ -+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1); -+ } -+ -+ /* -+ * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4] -+ * (38.4 - 17) * 4 = ~85 (0x55) -+ */ -+ cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10; -+ dev_dbg(dev, "ref_clk = %u clk_freqrange = %u", -+ isp->buttress.ref_clk, cfg_clk_freqrange); -+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE, -+ cfg_clk_freqrange, 0, 8); -+ -+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1); -+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1); -+ -+ return 0; -+} -+ -+static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master, -+ u32 slave, u32 mbps) -+{ -+ /* Config mastermacro */ -+ dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1); -+ dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1); -+ -+ /* Config master PHY clk lane to drive long channel clk */ -+ dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1); -+ dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1); -+ -+ /* Config both PHYs data lanes to get clk from long channel */ -+ dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1); -+ dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1); -+ dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1); -+ dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1); -+ -+ /* Config slave PHY clk lane to bypass long channel clk to DDR clk */ -+ dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1); -+ dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1); -+ -+ /* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */ -+ dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2); -+ -+ /* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */ -+ dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1); -+ dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1); -+ -+ /* Turn off slave PHY LP-RX clk lane */ -+ dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1); -+ dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5); -+} -+ -+#define PHY_E 4 -+static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 rescal_done; -+ int ret; -+ -+ ret = dwc_dphy_pwr_up(isys, phy_id); -+ if (ret != 0) { -+ dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret); -+ return ret; -+ } -+ -+ /* reset forcerxmode */ -+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1); -+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1); -+ -+ dev_dbg(dev, "Dphy %u is ready!", phy_id); -+ -+ if (phy_id != PHY_E || isys->phy_termcal_val) -+ return 0; -+ -+ usleep_range(100, 200); -+ rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1); -+ if (rescal_done) { -+ isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id, -+ 0x220, 2, 4); -+ dev_dbg(dev, "termcal done with value = %u", -+ isys->phy_termcal_val); -+ } -+ -+ return 0; -+} -+ -+static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id) -+{ -+ dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id); -+ -+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0); -+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0); -+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE, -+ TEST_IFC_ACCESS_MODE_FSM); -+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ, -+ TEST_IFC_REQ_RESET); -+} -+ -+int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys, -+ struct ipu6_isys_csi2_config *cfg, -+ const struct ipu6_isys_csi2_timing *timing, -+ bool on) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ u32 phy_id, primary, secondary; -+ u32 nlanes, port, mbps; -+ s64 link_freq; -+ int ret; -+ -+ port = cfg->port; -+ -+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) { -+ dev_warn(dev, "invalid port ID %d\n", port); -+ return -EINVAL; -+ } -+ -+ nlanes = cfg->nlanes; -+ /* only port 0, 2 and 4 support 4 lanes */ -+ if (nlanes == 4 && port % 2) { -+ dev_err(dev, "invalid csi-port %u with %u lanes\n", port, -+ nlanes); -+ return -EINVAL; -+ } -+ -+ link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]); -+ if (link_freq < 0) { -+ dev_err(dev, "get link freq failed(%lld).\n", link_freq); -+ return link_freq; -+ } -+ -+ mbps = div_u64(link_freq, 500000); -+ -+ phy_id = port; -+ primary = port & ~1; -+ secondary = primary + 1; -+ if (on) { -+ if (nlanes == 4) { -+ dev_dbg(dev, "config phy %u and %u in aggr mode\n", -+ primary, secondary); -+ -+ ipu6_isys_dwc_phy_reset(isys, primary); -+ ipu6_isys_dwc_phy_reset(isys, secondary); -+ ipu6_isys_dwc_phy_aggr_setup(isys, primary, -+ secondary, mbps); -+ -+ ret = ipu6_isys_dwc_phy_config(isys, primary, mbps); -+ if (ret) -+ return ret; -+ ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps); -+ if (ret) -+ return ret; -+ -+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary); -+ if (ret) -+ return ret; -+ -+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary); -+ return ret; -+ } -+ -+ dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n", -+ phy_id, nlanes); -+ -+ ipu6_isys_dwc_phy_reset(isys, phy_id); -+ ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps); -+ if (ret) -+ return ret; -+ -+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id); -+ return ret; -+ } -+ -+ if (nlanes == 4) { -+ dev_dbg(dev, "Power down phy %u and phy %u for port %u\n", -+ primary, secondary, port); -+ ipu6_isys_dwc_phy_reset(isys, secondary); -+ ipu6_isys_dwc_phy_reset(isys, primary); -+ -+ return 0; -+ } -+ -+ dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes); -+ -+ ipu6_isys_dwc_phy_reset(isys, phy_id); -+ -+ return 0; -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c -new file mode 100644 -index 000000000000..dcc7743e0cee ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c -@@ -0,0 +1,242 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/bitfield.h> -+#include <linux/bits.h> -+#include <linux/device.h> -+#include <linux/io.h> -+ -+#include "ipu6-bus.h" -+#include "ipu6-isys.h" -+#include "ipu6-isys-csi2.h" -+#include "ipu6-platform-isys-csi2-reg.h" -+ -+/* only use BB0, BB2, BB4, and BB6 on PHY0 */ -+#define IPU6SE_ISYS_PHY_BB_NUM 4 -+#define IPU6SE_ISYS_PHY_0_BASE 0x10000 -+ -+#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x)) -+#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x)) -+#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x)) -+#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x)) -+ -+/* -+ * use port_cfg to configure that which data lanes used -+ * +---------+ +------+ +-----+ -+ * | port0 x4<-----| | | | -+ * | | | port | | | -+ * | port1 x2<-----| | | | -+ * | | | <-| PHY | -+ * | port2 x4<-----| | | | -+ * | | |config| | | -+ * | port3 x2<-----| | | | -+ * +---------+ +------+ +-----+ -+ */ -+static const unsigned int csi2_port_cfg[][3] = { -+ {0, 0, 0x1f}, /* no link */ -+ {4, 0, 0x10}, /* x4 + x4 config */ -+ {2, 0, 0x12}, /* x2 + x2 config */ -+ {1, 0, 0x13}, /* x1 + x1 config */ -+ {2, 1, 0x15}, /* x2x1 + x2x1 config */ -+ {1, 1, 0x16}, /* x1x1 + x1x1 config */ -+ {2, 2, 0x18}, /* x2x2 + x2x2 config */ -+ {1, 2, 0x19} /* x1x2 + x1x2 config */ -+}; -+ -+/* port, nlanes, bbindex, portcfg */ -+static const unsigned int phy_port_cfg[][4] = { -+ /* sip0 */ -+ {0, 1, 0, 0x15}, -+ {0, 2, 0, 0x15}, -+ {0, 4, 0, 0x15}, -+ {0, 4, 2, 0x22}, -+ /* sip1 */ -+ {2, 1, 4, 0x15}, -+ {2, 2, 4, 0x15}, -+ {2, 4, 4, 0x15}, -+ {2, 4, 6, 0x22} -+}; -+ -+static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys *isys, -+ unsigned int port, -+ unsigned int nlanes) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *base = isys->adev->isp->base; -+ unsigned int bbnum; -+ u32 val, reg, i; -+ -+ dev_dbg(dev, "port %u with %u lanes", port, nlanes); -+ -+ /* only support <1.5Gbps */ -+ for (i = 0; i < IPU6SE_ISYS_PHY_BB_NUM; i++) { -+ /* cphy_dll_ovrd.crcdc_fsm_dlane0 = 13 */ -+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i); -+ val = readl(base + reg); -+ val |= FIELD_PREP(GENMASK(6, 1), 13); -+ writel(val, base + reg); -+ -+ /* cphy_rx_control1.en_crc1 = 1 */ -+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_RX_CONTROL1(i); -+ val = readl(base + reg); -+ val |= BIT(31); -+ writel(val, base + reg); -+ -+ /* dphy_cfg.reserved = 1, .lden_from_dll_ovrd_0 = 1 */ -+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_DPHY_CFG(i); -+ val = readl(base + reg); -+ val |= BIT(25) | BIT(26); -+ writel(val, base + reg); -+ -+ /* cphy_dll_ovrd.lden_crcdc_fsm_dlane0 = 1 */ -+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i); -+ val = readl(base + reg); -+ val |= BIT(0); -+ writel(val, base + reg); -+ } -+ -+ /* Front end config, use minimal channel loss */ -+ for (i = 0; i < ARRAY_SIZE(phy_port_cfg); i++) { -+ if (phy_port_cfg[i][0] == port && -+ phy_port_cfg[i][1] == nlanes) { -+ bbnum = phy_port_cfg[i][2] / 2; -+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_BB_AFE_CONFIG(bbnum); -+ val = readl(base + reg); -+ val |= phy_port_cfg[i][3]; -+ writel(val, base + reg); -+ } -+ } -+} -+ -+static void ipu6_isys_csi2_rx_control(struct ipu6_isys *isys) -+{ -+ void __iomem *base = isys->adev->isp->base; -+ u32 val, reg; -+ -+ reg = CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL; -+ val = readl(base + reg); -+ val |= BIT(0); -+ writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL); -+ -+ reg = CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL; -+ val = readl(base + reg); -+ val |= BIT(0); -+ writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL); -+ -+ reg = CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL; -+ val = readl(base + reg); -+ val |= BIT(0); -+ writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL); -+ -+ reg = CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL; -+ val = readl(base + reg); -+ val |= BIT(0); -+ writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL); -+} -+ -+static int ipu6_isys_csi2_set_port_cfg(struct ipu6_isys *isys, -+ unsigned int port, unsigned int nlanes) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int sip = port / 2; -+ unsigned int index; -+ -+ switch (nlanes) { -+ case 1: -+ index = 5; -+ break; -+ case 2: -+ index = 6; -+ break; -+ case 4: -+ index = 1; -+ break; -+ default: -+ dev_err(dev, "lanes nr %u is unsupported\n", nlanes); -+ return -EINVAL; -+ } -+ -+ dev_dbg(dev, "port config for port %u with %u lanes\n", port, nlanes); -+ -+ writel(csi2_port_cfg[index][2], -+ isys->pdata->base + CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip)); -+ -+ return 0; -+} -+ -+static void -+ipu6_isys_csi2_set_timing(struct ipu6_isys *isys, -+ const struct ipu6_isys_csi2_timing *timing, -+ unsigned int port, unsigned int nlanes) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *reg; -+ u32 port_base; -+ u32 i; -+ -+ port_base = (port % 2) ? CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) : -+ CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port); -+ -+ dev_dbg(dev, "set timing for port %u with %u lanes\n", port, nlanes); -+ -+ reg = isys->pdata->base + port_base; -+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE; -+ -+ writel(timing->ctermen, reg); -+ -+ reg = isys->pdata->base + port_base; -+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE; -+ writel(timing->csettle, reg); -+ -+ for (i = 0; i < nlanes; i++) { -+ reg = isys->pdata->base + port_base; -+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(i); -+ writel(timing->dtermen, reg); -+ -+ reg = isys->pdata->base + port_base; -+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(i); -+ writel(timing->dsettle, reg); -+ } -+} -+ -+#define DPHY_TIMER_INCR 0x28 -+int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys, -+ struct ipu6_isys_csi2_config *cfg, -+ const struct ipu6_isys_csi2_timing *timing, -+ bool on) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ int ret = 0; -+ u32 nlanes; -+ u32 port; -+ -+ if (!on) -+ return 0; -+ -+ port = cfg->port; -+ nlanes = cfg->nlanes; -+ -+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) { -+ dev_warn(dev, "invalid port ID %d\n", port); -+ return -EINVAL; -+ } -+ -+ ipu6_isys_csi2_phy_config_by_port(isys, port, nlanes); -+ -+ writel(DPHY_TIMER_INCR, -+ isys->pdata->base + CSI2_HUB_GPREG_DPHY_TIMER_INCR); -+ -+ /* set port cfg and rx timing */ -+ ipu6_isys_csi2_set_timing(isys, timing, port, nlanes); -+ -+ ret = ipu6_isys_csi2_set_port_cfg(isys, port, nlanes); -+ if (ret) -+ return ret; -+ -+ ipu6_isys_csi2_rx_control(isys); -+ -+ return 0; -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c -new file mode 100644 -index 000000000000..9abf389a05f1 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c -@@ -0,0 +1,720 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/bits.h> -+#include <linux/container_of.h> -+#include <linux/device.h> -+#include <linux/iopoll.h> -+#include <linux/list.h> -+#include <linux/refcount.h> -+#include <linux/time64.h> -+ -+#include <media/v4l2-async.h> -+ -+#include "ipu6.h" -+#include "ipu6-bus.h" -+#include "ipu6-isys.h" -+#include "ipu6-isys-csi2.h" -+#include "ipu6-platform-isys-csi2-reg.h" -+ -+#define CSI_REG_HUB_GPREG_PHY_CTL(id) (CSI_REG_BASE + 0x18008 + (id) * 0x8) -+#define CSI_REG_HUB_GPREG_PHY_CTL_RESET BIT(4) -+#define CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN BIT(0) -+#define CSI_REG_HUB_GPREG_PHY_STATUS(id) (CSI_REG_BASE + 0x1800c + (id) * 0x8) -+#define CSI_REG_HUB_GPREG_PHY_POWER_ACK BIT(0) -+#define CSI_REG_HUB_GPREG_PHY_READY BIT(4) -+ -+#define MCD_PHY_POWER_STATUS_TIMEOUT (200 * USEC_PER_MSEC) -+ -+/* -+ * bridge to phy in buttress reg map, each phy has 16 kbytes -+ * only 2 phys for TGL U and Y -+ */ -+#define IPU6_ISYS_MCD_PHY_BASE(i) (0x10000 + (i) * 0x4000) -+ -+/* -+ * There are 2 MCD DPHY instances on TGL and 1 MCD DPHY instance on ADL. -+ * Each MCD PHY has 12-lanes which has 8 data lanes and 4 clock lanes. -+ * CSI port 1, 3 (5, 7) can support max 2 data lanes. -+ * CSI port 0, 2 (4, 6) can support max 4 data lanes. -+ * PHY configurations are PPI based instead of port. -+ * Left: -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | PPI | PPI5 | PPI4 | PPI3 | PPI2 | PPI1 | PPI0 | -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x4 | unused | D3 | D2 | C0 | D0 | D1 | -+ * |---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x2x2 | C1 | D0 | D1 | C0 | D0 | D1 | -+ * ----------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x2x1 | C1 | D0 | unused | C0 | D0 | D1 | -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x1x1 | C1 | D0 | unused | C0 | D0 | unused | -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x1x2 | C1 | D0 | D1 | C0 | D0 | unused | -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * -+ * Right: -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | PPI | PPI6 | PPI7 | PPI8 | PPI9 | PPI10 | PPI11 | -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x4 | D1 | D0 | C2 | D2 | D3 | unused | -+ * |---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x2x2 | D1 | D0 | C2 | D1 | D0 | C3 | -+ * ----------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x2x1 | D1 | D0 | C2 | unused | D0 | C3 | -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x1x1 | unused | D0 | C2 | unused | D0 | C3 | -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * | | | | | | | | -+ * | x1x2 | unused | D0 | C2 | D1 | D0 | C3 | -+ * +---------+---------+---------+---------+--------+---------+----------+ -+ * -+ * ppi mapping per phy : -+ * -+ * x4 + x4: -+ * Left : port0 - PPI range {0, 1, 2, 3, 4} -+ * Right: port2 - PPI range {6, 7, 8, 9, 10} -+ * -+ * x4 + x2x2: -+ * Left: port0 - PPI range {0, 1, 2, 3, 4} -+ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11} -+ * -+ * x2x2 + x4: -+ * Left: port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5} -+ * Right: port2 - PPI range {6, 7, 8, 9, 10} -+ * -+ * x2x2 + x2x2: -+ * Left : port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5} -+ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11} -+ */ -+ -+struct phy_reg { -+ u32 reg; -+ u32 val; -+}; -+ -+static const struct phy_reg common_init_regs[] = { -+ /* for TGL-U, use 0x80000000 */ -+ {0x00000040, 0x80000000}, -+ {0x00000044, 0x00a80880}, -+ {0x00000044, 0x00b80880}, -+ {0x00000010, 0x0000078c}, -+ {0x00000344, 0x2f4401e2}, -+ {0x00000544, 0x924401e2}, -+ {0x00000744, 0x594401e2}, -+ {0x00000944, 0x624401e2}, -+ {0x00000b44, 0xfc4401e2}, -+ {0x00000d44, 0xc54401e2}, -+ {0x00000f44, 0x034401e2}, -+ {0x00001144, 0x8f4401e2}, -+ {0x00001344, 0x754401e2}, -+ {0x00001544, 0xe94401e2}, -+ {0x00001744, 0xcb4401e2}, -+ {0x00001944, 0xfa4401e2} -+}; -+ -+static const struct phy_reg x1_port0_config_regs[] = { -+ {0x00000694, 0xc80060fa}, -+ {0x00000680, 0x3d4f78ea}, -+ {0x00000690, 0x10a0140b}, -+ {0x000006a8, 0xdf04010a}, -+ {0x00000700, 0x57050060}, -+ {0x00000710, 0x0030001c}, -+ {0x00000738, 0x5f004444}, -+ {0x0000073c, 0x78464204}, -+ {0x00000748, 0x7821f940}, -+ {0x0000074c, 0xb2000433}, -+ {0x00000494, 0xfe6030fa}, -+ {0x00000480, 0x29ef5ed0}, -+ {0x00000490, 0x10a0540b}, -+ {0x000004a8, 0x7a01010a}, -+ {0x00000500, 0xef053460}, -+ {0x00000510, 0xe030101c}, -+ {0x00000538, 0xdf808444}, -+ {0x0000053c, 0xc8422204}, -+ {0x00000540, 0x0180088c}, -+ {0x00000574, 0x00000000}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x1_port1_config_regs[] = { -+ {0x00000c94, 0xc80060fa}, -+ {0x00000c80, 0xcf47abea}, -+ {0x00000c90, 0x10a0840b}, -+ {0x00000ca8, 0xdf04010a}, -+ {0x00000d00, 0x57050060}, -+ {0x00000d10, 0x0030001c}, -+ {0x00000d38, 0x5f004444}, -+ {0x00000d3c, 0x78464204}, -+ {0x00000d48, 0x7821f940}, -+ {0x00000d4c, 0xb2000433}, -+ {0x00000a94, 0xc91030fa}, -+ {0x00000a80, 0x5a166ed0}, -+ {0x00000a90, 0x10a0540b}, -+ {0x00000aa8, 0x5d060100}, -+ {0x00000b00, 0xef053460}, -+ {0x00000b10, 0xa030101c}, -+ {0x00000b38, 0xdf808444}, -+ {0x00000b3c, 0xc8422204}, -+ {0x00000b40, 0x0180088c}, -+ {0x00000b74, 0x00000000}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x1_port2_config_regs[] = { -+ {0x00001294, 0x28f000fa}, -+ {0x00001280, 0x08130cea}, -+ {0x00001290, 0x10a0140b}, -+ {0x000012a8, 0xd704010a}, -+ {0x00001300, 0x8d050060}, -+ {0x00001310, 0x0030001c}, -+ {0x00001338, 0xdf008444}, -+ {0x0000133c, 0x78422204}, -+ {0x00001348, 0x7821f940}, -+ {0x0000134c, 0x5a000433}, -+ {0x00001094, 0x2d20b0fa}, -+ {0x00001080, 0xade75dd0}, -+ {0x00001090, 0x10a0540b}, -+ {0x000010a8, 0xb101010a}, -+ {0x00001100, 0x33053460}, -+ {0x00001110, 0x0030101c}, -+ {0x00001138, 0xdf808444}, -+ {0x0000113c, 0xc8422204}, -+ {0x00001140, 0x8180088c}, -+ {0x00001174, 0x00000000}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x1_port3_config_regs[] = { -+ {0x00001894, 0xc80060fa}, -+ {0x00001880, 0x0f90fd6a}, -+ {0x00001890, 0x10a0840b}, -+ {0x000018a8, 0xdf04010a}, -+ {0x00001900, 0x57050060}, -+ {0x00001910, 0x0030001c}, -+ {0x00001938, 0x5f004444}, -+ {0x0000193c, 0x78464204}, -+ {0x00001948, 0x7821f940}, -+ {0x0000194c, 0xb2000433}, -+ {0x00001694, 0x3050d0fa}, -+ {0x00001680, 0x0ef6d050}, -+ {0x00001690, 0x10a0540b}, -+ {0x000016a8, 0xe301010a}, -+ {0x00001700, 0x69053460}, -+ {0x00001710, 0xa030101c}, -+ {0x00001738, 0xdf808444}, -+ {0x0000173c, 0xc8422204}, -+ {0x00001740, 0x0180088c}, -+ {0x00001774, 0x00000000}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x2_port0_config_regs[] = { -+ {0x00000694, 0xc80060fa}, -+ {0x00000680, 0x3d4f78ea}, -+ {0x00000690, 0x10a0140b}, -+ {0x000006a8, 0xdf04010a}, -+ {0x00000700, 0x57050060}, -+ {0x00000710, 0x0030001c}, -+ {0x00000738, 0x5f004444}, -+ {0x0000073c, 0x78464204}, -+ {0x00000748, 0x7821f940}, -+ {0x0000074c, 0xb2000433}, -+ {0x00000494, 0xc80060fa}, -+ {0x00000480, 0x29ef5ed8}, -+ {0x00000490, 0x10a0540b}, -+ {0x000004a8, 0x7a01010a}, -+ {0x00000500, 0xef053460}, -+ {0x00000510, 0xe030101c}, -+ {0x00000538, 0xdf808444}, -+ {0x0000053c, 0xc8422204}, -+ {0x00000540, 0x0180088c}, -+ {0x00000574, 0x00000000}, -+ {0x00000294, 0xc80060fa}, -+ {0x00000280, 0xcb45b950}, -+ {0x00000290, 0x10a0540b}, -+ {0x000002a8, 0x8c01010a}, -+ {0x00000300, 0xef053460}, -+ {0x00000310, 0x8030101c}, -+ {0x00000338, 0x41808444}, -+ {0x0000033c, 0x32422204}, -+ {0x00000340, 0x0180088c}, -+ {0x00000374, 0x00000000}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x2_port1_config_regs[] = { -+ {0x00000c94, 0xc80060fa}, -+ {0x00000c80, 0xcf47abea}, -+ {0x00000c90, 0x10a0840b}, -+ {0x00000ca8, 0xdf04010a}, -+ {0x00000d00, 0x57050060}, -+ {0x00000d10, 0x0030001c}, -+ {0x00000d38, 0x5f004444}, -+ {0x00000d3c, 0x78464204}, -+ {0x00000d48, 0x7821f940}, -+ {0x00000d4c, 0xb2000433}, -+ {0x00000a94, 0xc80060fa}, -+ {0x00000a80, 0x5a166ed8}, -+ {0x00000a90, 0x10a0540b}, -+ {0x00000aa8, 0x7a01010a}, -+ {0x00000b00, 0xef053460}, -+ {0x00000b10, 0xa030101c}, -+ {0x00000b38, 0xdf808444}, -+ {0x00000b3c, 0xc8422204}, -+ {0x00000b40, 0x0180088c}, -+ {0x00000b74, 0x00000000}, -+ {0x00000894, 0xc80060fa}, -+ {0x00000880, 0x4d4f21d0}, -+ {0x00000890, 0x10a0540b}, -+ {0x000008a8, 0x5601010a}, -+ {0x00000900, 0xef053460}, -+ {0x00000910, 0x8030101c}, -+ {0x00000938, 0xdf808444}, -+ {0x0000093c, 0xc8422204}, -+ {0x00000940, 0x0180088c}, -+ {0x00000974, 0x00000000}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x2_port2_config_regs[] = { -+ {0x00001294, 0xc80060fa}, -+ {0x00001280, 0x08130cea}, -+ {0x00001290, 0x10a0140b}, -+ {0x000012a8, 0xd704010a}, -+ {0x00001300, 0x8d050060}, -+ {0x00001310, 0x0030001c}, -+ {0x00001338, 0xdf008444}, -+ {0x0000133c, 0x78422204}, -+ {0x00001348, 0x7821f940}, -+ {0x0000134c, 0x5a000433}, -+ {0x00001094, 0xc80060fa}, -+ {0x00001080, 0xade75dd8}, -+ {0x00001090, 0x10a0540b}, -+ {0x000010a8, 0xb101010a}, -+ {0x00001100, 0x33053460}, -+ {0x00001110, 0x0030101c}, -+ {0x00001138, 0xdf808444}, -+ {0x0000113c, 0xc8422204}, -+ {0x00001140, 0x8180088c}, -+ {0x00001174, 0x00000000}, -+ {0x00000e94, 0xc80060fa}, -+ {0x00000e80, 0x0fbf16d0}, -+ {0x00000e90, 0x10a0540b}, -+ {0x00000ea8, 0x7a01010a}, -+ {0x00000f00, 0xf5053460}, -+ {0x00000f10, 0xc030101c}, -+ {0x00000f38, 0xdf808444}, -+ {0x00000f3c, 0xc8422204}, -+ {0x00000f40, 0x8180088c}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x2_port3_config_regs[] = { -+ {0x00001894, 0xc80060fa}, -+ {0x00001880, 0x0f90fd6a}, -+ {0x00001890, 0x10a0840b}, -+ {0x000018a8, 0xdf04010a}, -+ {0x00001900, 0x57050060}, -+ {0x00001910, 0x0030001c}, -+ {0x00001938, 0x5f004444}, -+ {0x0000193c, 0x78464204}, -+ {0x00001948, 0x7821f940}, -+ {0x0000194c, 0xb2000433}, -+ {0x00001694, 0xc80060fa}, -+ {0x00001680, 0x0ef6d058}, -+ {0x00001690, 0x10a0540b}, -+ {0x000016a8, 0x7a01010a}, -+ {0x00001700, 0x69053460}, -+ {0x00001710, 0xa030101c}, -+ {0x00001738, 0xdf808444}, -+ {0x0000173c, 0xc8422204}, -+ {0x00001740, 0x0180088c}, -+ {0x00001774, 0x00000000}, -+ {0x00001494, 0xc80060fa}, -+ {0x00001480, 0xf9d34bd0}, -+ {0x00001490, 0x10a0540b}, -+ {0x000014a8, 0x7a01010a}, -+ {0x00001500, 0x1b053460}, -+ {0x00001510, 0x0030101c}, -+ {0x00001538, 0xdf808444}, -+ {0x0000153c, 0xc8422204}, -+ {0x00001540, 0x8180088c}, -+ {0x00001574, 0x00000000}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x4_port0_config_regs[] = { -+ {0x00000694, 0xc80060fa}, -+ {0x00000680, 0x3d4f78fa}, -+ {0x00000690, 0x10a0140b}, -+ {0x000006a8, 0xdf04010a}, -+ {0x00000700, 0x57050060}, -+ {0x00000710, 0x0030001c}, -+ {0x00000738, 0x5f004444}, -+ {0x0000073c, 0x78464204}, -+ {0x00000748, 0x7821f940}, -+ {0x0000074c, 0xb2000433}, -+ {0x00000494, 0xfe6030fa}, -+ {0x00000480, 0x29ef5ed8}, -+ {0x00000490, 0x10a0540b}, -+ {0x000004a8, 0x7a01010a}, -+ {0x00000500, 0xef053460}, -+ {0x00000510, 0xe030101c}, -+ {0x00000538, 0xdf808444}, -+ {0x0000053c, 0xc8422204}, -+ {0x00000540, 0x0180088c}, -+ {0x00000574, 0x00000004}, -+ {0x00000294, 0x23e030fa}, -+ {0x00000280, 0xcb45b950}, -+ {0x00000290, 0x10a0540b}, -+ {0x000002a8, 0x8c01010a}, -+ {0x00000300, 0xef053460}, -+ {0x00000310, 0x8030101c}, -+ {0x00000338, 0x41808444}, -+ {0x0000033c, 0x32422204}, -+ {0x00000340, 0x0180088c}, -+ {0x00000374, 0x00000004}, -+ {0x00000894, 0x5620b0fa}, -+ {0x00000880, 0x4d4f21dc}, -+ {0x00000890, 0x10a0540b}, -+ {0x000008a8, 0x5601010a}, -+ {0x00000900, 0xef053460}, -+ {0x00000910, 0x8030101c}, -+ {0x00000938, 0xdf808444}, -+ {0x0000093c, 0xc8422204}, -+ {0x00000940, 0x0180088c}, -+ {0x00000974, 0x00000004}, -+ {0x00000a94, 0xc91030fa}, -+ {0x00000a80, 0x5a166ecc}, -+ {0x00000a90, 0x10a0540b}, -+ {0x00000aa8, 0x5d01010a}, -+ {0x00000b00, 0xef053460}, -+ {0x00000b10, 0xa030101c}, -+ {0x00000b38, 0xdf808444}, -+ {0x00000b3c, 0xc8422204}, -+ {0x00000b40, 0x0180088c}, -+ {0x00000b74, 0x00000004}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x4_port1_config_regs[] = { -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x4_port2_config_regs[] = { -+ {0x00001294, 0x28f000fa}, -+ {0x00001280, 0x08130cfa}, -+ {0x00001290, 0x10c0140b}, -+ {0x000012a8, 0xd704010a}, -+ {0x00001300, 0x8d050060}, -+ {0x00001310, 0x0030001c}, -+ {0x00001338, 0xdf008444}, -+ {0x0000133c, 0x78422204}, -+ {0x00001348, 0x7821f940}, -+ {0x0000134c, 0x5a000433}, -+ {0x00001094, 0x2d20b0fa}, -+ {0x00001080, 0xade75dd8}, -+ {0x00001090, 0x10a0540b}, -+ {0x000010a8, 0xb101010a}, -+ {0x00001100, 0x33053460}, -+ {0x00001110, 0x0030101c}, -+ {0x00001138, 0xdf808444}, -+ {0x0000113c, 0xc8422204}, -+ {0x00001140, 0x8180088c}, -+ {0x00001174, 0x00000004}, -+ {0x00000e94, 0xd308d0fa}, -+ {0x00000e80, 0x0fbf16d0}, -+ {0x00000e90, 0x10a0540b}, -+ {0x00000ea8, 0x2c01010a}, -+ {0x00000f00, 0xf5053460}, -+ {0x00000f10, 0xc030101c}, -+ {0x00000f38, 0xdf808444}, -+ {0x00000f3c, 0xc8422204}, -+ {0x00000f40, 0x8180088c}, -+ {0x00000f74, 0x00000004}, -+ {0x00001494, 0x136850fa}, -+ {0x00001480, 0xf9d34bdc}, -+ {0x00001490, 0x10a0540b}, -+ {0x000014a8, 0x5a01010a}, -+ {0x00001500, 0x1b053460}, -+ {0x00001510, 0x0030101c}, -+ {0x00001538, 0xdf808444}, -+ {0x0000153c, 0xc8422204}, -+ {0x00001540, 0x8180088c}, -+ {0x00001574, 0x00000004}, -+ {0x00001694, 0x3050d0fa}, -+ {0x00001680, 0x0ef6d04c}, -+ {0x00001690, 0x10a0540b}, -+ {0x000016a8, 0xe301010a}, -+ {0x00001700, 0x69053460}, -+ {0x00001710, 0xa030101c}, -+ {0x00001738, 0xdf808444}, -+ {0x0000173c, 0xc8422204}, -+ {0x00001740, 0x0180088c}, -+ {0x00001774, 0x00000004}, -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg x4_port3_config_regs[] = { -+ {0x00000000, 0x00000000} -+}; -+ -+static const struct phy_reg *x1_config_regs[4] = { -+ x1_port0_config_regs, -+ x1_port1_config_regs, -+ x1_port2_config_regs, -+ x1_port3_config_regs -+}; -+ -+static const struct phy_reg *x2_config_regs[4] = { -+ x2_port0_config_regs, -+ x2_port1_config_regs, -+ x2_port2_config_regs, -+ x2_port3_config_regs -+}; -+ -+static const struct phy_reg *x4_config_regs[4] = { -+ x4_port0_config_regs, -+ x4_port1_config_regs, -+ x4_port2_config_regs, -+ x4_port3_config_regs -+}; -+ -+static const struct phy_reg **config_regs[3] = { -+ x1_config_regs, -+ x2_config_regs, -+ x4_config_regs -+}; -+ -+static int ipu6_isys_mcd_phy_powerup_ack(struct ipu6_isys *isys, u8 id) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ u32 val; -+ int ret; -+ -+ val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); -+ val |= CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN; -+ writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); -+ -+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id), -+ val, val & CSI_REG_HUB_GPREG_PHY_POWER_ACK, -+ 200, MCD_PHY_POWER_STATUS_TIMEOUT); -+ if (ret) -+ dev_err(dev, "PHY%d powerup ack timeout", id); -+ -+ return ret; -+} -+ -+static int ipu6_isys_mcd_phy_powerdown_ack(struct ipu6_isys *isys, u8 id) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ u32 val; -+ int ret; -+ -+ writel(0, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); -+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id), -+ val, !(val & CSI_REG_HUB_GPREG_PHY_POWER_ACK), -+ 200, MCD_PHY_POWER_STATUS_TIMEOUT); -+ if (ret) -+ dev_err(dev, "PHY%d powerdown ack timeout", id); -+ -+ return ret; -+} -+ -+static void ipu6_isys_mcd_phy_reset(struct ipu6_isys *isys, u8 id, bool assert) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ u32 val; -+ -+ val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); -+ if (assert) -+ val |= CSI_REG_HUB_GPREG_PHY_CTL_RESET; -+ else -+ val &= ~(CSI_REG_HUB_GPREG_PHY_CTL_RESET); -+ -+ writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id)); -+} -+ -+static int ipu6_isys_mcd_phy_ready(struct ipu6_isys *isys, u8 id) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ u32 val; -+ int ret; -+ -+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id), -+ val, val & CSI_REG_HUB_GPREG_PHY_READY, -+ 200, MCD_PHY_POWER_STATUS_TIMEOUT); -+ if (ret) -+ dev_err(dev, "PHY%d ready ack timeout", id); -+ -+ return ret; -+} -+ -+static void ipu6_isys_mcd_phy_common_init(struct ipu6_isys *isys) -+{ -+ struct ipu6_bus_device *adev = isys->adev; -+ struct ipu6_device *isp = adev->isp; -+ void __iomem *isp_base = isp->base; -+ struct sensor_async_sd *s_asd; -+ struct v4l2_async_connection *asc; -+ void __iomem *phy_base; -+ unsigned int i; -+ u8 phy_id; -+ -+ list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) { -+ s_asd = container_of(asc, struct sensor_async_sd, asc); -+ phy_id = s_asd->csi2.port / 4; -+ phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id); -+ -+ for (i = 0; i < ARRAY_SIZE(common_init_regs); i++) -+ writel(common_init_regs[i].val, -+ phy_base + common_init_regs[i].reg); -+ } -+} -+ -+static int ipu6_isys_driver_port_to_phy_port(struct ipu6_isys_csi2_config *cfg) -+{ -+ int phy_port; -+ int ret; -+ -+ if (!(cfg->nlanes == 4 || cfg->nlanes == 2 || cfg->nlanes == 1)) -+ return -EINVAL; -+ -+ /* B,F -> C0 A,E -> C1 C,G -> C2 D,H -> C4 */ -+ /* normalize driver port number */ -+ phy_port = cfg->port % 4; -+ -+ /* swap port number only for A and B */ -+ if (phy_port == 0) -+ phy_port = 1; -+ else if (phy_port == 1) -+ phy_port = 0; -+ -+ ret = phy_port; -+ -+ /* check validity per lane configuration */ -+ if (cfg->nlanes == 4 && !(phy_port == 0 || phy_port == 2)) -+ ret = -EINVAL; -+ else if ((cfg->nlanes == 2 || cfg->nlanes == 1) && -+ !(phy_port >= 0 && phy_port <= 3)) -+ ret = -EINVAL; -+ -+ return ret; -+} -+ -+static int ipu6_isys_mcd_phy_config(struct ipu6_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu6_bus_device *adev = isys->adev; -+ const struct phy_reg **phy_config_regs; -+ struct ipu6_device *isp = adev->isp; -+ void __iomem *isp_base = isp->base; -+ struct sensor_async_sd *s_asd; -+ struct ipu6_isys_csi2_config cfg; -+ struct v4l2_async_connection *asc; -+ u8 phy_port, phy_id; -+ unsigned int i; -+ void __iomem *phy_base; -+ -+ list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) { -+ s_asd = container_of(asc, struct sensor_async_sd, asc); -+ cfg.port = s_asd->csi2.port; -+ cfg.nlanes = s_asd->csi2.nlanes; -+ phy_port = ipu6_isys_driver_port_to_phy_port(&cfg); -+ if (phy_port < 0) { -+ dev_err(dev, "invalid port %d for lane %d", cfg.port, -+ cfg.nlanes); -+ return -ENXIO; -+ } -+ -+ phy_id = cfg.port / 4; -+ phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id); -+ dev_dbg(dev, "port%d PHY%u lanes %u\n", cfg.port, phy_id, -+ cfg.nlanes); -+ -+ phy_config_regs = config_regs[cfg.nlanes / 2]; -+ cfg.port = phy_port; -+ for (i = 0; phy_config_regs[cfg.port][i].reg; i++) -+ writel(phy_config_regs[cfg.port][i].val, -+ phy_base + phy_config_regs[cfg.port][i].reg); -+ } -+ -+ return 0; -+} -+ -+#define CSI_MCD_PHY_NUM 2 -+static refcount_t phy_power_ref_count[CSI_MCD_PHY_NUM]; -+ -+int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys, -+ struct ipu6_isys_csi2_config *cfg, -+ const struct ipu6_isys_csi2_timing *timing, -+ bool on) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ u8 port, phy_id; -+ refcount_t *ref; -+ int ret; -+ -+ port = cfg->port; -+ phy_id = port / 4; -+ -+ ref = &phy_power_ref_count[phy_id]; -+ -+ dev_dbg(dev, "for phy %d port %d, lanes: %d\n", phy_id, port, -+ cfg->nlanes); -+ -+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) { -+ dev_warn(dev, "invalid port ID %d\n", port); -+ return -EINVAL; -+ } -+ -+ if (on) { -+ if (refcount_read(ref)) { -+ dev_dbg(dev, "for phy %d is already UP", phy_id); -+ refcount_inc(ref); -+ return 0; -+ } -+ -+ ret = ipu6_isys_mcd_phy_powerup_ack(isys, phy_id); -+ if (ret) -+ return ret; -+ -+ ipu6_isys_mcd_phy_reset(isys, phy_id, 0); -+ ipu6_isys_mcd_phy_common_init(isys); -+ -+ ret = ipu6_isys_mcd_phy_config(isys); -+ if (ret) -+ return ret; -+ -+ ipu6_isys_mcd_phy_reset(isys, phy_id, 1); -+ ret = ipu6_isys_mcd_phy_ready(isys, phy_id); -+ if (ret) -+ return ret; -+ -+ refcount_set(ref, 1); -+ return 0; -+ } -+ -+ if (!refcount_dec_and_test(ref)) -+ return 0; -+ -+ return ipu6_isys_mcd_phy_powerdown_ack(isys, phy_id); -+} --- -2.43.2 - - -From 5d8544bd1d6dc7fce10a3bf01d10d926b8990b54 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:24 +0800 -Subject: [PATCH 17/33] media: intel/ipu6: add input system driver - -Input system driver do basic isys hardware setup and irq handling -and work with fwnode and v4l2 to register the ISYS v4l2 devices. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - drivers/media/pci/intel/ipu6/ipu6-isys.c | 1353 ++++++++++++++++++++++ - drivers/media/pci/intel/ipu6/ipu6-isys.h | 207 ++++ - 2 files changed, 1560 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c -new file mode 100644 -index 000000000000..e8983363a0da ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c -@@ -0,0 +1,1353 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/auxiliary_bus.h> -+#include <linux/bitfield.h> -+#include <linux/bits.h> -+#include <linux/completion.h> -+#include <linux/container_of.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/dma-mapping.h> -+#include <linux/err.h> -+#include <linux/firmware.h> -+#include <linux/io.h> -+#include <linux/irqreturn.h> -+#include <linux/list.h> -+#include <linux/module.h> -+#include <linux/mutex.h> -+#include <linux/pci.h> -+#include <linux/pm_runtime.h> -+#include <linux/pm_qos.h> -+#include <linux/slab.h> -+#include <linux/spinlock.h> -+#include <linux/string.h> -+ -+#include <media/ipu-bridge.h> -+#include <media/media-device.h> -+#include <media/media-entity.h> -+#include <media/v4l2-async.h> -+#include <media/v4l2-device.h> -+#include <media/v4l2-fwnode.h> -+ -+#include "ipu6-bus.h" -+#include "ipu6-cpd.h" -+#include "ipu6-isys.h" -+#include "ipu6-isys-csi2.h" -+#include "ipu6-mmu.h" -+#include "ipu6-platform-buttress-regs.h" -+#include "ipu6-platform-isys-csi2-reg.h" -+#include "ipu6-platform-regs.h" -+ -+#define IPU6_BUTTRESS_FABIC_CONTROL 0x68 -+#define GDA_ENABLE_IWAKE_INDEX 2 -+#define GDA_IWAKE_THRESHOLD_INDEX 1 -+#define GDA_IRQ_CRITICAL_THRESHOLD_INDEX 0 -+#define GDA_MEMOPEN_THRESHOLD_INDEX 3 -+#define DEFAULT_DID_RATIO 90 -+#define DEFAULT_IWAKE_THRESHOLD 0x42 -+#define DEFAULT_MEM_OPEN_TIME 10 -+#define ONE_THOUSAND_MICROSECOND 1000 -+/* One page is 2KB, 8 x 16 x 16 = 2048B = 2KB */ -+#define ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE 0x800 -+ -+/* LTR & DID value are 10 bit at most */ -+#define LTR_DID_VAL_MAX 1023 -+#define LTR_DEFAULT_VALUE 0x70503c19 -+#define FILL_TIME_DEFAULT_VALUE 0xfff0783c -+#define LTR_DID_PKGC_2R 20 -+#define LTR_SCALE_DEFAULT 5 -+#define LTR_SCALE_1024NS 2 -+#define DID_SCALE_1US 2 -+#define DID_SCALE_32US 3 -+#define REG_PKGC_PMON_CFG 0xb00 -+ -+#define VAL_PKGC_PMON_CFG_RESET 0x38 -+#define VAL_PKGC_PMON_CFG_START 0x7 -+ -+#define IS_PIXEL_BUFFER_PAGES 0x80 -+/* -+ * when iwake mode is disabled, the critical threshold is statically set -+ * to 75% of the IS pixel buffer, criticalThreshold = (128 * 3) / 4 -+ */ -+#define CRITICAL_THRESHOLD_IWAKE_DISABLE (IS_PIXEL_BUFFER_PAGES * 3 / 4) -+ -+union fabric_ctrl { -+ struct { -+ u16 ltr_val : 10; -+ u16 ltr_scale : 3; -+ u16 reserved : 3; -+ u16 did_val : 10; -+ u16 did_scale : 3; -+ u16 reserved2 : 1; -+ u16 keep_power_in_D0 : 1; -+ u16 keep_power_override : 1; -+ } bits; -+ u32 value; -+}; -+ -+enum ltr_did_type { -+ LTR_IWAKE_ON, -+ LTR_IWAKE_OFF, -+ LTR_ISYS_ON, -+ LTR_ISYS_OFF, -+ LTR_ENHANNCE_IWAKE, -+ LTR_TYPE_MAX -+}; -+ -+#define ISYS_PM_QOS_VALUE 300 -+ -+static int isys_isr_one(struct ipu6_bus_device *adev); -+ -+static int -+isys_complete_ext_device_registration(struct ipu6_isys *isys, -+ struct v4l2_subdev *sd, -+ struct ipu6_isys_csi2_config *csi2) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < sd->entity.num_pads; i++) { -+ if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) -+ break; -+ } -+ -+ if (i == sd->entity.num_pads) { -+ dev_warn(dev, "no src pad in external entity\n"); -+ ret = -ENOENT; -+ goto unregister_subdev; -+ } -+ -+ ret = media_create_pad_link(&sd->entity, i, -+ &isys->csi2[csi2->port].asd.sd.entity, -+ 0, 0); -+ if (ret) { -+ dev_warn(dev, "can't create link\n"); -+ goto unregister_subdev; -+ } -+ -+ isys->csi2[csi2->port].nlanes = csi2->nlanes; -+ -+ return 0; -+ -+unregister_subdev: -+ v4l2_device_unregister_subdev(sd); -+ -+ return ret; -+} -+ -+static void isys_stream_init(struct ipu6_isys *isys) -+{ -+ u32 i; -+ -+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { -+ mutex_init(&isys->streams[i].mutex); -+ init_completion(&isys->streams[i].stream_open_completion); -+ init_completion(&isys->streams[i].stream_close_completion); -+ init_completion(&isys->streams[i].stream_start_completion); -+ init_completion(&isys->streams[i].stream_stop_completion); -+ INIT_LIST_HEAD(&isys->streams[i].queues); -+ isys->streams[i].isys = isys; -+ isys->streams[i].stream_handle = i; -+ isys->streams[i].vc = INVALID_VC_ID; -+ } -+} -+ -+static void isys_csi2_unregister_subdevices(struct ipu6_isys *isys) -+{ -+ const struct ipu6_isys_internal_csi2_pdata *csi2 = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i; -+ -+ for (i = 0; i < csi2->nports; i++) -+ ipu6_isys_csi2_cleanup(&isys->csi2[i]); -+} -+ -+static int isys_csi2_register_subdevices(struct ipu6_isys *isys) -+{ -+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ int ret; -+ -+ isys->csi2 = devm_kcalloc(dev, csi2_pdata->nports, -+ sizeof(*isys->csi2), GFP_KERNEL); -+ if (!isys->csi2) -+ return -ENOMEM; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ ret = ipu6_isys_csi2_init(&isys->csi2[i], isys, -+ isys->pdata->base + -+ csi2_pdata->offsets[i], i); -+ if (ret) -+ goto fail; -+ -+ isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i); -+ } -+ -+ return 0; -+ -+fail: -+ while (i--) -+ ipu6_isys_csi2_cleanup(&isys->csi2[i]); -+ -+ return ret; -+} -+ -+static int isys_csi2_create_media_links(struct ipu6_isys *isys) -+{ -+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i, j, k; -+ int ret; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ struct media_entity *sd = &isys->csi2[i].asd.sd.entity; -+ -+ for (j = 0; j < NR_OF_VIDEO_DEVICE; j++) { -+ struct media_entity *v = &isys->av[j].vdev.entity; -+ u32 flag = MEDIA_LNK_FL_DYNAMIC; -+ -+ for (k = CSI2_PAD_SRC; k < NR_OF_CSI2_PADS; k++) { -+ ret = media_create_pad_link(sd, k, v, 0, flag); -+ if (ret) { -+ dev_err(dev, "CSI2 can't create link\n"); -+ return ret; -+ } -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static void isys_unregister_video_devices(struct ipu6_isys *isys) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < NR_OF_VIDEO_DEVICE; i++) -+ ipu6_isys_video_cleanup(&isys->av[i]); -+} -+ -+static int isys_register_video_devices(struct ipu6_isys *isys) -+{ -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < NR_OF_VIDEO_DEVICE; i++) { -+ snprintf(isys->av[i].vdev.name, sizeof(isys->av[i].vdev.name), -+ IPU6_ISYS_ENTITY_PREFIX " ISYS Capture %u", i); -+ isys->av[i].isys = isys; -+ isys->av[i].aq.vbq.buf_struct_size = -+ sizeof(struct ipu6_isys_video_buffer); -+ isys->av[i].pfmt = &ipu6_isys_pfmts[0]; -+ -+ ret = ipu6_isys_video_init(&isys->av[i]); -+ if (ret) -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ while (i--) -+ ipu6_isys_video_cleanup(&isys->av[i]); -+ -+ return ret; -+} -+ -+void isys_setup_hw(struct ipu6_isys *isys) -+{ -+ void __iomem *base = isys->pdata->base; -+ const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold; -+ u32 irqs = 0; -+ unsigned int i, nports; -+ -+ nports = isys->pdata->ipdata->csi2.nports; -+ -+ /* Enable irqs for all MIPI ports */ -+ for (i = 0; i < nports; i++) -+ irqs |= IPU6_ISYS_UNISPART_IRQ_CSI2(i); -+ -+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_edge); -+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_lnp); -+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_mask); -+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_enable); -+ writel(GENMASK(19, 0), -+ base + isys->pdata->ipdata->csi2.ctrl0_irq_clear); -+ -+ irqs = ISYS_UNISPART_IRQS; -+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_EDGE); -+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE); -+ writel(GENMASK(28, 0), base + IPU6_REG_ISYS_UNISPART_IRQ_CLEAR); -+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); -+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_ENABLE); -+ -+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG); -+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG); -+ -+ /* Write CDC FIFO threshold values for isys */ -+ for (i = 0; i < isys->pdata->ipdata->hw_variant.cdc_fifos; i++) -+ writel(thd[i], base + IPU6_REG_ISYS_CDC_THRESHOLD(i)); -+} -+ -+static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2) -+{ -+ struct ipu6_isys_stream *stream; -+ unsigned int i; -+ u32 status; -+ int source; -+ -+ ipu6_isys_register_errors(csi2); -+ -+ status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); -+ -+ writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + -+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); -+ -+ source = csi2->asd.source; -+ for (i = 0; i < NR_OF_CSI2_VC; i++) { -+ if (status & IPU_CSI_RX_IRQ_FS_VC(i)) { -+ stream = ipu6_isys_query_stream_by_source(csi2->isys, -+ source, i); -+ if (stream) { -+ ipu6_isys_csi2_sof_event_by_stream(stream); -+ ipu6_isys_put_stream(stream); -+ } -+ } -+ -+ if (status & IPU_CSI_RX_IRQ_FE_VC(i)) { -+ stream = ipu6_isys_query_stream_by_source(csi2->isys, -+ source, i); -+ if (stream) { -+ ipu6_isys_csi2_eof_event_by_stream(stream); -+ ipu6_isys_put_stream(stream); -+ } -+ } -+ } -+} -+ -+irqreturn_t isys_isr(struct ipu6_bus_device *adev) -+{ -+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); -+ void __iomem *base = isys->pdata->base; -+ u32 status_sw, status_csi; -+ u32 ctrl0_status, ctrl0_clear; -+ -+ spin_lock(&isys->power_lock); -+ if (!isys->power) { -+ spin_unlock(&isys->power_lock); -+ return IRQ_NONE; -+ } -+ -+ ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status; -+ ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear; -+ -+ status_csi = readl(isys->pdata->base + ctrl0_status); -+ status_sw = readl(isys->pdata->base + -+ IPU6_REG_ISYS_UNISPART_IRQ_STATUS); -+ -+ writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW, -+ base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); -+ -+ do { -+ writel(status_csi, isys->pdata->base + ctrl0_clear); -+ -+ writel(status_sw, isys->pdata->base + -+ IPU6_REG_ISYS_UNISPART_IRQ_CLEAR); -+ -+ if (isys->isr_csi2_bits & status_csi) { -+ unsigned int i; -+ -+ for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { -+ /* irq from not enabled port */ -+ if (!isys->csi2[i].base) -+ continue; -+ if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i)) -+ ipu6_isys_csi2_isr(&isys->csi2[i]); -+ } -+ } -+ -+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG); -+ -+ if (!isys_isr_one(adev)) -+ status_sw = IPU6_ISYS_UNISPART_IRQ_SW; -+ else -+ status_sw = 0; -+ -+ status_csi = readl(isys->pdata->base + ctrl0_status); -+ status_sw |= readl(isys->pdata->base + -+ IPU6_REG_ISYS_UNISPART_IRQ_STATUS); -+ } while ((status_csi & isys->isr_csi2_bits) || -+ (status_sw & IPU6_ISYS_UNISPART_IRQ_SW)); -+ -+ writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); -+ -+ spin_unlock(&isys->power_lock); -+ -+ return IRQ_HANDLED; -+} -+ -+static void get_lut_ltrdid(struct ipu6_isys *isys, struct ltr_did *pltr_did) -+{ -+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; -+ struct ltr_did ltrdid_default; -+ -+ ltrdid_default.lut_ltr.value = LTR_DEFAULT_VALUE; -+ ltrdid_default.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE; -+ -+ if (iwake_watermark->ltrdid.lut_ltr.value) -+ *pltr_did = iwake_watermark->ltrdid; -+ else -+ *pltr_did = ltrdid_default; -+} -+ -+static int set_iwake_register(struct ipu6_isys *isys, u32 index, u32 value) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 req_id = index; -+ u32 offset = 0; -+ int ret; -+ -+ ret = ipu6_fw_isys_send_proxy_token(isys, req_id, index, offset, value); -+ if (ret) -+ dev_err(dev, "write %d failed %d", index, ret); -+ -+ return ret; -+} -+ -+/* -+ * When input system is powered up and before enabling any new sensor capture, -+ * or after disabling any sensor capture the following values need to be set: -+ * LTR_value = LTR(usec) from calculation; -+ * LTR_scale = 2; -+ * DID_value = DID(usec) from calculation; -+ * DID_scale = 2; -+ * -+ * When input system is powered down, the LTR and DID values -+ * must be returned to the default values: -+ * LTR_value = 1023; -+ * LTR_scale = 5; -+ * DID_value = 1023; -+ * DID_scale = 2; -+ */ -+static void set_iwake_ltrdid(struct ipu6_isys *isys, u16 ltr, u16 did, -+ enum ltr_did_type use) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ u16 ltr_val, ltr_scale = LTR_SCALE_1024NS; -+ u16 did_val, did_scale = DID_SCALE_1US; -+ struct ipu6_device *isp = isys->adev->isp; -+ union fabric_ctrl fc; -+ -+ switch (use) { -+ case LTR_IWAKE_ON: -+ ltr_val = min_t(u16, ltr, (u16)LTR_DID_VAL_MAX); -+ did_val = min_t(u16, did, (u16)LTR_DID_VAL_MAX); -+ ltr_scale = (ltr == LTR_DID_VAL_MAX && -+ did == LTR_DID_VAL_MAX) ? -+ LTR_SCALE_DEFAULT : LTR_SCALE_1024NS; -+ break; -+ case LTR_ISYS_ON: -+ case LTR_IWAKE_OFF: -+ ltr_val = LTR_DID_PKGC_2R; -+ did_val = LTR_DID_PKGC_2R; -+ break; -+ case LTR_ISYS_OFF: -+ ltr_val = LTR_DID_VAL_MAX; -+ did_val = LTR_DID_VAL_MAX; -+ ltr_scale = LTR_SCALE_DEFAULT; -+ break; -+ case LTR_ENHANNCE_IWAKE: -+ if (ltr == LTR_DID_VAL_MAX && did == LTR_DID_VAL_MAX) { -+ ltr_val = LTR_DID_VAL_MAX; -+ did_val = LTR_DID_VAL_MAX; -+ ltr_scale = LTR_SCALE_DEFAULT; -+ } else if (did < ONE_THOUSAND_MICROSECOND) { -+ ltr_val = ltr; -+ did_val = did; -+ } else { -+ ltr_val = ltr; -+ /* div 90% value by 32 to account for scale change */ -+ did_val = did / 32; -+ did_scale = DID_SCALE_32US; -+ } -+ break; -+ default: -+ ltr_val = LTR_DID_VAL_MAX; -+ did_val = LTR_DID_VAL_MAX; -+ ltr_scale = LTR_SCALE_DEFAULT; -+ break; -+ } -+ -+ fc.value = readl(isp->base + IPU6_BUTTRESS_FABIC_CONTROL); -+ fc.bits.ltr_val = ltr_val; -+ fc.bits.ltr_scale = ltr_scale; -+ fc.bits.did_val = did_val; -+ fc.bits.did_scale = did_scale; -+ -+ dev_dbg(dev, "ltr: value %u scale %u, did: value %u scale %u\n", -+ ltr_val, ltr_scale, did_val, did_scale); -+ writel(fc.value, isp->base + IPU6_BUTTRESS_FABIC_CONTROL); -+} -+ -+/* -+ * Driver may clear register GDA_ENABLE_IWAKE before FW configures the -+ * stream for debug purpose. Otherwise driver should not access this register. -+ */ -+static void enable_iwake(struct ipu6_isys *isys, bool enable) -+{ -+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; -+ int ret; -+ -+ mutex_lock(&iwake_watermark->mutex); -+ -+ if (iwake_watermark->iwake_enabled == enable) { -+ mutex_unlock(&iwake_watermark->mutex); -+ return; -+ } -+ -+ ret = set_iwake_register(isys, GDA_ENABLE_IWAKE_INDEX, enable); -+ if (!ret) -+ iwake_watermark->iwake_enabled = enable; -+ -+ mutex_unlock(&iwake_watermark->mutex); -+} -+ -+void update_watermark_setting(struct ipu6_isys *isys) -+{ -+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; -+ u32 iwake_threshold, iwake_critical_threshold, page_num; -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 calc_fill_time_us = 0, ltr = 0, did = 0; -+ struct video_stream_watermark *p_watermark; -+ enum ltr_did_type ltr_did_type; -+ struct list_head *stream_node; -+ u64 isys_pb_datarate_mbs = 0; -+ u32 mem_open_threshold = 0; -+ struct ltr_did ltrdid; -+ u64 threshold_bytes; -+ u32 max_sram_size; -+ u32 shift; -+ -+ shift = isys->pdata->ipdata->sram_gran_shift; -+ max_sram_size = isys->pdata->ipdata->max_sram_size; -+ -+ mutex_lock(&iwake_watermark->mutex); -+ if (iwake_watermark->force_iwake_disable) { -+ set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF); -+ set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, -+ CRITICAL_THRESHOLD_IWAKE_DISABLE); -+ goto unlock_exit; -+ } -+ -+ if (list_empty(&iwake_watermark->video_list)) { -+ isys_pb_datarate_mbs = 0; -+ } else { -+ list_for_each(stream_node, &iwake_watermark->video_list) { -+ p_watermark = list_entry(stream_node, -+ struct video_stream_watermark, -+ stream_node); -+ isys_pb_datarate_mbs += p_watermark->stream_data_rate; -+ } -+ } -+ mutex_unlock(&iwake_watermark->mutex); -+ -+ if (!isys_pb_datarate_mbs) { -+ enable_iwake(isys, false); -+ set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF); -+ mutex_lock(&iwake_watermark->mutex); -+ set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, -+ CRITICAL_THRESHOLD_IWAKE_DISABLE); -+ goto unlock_exit; -+ } -+ -+ enable_iwake(isys, true); -+ calc_fill_time_us = max_sram_size / isys_pb_datarate_mbs; -+ -+ if (isys->pdata->ipdata->enhanced_iwake) { -+ ltr = isys->pdata->ipdata->ltr; -+ did = calc_fill_time_us * DEFAULT_DID_RATIO / 100; -+ ltr_did_type = LTR_ENHANNCE_IWAKE; -+ } else { -+ get_lut_ltrdid(isys, <rdid); -+ -+ if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th0) -+ ltr = 0; -+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th1) -+ ltr = ltrdid.lut_ltr.bits.val0; -+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th2) -+ ltr = ltrdid.lut_ltr.bits.val1; -+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th3) -+ ltr = ltrdid.lut_ltr.bits.val2; -+ else -+ ltr = ltrdid.lut_ltr.bits.val3; -+ -+ did = calc_fill_time_us - ltr; -+ ltr_did_type = LTR_IWAKE_ON; -+ } -+ -+ set_iwake_ltrdid(isys, ltr, did, ltr_did_type); -+ -+ /* calculate iwake threshold with 2KB granularity pages */ -+ threshold_bytes = did * isys_pb_datarate_mbs; -+ iwake_threshold = max_t(u32, 1, threshold_bytes >> shift); -+ iwake_threshold = min_t(u32, iwake_threshold, max_sram_size); -+ -+ mutex_lock(&iwake_watermark->mutex); -+ if (isys->pdata->ipdata->enhanced_iwake) { -+ set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX, -+ DEFAULT_IWAKE_THRESHOLD); -+ /* calculate number of pages that will be filled in 10 usec */ -+ page_num = (DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) / -+ ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE; -+ page_num += ((DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) % -+ ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE) ? 1 : 0; -+ mem_open_threshold = isys->pdata->ipdata->memopen_threshold; -+ mem_open_threshold = max_t(u32, mem_open_threshold, page_num); -+ dev_dbg(dev, "mem_open_threshold: %u\n", mem_open_threshold); -+ set_iwake_register(isys, GDA_MEMOPEN_THRESHOLD_INDEX, -+ mem_open_threshold); -+ } else { -+ set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX, -+ iwake_threshold); -+ } -+ -+ iwake_critical_threshold = iwake_threshold + -+ (IS_PIXEL_BUFFER_PAGES - iwake_threshold) / 2; -+ -+ dev_dbg(dev, "threshold: %u critical: %u\n", iwake_threshold, -+ iwake_critical_threshold); -+ -+ set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, -+ iwake_critical_threshold); -+ -+ writel(VAL_PKGC_PMON_CFG_RESET, -+ isys->adev->isp->base + REG_PKGC_PMON_CFG); -+ writel(VAL_PKGC_PMON_CFG_START, -+ isys->adev->isp->base + REG_PKGC_PMON_CFG); -+unlock_exit: -+ mutex_unlock(&iwake_watermark->mutex); -+} -+ -+static void isys_iwake_watermark_init(struct ipu6_isys *isys) -+{ -+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; -+ -+ INIT_LIST_HEAD(&iwake_watermark->video_list); -+ mutex_init(&iwake_watermark->mutex); -+ -+ iwake_watermark->ltrdid.lut_ltr.value = 0; -+ iwake_watermark->isys = isys; -+ iwake_watermark->iwake_enabled = false; -+ iwake_watermark->force_iwake_disable = false; -+} -+ -+static void isys_iwake_watermark_cleanup(struct ipu6_isys *isys) -+{ -+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; -+ -+ mutex_lock(&iwake_watermark->mutex); -+ list_del(&iwake_watermark->video_list); -+ mutex_unlock(&iwake_watermark->mutex); -+ -+ mutex_destroy(&iwake_watermark->mutex); -+} -+ -+/* The .bound() notifier callback when a match is found */ -+static int isys_notifier_bound(struct v4l2_async_notifier *notifier, -+ struct v4l2_subdev *sd, -+ struct v4l2_async_connection *asc) -+{ -+ struct ipu6_isys *isys = -+ container_of(notifier, struct ipu6_isys, notifier); -+ struct sensor_async_sd *s_asd = -+ container_of(asc, struct sensor_async_sd, asc); -+ int ret; -+ -+ ret = ipu_bridge_instantiate_vcm(sd->dev); -+ if (ret) { -+ dev_err(&isys->adev->auxdev.dev, "instantiate vcm failed\n"); -+ return ret; -+ } -+ -+ dev_dbg(&isys->adev->auxdev.dev, "bind %s nlanes is %d port is %d\n", -+ sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); -+ ret = isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); -+ if (ret) -+ return ret; -+ -+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+} -+ -+static int isys_notifier_complete(struct v4l2_async_notifier *notifier) -+{ -+ struct ipu6_isys *isys = -+ container_of(notifier, struct ipu6_isys, notifier); -+ -+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+} -+ -+static const struct v4l2_async_notifier_operations isys_async_ops = { -+ .bound = isys_notifier_bound, -+ .complete = isys_notifier_complete, -+}; -+ -+#define ISYS_MAX_PORTS 8 -+static int isys_notifier_init(struct ipu6_isys *isys) -+{ -+ struct ipu6_device *isp = isys->adev->isp; -+ struct device *dev = &isp->pdev->dev; -+ unsigned int i; -+ int ret; -+ -+ v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev); -+ -+ for (i = 0; i < ISYS_MAX_PORTS; i++) { -+ struct v4l2_fwnode_endpoint vep = { -+ .bus_type = V4L2_MBUS_CSI2_DPHY -+ }; -+ struct sensor_async_sd *s_asd; -+ struct fwnode_handle *ep; -+ -+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, -+ FWNODE_GRAPH_ENDPOINT_NEXT); -+ if (!ep) -+ continue; -+ -+ ret = v4l2_fwnode_endpoint_parse(ep, &vep); -+ if (ret) { -+ dev_err(dev, "fwnode endpoint parse failed: %d\n", ret); -+ goto err_parse; -+ } -+ -+ s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep, -+ struct sensor_async_sd); -+ if (IS_ERR(s_asd)) { -+ ret = PTR_ERR(s_asd); -+ dev_err(dev, "add remove fwnode failed: %d\n", ret); -+ goto err_parse; -+ } -+ -+ s_asd->csi2.port = vep.base.port; -+ s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; -+ -+ dev_dbg(dev, "remote endpoint port %d with %d lanes added\n", -+ s_asd->csi2.port, s_asd->csi2.nlanes); -+ -+ fwnode_handle_put(ep); -+ -+ continue; -+ -+err_parse: -+ fwnode_handle_put(ep); -+ return ret; -+ } -+ -+ isys->notifier.ops = &isys_async_ops; -+ ret = v4l2_async_nf_register(&isys->notifier); -+ if (ret) { -+ dev_err(dev, "failed to register async notifier : %d\n", ret); -+ v4l2_async_nf_cleanup(&isys->notifier); -+ } -+ -+ return ret; -+} -+ -+static void isys_notifier_cleanup(struct ipu6_isys *isys) -+{ -+ v4l2_async_nf_unregister(&isys->notifier); -+ v4l2_async_nf_cleanup(&isys->notifier); -+} -+ -+static int isys_register_devices(struct ipu6_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct pci_dev *pdev = isys->adev->isp->pdev; -+ int ret; -+ -+ isys->media_dev.dev = dev; -+ media_device_pci_init(&isys->media_dev, -+ pdev, IPU6_MEDIA_DEV_MODEL_NAME); -+ -+ strscpy(isys->v4l2_dev.name, isys->media_dev.model, -+ sizeof(isys->v4l2_dev.name)); -+ -+ ret = media_device_register(&isys->media_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ isys->v4l2_dev.mdev = &isys->media_dev; -+ isys->v4l2_dev.ctrl_handler = NULL; -+ -+ ret = v4l2_device_register(dev->parent, &isys->v4l2_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ ret = isys_register_video_devices(isys); -+ if (ret) -+ goto out_v4l2_device_unregister; -+ -+ ret = isys_csi2_register_subdevices(isys); -+ if (ret) -+ goto out_isys_unregister_video_device; -+ -+ ret = isys_csi2_create_media_links(isys); -+ if (ret) -+ goto out_isys_unregister_subdevices; -+ -+ ret = isys_notifier_init(isys); -+ if (ret) -+ goto out_isys_unregister_subdevices; -+ -+ return 0; -+ -+out_isys_unregister_subdevices: -+ isys_csi2_unregister_subdevices(isys); -+ -+out_isys_unregister_video_device: -+ isys_unregister_video_devices(isys); -+ -+out_v4l2_device_unregister: -+ v4l2_device_unregister(&isys->v4l2_dev); -+ -+out_media_device_unregister: -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+ -+ dev_err(dev, "failed to register isys devices\n"); -+ -+ return ret; -+} -+ -+static void isys_unregister_devices(struct ipu6_isys *isys) -+{ -+ isys_unregister_video_devices(isys); -+ isys_csi2_unregister_subdevices(isys); -+ v4l2_device_unregister(&isys->v4l2_dev); -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+} -+ -+static int isys_runtime_pm_resume(struct device *dev) -+{ -+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); -+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); -+ struct ipu6_device *isp = adev->isp; -+ unsigned long flags; -+ int ret; -+ -+ if (!isys) -+ return 0; -+ -+ ret = ipu6_mmu_hw_init(adev->mmu); -+ if (ret) -+ return ret; -+ -+ cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); -+ -+ ret = ipu6_buttress_start_tsc_sync(isp); -+ if (ret) -+ return ret; -+ -+ spin_lock_irqsave(&isys->power_lock, flags); -+ isys->power = 1; -+ spin_unlock_irqrestore(&isys->power_lock, flags); -+ -+ isys_setup_hw(isys); -+ -+ set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON); -+ -+ return 0; -+} -+ -+static int isys_runtime_pm_suspend(struct device *dev) -+{ -+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); -+ struct ipu6_isys *isys; -+ unsigned long flags; -+ -+ isys = dev_get_drvdata(dev); -+ if (!isys) -+ return 0; -+ -+ spin_lock_irqsave(&isys->power_lock, flags); -+ isys->power = 0; -+ spin_unlock_irqrestore(&isys->power_lock, flags); -+ -+ mutex_lock(&isys->mutex); -+ isys->need_reset = false; -+ mutex_unlock(&isys->mutex); -+ -+ isys->phy_termcal_val = 0; -+ cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -+ -+ set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF); -+ -+ ipu6_mmu_hw_cleanup(adev->mmu); -+ -+ return 0; -+} -+ -+static int isys_suspend(struct device *dev) -+{ -+ struct ipu6_isys *isys = dev_get_drvdata(dev); -+ -+ /* If stream is open, refuse to suspend */ -+ if (isys->stream_opened) -+ return -EBUSY; -+ -+ return 0; -+} -+ -+static int isys_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static const struct dev_pm_ops isys_pm_ops = { -+ .runtime_suspend = isys_runtime_pm_suspend, -+ .runtime_resume = isys_runtime_pm_resume, -+ .suspend = isys_suspend, -+ .resume = isys_resume, -+}; -+ -+static void isys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); -+ struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev); -+ struct ipu6_device *isp = adev->isp; -+ struct isys_fw_msgs *fwmsg, *safe; -+ unsigned int i; -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -+ dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -+ dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ isys_unregister_devices(isys); -+ isys_notifier_cleanup(isys); -+ -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ if (!isp->secure_mode) { -+ ipu6_cpd_free_pkg_dir(adev); -+ ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); -+ release_firmware(adev->fw); -+ } -+ -+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ isys_iwake_watermark_cleanup(isys); -+ mutex_destroy(&isys->stream_mutex); -+ mutex_destroy(&isys->mutex); -+} -+ -+static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct isys_fw_msgs *addr; -+ dma_addr_t dma_addr; -+ unsigned long flags; -+ unsigned int i; -+ -+ for (i = 0; i < amount; i++) { -+ addr = dma_alloc_attrs(dev, sizeof(struct isys_fw_msgs), -+ &dma_addr, GFP_KERNEL, 0); -+ if (!addr) -+ break; -+ addr->dma_addr = dma_addr; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ list_add(&addr->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ } -+ -+ if (i == amount) -+ return 0; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ while (!list_empty(&isys->framebuflist)) { -+ addr = list_first_entry(&isys->framebuflist, -+ struct isys_fw_msgs, head); -+ list_del(&addr->head); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ dma_free_attrs(dev, sizeof(struct isys_fw_msgs), addr, -+ addr->dma_addr, 0); -+ spin_lock_irqsave(&isys->listlock, flags); -+ } -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ -+ return -ENOMEM; -+} -+ -+struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream) -+{ -+ struct ipu6_isys *isys = stream->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct isys_fw_msgs *msg; -+ unsigned long flags; -+ int ret; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ if (list_empty(&isys->framebuflist)) { -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ dev_dbg(dev, "Frame list empty\n"); -+ -+ ret = alloc_fw_msg_bufs(isys, 5); -+ if (ret < 0) -+ return NULL; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ if (list_empty(&isys->framebuflist)) { -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ dev_err(dev, "Frame list empty\n"); -+ return NULL; -+ } -+ } -+ msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); -+ list_move(&msg->head, &isys->framebuflist_fw); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); -+ -+ return msg; -+} -+ -+void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys) -+{ -+ struct isys_fw_msgs *fwmsg, *fwmsg0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) -+ list_move(&fwmsg->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+} -+ -+void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data) -+{ -+ struct isys_fw_msgs *msg; -+ unsigned long flags; -+ u64 *ptr = (u64 *)data; -+ -+ if (!ptr) -+ return; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); -+ list_move(&msg->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+} -+ -+static int isys_probe(struct auxiliary_device *auxdev, -+ const struct auxiliary_device_id *auxdev_id) -+{ -+ struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); -+ struct ipu6_device *isp = adev->isp; -+ const struct firmware *fw; -+ struct ipu6_isys *isys; -+ unsigned int i; -+ int ret; -+ -+ if (!isp->bus_ready_to_probe) -+ return -EPROBE_DEFER; -+ -+ isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL); -+ if (!isys) -+ return -ENOMEM; -+ -+ ret = ipu6_mmu_hw_init(adev->mmu); -+ if (ret) -+ return ret; -+ -+ adev->auxdrv_data = -+ (const struct ipu6_auxdrv_data *)auxdev_id->driver_data; -+ adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver); -+ isys->adev = adev; -+ isys->pdata = adev->pdata; -+ -+ /* initial sensor type */ -+ isys->sensor_type = isys->pdata->ipdata->sensor_type_start; -+ -+ spin_lock_init(&isys->streams_lock); -+ spin_lock_init(&isys->power_lock); -+ isys->power = 0; -+ isys->phy_termcal_val = 0; -+ -+ mutex_init(&isys->mutex); -+ mutex_init(&isys->stream_mutex); -+ -+ spin_lock_init(&isys->listlock); -+ INIT_LIST_HEAD(&isys->framebuflist); -+ INIT_LIST_HEAD(&isys->framebuflist_fw); -+ -+ isys->line_align = IPU6_ISYS_2600_MEM_LINE_ALIGN; -+ isys->icache_prefetch = 0; -+ -+ dev_set_drvdata(&auxdev->dev, isys); -+ -+ isys_stream_init(isys); -+ -+ if (!isp->secure_mode) { -+ fw = isp->cpd_fw; -+ ret = ipu6_buttress_map_fw_image(adev, fw, &adev->fw_sgt); -+ if (ret) -+ goto release_firmware; -+ -+ ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data); -+ if (ret) -+ goto remove_shared_buffer; -+ } -+ -+ cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -+ -+ ret = alloc_fw_msg_bufs(isys, 20); -+ if (ret < 0) -+ goto out_remove_pkg_dir_shared_buffer; -+ -+ isys_iwake_watermark_init(isys); -+ -+ if (is_ipu6se(adev->isp->hw_ver)) -+ isys->phy_set_power = ipu6_isys_jsl_phy_set_power; -+ else if (is_ipu6ep_mtl(adev->isp->hw_ver)) -+ isys->phy_set_power = ipu6_isys_dwc_phy_set_power; -+ else -+ isys->phy_set_power = ipu6_isys_mcd_phy_set_power; -+ -+ ret = isys_register_devices(isys); -+ if (ret) -+ goto out_remove_pkg_dir_shared_buffer; -+ -+ ipu6_mmu_hw_cleanup(adev->mmu); -+ -+ return 0; -+ -+out_remove_pkg_dir_shared_buffer: -+ if (!isp->secure_mode) -+ ipu6_cpd_free_pkg_dir(adev); -+remove_shared_buffer: -+ if (!isp->secure_mode) -+ ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); -+release_firmware: -+ if (!isp->secure_mode) -+ release_firmware(adev->fw); -+ -+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ mutex_destroy(&isys->mutex); -+ mutex_destroy(&isys->stream_mutex); -+ -+ ipu6_mmu_hw_cleanup(adev->mmu); -+ -+ return ret; -+} -+ -+struct fwmsg { -+ int type; -+ char *msg; -+ bool valid_ts; -+}; -+ -+static const struct fwmsg fw_msg[] = { -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0}, -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0}, -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0}, -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, -+ "STREAM_START_AND_CAPTURE_ACK", 0}, -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0}, -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0}, -+ {IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1}, -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0}, -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, -+ "STREAM_START_AND_CAPTURE_DONE", 1}, -+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1}, -+ {IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1}, -+ {IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1}, -+ {IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1}, -+ {-1, "UNKNOWN MESSAGE", 0} -+}; -+ -+static u32 resp_type_to_index(int type) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(fw_msg); i++) -+ if (fw_msg[i].type == type) -+ return i; -+ -+ return ARRAY_SIZE(fw_msg) - 1; -+} -+ -+static int isys_isr_one(struct ipu6_bus_device *adev) -+{ -+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); -+ struct ipu6_fw_isys_resp_info_abi *resp; -+ struct ipu6_isys_stream *stream; -+ struct ipu6_isys_csi2 *csi2 = NULL; -+ u32 index; -+ u64 ts; -+ -+ if (!isys->fwcom) -+ return 1; -+ -+ resp = ipu6_fw_isys_get_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES); -+ if (!resp) -+ return 1; -+ -+ ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0]; -+ -+ index = resp_type_to_index(resp->type); -+ dev_dbg(&adev->auxdev.dev, -+ "FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n", -+ resp->type, fw_msg[index].msg, resp->stream_handle, -+ fw_msg[index].valid_ts ? ts : 0, resp->pin_id); -+ -+ if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION) -+ /* Suspension is kind of special case: not enough buffers */ -+ dev_dbg(&adev->auxdev.dev, -+ "FW error resp SUSPENSION, details %d\n", -+ resp->error_info.error_details); -+ else if (resp->error_info.error) -+ dev_dbg(&adev->auxdev.dev, -+ "FW error resp error %d, details %d\n", -+ resp->error_info.error, resp->error_info.error_details); -+ -+ if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) { -+ dev_err(&adev->auxdev.dev, "bad stream handle %u\n", -+ resp->stream_handle); -+ goto leave; -+ } -+ -+ stream = ipu6_isys_query_stream_by_handle(isys, resp->stream_handle); -+ if (!stream) { -+ dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n", -+ resp->stream_handle); -+ goto leave; -+ } -+ stream->error = resp->error_info.error; -+ -+ csi2 = ipu6_isys_subdev_to_csi2(stream->asd); -+ -+ switch (resp->type) { -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE: -+ complete(&stream->stream_open_completion); -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK: -+ complete(&stream->stream_close_completion); -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK: -+ complete(&stream->stream_start_completion); -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: -+ complete(&stream->stream_start_completion); -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK: -+ complete(&stream->stream_stop_completion); -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK: -+ complete(&stream->stream_stop_completion); -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY: -+ /* -+ * firmware only release the capture msg until software -+ * get pin_data_ready event -+ */ -+ ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), resp->buf_id); -+ if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS && -+ stream->output_pins[resp->pin_id].pin_ready) -+ stream->output_pins[resp->pin_id].pin_ready(stream, -+ resp); -+ else -+ dev_warn(&adev->auxdev.dev, -+ "%d:No data pin ready handler for pin id %d\n", -+ resp->stream_handle, resp->pin_id); -+ if (csi2) -+ ipu6_isys_csi2_error(csi2); -+ -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: -+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF: -+ -+ ipu6_isys_csi2_sof_event_by_stream(stream); -+ stream->seq[stream->seq_index].sequence = -+ atomic_read(&stream->sequence) - 1; -+ stream->seq[stream->seq_index].timestamp = ts; -+ dev_dbg(&adev->auxdev.dev, -+ "sof: handle %d: (index %u), timestamp 0x%16.16llx\n", -+ resp->stream_handle, -+ stream->seq[stream->seq_index].sequence, ts); -+ stream->seq_index = (stream->seq_index + 1) -+ % IPU6_ISYS_MAX_PARALLEL_SOF; -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF: -+ ipu6_isys_csi2_eof_event_by_stream(stream); -+ dev_dbg(&adev->auxdev.dev, -+ "eof: handle %d: (index %u), timestamp 0x%16.16llx\n", -+ resp->stream_handle, -+ stream->seq[stream->seq_index].sequence, ts); -+ break; -+ case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY: -+ break; -+ default: -+ dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n", -+ resp->stream_handle, resp->type); -+ break; -+ } -+ -+ ipu6_isys_put_stream(stream); -+leave: -+ ipu6_fw_isys_put_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES); -+ return 0; -+} -+ -+static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = { -+ .isr = isys_isr, -+ .isr_threaded = NULL, -+ .wake_isr_thread = false, -+}; -+ -+static const struct auxiliary_device_id ipu6_isys_id_table[] = { -+ { -+ .name = "intel_ipu6.isys", -+ .driver_data = (kernel_ulong_t)&ipu6_isys_auxdrv_data, -+ }, -+ { } -+}; -+MODULE_DEVICE_TABLE(auxiliary, ipu6_isys_id_table); -+ -+static struct auxiliary_driver isys_driver = { -+ .name = IPU6_ISYS_NAME, -+ .probe = isys_probe, -+ .remove = isys_remove, -+ .id_table = ipu6_isys_id_table, -+ .driver = { -+ .pm = &isys_pm_ops, -+ }, -+}; -+ -+module_auxiliary_driver(isys_driver); -+ -+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); -+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); -+MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); -+MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>"); -+MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel IPU6 input system driver"); -+MODULE_IMPORT_NS(INTEL_IPU6); -+MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h -new file mode 100644 -index 000000000000..cf7a90bfedc9 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h -@@ -0,0 +1,207 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_ISYS_H -+#define IPU6_ISYS_H -+ -+#include <linux/irqreturn.h> -+#include <linux/list.h> -+#include <linux/mutex.h> -+#include <linux/pm_qos.h> -+#include <linux/spinlock_types.h> -+#include <linux/types.h> -+ -+#include <media/media-device.h> -+#include <media/v4l2-async.h> -+#include <media/v4l2-device.h> -+ -+#include "ipu6.h" -+#include "ipu6-fw-isys.h" -+#include "ipu6-isys-csi2.h" -+#include "ipu6-isys-video.h" -+ -+struct ipu6_bus_device; -+ -+#define IPU6_ISYS_ENTITY_PREFIX "Intel IPU6" -+/* FW support max 16 streams */ -+#define IPU6_ISYS_MAX_STREAMS 16 -+#define ISYS_UNISPART_IRQS (IPU6_ISYS_UNISPART_IRQ_SW | \ -+ IPU6_ISYS_UNISPART_IRQ_CSI0 | \ -+ IPU6_ISYS_UNISPART_IRQ_CSI1) -+ -+#define IPU6_ISYS_2600_MEM_LINE_ALIGN 64 -+ -+/* -+ * Current message queue configuration. These must be big enough -+ * so that they never gets full. Queues are located in system memory -+ */ -+#define IPU6_ISYS_SIZE_RECV_QUEUE 40 -+#define IPU6_ISYS_SIZE_SEND_QUEUE 40 -+#define IPU6_ISYS_SIZE_PROXY_RECV_QUEUE 5 -+#define IPU6_ISYS_SIZE_PROXY_SEND_QUEUE 5 -+#define IPU6_ISYS_NUM_RECV_QUEUE 1 -+ -+#define IPU6_ISYS_MIN_WIDTH 1U -+#define IPU6_ISYS_MIN_HEIGHT 1U -+#define IPU6_ISYS_MAX_WIDTH 4672U -+#define IPU6_ISYS_MAX_HEIGHT 3416U -+ -+/* the threshold granularity is 2KB on IPU6 */ -+#define IPU6_SRAM_GRANULARITY_SHIFT 11 -+#define IPU6_SRAM_GRANULARITY_SIZE 2048 -+/* the threshold granularity is 1KB on IPU6SE */ -+#define IPU6SE_SRAM_GRANULARITY_SHIFT 10 -+#define IPU6SE_SRAM_GRANULARITY_SIZE 1024 -+/* IS pixel buffer is 256KB, MaxSRAMSize is 200KB on IPU6 */ -+#define IPU6_MAX_SRAM_SIZE (200 << 10) -+/* IS pixel buffer is 128KB, MaxSRAMSize is 96KB on IPU6SE */ -+#define IPU6SE_MAX_SRAM_SIZE (96 << 10) -+ -+#define IPU6EP_LTR_VALUE 200 -+#define IPU6EP_MIN_MEMOPEN_TH 0x4 -+#define IPU6EP_MTL_LTR_VALUE 1023 -+#define IPU6EP_MTL_MIN_MEMOPEN_TH 0xc -+ -+struct ltr_did { -+ union { -+ u32 value; -+ struct { -+ u8 val0; -+ u8 val1; -+ u8 val2; -+ u8 val3; -+ } bits; -+ } lut_ltr; -+ union { -+ u32 value; -+ struct { -+ u8 th0; -+ u8 th1; -+ u8 th2; -+ u8 th3; -+ } bits; -+ } lut_fill_time; -+}; -+ -+struct isys_iwake_watermark { -+ bool iwake_enabled; -+ bool force_iwake_disable; -+ u32 iwake_threshold; -+ u64 isys_pixelbuffer_datarate; -+ struct ltr_did ltrdid; -+ struct mutex mutex; /* protect whole struct */ -+ struct ipu6_isys *isys; -+ struct list_head video_list; -+}; -+ -+struct ipu6_isys_csi2_config { -+ u32 nlanes; -+ u32 port; -+}; -+ -+struct sensor_async_sd { -+ struct v4l2_async_connection asc; -+ struct ipu6_isys_csi2_config csi2; -+}; -+ -+/* -+ * struct ipu6_isys -+ * -+ * @media_dev: Media device -+ * @v4l2_dev: V4L2 device -+ * @adev: ISYS bus device -+ * @power: Is ISYS powered on or not? -+ * @isr_bits: Which bits does the ISR handle? -+ * @power_lock: Serialise access to power (power state in general) -+ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers -+ * @streams_lock: serialise access to streams -+ * @streams: streams per firmware stream ID -+ * @fwcom: fw communication layer private pointer -+ * or optional external library private pointer -+ * @line_align: line alignment in memory -+ * @phy_termcal_val: the termination calibration value, only used for DWC PHY -+ * @need_reset: Isys requires d0i0->i3 transition -+ * @ref_count: total number of callers fw open -+ * @mutex: serialise access isys video open/release related operations -+ * @stream_mutex: serialise stream start and stop, queueing requests -+ * @pdata: platform data pointer -+ * @csi2: CSI-2 receivers -+ */ -+struct ipu6_isys { -+ struct media_device media_dev; -+ struct v4l2_device v4l2_dev; -+ struct ipu6_bus_device *adev; -+ -+ int power; -+ spinlock_t power_lock; -+ u32 isr_csi2_bits; -+ u32 csi2_rx_ctrl_cached; -+ spinlock_t streams_lock; -+ struct ipu6_isys_stream streams[IPU6_ISYS_MAX_STREAMS]; -+ int streams_ref_count[IPU6_ISYS_MAX_STREAMS]; -+ void *fwcom; -+ unsigned int line_align; -+ u32 phy_termcal_val; -+ bool need_reset; -+ bool icache_prefetch; -+ bool csi2_cse_ipc_not_supported; -+ unsigned int ref_count; -+ unsigned int stream_opened; -+ unsigned int sensor_type; -+ -+ struct mutex mutex; -+ struct mutex stream_mutex; -+ -+ struct ipu6_isys_pdata *pdata; -+ -+ int (*phy_set_power)(struct ipu6_isys *isys, -+ struct ipu6_isys_csi2_config *cfg, -+ const struct ipu6_isys_csi2_timing *timing, -+ bool on); -+ -+ struct ipu6_isys_csi2 *csi2; -+ struct ipu6_isys_video av[NR_OF_VIDEO_DEVICE]; -+ -+ struct pm_qos_request pm_qos; -+ spinlock_t listlock; /* Protect framebuflist */ -+ struct list_head framebuflist; -+ struct list_head framebuflist_fw; -+ struct v4l2_async_notifier notifier; -+ struct isys_iwake_watermark iwake_watermark; -+}; -+ -+struct isys_fw_msgs { -+ union { -+ u64 dummy; -+ struct ipu6_fw_isys_frame_buff_set_abi frame; -+ struct ipu6_fw_isys_stream_cfg_data_abi stream; -+ } fw_msg; -+ struct list_head head; -+ dma_addr_t dma_addr; -+}; -+ -+struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream); -+void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data); -+void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys); -+ -+extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops; -+ -+void isys_setup_hw(struct ipu6_isys *isys); -+irqreturn_t isys_isr(struct ipu6_bus_device *adev); -+void update_watermark_setting(struct ipu6_isys *isys); -+ -+int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys, -+ struct ipu6_isys_csi2_config *cfg, -+ const struct ipu6_isys_csi2_timing *timing, -+ bool on); -+ -+int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys, -+ struct ipu6_isys_csi2_config *cfg, -+ const struct ipu6_isys_csi2_timing *timing, -+ bool on); -+ -+int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys, -+ struct ipu6_isys_csi2_config *cfg, -+ const struct ipu6_isys_csi2_timing *timing, -+ bool on); -+#endif /* IPU6_ISYS_H */ --- -2.43.2 - - -From 7cdb944c1cc8beb6f61268e2fe177d585fa5f415 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:25 +0800 -Subject: [PATCH 18/33] media: intel/ipu6: input system video capture nodes - -Register v4l2 video device and setup the vb2 queue to -support basic video capture. Video streaming callback -will trigger the input system driver to construct a -input system stream configuration for firmware based on -data type and stream ID and then queue buffers to firmware -to do capture. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - .../media/pci/intel/ipu6/ipu6-isys-queue.c | 825 +++++++++++ - .../media/pci/intel/ipu6/ipu6-isys-queue.h | 76 + - .../media/pci/intel/ipu6/ipu6-isys-video.c | 1253 +++++++++++++++++ - .../media/pci/intel/ipu6/ipu6-isys-video.h | 136 ++ - 4 files changed, 2290 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-queue.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-queue.h - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-video.c - create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-video.h - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c -new file mode 100644 -index 000000000000..735d2d642d87 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c -@@ -0,0 +1,825 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+#include <linux/atomic.h> -+#include <linux/bug.h> -+#include <linux/device.h> -+#include <linux/list.h> -+#include <linux/lockdep.h> -+#include <linux/mutex.h> -+#include <linux/spinlock.h> -+#include <linux/types.h> -+ -+#include <media/media-entity.h> -+#include <media/v4l2-subdev.h> -+#include <media/videobuf2-dma-contig.h> -+#include <media/videobuf2-v4l2.h> -+ -+#include "ipu6-bus.h" -+#include "ipu6-fw-isys.h" -+#include "ipu6-isys.h" -+#include "ipu6-isys-video.h" -+ -+static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, -+ unsigned int *num_planes, unsigned int sizes[], -+ struct device *alloc_devs[]) -+{ -+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ bool use_fmt = false; -+ unsigned int i; -+ u32 size; -+ -+ /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ -+ if (!*num_planes) { -+ use_fmt = true; -+ *num_planes = av->mpix.num_planes; -+ } -+ -+ for (i = 0; i < *num_planes; i++) { -+ size = av->mpix.plane_fmt[i].sizeimage; -+ if (use_fmt) { -+ sizes[i] = size; -+ } else if (sizes[i] < size) { -+ dev_err(dev, "%s: queue setup: plane %d size %u < %u\n", -+ av->vdev.name, i, sizes[i], size); -+ return -EINVAL; -+ } -+ -+ alloc_devs[i] = aq->dev; -+ } -+ -+ return 0; -+} -+ -+static int ipu6_isys_buf_prepare(struct vb2_buffer *vb) -+{ -+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n", -+ av->vdev.name, av->mpix.plane_fmt[0].sizeimage, -+ vb2_plane_size(vb, 0)); -+ -+ if (av->mpix.plane_fmt[0].sizeimage > vb2_plane_size(vb, 0)) -+ return -EINVAL; -+ -+ vb2_set_plane_payload(vb, 0, av->mpix.plane_fmt[0].bytesperline * -+ av->mpix.height); -+ vb->planes[0].data_offset = 0; -+ -+ return 0; -+} -+ -+/* -+ * Queue a buffer list back to incoming or active queues. The buffers -+ * are removed from the buffer list. -+ */ -+void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl, -+ unsigned long op_flags, -+ enum vb2_buffer_state state) -+{ -+ struct ipu6_isys_buffer *ib, *ib_safe; -+ unsigned long flags; -+ bool first = true; -+ -+ if (!bl) -+ return; -+ -+ WARN_ON_ONCE(!bl->nbufs); -+ WARN_ON_ONCE(op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE && -+ op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING); -+ -+ list_for_each_entry_safe(ib, ib_safe, &bl->head, head) { -+ struct ipu6_isys_video *av; -+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); -+ struct ipu6_isys_queue *aq = -+ vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct device *dev; -+ -+ av = ipu6_isys_queue_to_video(aq); -+ dev = &av->isys->adev->auxdev.dev; -+ spin_lock_irqsave(&aq->lock, flags); -+ list_del(&ib->head); -+ if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE) -+ list_add(&ib->head, &aq->active); -+ else if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING) -+ list_add_tail(&ib->head, &aq->incoming); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_SET_STATE) -+ vb2_buffer_done(vb, state); -+ -+ if (first) { -+ dev_dbg(dev, -+ "queue buf list %p flags %lx, s %d, %d bufs\n", -+ bl, op_flags, state, bl->nbufs); -+ first = false; -+ } -+ -+ bl->nbufs--; -+ } -+ -+ WARN_ON(bl->nbufs); -+} -+ -+/* -+ * flush_firmware_streamon_fail() - Flush in cases where requests may -+ * have been queued to firmware and the *firmware streamon fails for a -+ * reason or another. -+ */ -+static void flush_firmware_streamon_fail(struct ipu6_isys_stream *stream) -+{ -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu6_isys_queue *aq; -+ unsigned long flags; -+ -+ lockdep_assert_held(&stream->mutex); -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct ipu6_isys_buffer *ib, *ib_safe; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ list_for_each_entry_safe(ib, ib_safe, &aq->active, head) { -+ struct vb2_buffer *vb = -+ ipu6_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ if (av->streaming) { -+ dev_dbg(dev, -+ "%s: queue buffer %u back to incoming\n", -+ av->vdev.name, vb->index); -+ /* Queue already streaming, return to driver. */ -+ list_add(&ib->head, &aq->incoming); -+ continue; -+ } -+ /* Queue not yet streaming, return to user. */ -+ dev_dbg(dev, "%s: return %u back to videobuf2\n", -+ av->vdev.name, vb->index); -+ vb2_buffer_done(ipu6_isys_buffer_to_vb2_buffer(ib), -+ VB2_BUF_STATE_QUEUED); -+ } -+ spin_unlock_irqrestore(&aq->lock, flags); -+ } -+} -+ -+/* -+ * Attempt obtaining a buffer list from the incoming queues, a list of buffers -+ * that contains one entry from each video buffer queue. If a buffer can't be -+ * obtained from every queue, the buffers are returned back to the queue. -+ */ -+static int buffer_list_get(struct ipu6_isys_stream *stream, -+ struct ipu6_isys_buffer_list *bl) -+{ -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu6_isys_queue *aq; -+ unsigned long flags; -+ unsigned long buf_flag = IPU6_ISYS_BUFFER_LIST_FL_INCOMING; -+ -+ bl->nbufs = 0; -+ INIT_LIST_HEAD(&bl->head); -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu6_isys_buffer *ib; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ if (list_empty(&aq->incoming)) { -+ spin_unlock_irqrestore(&aq->lock, flags); -+ if (!list_empty(&bl->head)) -+ ipu6_isys_buffer_list_queue(bl, buf_flag, 0); -+ return -ENODATA; -+ } -+ -+ ib = list_last_entry(&aq->incoming, -+ struct ipu6_isys_buffer, head); -+ -+ dev_dbg(dev, "buffer: %s: buffer %u\n", -+ ipu6_isys_queue_to_video(aq)->vdev.name, -+ ipu6_isys_buffer_to_vb2_buffer(ib)->index); -+ list_del(&ib->head); -+ list_add(&ib->head, &bl->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ bl->nbufs++; -+ } -+ -+ dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs); -+ -+ return 0; -+} -+ -+static void -+ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, -+ struct ipu6_fw_isys_frame_buff_set_abi *set) -+{ -+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ -+ set->output_pins[aq->fw_output].addr = -+ vb2_dma_contig_plane_dma_addr(vb, 0); -+ set->output_pins[aq->fw_output].out_buf_id = vb->index + 1; -+} -+ -+/* -+ * Convert a buffer list to a isys fw ABI framebuffer set. The -+ * buffer list is not modified. -+ */ -+#define IPU6_ISYS_FRAME_NUM_THRESHOLD (30) -+void -+ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set, -+ struct ipu6_isys_stream *stream, -+ struct ipu6_isys_buffer_list *bl) -+{ -+ struct ipu6_isys_buffer *ib; -+ -+ WARN_ON(!bl->nbufs); -+ -+ set->send_irq_sof = 1; -+ set->send_resp_sof = 1; -+ set->send_irq_eof = 0; -+ set->send_resp_eof = 0; -+ -+ if (stream->streaming) -+ set->send_irq_capture_ack = 0; -+ else -+ set->send_irq_capture_ack = 1; -+ set->send_irq_capture_done = 0; -+ -+ set->send_resp_capture_ack = 1; -+ set->send_resp_capture_done = 1; -+ if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) { -+ set->send_resp_capture_ack = 0; -+ set->send_resp_capture_done = 0; -+ } -+ -+ list_for_each_entry(ib, &bl->head, head) { -+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); -+ -+ ipu6_isys_buf_to_fw_frame_buf_pin(vb, set); -+ } -+} -+ -+/* Start streaming for real. The buffer list must be available. */ -+static int ipu6_isys_stream_start(struct ipu6_isys_video *av, -+ struct ipu6_isys_buffer_list *bl, bool error) -+{ -+ struct ipu6_isys_stream *stream = av->stream; -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu6_isys_buffer_list __bl; -+ int ret; -+ -+ mutex_lock(&stream->isys->stream_mutex); -+ ret = ipu6_isys_video_set_streaming(av, 1, bl); -+ mutex_unlock(&stream->isys->stream_mutex); -+ if (ret) -+ goto out_requeue; -+ -+ stream->streaming = 1; -+ -+ bl = &__bl; -+ -+ do { -+ struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; -+ struct isys_fw_msgs *msg; -+ u16 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE; -+ -+ ret = buffer_list_get(stream, bl); -+ if (ret < 0) -+ break; -+ -+ msg = ipu6_get_fw_msg_buf(stream); -+ if (!msg) -+ return -ENOMEM; -+ -+ buf = &msg->fw_msg.frame; -+ ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl); -+ ipu6_fw_isys_dump_frame_buff_set(dev, buf, -+ stream->nr_output_pins); -+ ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, -+ 0); -+ ret = ipu6_fw_isys_complex_cmd(stream->isys, -+ stream->stream_handle, buf, -+ msg->dma_addr, sizeof(*buf), -+ send_type); -+ } while (!WARN_ON(ret)); -+ -+ return 0; -+ -+out_requeue: -+ if (bl && bl->nbufs) -+ ipu6_isys_buffer_list_queue(bl, -+ (IPU6_ISYS_BUFFER_LIST_FL_INCOMING | -+ error) ? -+ IPU6_ISYS_BUFFER_LIST_FL_SET_STATE : -+ 0, error ? VB2_BUF_STATE_ERROR : -+ VB2_BUF_STATE_QUEUED); -+ flush_firmware_streamon_fail(stream); -+ -+ return ret; -+} -+ -+static void buf_queue(struct vb2_buffer *vb) -+{ -+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu6_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu6_isys_video_buffer(vvb); -+ struct ipu6_isys_buffer *ib = &ivb->ib; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct media_pipeline *media_pipe = -+ media_entity_pipeline(&av->vdev.entity); -+ struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; -+ struct ipu6_isys_stream *stream = av->stream; -+ struct ipu6_isys_buffer_list bl; -+ struct isys_fw_msgs *msg; -+ unsigned long flags; -+ dma_addr_t dma; -+ unsigned int i; -+ int ret; -+ -+ dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name); -+ -+ for (i = 0; i < vb->num_planes; i++) { -+ dma = vb2_dma_contig_plane_dma_addr(vb, i); -+ dev_dbg(dev, "iova: plane %u iova %pad\n", i, &dma); -+ } -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ list_add(&ib->head, &aq->incoming); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ if (!media_pipe || !vb->vb2_queue->start_streaming_called) { -+ dev_dbg(dev, "media pipeline is not ready for %s\n", -+ av->vdev.name); -+ return; -+ } -+ -+ mutex_lock(&stream->mutex); -+ -+ if (stream->nr_streaming != stream->nr_queues) { -+ dev_dbg(dev, "not streaming yet, adding to incoming\n"); -+ goto out; -+ } -+ -+ /* -+ * We just put one buffer to the incoming list of this queue -+ * (above). Let's see whether all queues in the pipeline would -+ * have a buffer. -+ */ -+ ret = buffer_list_get(stream, &bl); -+ if (ret < 0) { -+ dev_warn(dev, "No buffers available\n"); -+ goto out; -+ } -+ -+ msg = ipu6_get_fw_msg_buf(stream); -+ if (!msg) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ buf = &msg->fw_msg.frame; -+ ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl); -+ ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); -+ -+ if (!stream->streaming) { -+ ret = ipu6_isys_stream_start(av, &bl, true); -+ if (ret) -+ dev_err(dev, "stream start failed.\n"); -+ goto out; -+ } -+ -+ /* -+ * We must queue the buffers in the buffer list to the -+ * appropriate video buffer queues BEFORE passing them to the -+ * firmware since we could get a buffer event back before we -+ * have queued them ourselves to the active queue. -+ */ -+ ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -+ -+ ret = ipu6_fw_isys_complex_cmd(stream->isys, stream->stream_handle, -+ buf, msg->dma_addr, sizeof(*buf), -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE); -+ if (ret < 0) -+ dev_err(dev, "send stream capture failed\n"); -+ -+out: -+ mutex_unlock(&stream->mutex); -+} -+ -+static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq) -+{ -+ struct v4l2_mbus_framefmt format; -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct media_pad *remote_pad = -+ media_pad_remote_pad_first(av->vdev.entity.pads); -+ struct v4l2_subdev *sd; -+ u32 r_stream; -+ int ret; -+ -+ if (!remote_pad) -+ return -ENOTCONN; -+ -+ sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, remote_pad->index); -+ -+ ret = ipu6_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream, -+ &format); -+ -+ if (ret) { -+ dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n", -+ sd->entity.name, remote_pad->index, r_stream); -+ return ret; -+ } -+ -+ if (format.width != av->mpix.width || -+ format.height != av->mpix.height) { -+ dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n", -+ av->mpix.width, av->mpix.height, -+ format.width, format.height); -+ return -EINVAL; -+ } -+ -+ if (format.field != av->mpix.field) { -+ dev_dbg(dev, "wrong field value 0x%8.8x (0x%8.8x expected)\n", -+ av->mpix.field, format.field); -+ return -EINVAL; -+ } -+ -+ if (format.code != av->pfmt->code) { -+ dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n", -+ av->pfmt->code, format.code); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static void return_buffers(struct ipu6_isys_queue *aq, -+ enum vb2_buffer_state state) -+{ -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct ipu6_isys_buffer *ib; -+ bool need_reset = false; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ while (!list_empty(&aq->incoming)) { -+ struct vb2_buffer *vb; -+ -+ ib = list_first_entry(&aq->incoming, struct ipu6_isys_buffer, -+ head); -+ vb = ipu6_isys_buffer_to_vb2_buffer(ib); -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, state); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ } -+ -+ /* -+ * Something went wrong (FW crash / HW hang / not all buffers -+ * returned from isys) if there are still buffers queued in active -+ * queue. We have to clean up places a bit. -+ */ -+ while (!list_empty(&aq->active)) { -+ struct vb2_buffer *vb; -+ -+ ib = list_first_entry(&aq->active, struct ipu6_isys_buffer, -+ head); -+ vb = ipu6_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, state); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ need_reset = true; -+ } -+ -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ if (need_reset) { -+ mutex_lock(&av->isys->mutex); -+ av->isys->need_reset = true; -+ mutex_unlock(&av->isys->mutex); -+ } -+} -+ -+static void ipu6_isys_stream_cleanup(struct ipu6_isys_video *av) -+{ -+ video_device_pipeline_stop(&av->vdev); -+ ipu6_isys_put_stream(av->stream); -+ av->stream = NULL; -+} -+ -+static int start_streaming(struct vb2_queue *q, unsigned int count) -+{ -+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu6_isys_buffer_list __bl, *bl = NULL; -+ struct ipu6_isys_stream *stream; -+ struct media_entity *source_entity = NULL; -+ int nr_queues, ret; -+ -+ dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", -+ av->vdev.name, av->mpix.width, av->mpix.height, -+ av->pfmt->css_pixelformat); -+ -+ ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues); -+ if (ret < 0) { -+ dev_err(dev, "failed to setup video\n"); -+ goto out_return_buffers; -+ } -+ -+ ret = ipu6_isys_link_fmt_validate(aq); -+ if (ret) { -+ dev_err(dev, -+ "%s: link format validation failed (%d)\n", -+ av->vdev.name, ret); -+ goto out_pipeline_stop; -+ } -+ -+ ret = ipu6_isys_fw_open(av->isys); -+ if (ret) -+ goto out_pipeline_stop; -+ -+ stream = av->stream; -+ mutex_lock(&stream->mutex); -+ if (!stream->nr_streaming) { -+ ret = ipu6_isys_video_prepare_stream(av, source_entity, -+ nr_queues); -+ if (ret) -+ goto out_fw_close; -+ } -+ -+ stream->nr_streaming++; -+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -+ stream->nr_queues); -+ -+ list_add(&aq->node, &stream->queues); -+ ipu6_isys_set_csi2_streams_status(av, true); -+ ipu6_isys_configure_stream_watermark(av, true); -+ ipu6_isys_update_stream_watermark(av, true); -+ -+ if (stream->nr_streaming != stream->nr_queues) -+ goto out; -+ -+ bl = &__bl; -+ ret = buffer_list_get(stream, bl); -+ if (ret < 0) { -+ dev_dbg(dev, -+ "no buffer available, postponing streamon\n"); -+ goto out; -+ } -+ -+ ret = ipu6_isys_stream_start(av, bl, false); -+ if (ret) -+ goto out_stream_start; -+ -+out: -+ mutex_unlock(&stream->mutex); -+ -+ return 0; -+ -+out_stream_start: -+ list_del(&aq->node); -+ stream->nr_streaming--; -+ -+out_fw_close: -+ mutex_unlock(&stream->mutex); -+ ipu6_isys_fw_close(av->isys); -+ -+out_pipeline_stop: -+ ipu6_isys_stream_cleanup(av); -+ -+out_return_buffers: -+ return_buffers(aq, VB2_BUF_STATE_QUEUED); -+ -+ return ret; -+} -+ -+static void stop_streaming(struct vb2_queue *q) -+{ -+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct ipu6_isys_stream *stream = av->stream; -+ -+ ipu6_isys_set_csi2_streams_status(av, false); -+ -+ mutex_lock(&stream->mutex); -+ -+ ipu6_isys_update_stream_watermark(av, false); -+ -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu6_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ mutex_unlock(&stream->mutex); -+ -+ ipu6_isys_stream_cleanup(av); -+ -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ -+ ipu6_isys_fw_close(av->isys); -+} -+ -+static unsigned int -+get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream, -+ struct ipu6_fw_isys_resp_info_abi *info) -+{ -+ u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0]; -+ struct ipu6_isys *isys = stream->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ -+ /* -+ * The timestamp is invalid as no TSC in some FPGA platform, -+ * so get the sequence from pipeline directly in this case. -+ */ -+ if (time == 0) -+ return atomic_read(&stream->sequence) - 1; -+ -+ for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++) -+ if (time == stream->seq[i].timestamp) { -+ dev_dbg(dev, "sof: using seq nr %u for ts %llu\n", -+ stream->seq[i].sequence, time); -+ return stream->seq[i].sequence; -+ } -+ -+ for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++) -+ dev_dbg(dev, "sof: sequence %u, timestamp value %llu\n", -+ stream->seq[i].sequence, stream->seq[i].timestamp); -+ -+ return 0; -+} -+ -+static u64 get_sof_ns_delta(struct ipu6_isys_video *av, -+ struct ipu6_fw_isys_resp_info_abi *info) -+{ -+ struct ipu6_bus_device *adev = av->isys->adev; -+ struct ipu6_device *isp = adev->isp; -+ u64 delta, tsc_now; -+ -+ ipu6_buttress_tsc_read(isp, &tsc_now); -+ if (!tsc_now) -+ return 0; -+ -+ delta = tsc_now - ((u64)info->timestamp[1] << 32 | info->timestamp[0]); -+ -+ return ipu6_buttress_tsc_ticks_to_ns(delta, isp); -+} -+ -+void ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, -+ struct ipu6_fw_isys_resp_info_abi *info) -+{ -+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); -+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); -+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu6_isys_stream *stream = av->stream; -+ u64 ns; -+ u32 sequence; -+ -+ ns = ktime_get_ns() - get_sof_ns_delta(av, info); -+ sequence = get_sof_sequence_by_timestamp(stream, info); -+ -+ vbuf->vb2_buf.timestamp = ns; -+ vbuf->sequence = sequence; -+ -+ dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n", -+ av->vdev.name, ktime_get_ns(), sequence); -+ dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index, -+ vbuf->vb2_buf.timestamp); -+} -+ -+void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib) -+{ -+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); -+ -+ if (atomic_read(&ib->str2mmio_flag)) { -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ /* -+ * Operation on buffer is ended with error and will be reported -+ * to the userspace when it is de-queued -+ */ -+ atomic_set(&ib->str2mmio_flag, 0); -+ } else { -+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE); -+ } -+} -+ -+void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, -+ struct ipu6_fw_isys_resp_info_abi *info) -+{ -+ struct ipu6_isys_queue *aq = stream->output_pins[info->pin_id].aq; -+ struct ipu6_isys *isys = stream->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu6_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ bool first = true; -+ struct vb2_v4l2_buffer *buf; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ if (list_empty(&aq->active)) { -+ spin_unlock_irqrestore(&aq->lock, flags); -+ dev_err(dev, "active queue empty\n"); -+ return; -+ } -+ -+ list_for_each_entry_reverse(ib, &aq->active, head) { -+ dma_addr_t addr; -+ -+ vb = ipu6_isys_buffer_to_vb2_buffer(ib); -+ addr = vb2_dma_contig_plane_dma_addr(vb, 0); -+ -+ if (info->pin.addr != addr) { -+ if (first) -+ dev_err(dev, "Unexpected buffer address %pad\n", -+ &addr); -+ first = false; -+ continue; -+ } -+ -+ if (info->error_info.error == -+ IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) { -+ /* -+ * Check for error message: -+ * 'IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO' -+ */ -+ atomic_set(&ib->str2mmio_flag, 1); -+ } -+ dev_dbg(dev, "buffer: found buffer %pad\n", &addr); -+ -+ buf = to_vb2_v4l2_buffer(vb); -+ buf->field = V4L2_FIELD_NONE; -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ ipu6_isys_buf_calc_sequence_time(ib, info); -+ -+ ipu6_isys_queue_buf_done(ib); -+ -+ return; -+ } -+ -+ dev_err(dev, "Failed to find a matching video buffer"); -+ -+ spin_unlock_irqrestore(&aq->lock, flags); -+} -+ -+static const struct vb2_ops ipu6_isys_queue_ops = { -+ .queue_setup = queue_setup, -+ .wait_prepare = vb2_ops_wait_prepare, -+ .wait_finish = vb2_ops_wait_finish, -+ .buf_prepare = ipu6_isys_buf_prepare, -+ .start_streaming = start_streaming, -+ .stop_streaming = stop_streaming, -+ .buf_queue = buf_queue, -+}; -+ -+int ipu6_isys_queue_init(struct ipu6_isys_queue *aq) -+{ -+ struct ipu6_isys *isys = ipu6_isys_queue_to_video(aq)->isys; -+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); -+ int ret; -+ -+ /* no support for userptr */ -+ if (!aq->vbq.io_modes) -+ aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF; -+ -+ aq->vbq.drv_priv = aq; -+ aq->vbq.ops = &ipu6_isys_queue_ops; -+ aq->vbq.lock = &av->mutex; -+ aq->vbq.mem_ops = &vb2_dma_contig_memops; -+ aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; -+ aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -+ -+ ret = vb2_queue_init(&aq->vbq); -+ if (ret) -+ return ret; -+ -+ aq->dev = &isys->adev->auxdev.dev; -+ aq->vbq.dev = &isys->adev->auxdev.dev; -+ spin_lock_init(&aq->lock); -+ INIT_LIST_HEAD(&aq->active); -+ INIT_LIST_HEAD(&aq->incoming); -+ -+ return 0; -+} -+ -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h -new file mode 100644 -index 000000000000..9fb454577bb5 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h -@@ -0,0 +1,76 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_ISYS_QUEUE_H -+#define IPU6_ISYS_QUEUE_H -+ -+#include <linux/container_of.h> -+#include <linux/atomic.h> -+#include <linux/device.h> -+#include <linux/list.h> -+#include <linux/spinlock_types.h> -+ -+#include <media/videobuf2-v4l2.h> -+ -+#include "ipu6-fw-isys.h" -+#include "ipu6-isys-video.h" -+ -+struct ipu6_isys_queue { -+ struct vb2_queue vbq; -+ struct list_head node; -+ struct device *dev; -+ /* -+ * @lock: serialise access to queued and pre_streamon_queued -+ */ -+ spinlock_t lock; -+ struct list_head active; -+ struct list_head incoming; -+ unsigned int fw_output; -+}; -+ -+struct ipu6_isys_buffer { -+ struct list_head head; -+ atomic_t str2mmio_flag; -+}; -+ -+struct ipu6_isys_video_buffer { -+ struct vb2_v4l2_buffer vb_v4l2; -+ struct ipu6_isys_buffer ib; -+}; -+ -+#define IPU6_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) -+#define IPU6_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) -+#define IPU6_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) -+ -+struct ipu6_isys_buffer_list { -+ struct list_head head; -+ unsigned int nbufs; -+}; -+ -+#define vb2_queue_to_isys_queue(__vb2) \ -+ container_of(__vb2, struct ipu6_isys_queue, vbq) -+ -+#define ipu6_isys_to_isys_video_buffer(__ib) \ -+ container_of(__ib, struct ipu6_isys_video_buffer, ib) -+ -+#define vb2_buffer_to_ipu6_isys_video_buffer(__vvb) \ -+ container_of(__vvb, struct ipu6_isys_video_buffer, vb_v4l2) -+ -+#define ipu6_isys_buffer_to_vb2_buffer(__ib) \ -+ (&ipu6_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf) -+ -+void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl, -+ unsigned long op_flags, -+ enum vb2_buffer_state state); -+void -+ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set, -+ struct ipu6_isys_stream *stream, -+ struct ipu6_isys_buffer_list *bl); -+void -+ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, -+ struct ipu6_fw_isys_resp_info_abi *info); -+void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib); -+void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, -+ struct ipu6_fw_isys_resp_info_abi *info); -+int ipu6_isys_queue_init(struct ipu6_isys_queue *aq); -+#endif /* IPU6_ISYS_QUEUE_H */ -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -new file mode 100644 -index 000000000000..847eac26bcd6 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -@@ -0,0 +1,1253 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2023 Intel Corporation -+ */ -+ -+#include <linux/align.h> -+#include <linux/bits.h> -+#include <linux/bug.h> -+#include <linux/completion.h> -+#include <linux/container_of.h> -+#include <linux/device.h> -+#include <linux/list.h> -+#include <linux/math64.h> -+#include <linux/minmax.h> -+#include <linux/module.h> -+#include <linux/mutex.h> -+#include <linux/pm_runtime.h> -+#include <linux/spinlock.h> -+#include <linux/string.h> -+ -+#include <media/media-entity.h> -+#include <media/v4l2-ctrls.h> -+#include <media/v4l2-dev.h> -+#include <media/v4l2-fh.h> -+#include <media/v4l2-ioctl.h> -+#include <media/v4l2-subdev.h> -+#include <media/videobuf2-v4l2.h> -+ -+#include "ipu6.h" -+#include "ipu6-bus.h" -+#include "ipu6-cpd.h" -+#include "ipu6-fw-isys.h" -+#include "ipu6-isys.h" -+#include "ipu6-isys-csi2.h" -+#include "ipu6-isys-queue.h" -+#include "ipu6-isys-video.h" -+#include "ipu6-platform-regs.h" -+ -+const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { -+ {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, -+ IPU6_FW_ISYS_FRAME_FORMAT_UYVY}, -+ {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, -+ IPU6_FW_ISYS_FRAME_FORMAT_YUYV}, -+ {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, -+ IPU6_FW_ISYS_FRAME_FORMAT_RGB565}, -+ {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, -+ IPU6_FW_ISYS_FRAME_FORMAT_RGBA888}, -+}; -+ -+static int video_open(struct file *file) -+{ -+ struct ipu6_isys_video *av = video_drvdata(file); -+ struct ipu6_isys *isys = av->isys; -+ struct ipu6_bus_device *adev = isys->adev; -+ -+ mutex_lock(&isys->mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->mutex); -+ dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); -+ return -EIO; -+ } -+ mutex_unlock(&isys->mutex); -+ -+ return v4l2_fh_open(file); -+} -+ -+static int video_release(struct file *file) -+{ -+ return vb2_fop_release(file); -+} -+ -+static const struct ipu6_isys_pixelformat * -+ipu6_isys_get_pixelformat(u32 pixelformat) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { -+ const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i]; -+ -+ if (pfmt->pixelformat == pixelformat) -+ return pfmt; -+ } -+ -+ return &ipu6_isys_pfmts[0]; -+} -+ -+int ipu6_isys_vidioc_querycap(struct file *file, void *fh, -+ struct v4l2_capability *cap) -+{ -+ struct ipu6_isys_video *av = video_drvdata(file); -+ -+ strscpy(cap->driver, IPU6_ISYS_NAME, sizeof(cap->driver)); -+ strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); -+ -+ return 0; -+} -+ -+int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh, -+ struct v4l2_fmtdesc *f) -+{ -+ unsigned int i, found = 0; -+ -+ if (f->index >= ARRAY_SIZE(ipu6_isys_pfmts)) -+ return -EINVAL; -+ -+ if (!f->mbus_code) { -+ f->flags = 0; -+ f->pixelformat = ipu6_isys_pfmts[f->index].pixelformat; -+ return 0; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { -+ if (f->mbus_code != ipu6_isys_pfmts[i].code) -+ continue; -+ -+ if (f->index == found) { -+ f->flags = 0; -+ f->pixelformat = ipu6_isys_pfmts[i].pixelformat; -+ return 0; -+ } -+ found++; -+ } -+ -+ return -EINVAL; -+} -+ -+static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh, -+ struct v4l2_frmsizeenum *fsize) -+{ -+ if (fsize->index > 0) -+ return -EINVAL; -+ -+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; -+ fsize->stepwise.min_width = IPU6_ISYS_MIN_WIDTH; -+ fsize->stepwise.max_width = IPU6_ISYS_MAX_WIDTH; -+ fsize->stepwise.min_height = IPU6_ISYS_MIN_HEIGHT; -+ fsize->stepwise.max_height = IPU6_ISYS_MAX_HEIGHT; -+ fsize->stepwise.step_width = 2; -+ fsize->stepwise.step_height = 2; -+ -+ return 0; -+} -+ -+static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh, -+ struct v4l2_format *fmt) -+{ -+ struct ipu6_isys_video *av = video_drvdata(file); -+ -+ fmt->fmt.pix_mp = av->mpix; -+ -+ return 0; -+} -+ -+static const struct ipu6_isys_pixelformat * -+ipu6_isys_video_try_fmt_vid_mplane(struct ipu6_isys_video *av, -+ struct v4l2_pix_format_mplane *mpix) -+{ -+ const struct ipu6_isys_pixelformat *pfmt = -+ ipu6_isys_get_pixelformat(mpix->pixelformat); -+ -+ mpix->pixelformat = pfmt->pixelformat; -+ mpix->num_planes = 1; -+ -+ mpix->width = clamp(mpix->width, IPU6_ISYS_MIN_WIDTH, -+ IPU6_ISYS_MAX_WIDTH); -+ mpix->height = clamp(mpix->height, IPU6_ISYS_MIN_HEIGHT, -+ IPU6_ISYS_MAX_HEIGHT); -+ -+ if (pfmt->bpp != pfmt->bpp_packed) -+ mpix->plane_fmt[0].bytesperline = -+ mpix->width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); -+ else -+ mpix->plane_fmt[0].bytesperline = -+ DIV_ROUND_UP((unsigned int)mpix->width * pfmt->bpp, -+ BITS_PER_BYTE); -+ -+ mpix->plane_fmt[0].bytesperline = ALIGN(mpix->plane_fmt[0].bytesperline, -+ av->isys->line_align); -+ -+ /* -+ * (height + 1) * bytesperline due to a hardware issue: the DMA unit -+ * is a power of two, and a line should be transferred as few units -+ * as possible. The result is that up to line length more data than -+ * the image size may be transferred to memory after the image. -+ * Another limitation is the GDA allocation unit size. For low -+ * resolution it gives a bigger number. Use larger one to avoid -+ * memory corruption. -+ */ -+ mpix->plane_fmt[0].sizeimage = -+ max(mpix->plane_fmt[0].sizeimage, -+ mpix->plane_fmt[0].bytesperline * mpix->height + -+ max(mpix->plane_fmt[0].bytesperline, -+ av->isys->pdata->ipdata->isys_dma_overshoot)); -+ -+ memset(mpix->plane_fmt[0].reserved, 0, -+ sizeof(mpix->plane_fmt[0].reserved)); -+ -+ mpix->field = V4L2_FIELD_NONE; -+ -+ mpix->colorspace = V4L2_COLORSPACE_RAW; -+ mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; -+ mpix->quantization = V4L2_QUANTIZATION_DEFAULT; -+ mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT; -+ -+ return pfmt; -+} -+ -+static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct ipu6_isys_video *av = video_drvdata(file); -+ -+ if (av->aq.vbq.streaming) -+ return -EBUSY; -+ -+ av->pfmt = ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp); -+ av->mpix = f->fmt.pix_mp; -+ -+ return 0; -+} -+ -+static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct ipu6_isys_video *av = video_drvdata(file); -+ -+ ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp); -+ -+ return 0; -+} -+ -+static int link_validate(struct media_link *link) -+{ -+ struct ipu6_isys_video *av = -+ container_of(link->sink, struct ipu6_isys_video, pad); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct v4l2_subdev_state *s_state; -+ struct v4l2_subdev *s_sd; -+ struct v4l2_mbus_framefmt *s_fmt; -+ struct media_pad *s_pad; -+ u32 s_stream; -+ int ret = -EPIPE; -+ -+ if (!link->source->entity) -+ return ret; -+ -+ s_sd = media_entity_to_v4l2_subdev(link->source->entity); -+ s_state = v4l2_subdev_get_unlocked_active_state(s_sd); -+ if (!s_state) -+ return ret; -+ -+ dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n", -+ link->source->entity->name, link->source->index, -+ link->sink->entity->name); -+ -+ s_pad = media_pad_remote_pad_first(&av->pad); -+ s_stream = ipu6_isys_get_src_stream_by_src_pad(s_sd, s_pad->index); -+ -+ v4l2_subdev_lock_state(s_state); -+ -+ s_fmt = v4l2_subdev_state_get_stream_format(s_state, s_pad->index, -+ s_stream); -+ if (!s_fmt) { -+ dev_err(dev, "failed to get source pad format\n"); -+ goto unlock; -+ } -+ -+ if (s_fmt->width != av->mpix.width || -+ s_fmt->height != av->mpix.height || s_fmt->code != av->pfmt->code) { -+ dev_err(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", -+ s_fmt->width, s_fmt->height, s_fmt->code, -+ av->mpix.width, av->mpix.height, av->pfmt->code); -+ goto unlock; -+ } -+ -+ v4l2_subdev_unlock_state(s_state); -+ -+ return 0; -+unlock: -+ v4l2_subdev_unlock_state(s_state); -+ -+ return ret; -+} -+ -+static void get_stream_opened(struct ipu6_isys_video *av) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&av->isys->streams_lock, flags); -+ av->isys->stream_opened++; -+ spin_unlock_irqrestore(&av->isys->streams_lock, flags); -+} -+ -+static void put_stream_opened(struct ipu6_isys_video *av) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&av->isys->streams_lock, flags); -+ av->isys->stream_opened--; -+ spin_unlock_irqrestore(&av->isys->streams_lock, flags); -+} -+ -+static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av, -+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg) -+{ -+ struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); -+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); -+ struct ipu6_fw_isys_input_pin_info_abi *input_pin; -+ struct ipu6_fw_isys_output_pin_info_abi *output_pin; -+ struct ipu6_isys_stream *stream = av->stream; -+ struct ipu6_isys_queue *aq = &av->aq; -+ struct v4l2_mbus_framefmt fmt; -+ struct v4l2_rect v4l2_crop; -+ struct ipu6_isys *isys = av->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ int input_pins = cfg->nof_input_pins++; -+ int output_pins; -+ u32 src_stream; -+ int ret; -+ -+ src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index); -+ ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, -+ &fmt); -+ if (ret < 0) { -+ dev_err(dev, "can't get stream format (%d)\n", ret); -+ return ret; -+ } -+ -+ ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream, -+ &v4l2_crop); -+ if (ret < 0) { -+ dev_err(dev, "can't get stream crop (%d)\n", ret); -+ return ret; -+ } -+ -+ input_pin = &cfg->input_pins[input_pins]; -+ input_pin->input_res.width = fmt.width; -+ input_pin->input_res.height = fmt.height; -+ input_pin->dt = av->dt; -+ input_pin->bits_per_pix = av->pfmt->bpp_packed; -+ input_pin->mapped_dt = 0x40; /* invalid mipi data type */ -+ input_pin->mipi_decompression = 0; -+ input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR; -+ input_pin->mipi_store_mode = av->pfmt->bpp == av->pfmt->bpp_packed ? -+ IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER : -+ IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL; -+ input_pin->crop_first_and_last_lines = v4l2_crop.top & 1; -+ -+ output_pins = cfg->nof_output_pins++; -+ aq->fw_output = output_pins; -+ stream->output_pins[output_pins].pin_ready = ipu6_isys_queue_buf_ready; -+ stream->output_pins[output_pins].aq = aq; -+ -+ output_pin = &cfg->output_pins[output_pins]; -+ output_pin->input_pin_id = input_pins; -+ output_pin->output_res.width = av->mpix.width; -+ output_pin->output_res.height = av->mpix.height; -+ -+ output_pin->stride = av->mpix.plane_fmt[0].bytesperline; -+ if (av->pfmt->bpp != av->pfmt->bpp_packed) -+ output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC; -+ else -+ output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI; -+ output_pin->ft = av->pfmt->css_pixelformat; -+ output_pin->send_irq = 1; -+ memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets)); -+ output_pin->s2m_pixel_soc_pixel_remapping = -+ S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING; -+ output_pin->csi_be_soc_pixel_remapping = -+ CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING; -+ -+ output_pin->snoopable = true; -+ output_pin->error_handling_enable = false; -+ output_pin->sensor_type = isys->sensor_type++; -+ if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end) -+ isys->sensor_type = isys->pdata->ipdata->sensor_type_start; -+ -+ return 0; -+} -+ -+static int start_stream_firmware(struct ipu6_isys_video *av, -+ struct ipu6_isys_buffer_list *bl) -+{ -+ struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg; -+ struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; -+ struct ipu6_isys_stream *stream = av->stream; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct isys_fw_msgs *msg = NULL; -+ struct ipu6_isys_queue *aq; -+ int ret, retout, tout; -+ u16 send_type; -+ -+ msg = ipu6_get_fw_msg_buf(stream); -+ if (!msg) -+ return -ENOMEM; -+ -+ stream_cfg = &msg->fw_msg.stream; -+ stream_cfg->src = stream->stream_source; -+ stream_cfg->vc = stream->vc; -+ stream_cfg->isl_use = 0; -+ stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL; -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq); -+ -+ ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg); -+ if (ret < 0) { -+ ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg); -+ return ret; -+ } -+ } -+ -+ ipu6_fw_isys_dump_stream_cfg(dev, stream_cfg); -+ -+ stream->nr_output_pins = stream_cfg->nof_output_pins; -+ -+ reinit_completion(&stream->stream_open_completion); -+ -+ ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle, -+ stream_cfg, msg->dma_addr, -+ sizeof(*stream_cfg), -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN); -+ if (ret < 0) { -+ dev_err(dev, "can't open stream (%d)\n", ret); -+ ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg); -+ return ret; -+ } -+ -+ get_stream_opened(av); -+ -+ tout = wait_for_completion_timeout(&stream->stream_open_completion, -+ IPU6_FW_CALL_TIMEOUT_JIFFIES); -+ -+ ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg); -+ -+ if (!tout) { -+ dev_err(dev, "stream open time out\n"); -+ ret = -ETIMEDOUT; -+ goto out_put_stream_opened; -+ } -+ if (stream->error) { -+ dev_err(dev, "stream open error: %d\n", stream->error); -+ ret = -EIO; -+ goto out_put_stream_opened; -+ } -+ dev_dbg(dev, "start stream: open complete\n"); -+ -+ if (bl) { -+ msg = ipu6_get_fw_msg_buf(stream); -+ if (!msg) { -+ ret = -ENOMEM; -+ goto out_put_stream_opened; -+ } -+ buf = &msg->fw_msg.frame; -+ ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl); -+ ipu6_isys_buffer_list_queue(bl, -+ IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -+ } -+ -+ reinit_completion(&stream->stream_start_completion); -+ -+ if (bl) { -+ send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; -+ ipu6_fw_isys_dump_frame_buff_set(dev, buf, -+ stream_cfg->nof_output_pins); -+ ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle, -+ buf, msg->dma_addr, -+ sizeof(*buf), send_type); -+ } else { -+ send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START; -+ ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ send_type); -+ } -+ -+ if (ret < 0) { -+ dev_err(dev, "can't start streaming (%d)\n", ret); -+ goto out_stream_close; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_start_completion, -+ IPU6_FW_CALL_TIMEOUT_JIFFIES); -+ if (!tout) { -+ dev_err(dev, "stream start time out\n"); -+ ret = -ETIMEDOUT; -+ goto out_stream_close; -+ } -+ if (stream->error) { -+ dev_err(dev, "stream start error: %d\n", stream->error); -+ ret = -EIO; -+ goto out_stream_close; -+ } -+ dev_dbg(dev, "start stream: complete\n"); -+ -+ return 0; -+ -+out_stream_close: -+ reinit_completion(&stream->stream_close_completion); -+ -+ retout = ipu6_fw_isys_simple_cmd(av->isys, -+ stream->stream_handle, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE); -+ if (retout < 0) { -+ dev_dbg(dev, "can't close stream (%d)\n", retout); -+ goto out_put_stream_opened; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_close_completion, -+ IPU6_FW_CALL_TIMEOUT_JIFFIES); -+ if (!tout) -+ dev_err(dev, "stream close time out\n"); -+ else if (stream->error) -+ dev_err(dev, "stream close error: %d\n", stream->error); -+ else -+ dev_dbg(dev, "stream close complete\n"); -+ -+out_put_stream_opened: -+ put_stream_opened(av); -+ -+ return ret; -+} -+ -+static void stop_streaming_firmware(struct ipu6_isys_video *av) -+{ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu6_isys_stream *stream = av->stream; -+ int ret, tout; -+ -+ reinit_completion(&stream->stream_stop_completion); -+ -+ ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH); -+ -+ if (ret < 0) { -+ dev_err(dev, "can't stop stream (%d)\n", ret); -+ return; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_stop_completion, -+ IPU6_FW_CALL_TIMEOUT_JIFFIES); -+ if (!tout) -+ dev_warn(dev, "stream stop time out\n"); -+ else if (stream->error) -+ dev_warn(dev, "stream stop error: %d\n", stream->error); -+ else -+ dev_dbg(dev, "stop stream: complete\n"); -+} -+ -+static void close_streaming_firmware(struct ipu6_isys_video *av) -+{ -+ struct ipu6_isys_stream *stream = av->stream; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ int ret, tout; -+ -+ reinit_completion(&stream->stream_close_completion); -+ -+ ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE); -+ if (ret < 0) { -+ dev_err(dev, "can't close stream (%d)\n", ret); -+ return; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_close_completion, -+ IPU6_FW_CALL_TIMEOUT_JIFFIES); -+ if (!tout) -+ dev_warn(dev, "stream close time out\n"); -+ else if (stream->error) -+ dev_warn(dev, "stream close error: %d\n", stream->error); -+ else -+ dev_dbg(dev, "close stream: complete\n"); -+ -+ put_stream_opened(av); -+} -+ -+int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av, -+ struct media_entity *source_entity, -+ int nr_queues) -+{ -+ struct ipu6_isys_stream *stream = av->stream; -+ struct ipu6_isys_csi2 *csi2; -+ -+ if (WARN_ON(stream->nr_streaming)) -+ return -EINVAL; -+ -+ stream->nr_queues = nr_queues; -+ atomic_set(&stream->sequence, 0); -+ -+ stream->seq_index = 0; -+ memset(stream->seq, 0, sizeof(stream->seq)); -+ -+ if (WARN_ON(!list_empty(&stream->queues))) -+ return -EINVAL; -+ -+ stream->stream_source = stream->asd->source; -+ csi2 = ipu6_isys_subdev_to_csi2(stream->asd); -+ csi2->receiver_errors = 0; -+ stream->source_entity = source_entity; -+ -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "prepare stream: external entity %s\n", -+ stream->source_entity->name); -+ -+ return 0; -+} -+ -+void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, -+ bool state) -+{ -+ struct ipu6_isys *isys = av->isys; -+ struct ipu6_isys_csi2 *csi2 = NULL; -+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct v4l2_mbus_framefmt format; -+ struct v4l2_subdev *esd; -+ struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 }; -+ unsigned int bpp, lanes; -+ s64 link_freq = 0; -+ u64 pixel_rate = 0; -+ int ret; -+ -+ if (!state) -+ return; -+ -+ esd = media_entity_to_v4l2_subdev(av->stream->source_entity); -+ -+ av->watermark.width = av->mpix.width; -+ av->watermark.height = av->mpix.height; -+ av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift; -+ av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size; -+ -+ ret = v4l2_g_ctrl(esd->ctrl_handler, &hb); -+ if (!ret && hb.value >= 0) -+ av->watermark.hblank = hb.value; -+ else -+ av->watermark.hblank = 0; -+ -+ csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd); -+ link_freq = ipu6_isys_csi2_get_link_freq(csi2); -+ if (link_freq > 0) { -+ lanes = csi2->nlanes; -+ ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0, -+ av->source_stream, &format); -+ if (!ret) { -+ bpp = ipu6_isys_mbus_code_to_bpp(format.code); -+ pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp); -+ } -+ } -+ -+ av->watermark.pixel_rate = pixel_rate; -+ -+ if (!pixel_rate) { -+ mutex_lock(&iwake_watermark->mutex); -+ iwake_watermark->force_iwake_disable = true; -+ mutex_unlock(&iwake_watermark->mutex); -+ dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n", -+ av->stream->source_entity->name); -+ } -+} -+ -+static void calculate_stream_datarate(struct ipu6_isys_video *av) -+{ -+ struct video_stream_watermark *watermark = &av->watermark; -+ u32 bpp = av->pfmt->bpp; -+ u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line; -+ u64 line_time_ns, stream_data_rate; -+ u16 shift, size; -+ -+ shift = watermark->sram_gran_shift; -+ size = watermark->sram_gran_size; -+ -+ pixels_per_line = watermark->width + watermark->hblank; -+ line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC, -+ watermark->pixel_rate); -+ bytes_per_line = watermark->width * bpp / 8; -+ pages_per_line = DIV_ROUND_UP(bytes_per_line, size); -+ pb_bytes_per_line = pages_per_line << shift; -+ stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns); -+ -+ watermark->stream_data_rate = stream_data_rate; -+} -+ -+void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state) -+{ -+ struct isys_iwake_watermark *iwake_watermark = -+ &av->isys->iwake_watermark; -+ -+ if (!av->watermark.pixel_rate) -+ return; -+ -+ if (state) { -+ calculate_stream_datarate(av); -+ mutex_lock(&iwake_watermark->mutex); -+ list_add(&av->watermark.stream_node, -+ &iwake_watermark->video_list); -+ mutex_unlock(&iwake_watermark->mutex); -+ } else { -+ av->watermark.stream_data_rate = 0; -+ mutex_lock(&iwake_watermark->mutex); -+ list_del(&av->watermark.stream_node); -+ mutex_unlock(&iwake_watermark->mutex); -+ } -+ -+ update_watermark_setting(av->isys); -+} -+ -+void ipu6_isys_put_stream(struct ipu6_isys_stream *stream) -+{ -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ unsigned int i; -+ unsigned long flags; -+ -+ if (!stream) { -+ dev_err(dev, "no available stream\n"); -+ return; -+ } -+ -+ spin_lock_irqsave(&stream->isys->streams_lock, flags); -+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { -+ if (&stream->isys->streams[i] == stream) { -+ if (stream->isys->streams_ref_count[i] > 0) -+ stream->isys->streams_ref_count[i]--; -+ else -+ dev_warn(dev, "invalid stream %d\n", i); -+ -+ break; -+ } -+ } -+ spin_unlock_irqrestore(&stream->isys->streams_lock, flags); -+} -+ -+static struct ipu6_isys_stream * -+ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd) -+{ -+ struct ipu6_isys_stream *stream = NULL; -+ struct ipu6_isys *isys = av->isys; -+ unsigned long flags; -+ unsigned int i; -+ u8 vc = av->vc; -+ -+ if (!isys) -+ return NULL; -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { -+ if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && -+ isys->streams[i].asd == asd) { -+ isys->streams_ref_count[i]++; -+ stream = &isys->streams[i]; -+ break; -+ } -+ } -+ -+ if (!stream) { -+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { -+ if (!isys->streams_ref_count[i]) { -+ isys->streams_ref_count[i]++; -+ stream = &isys->streams[i]; -+ stream->vc = vc; -+ stream->asd = asd; -+ break; -+ } -+ } -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+struct ipu6_isys_stream * -+ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle) -+{ -+ unsigned long flags; -+ struct ipu6_isys_stream *stream = NULL; -+ -+ if (!isys) -+ return NULL; -+ -+ if (stream_handle >= IPU6_ISYS_MAX_STREAMS) { -+ dev_err(&isys->adev->auxdev.dev, -+ "stream_handle %d is invalid\n", stream_handle); -+ return NULL; -+ } -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ if (isys->streams_ref_count[stream_handle] > 0) { -+ isys->streams_ref_count[stream_handle]++; -+ stream = &isys->streams[stream_handle]; -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+struct ipu6_isys_stream * -+ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc) -+{ -+ struct ipu6_isys_stream *stream = NULL; -+ unsigned long flags; -+ unsigned int i; -+ -+ if (!isys) -+ return NULL; -+ -+ if (source < 0) { -+ dev_err(&stream->isys->adev->auxdev.dev, -+ "query stream with invalid port number\n"); -+ return NULL; -+ } -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { -+ if (!isys->streams_ref_count[i]) -+ continue; -+ -+ if (isys->streams[i].stream_source == source && -+ isys->streams[i].vc == vc) { -+ stream = &isys->streams[i]; -+ isys->streams_ref_count[i]++; -+ break; -+ } -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *av) -+{ -+ struct media_pipeline *pipeline = -+ media_entity_pipeline(&av->vdev.entity); -+ struct media_entity *entity; -+ unsigned int i; -+ u64 stream_mask = 0; -+ -+ for (i = 0; i < NR_OF_VIDEO_DEVICE; i++) { -+ entity = &av->isys->av[i].vdev.entity; -+ if (pipeline == media_entity_pipeline(entity)) -+ stream_mask |= BIT_ULL(av->isys->av[i].source_stream); -+ } -+ -+ return stream_mask; -+} -+ -+int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, -+ struct ipu6_isys_buffer_list *bl) -+{ -+ struct v4l2_subdev_krouting *routing; -+ struct ipu6_isys_stream *stream = av->stream; -+ struct v4l2_subdev_state *subdev_state; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct v4l2_subdev *sd = NULL; -+ struct v4l2_subdev *ssd = NULL; -+ struct media_pad *r_pad; -+ struct media_pad *s_pad = NULL; -+ u32 sink_pad, sink_stream; -+ u64 r_stream; -+ u64 stream_mask = 0; -+ int ret = 0; -+ -+ dev_dbg(dev, "set stream: %d\n", state); -+ -+ if (WARN(!stream->source_entity, "No source entity for stream\n")) -+ return -ENODEV; -+ -+ ssd = media_entity_to_v4l2_subdev(stream->source_entity); -+ sd = &stream->asd->sd; -+ r_pad = media_pad_remote_pad_first(&av->pad); -+ r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); -+ -+ subdev_state = v4l2_subdev_lock_and_get_active_state(sd); -+ routing = &subdev_state->routing; -+ ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index, -+ r_stream, &sink_pad, -+ &sink_stream); -+ v4l2_subdev_unlock_state(subdev_state); -+ if (ret) -+ return ret; -+ -+ s_pad = media_pad_remote_pad_first(&stream->asd->pad[sink_pad]); -+ -+ stream_mask = get_stream_mask_by_pipeline(av); -+ if (!state) { -+ stop_streaming_firmware(av); -+ -+ /* stop external sub-device now. */ -+ dev_dbg(dev, "disable streams 0x%llx of %s\n", stream_mask, -+ ssd->name); -+ ret = v4l2_subdev_disable_streams(ssd, s_pad->index, -+ stream_mask); -+ if (ret) { -+ dev_err(dev, "disable streams of %s failed with %d\n", -+ ssd->name, ret); -+ return ret; -+ } -+ -+ /* stop sub-device which connects with video */ -+ dev_dbg(dev, "stream off entity %s pad:%d\n", sd->name, -+ r_pad->index); -+ ret = v4l2_subdev_call(sd, video, s_stream, state); -+ if (ret) { -+ dev_err(dev, "stream off %s failed with %d\n", sd->name, -+ ret); -+ return ret; -+ } -+ close_streaming_firmware(av); -+ } else { -+ ret = start_stream_firmware(av, bl); -+ if (ret) { -+ dev_err(dev, "start stream of firmware failed\n"); -+ goto out_clear_stream_watermark; -+ } -+ -+ /* start sub-device which connects with video */ -+ dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index); -+ ret = v4l2_subdev_call(sd, video, s_stream, state); -+ if (ret) { -+ dev_err(dev, "stream on %s failed with %d\n", sd->name, -+ ret); -+ goto out_media_entity_stop_streaming_firmware; -+ } -+ -+ /* start external sub-device now. */ -+ dev_dbg(dev, "enable streams 0x%llx of %s\n", stream_mask, -+ ssd->name); -+ ret = v4l2_subdev_enable_streams(ssd, s_pad->index, -+ stream_mask); -+ if (ret) { -+ dev_err(dev, -+ "enable streams 0x%llx of %s failed with %d\n", -+ stream_mask, stream->source_entity->name, ret); -+ goto out_media_entity_stop_streaming; -+ } -+ } -+ -+ av->streaming = state; -+ -+ return 0; -+ -+out_media_entity_stop_streaming: -+ v4l2_subdev_disable_streams(sd, r_pad->index, BIT(r_stream)); -+ -+out_media_entity_stop_streaming_firmware: -+ stop_streaming_firmware(av); -+ -+out_clear_stream_watermark: -+ ipu6_isys_update_stream_watermark(av, 0); -+ -+ return ret; -+} -+ -+static const struct v4l2_ioctl_ops ioctl_ops_mplane = { -+ .vidioc_querycap = ipu6_isys_vidioc_querycap, -+ .vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt, -+ .vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes, -+ .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, -+ .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, -+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, -+ .vidioc_reqbufs = vb2_ioctl_reqbufs, -+ .vidioc_create_bufs = vb2_ioctl_create_bufs, -+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -+ .vidioc_querybuf = vb2_ioctl_querybuf, -+ .vidioc_qbuf = vb2_ioctl_qbuf, -+ .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ .vidioc_streamon = vb2_ioctl_streamon, -+ .vidioc_streamoff = vb2_ioctl_streamoff, -+ .vidioc_expbuf = vb2_ioctl_expbuf, -+}; -+ -+static const struct media_entity_operations entity_ops = { -+ .link_validate = link_validate, -+}; -+ -+static const struct v4l2_file_operations isys_fops = { -+ .owner = THIS_MODULE, -+ .poll = vb2_fop_poll, -+ .unlocked_ioctl = video_ioctl2, -+ .mmap = vb2_fop_mmap, -+ .open = video_open, -+ .release = video_release, -+}; -+ -+int ipu6_isys_fw_open(struct ipu6_isys *isys) -+{ -+ struct ipu6_bus_device *adev = isys->adev; -+ const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata; -+ int ret; -+ -+ ret = pm_runtime_resume_and_get(&adev->auxdev.dev); -+ if (ret < 0) -+ return ret; -+ -+ mutex_lock(&isys->mutex); -+ -+ if (isys->ref_count++) -+ goto unlock; -+ -+ ipu6_configure_spc(adev->isp, &ipdata->hw_variant, -+ IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base, -+ adev->pkg_dir, adev->pkg_dir_dma_addr); -+ -+ /* -+ * Buffers could have been left to wrong queue at last closure. -+ * Move them now back to empty buffer queue. -+ */ -+ ipu6_cleanup_fw_msg_bufs(isys); -+ -+ if (isys->fwcom) { -+ /* -+ * Something went wrong in previous shutdown. As we are now -+ * restarting isys we can safely delete old context. -+ */ -+ dev_warn(&adev->auxdev.dev, "clearing old context\n"); -+ ipu6_fw_isys_cleanup(isys); -+ } -+ -+ ret = ipu6_fw_isys_init(isys, ipdata->num_parallel_streams); -+ if (ret < 0) -+ goto out; -+ -+unlock: -+ mutex_unlock(&isys->mutex); -+ -+ return 0; -+ -+out: -+ isys->ref_count--; -+ mutex_unlock(&isys->mutex); -+ pm_runtime_put(&adev->auxdev.dev); -+ -+ return ret; -+} -+ -+void ipu6_isys_fw_close(struct ipu6_isys *isys) -+{ -+ mutex_lock(&isys->mutex); -+ -+ isys->ref_count--; -+ if (!isys->ref_count) { -+ ipu6_fw_isys_close(isys); -+ if (isys->fwcom) { -+ isys->need_reset = true; -+ dev_warn(&isys->adev->auxdev.dev, -+ "failed to close fw isys\n"); -+ } -+ } -+ -+ mutex_unlock(&isys->mutex); -+ -+ if (isys->need_reset) -+ pm_runtime_put_sync(&isys->adev->auxdev.dev); -+ else -+ pm_runtime_put(&isys->adev->auxdev.dev); -+} -+ -+int ipu6_isys_setup_video(struct ipu6_isys_video *av, -+ struct media_entity **source_entity, int *nr_queues) -+{ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct v4l2_mbus_frame_desc_entry entry; -+ struct v4l2_subdev_route *route = NULL; -+ struct v4l2_subdev_route *r; -+ struct v4l2_subdev_state *state; -+ struct ipu6_isys_subdev *asd; -+ struct v4l2_subdev *remote_sd; -+ struct media_pipeline *pipeline; -+ struct media_pad *source_pad, *remote_pad; -+ int ret = -EINVAL; -+ -+ remote_pad = media_pad_remote_pad_first(&av->pad); -+ if (!remote_pad) { -+ dev_dbg(dev, "failed to get remote pad\n"); -+ return -ENODEV; -+ } -+ -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ asd = to_ipu6_isys_subdev(remote_sd); -+ source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); -+ if (!source_pad) { -+ dev_dbg(dev, "No external source entity\n"); -+ return -ENODEV; -+ } -+ -+ *source_entity = source_pad->entity; -+ -+ /* Find the root */ -+ state = v4l2_subdev_lock_and_get_active_state(remote_sd); -+ for_each_active_route(&state->routing, r) { -+ if (r->source_pad != remote_pad->index) -+ continue; -+ -+ route = r; -+ break; -+ } -+ -+ if (!route) { -+ v4l2_subdev_unlock_state(state); -+ dev_dbg(dev, "Failed to find route\n"); -+ return -ENODEV; -+ } -+ v4l2_subdev_unlock_state(state); -+ av->source_stream = route->sink_stream; -+ -+ ret = ipu6_isys_csi2_get_remote_desc(av->source_stream, -+ to_ipu6_isys_csi2(asd), -+ *source_entity, &entry, -+ nr_queues); -+ if (ret == -ENOIOCTLCMD) { -+ av->vc = 0; -+ av->dt = ipu6_isys_mbus_code_to_mipi(av->pfmt->code); -+ *nr_queues = 1; -+ } else if (!ret) { -+ dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", -+ entry.stream, entry.length, entry.bus.csi2.vc, -+ entry.bus.csi2.dt); -+ -+ av->vc = entry.bus.csi2.vc; -+ av->dt = entry.bus.csi2.dt; -+ } else { -+ dev_err(dev, "failed to get remote frame desc\n"); -+ return ret; -+ } -+ -+ pipeline = media_entity_pipeline(&av->vdev.entity); -+ if (!pipeline) -+ ret = video_device_pipeline_alloc_start(&av->vdev); -+ else -+ ret = video_device_pipeline_start(&av->vdev, pipeline); -+ if (ret < 0) { -+ dev_dbg(dev, "media pipeline start failed\n"); -+ return ret; -+ } -+ -+ av->stream = ipu6_isys_get_stream(av, asd); -+ if (!av->stream) { -+ video_device_pipeline_stop(&av->vdev); -+ dev_err(dev, "no available stream for firmware\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Do everything that's needed to initialise things related to video -+ * buffer queue, video node, and the related media entity. The caller -+ * is expected to assign isys field and set the name of the video -+ * device. -+ */ -+int ipu6_isys_video_init(struct ipu6_isys_video *av) -+{ -+ struct v4l2_format format = { -+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, -+ .fmt.pix_mp = { -+ .width = 1920, -+ .height = 1080, -+ }, -+ }; -+ int ret; -+ -+ mutex_init(&av->mutex); -+ av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | -+ V4L2_CAP_VIDEO_CAPTURE_MPLANE; -+ av->vdev.vfl_dir = VFL_DIR_RX; -+ -+ ret = ipu6_isys_queue_init(&av->aq); -+ if (ret) -+ goto out_free_watermark; -+ -+ av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; -+ ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); -+ if (ret) -+ goto out_vb2_queue_release; -+ -+ av->vdev.entity.ops = &entity_ops; -+ av->vdev.release = video_device_release_empty; -+ av->vdev.fops = &isys_fops; -+ av->vdev.v4l2_dev = &av->isys->v4l2_dev; -+ if (!av->vdev.ioctl_ops) -+ av->vdev.ioctl_ops = &ioctl_ops_mplane; -+ av->vdev.queue = &av->aq.vbq; -+ av->vdev.lock = &av->mutex; -+ -+ ipu6_isys_video_try_fmt_vid_mplane(av, &format.fmt.pix_mp); -+ av->mpix = format.fmt.pix_mp; -+ -+ set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); -+ video_set_drvdata(&av->vdev, av); -+ -+ ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); -+ if (ret) -+ goto out_media_entity_cleanup; -+ -+ return ret; -+ -+out_media_entity_cleanup: -+ vb2_video_unregister_device(&av->vdev); -+ media_entity_cleanup(&av->vdev.entity); -+ -+out_vb2_queue_release: -+ vb2_queue_release(&av->aq.vbq); -+ -+out_free_watermark: -+ mutex_destroy(&av->mutex); -+ -+ return ret; -+} -+ -+void ipu6_isys_video_cleanup(struct ipu6_isys_video *av) -+{ -+ vb2_video_unregister_device(&av->vdev); -+ media_entity_cleanup(&av->vdev.entity); -+ mutex_destroy(&av->mutex); -+} -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h -new file mode 100644 -index 000000000000..21cd33c7e277 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h -@@ -0,0 +1,136 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef IPU6_ISYS_VIDEO_H -+#define IPU6_ISYS_VIDEO_H -+ -+#include <linux/atomic.h> -+#include <linux/completion.h> -+#include <linux/container_of.h> -+#include <linux/list.h> -+#include <linux/mutex.h> -+ -+#include <media/media-entity.h> -+#include <media/v4l2-dev.h> -+ -+#include "ipu6-isys-queue.h" -+ -+#define IPU6_ISYS_OUTPUT_PINS 11 -+#define IPU6_ISYS_MAX_PARALLEL_SOF 2 -+#define NR_OF_VIDEO_DEVICE 31 -+ -+struct file; -+struct ipu6_isys; -+struct ipu6_isys_subdev; -+ -+struct ipu6_isys_pixelformat { -+ u32 pixelformat; -+ u32 bpp; -+ u32 bpp_packed; -+ u32 code; -+ u32 css_pixelformat; -+}; -+ -+struct sequence_info { -+ unsigned int sequence; -+ u64 timestamp; -+}; -+ -+struct output_pin_data { -+ void (*pin_ready)(struct ipu6_isys_stream *stream, -+ struct ipu6_fw_isys_resp_info_abi *info); -+ struct ipu6_isys_queue *aq; -+}; -+ -+/* -+ * Align with firmware stream. Each stream represents a CSI virtual channel. -+ * May map to multiple video devices -+ */ -+struct ipu6_isys_stream { -+ struct mutex mutex; -+ struct media_entity *source_entity; -+ atomic_t sequence; -+ unsigned int seq_index; -+ struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF]; -+ int stream_source; -+ int stream_handle; -+ unsigned int nr_output_pins; -+ struct ipu6_isys_subdev *asd; -+ -+ int nr_queues; /* Number of capture queues */ -+ int nr_streaming; -+ int streaming; /* Has streaming been really started? */ -+ struct list_head queues; -+ struct completion stream_open_completion; -+ struct completion stream_close_completion; -+ struct completion stream_start_completion; -+ struct completion stream_stop_completion; -+ struct ipu6_isys *isys; -+ -+ struct output_pin_data output_pins[IPU6_ISYS_OUTPUT_PINS]; -+ int error; -+ u8 vc; -+}; -+ -+struct video_stream_watermark { -+ u32 width; -+ u32 height; -+ u32 hblank; -+ u32 frame_rate; -+ u64 pixel_rate; -+ u64 stream_data_rate; -+ u16 sram_gran_shift; -+ u16 sram_gran_size; -+ struct list_head stream_node; -+}; -+ -+struct ipu6_isys_video { -+ struct ipu6_isys_queue aq; -+ /* Serialise access to other fields in the struct. */ -+ struct mutex mutex; -+ struct media_pad pad; -+ struct video_device vdev; -+ struct v4l2_pix_format_mplane mpix; -+ const struct ipu6_isys_pixelformat *pfmt; -+ struct ipu6_isys *isys; -+ struct ipu6_isys_stream *stream; -+ unsigned int streaming; -+ struct video_stream_watermark watermark; -+ u32 source_stream; -+ u8 vc; -+ u8 dt; -+}; -+ -+#define ipu6_isys_queue_to_video(__aq) \ -+ container_of(__aq, struct ipu6_isys_video, aq) -+ -+extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts[]; -+extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[]; -+ -+int ipu6_isys_vidioc_querycap(struct file *file, void *fh, -+ struct v4l2_capability *cap); -+ -+int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh, -+ struct v4l2_fmtdesc *f); -+int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av, -+ struct media_entity *source_entity, -+ int nr_queues); -+int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, -+ struct ipu6_isys_buffer_list *bl); -+int ipu6_isys_fw_open(struct ipu6_isys *isys); -+void ipu6_isys_fw_close(struct ipu6_isys *isys); -+int ipu6_isys_setup_video(struct ipu6_isys_video *av, -+ struct media_entity **source_entity, int *nr_queues); -+int ipu6_isys_video_init(struct ipu6_isys_video *av); -+void ipu6_isys_video_cleanup(struct ipu6_isys_video *av); -+void ipu6_isys_put_stream(struct ipu6_isys_stream *stream); -+struct ipu6_isys_stream * -+ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle); -+struct ipu6_isys_stream * -+ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc); -+ -+void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, -+ bool state); -+void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state); -+ -+#endif /* IPU6_ISYS_VIDEO_H */ --- -2.43.2 - - -From cc79447bab87ce8c498b0e7a5f849c7d4f6262c0 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:26 +0800 -Subject: [PATCH 19/33] media: add Kconfig and Makefile for IPU6 - -Add IPU6 support in Kconfig and Makefile, with this patch you can -build the Intel IPU6 and input system modules by select the -CONFIG_VIDEO_INTEL_IPU6 in config. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> -Signed-off-by: Andreas Helbech Kleist <andreaskleist@gmail.com> ---- - drivers/media/pci/intel/Kconfig | 1 + - drivers/media/pci/intel/Makefile | 1 + - drivers/media/pci/intel/ipu6/Kconfig | 17 +++++++++++++++++ - drivers/media/pci/intel/ipu6/Makefile | 23 +++++++++++++++++++++++ - 4 files changed, 42 insertions(+) - create mode 100644 drivers/media/pci/intel/ipu6/Kconfig - create mode 100644 drivers/media/pci/intel/ipu6/Makefile - -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index ee4684159d3d..04cb3d253486 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -1,6 +1,7 @@ - # SPDX-License-Identifier: GPL-2.0-only - - source "drivers/media/pci/intel/ipu3/Kconfig" -+source "drivers/media/pci/intel/ipu6/Kconfig" - source "drivers/media/pci/intel/ivsc/Kconfig" - - config IPU_BRIDGE -diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile -index f199a97e1d78..3a2cc6567159 100644 ---- a/drivers/media/pci/intel/Makefile -+++ b/drivers/media/pci/intel/Makefile -@@ -5,3 +5,4 @@ - obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o - obj-y += ipu3/ - obj-y += ivsc/ -+obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ -diff --git a/drivers/media/pci/intel/ipu6/Kconfig b/drivers/media/pci/intel/ipu6/Kconfig -new file mode 100644 -index 000000000000..5cb4f3c2d59f ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/Kconfig -@@ -0,0 +1,17 @@ -+config VIDEO_INTEL_IPU6 -+ tristate "Intel IPU6 driver" -+ depends on ACPI || COMPILE_TEST -+ depends on MEDIA_SUPPORT -+ depends on MEDIA_PCI_SUPPORT -+ depends on X86 && X86_64 -+ select IOMMU_IOVA -+ select VIDEO_V4L2_SUBDEV_API -+ select VIDEOBUF2_DMA_CONTIG -+ select V4L2_FWNODE -+ select IPU_BRIDGE -+ help -+ This is the 6th Gen Intel Image Processing Unit, found in Intel SoCs -+ and used for capturing images and video from camera sensors. -+ -+ To compile this driver, say Y here! It contains 2 modules - -+ intel_ipu6 and intel_ipu6_isys. -diff --git a/drivers/media/pci/intel/ipu6/Makefile b/drivers/media/pci/intel/ipu6/Makefile -new file mode 100644 -index 000000000000..a821b0a1567f ---- /dev/null -+++ b/drivers/media/pci/intel/ipu6/Makefile -@@ -0,0 +1,23 @@ -+# SPDX-License-Identifier: GPL-2.0-only -+ -+intel-ipu6-y := ipu6.o \ -+ ipu6-bus.o \ -+ ipu6-dma.o \ -+ ipu6-mmu.o \ -+ ipu6-buttress.o \ -+ ipu6-cpd.o \ -+ ipu6-fw-com.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6.o -+ -+intel-ipu6-isys-y := ipu6-isys.o \ -+ ipu6-isys-csi2.o \ -+ ipu6-fw-isys.o \ -+ ipu6-isys-video.o \ -+ ipu6-isys-queue.o \ -+ ipu6-isys-subdev.o \ -+ ipu6-isys-mcd-phy.o \ -+ ipu6-isys-jsl-phy.o \ -+ ipu6-isys-dwc-phy.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-isys.o --- -2.43.2 - - -From edc6bed6991727e64f1eb60c0392403c39b96ba4 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:27 +0800 -Subject: [PATCH 20/33] MAINTAINERS: add maintainers for Intel IPU6 input - system driver - -Update MAINTAINERS file for Intel IPU6 input system driver. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - MAINTAINERS | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/MAINTAINERS b/MAINTAINERS -index 1aabf1c15bb3..5346d472cb0f 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -10899,6 +10899,16 @@ F: Documentation/admin-guide/media/ipu3_rcb.svg - F: Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst - F: drivers/staging/media/ipu3/ - -+INTEL IPU6 INPUT SYSTEM DRIVER -+M: Sakari Ailus <sakari.ailus@linux.intel.com> -+M: Bingbu Cao <bingbu.cao@intel.com> -+R: Tianshu Qiu <tian.shu.qiu@intel.com> -+L: linux-media@vger.kernel.org -+S: Maintained -+T: git git://linuxtv.org/media_tree.git -+F: Documentation/admin-guide/media/ipu6-isys.rst -+F: drivers/media/pci/intel/ipu6/ -+ - INTEL ISHTP ECLITE DRIVER - M: Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com> - L: platform-driver-x86@vger.kernel.org --- -2.43.2 - - -From a12041e5f7fb32b93669f19b579bc1940a026bbe Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:28 +0800 -Subject: [PATCH 21/33] Documentation: add Intel IPU6 ISYS driver admin-guide - doc - -This document mainly describe the functionality of IPU6 and -IPU6 isys driver, and gives an example that how user can do -imaging capture with tools. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - Documentation/admin-guide/media/ipu6-isys.rst | 158 ++++++++++++++++ - .../admin-guide/media/ipu6_isys_graph.svg | 174 ++++++++++++++++++ - .../admin-guide/media/v4l-drivers.rst | 1 + - 3 files changed, 333 insertions(+) - create mode 100644 Documentation/admin-guide/media/ipu6-isys.rst - create mode 100644 Documentation/admin-guide/media/ipu6_isys_graph.svg - -diff --git a/Documentation/admin-guide/media/ipu6-isys.rst b/Documentation/admin-guide/media/ipu6-isys.rst -new file mode 100644 -index 000000000000..5e78ab88c649 ---- /dev/null -+++ b/Documentation/admin-guide/media/ipu6-isys.rst -@@ -0,0 +1,158 @@ -+.. SPDX-License-Identifier: GPL-2.0 -+ -+.. include:: <isonum.txt> -+ -+======================================================== -+Intel Image Processing Unit 6 (IPU6) Input System driver -+======================================================== -+ -+Copyright |copy| 2023 Intel Corporation -+ -+Introduction -+============ -+ -+This file documents the Intel IPU6 (6th generation Image Processing Unit) -+Input System (MIPI CSI2 receiver) drivers located under -+drivers/media/pci/intel/ipu6. -+ -+The Intel IPU6 can be found in certain Intel Chipsets but not in all SKUs: -+ -+* TigerLake -+* JasperLake -+* AlderLake -+* RaptorLake -+* MeteorLake -+ -+Intel IPU6 is made up of two components - Input System (ISYS) and Processing -+System (PSYS). -+ -+The Input System mainly works as MIPI CSI2 receiver which receives and -+processes the imaging data from the sensors and outputs the frames to memory. -+ -+There are 2 driver modules - intel_ipu6 and intel_ipu6_isys. intel_ipu6 is an -+IPU6 common driver which does PCI configuration, firmware loading and parsing, -+firmware authentication, DMA mapping and IPU-MMU (internal Memory mapping Unit) -+configuration. intel_ipu6_isys implements V4L2, Media Controller and V4L2 -+sub-device interfaces. The IPU6 ISYS driver supports camera sensors connected -+to the IPU6 ISYS through V4L2 sub-device sensor drivers. -+ -+.. Note:: See Documentation/driver-api/media/drivers/ipu6.rst for more -+ information about the IPU6 hardware. -+ -+ -+Input system driver -+=================== -+ -+The input System driver mainly configures CSI2 DPHY, constructs the firmware -+stream configuration, sends commands to firmware, gets response from hardware -+and firmware and then returns buffers to user. -+The ISYS is represented as several V4L2 sub-devices - 'Intel IPU6 CSI2 $port', -+which provide V4L2 subdev interfaces to the user space, there are also several -+video nodes for each CSI-2 stream capture - 'Intel IPU6 ISYS capture $num' which -+provide interface to user to set formats, queue buffers and streaming. -+ -+.. kernel-figure:: ipu6_isys_graph.svg -+ :alt: ipu6 isys media graph with multiple streams support -+ -+ ipu6 isys media graph with multiple streams support -+ -+Capturing frames by IPU6 ISYS -+----------------------------- -+ -+IPU6 ISYS is used to capture frames from the camera sensors connected to the -+CSI2 ports. The supported input formats of ISYS are listed in table below: -+ -+.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}| -+ -+.. flat-table:: -+ :header-rows: 1 -+ -+ * - IPU6 ISYS supported input formats -+ -+ * - RGB565, RGB888 -+ -+ * - UYVY8, YUYV8 -+ -+ * - RAW8, RAW10, RAW12 -+ -+.. _ipu6_isys_capture_examples: -+ -+Examples -+~~~~~~~~ -+Here is an example of IPU6 ISYS raw capture on Dell XPS 9315 laptop. On this -+machine, ov01a10 sensor is connected to IPU ISYS CSI2 port 2, which can -+generate images at sBGGR10 with resolution 1280x800. -+ -+Using the media controller APIs, we can configure ov01a10 sensor by -+media-ctl [#f1]_ and yavta [#f2]_ to transmit frames to IPU6 ISYS. -+ -+.. code-block:: none -+ -+ # Example 1 capture frame from ov01a10 camera sensor -+ # This example assumes /dev/media0 as the IPU ISYS media device -+ export MDEV=/dev/media0 -+ -+ # Establish the link for the media devices using media-ctl -+ media-ctl -d $MDEV -l "\"ov01a10 3-0036\":0 -> \"Intel IPU6 CSI2 2\":0[1]" -+ -+ # Set the format for the media devices -+ media-ctl -d $MDEV -V "ov01a10:0 [fmt:SBGGR10/1280x800]" -+ media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:0 [fmt:SBGGR10/1280x800]" -+ media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:1 [fmt:SBGGR10/1280x800]" -+ -+Once the media pipeline is configured, desired sensor specific settings -+(such as exposure and gain settings) can be set, using the yavta tool. -+ -+e.g -+ -+.. code-block:: none -+ -+ # and that ov01a10 sensor is connected to i2c bus 3 with address 0x36 -+ export SDEV=$(media-ctl -d $MDEV -e "ov01a10 3-0036") -+ -+ yavta -w 0x009e0903 400 $SDEV -+ yavta -w 0x009e0913 1000 $SDEV -+ yavta -w 0x009e0911 2000 $SDEV -+ -+Once the desired sensor settings are set, frame captures can be done as below. -+ -+e.g -+ -+.. code-block:: none -+ -+ yavta --data-prefix -u -c10 -n5 -I -s 1280x800 --file=/tmp/frame-#.bin \ -+ -f SBGGR10 $(media-ctl -d $MDEV -e "Intel IPU6 ISYS Capture 0") -+ -+With the above command, 10 frames are captured at 1280x800 resolution with -+sBGGR10 format. The captured frames are available as /tmp/frame-#.bin files. -+ -+Here is another example of IPU6 ISYS RAW and metadata capture from camera -+sensor ov2740 on Lenovo X1 Yoga laptop. -+ -+.. code-block:: none -+ -+ media-ctl -l "\"ov2740 14-0036\":0 -> \"Intel IPU6 CSI2 1\":0[1]" -+ media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[5]" -+ media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[5]" -+ -+ # set routing -+ media-ctl -v -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]" -+ -+ media-ctl -v "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]" -+ media-ctl -v "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]" -+ media-ctl -v "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]" -+ media-ctl -v "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]" -+ -+ CAPTURE_DEV=$(media-ctl -e "Intel IPU6 ISYS Capture 0") -+ ./yavta --data-prefix -c100 -n5 -I -s1932x1092 --file=/tmp/frame-#.bin \ -+ -f SGRBG10 ${CAPTURE_DEV} -+ -+ CAPTURE_META=$(media-ctl -e "Intel IPU6 ISYS Capture 1") -+ ./yavta --data-prefix -c100 -n5 -I -s97x1 -B meta-capture \ -+ --file=/tmp/meta-#.bin -f GENERIC_8 ${CAPTURE_META} -+ -+References -+========== -+ -+.. [#f1] https://git.ideasonboard.org/?p=media-ctl.git;a=summary -+.. [#f2] https://git.ideasonboard.org/yavta.git -diff --git a/Documentation/admin-guide/media/ipu6_isys_graph.svg b/Documentation/admin-guide/media/ipu6_isys_graph.svg -new file mode 100644 -index 000000000000..707747c75280 ---- /dev/null -+++ b/Documentation/admin-guide/media/ipu6_isys_graph.svg -@@ -0,0 +1,174 @@ -+<?xml version="1.0" encoding="UTF-8" standalone="no"?> -+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" -+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -+<!-- Generated by graphviz version 2.38.0 (20140413.2041) -+ --> -+<!-- Title: board Pages: 1 --> -+<svg width="559pt" height="810pt" -+ viewBox="0.00 0.00 559.00 809.50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> -+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 805.5)"> -+<title>board</title> -+<polygon fill="white" stroke="none" points="-4,4 -4,-805.5 555,-805.5 555,4 -4,4"/> -+<!-- n00000001 --> -+<g id="node1" class="node"><title>n00000001</title> -+<polygon fill="#66cd00" stroke="black" points="551,-192.5 387,-192.5 387,-154.5 551,-154.5 551,-192.5"/> -+<text text-anchor="middle" x="469" y="-177.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 0</text> -+<text text-anchor="middle" x="469" y="-162.3" font-family="Times,serif" font-size="14.00">/dev/video0</text> -+</g> -+<!-- n00000002 --> -+<g id="node2" class="node"><title>n00000002</title> -+<polygon fill="#66cd00" stroke="black" points="551,-395.5 387,-395.5 387,-357.5 551,-357.5 551,-395.5"/> -+<text text-anchor="middle" x="469" y="-380.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 1</text> -+<text text-anchor="middle" x="469" y="-365.3" font-family="Times,serif" font-size="14.00">/dev/video1</text> -+</g> -+<!-- n00000003 --> -+<g id="node3" class="node"><title>n00000003</title> -+<polygon fill="#66cd00" stroke="black" points="551,-598.5 387,-598.5 387,-560.5 551,-560.5 551,-598.5"/> -+<text text-anchor="middle" x="469" y="-583.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 2</text> -+<text text-anchor="middle" x="469" y="-568.3" font-family="Times,serif" font-size="14.00">/dev/video2</text> -+</g> -+<!-- n00000004 --> -+<g id="node4" class="node"><title>n00000004</title> -+<polygon fill="#66cd00" stroke="black" points="551,-801.5 387,-801.5 387,-763.5 551,-763.5 551,-801.5"/> -+<text text-anchor="middle" x="469" y="-786.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 3</text> -+<text text-anchor="middle" x="469" y="-771.3" font-family="Times,serif" font-size="14.00">/dev/video3</text> -+</g> -+<!-- n0000007d --> -+<g id="node5" class="node"><title>n0000007d</title> -+<path fill="#ffb90f" stroke="black" d="M201,-0.5C201,-0.5 339,-0.5 339,-0.5 345,-0.5 351,-6.5 351,-12.5 351,-12.5 351,-172.5 351,-172.5 351,-178.5 345,-184.5 339,-184.5 339,-184.5 201,-184.5 201,-184.5 195,-184.5 189,-178.5 189,-172.5 189,-172.5 189,-12.5 189,-12.5 189,-6.5 195,-0.5 201,-0.5"/> -+<text text-anchor="middle" x="200.5" y="-88.8" font-family="Times,serif" font-size="14.00">0</text> -+<polyline fill="none" stroke="black" points="212,-0.5 212,-184.5 "/> -+<text text-anchor="middle" x="270" y="-96.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 0</text> -+<text text-anchor="middle" x="270" y="-81.3" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev0</text> -+<polyline fill="none" stroke="black" points="328,-0.5 328,-184.5 "/> -+<text text-anchor="middle" x="339.5" y="-169.3" font-family="Times,serif" font-size="14.00">1</text> -+<polyline fill="none" stroke="black" points="328,-161.5 351,-161.5 "/> -+<text text-anchor="middle" x="339.5" y="-146.3" font-family="Times,serif" font-size="14.00">2</text> -+<polyline fill="none" stroke="black" points="328,-138.5 351,-138.5 "/> -+<text text-anchor="middle" x="339.5" y="-123.3" font-family="Times,serif" font-size="14.00">3</text> -+<polyline fill="none" stroke="black" points="328,-115.5 351,-115.5 "/> -+<text text-anchor="middle" x="339.5" y="-100.3" font-family="Times,serif" font-size="14.00">4</text> -+<polyline fill="none" stroke="black" points="328,-92.5 351,-92.5 "/> -+<text text-anchor="middle" x="339.5" y="-77.3" font-family="Times,serif" font-size="14.00">5</text> -+<polyline fill="none" stroke="black" points="328,-69.5 351,-69.5 "/> -+<text text-anchor="middle" x="339.5" y="-54.3" font-family="Times,serif" font-size="14.00">6</text> -+<polyline fill="none" stroke="black" points="328,-46.5 351,-46.5 "/> -+<text text-anchor="middle" x="339.5" y="-31.3" font-family="Times,serif" font-size="14.00">7</text> -+<polyline fill="none" stroke="black" points="328,-23.5 351,-23.5 "/> -+<text text-anchor="middle" x="339.5" y="-8.3" font-family="Times,serif" font-size="14.00">8</text> -+</g> -+<!-- n0000007d->n00000001 --> -+<g id="edge1" class="edge"><title>n0000007d:port1->n00000001</title> -+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-173.5C359.322,-173.5 367.976,-173.5 376.644,-173.5"/> -+<polygon fill="black" stroke="black" points="376.807,-177 386.807,-173.5 376.807,-170 376.807,-177"/> -+</g> -+<!-- n00000087 --> -+<g id="node6" class="node"><title>n00000087</title> -+<path fill="#ffb90f" stroke="black" d="M201,-203.5C201,-203.5 339,-203.5 339,-203.5 345,-203.5 351,-209.5 351,-215.5 351,-215.5 351,-375.5 351,-375.5 351,-381.5 345,-387.5 339,-387.5 339,-387.5 201,-387.5 201,-387.5 195,-387.5 189,-381.5 189,-375.5 189,-375.5 189,-215.5 189,-215.5 189,-209.5 195,-203.5 201,-203.5"/> -+<text text-anchor="middle" x="200.5" y="-291.8" font-family="Times,serif" font-size="14.00">0</text> -+<polyline fill="none" stroke="black" points="212,-203.5 212,-387.5 "/> -+<text text-anchor="middle" x="270" y="-299.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 1</text> -+<text text-anchor="middle" x="270" y="-284.3" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev1</text> -+<polyline fill="none" stroke="black" points="328,-203.5 328,-387.5 "/> -+<text text-anchor="middle" x="339.5" y="-372.3" font-family="Times,serif" font-size="14.00">1</text> -+<polyline fill="none" stroke="black" points="328,-364.5 351,-364.5 "/> -+<text text-anchor="middle" x="339.5" y="-349.3" font-family="Times,serif" font-size="14.00">2</text> -+<polyline fill="none" stroke="black" points="328,-341.5 351,-341.5 "/> -+<text text-anchor="middle" x="339.5" y="-326.3" font-family="Times,serif" font-size="14.00">3</text> -+<polyline fill="none" stroke="black" points="328,-318.5 351,-318.5 "/> -+<text text-anchor="middle" x="339.5" y="-303.3" font-family="Times,serif" font-size="14.00">4</text> -+<polyline fill="none" stroke="black" points="328,-295.5 351,-295.5 "/> -+<text text-anchor="middle" x="339.5" y="-280.3" font-family="Times,serif" font-size="14.00">5</text> -+<polyline fill="none" stroke="black" points="328,-272.5 351,-272.5 "/> -+<text text-anchor="middle" x="339.5" y="-257.3" font-family="Times,serif" font-size="14.00">6</text> -+<polyline fill="none" stroke="black" points="328,-249.5 351,-249.5 "/> -+<text text-anchor="middle" x="339.5" y="-234.3" font-family="Times,serif" font-size="14.00">7</text> -+<polyline fill="none" stroke="black" points="328,-226.5 351,-226.5 "/> -+<text text-anchor="middle" x="339.5" y="-211.3" font-family="Times,serif" font-size="14.00">8</text> -+</g> -+<!-- n00000087->n00000002 --> -+<g id="edge2" class="edge"><title>n00000087:port1->n00000002</title> -+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-376.5C359.322,-376.5 367.976,-376.5 376.644,-376.5"/> -+<polygon fill="black" stroke="black" points="376.807,-380 386.807,-376.5 376.807,-373 376.807,-380"/> -+</g> -+<!-- n00000091 --> -+<g id="node7" class="node"><title>n00000091</title> -+<path fill="#ffb90f" stroke="black" d="M201,-406.5C201,-406.5 339,-406.5 339,-406.5 345,-406.5 351,-412.5 351,-418.5 351,-418.5 351,-578.5 351,-578.5 351,-584.5 345,-590.5 339,-590.5 339,-590.5 201,-590.5 201,-590.5 195,-590.5 189,-584.5 189,-578.5 189,-578.5 189,-418.5 189,-418.5 189,-412.5 195,-406.5 201,-406.5"/> -+<text text-anchor="middle" x="200.5" y="-494.8" font-family="Times,serif" font-size="14.00">0</text> -+<polyline fill="none" stroke="black" points="212,-406.5 212,-590.5 "/> -+<text text-anchor="middle" x="270" y="-502.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 2</text> -+<text text-anchor="middle" x="270" y="-487.3" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev2</text> -+<polyline fill="none" stroke="black" points="328,-406.5 328,-590.5 "/> -+<text text-anchor="middle" x="339.5" y="-575.3" font-family="Times,serif" font-size="14.00">1</text> -+<polyline fill="none" stroke="black" points="328,-567.5 351,-567.5 "/> -+<text text-anchor="middle" x="339.5" y="-552.3" font-family="Times,serif" font-size="14.00">2</text> -+<polyline fill="none" stroke="black" points="328,-544.5 351,-544.5 "/> -+<text text-anchor="middle" x="339.5" y="-529.3" font-family="Times,serif" font-size="14.00">3</text> -+<polyline fill="none" stroke="black" points="328,-521.5 351,-521.5 "/> -+<text text-anchor="middle" x="339.5" y="-506.3" font-family="Times,serif" font-size="14.00">4</text> -+<polyline fill="none" stroke="black" points="328,-498.5 351,-498.5 "/> -+<text text-anchor="middle" x="339.5" y="-483.3" font-family="Times,serif" font-size="14.00">5</text> -+<polyline fill="none" stroke="black" points="328,-475.5 351,-475.5 "/> -+<text text-anchor="middle" x="339.5" y="-460.3" font-family="Times,serif" font-size="14.00">6</text> -+<polyline fill="none" stroke="black" points="328,-452.5 351,-452.5 "/> -+<text text-anchor="middle" x="339.5" y="-437.3" font-family="Times,serif" font-size="14.00">7</text> -+<polyline fill="none" stroke="black" points="328,-429.5 351,-429.5 "/> -+<text text-anchor="middle" x="339.5" y="-414.3" font-family="Times,serif" font-size="14.00">8</text> -+</g> -+<!-- n00000091->n00000003 --> -+<g id="edge3" class="edge"><title>n00000091:port1->n00000003</title> -+<path fill="none" stroke="black" d="M351,-579.5C359.322,-579.5 367.976,-579.5 376.644,-579.5"/> -+<polygon fill="black" stroke="black" points="376.807,-583 386.807,-579.5 376.807,-576 376.807,-583"/> -+</g> -+<!-- n0000009b --> -+<g id="node8" class="node"><title>n0000009b</title> -+<path fill="#ffb90f" stroke="black" d="M201,-609.5C201,-609.5 339,-609.5 339,-609.5 345,-609.5 351,-615.5 351,-621.5 351,-621.5 351,-781.5 351,-781.5 351,-787.5 345,-793.5 339,-793.5 339,-793.5 201,-793.5 201,-793.5 195,-793.5 189,-787.5 189,-781.5 189,-781.5 189,-621.5 189,-621.5 189,-615.5 195,-609.5 201,-609.5"/> -+<text text-anchor="middle" x="200.5" y="-697.8" font-family="Times,serif" font-size="14.00">0</text> -+<polyline fill="none" stroke="black" points="212,-609.5 212,-793.5 "/> -+<text text-anchor="middle" x="270" y="-705.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 3</text> -+<text text-anchor="middle" x="270" y="-690.3" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev3</text> -+<polyline fill="none" stroke="black" points="328,-609.5 328,-793.5 "/> -+<text text-anchor="middle" x="339.5" y="-778.3" font-family="Times,serif" font-size="14.00">1</text> -+<polyline fill="none" stroke="black" points="328,-770.5 351,-770.5 "/> -+<text text-anchor="middle" x="339.5" y="-755.3" font-family="Times,serif" font-size="14.00">2</text> -+<polyline fill="none" stroke="black" points="328,-747.5 351,-747.5 "/> -+<text text-anchor="middle" x="339.5" y="-732.3" font-family="Times,serif" font-size="14.00">3</text> -+<polyline fill="none" stroke="black" points="328,-724.5 351,-724.5 "/> -+<text text-anchor="middle" x="339.5" y="-709.3" font-family="Times,serif" font-size="14.00">4</text> -+<polyline fill="none" stroke="black" points="328,-701.5 351,-701.5 "/> -+<text text-anchor="middle" x="339.5" y="-686.3" font-family="Times,serif" font-size="14.00">5</text> -+<polyline fill="none" stroke="black" points="328,-678.5 351,-678.5 "/> -+<text text-anchor="middle" x="339.5" y="-663.3" font-family="Times,serif" font-size="14.00">6</text> -+<polyline fill="none" stroke="black" points="328,-655.5 351,-655.5 "/> -+<text text-anchor="middle" x="339.5" y="-640.3" font-family="Times,serif" font-size="14.00">7</text> -+<polyline fill="none" stroke="black" points="328,-632.5 351,-632.5 "/> -+<text text-anchor="middle" x="339.5" y="-617.3" font-family="Times,serif" font-size="14.00">8</text> -+</g> -+<!-- n0000009b->n00000004 --> -+<g id="edge4" class="edge"><title>n0000009b:port1->n00000004</title> -+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-782.5C359.322,-782.5 367.976,-782.5 376.644,-782.5"/> -+<polygon fill="black" stroke="black" points="376.807,-786 386.807,-782.5 376.807,-779 376.807,-786"/> -+</g> -+<!-- n00000865 --> -+<g id="node9" class="node"><title>n00000865</title> -+<path fill="cornflowerblue" stroke="black" d="M12,-479.5C12,-479.5 141,-479.5 141,-479.5 147,-479.5 153,-485.5 153,-491.5 153,-491.5 153,-505.5 153,-505.5 153,-511.5 147,-517.5 141,-517.5 141,-517.5 12,-517.5 12,-517.5 6,-517.5 0,-511.5 0,-505.5 0,-505.5 0,-491.5 0,-491.5 0,-485.5 6,-479.5 12,-479.5"/> -+<text text-anchor="middle" x="10" y="-494.8" font-family="Times,serif" font-size="14.00"> </text> -+<polyline fill="none" stroke="black" points="20,-479.5 20,-517.5 "/> -+<text text-anchor="middle" x="75" y="-502.3" font-family="Times,serif" font-size="14.00">ov01a10 3-0036</text> -+<text text-anchor="middle" x="75" y="-487.3" font-family="Times,serif" font-size="14.00">/dev/v4l-subdev4</text> -+<polyline fill="none" stroke="black" points="130,-479.5 130,-517.5 "/> -+<text text-anchor="middle" x="141.5" y="-494.8" font-family="Times,serif" font-size="14.00">0</text> -+</g> -+<!-- n00000865->n00000091 --> -+<g id="edge5" class="edge"><title>n00000865:port0->n00000091:port0</title> -+<path fill="none" stroke="black" d="M153,-498.5C165,-498.5 170.25,-498.5 178.875,-498.5"/> -+<polygon fill="black" stroke="black" points="179,-502 189,-498.5 179,-495 179,-502"/> -+</g> -+<!-- n00000866 --> -+<!-- n00000866->n0000007d --> -+<!-- n00000867 --> -+<!-- n00000867->n00000087 --> -+<!-- n00000868 --> -+<!-- n00000868->n0000009b --> -+</g> -+</svg> -diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst -index f4bb2605f07e..4120eded9a13 100644 ---- a/Documentation/admin-guide/media/v4l-drivers.rst -+++ b/Documentation/admin-guide/media/v4l-drivers.rst -@@ -16,6 +16,7 @@ Video4Linux (V4L) driver-specific documentation - imx - imx7 - ipu3 -+ ipu6-isys - ivtv - mgb4 - omap3isp --- -2.43.2 - - -From 3e80683ecc9ffe38fdf6e6232089794b6019816b Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:29 +0800 -Subject: [PATCH 22/33] Documentation: add documentation of Intel IPU6 driver - and hardware overview - -Add a documentation for an overview of IPU6 hardware and describe the main -the components of IPU6 driver. - -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - .../driver-api/media/drivers/index.rst | 1 + - .../driver-api/media/drivers/ipu6.rst | 205 ++++++++++++++++++ - 2 files changed, 206 insertions(+) - create mode 100644 Documentation/driver-api/media/drivers/ipu6.rst - -diff --git a/Documentation/driver-api/media/drivers/index.rst b/Documentation/driver-api/media/drivers/index.rst -index c4123a16b5f9..7f6f3dcd5c90 100644 ---- a/Documentation/driver-api/media/drivers/index.rst -+++ b/Documentation/driver-api/media/drivers/index.rst -@@ -26,6 +26,7 @@ Video4Linux (V4L) drivers - vimc-devel - zoran - ccs/ccs -+ ipu6 - - - Digital TV drivers -diff --git a/Documentation/driver-api/media/drivers/ipu6.rst b/Documentation/driver-api/media/drivers/ipu6.rst -new file mode 100644 -index 000000000000..b6357155c13b ---- /dev/null -+++ b/Documentation/driver-api/media/drivers/ipu6.rst -@@ -0,0 +1,205 @@ -+.. SPDX-License-Identifier: GPL-2.0 -+ -+================== -+Intel IPU6 Driver -+================== -+ -+Author: Bingbu Cao <bingbu.cao@intel.com> -+ -+Overview -+========= -+ -+Intel IPU6 is the sixth generation of Intel Image Processing Unit used in some -+Intel Chipsets such as Tiger Lake, Jasper Lake, Alder Lake, Raptor Lake and -+Meteor Lake. IPU6 consists of two major systems: Input System (IS) and -+Processing System (PS). IPU6 are visible on the PCI bus as a single device, -+it can be found by ``lspci``: -+ -+``0000:00:05.0 Multimedia controller: Intel Corporation Device xxxx (rev xx)`` -+ -+IPU6 has a 16 MB BAR in PCI configuration Space for MMIO registers which is -+visible for driver. -+ -+Buttress -+========= -+ -+The IPU6 is connecting to the system fabric with ``Buttress`` which is enabling -+host driver to control the IPU6, it also allows IPU6 access the system memory to -+store and load frame pixel streams and any other metadata. -+ -+``Buttress`` mainly manages several system functionalities - power management, -+interrupt handling, firmware authentication and global timer sync. -+ -+IS and PS Power flow -+--------------------------- -+ -+IPU6 driver initialize the IS and PS power up or down request by setting the -+Buttress frequency control register for IS and PS - -+``IPU6_BUTTRESS_REG_IS_FREQ_CTL`` and ``IPU6_BUTTRESS_REG_PS_FREQ_CTL`` in -+function: -+ -+.. c:function:: int ipu6_buttress_power(..., bool on) -+ -+Buttress forwards the request to Punit, after Punit execute the power up flow, -+buttress indicates driver that IS or PS is powered up by updating the power -+status registers. -+ -+.. Note:: IS power up needs take place prior to PS power up, IS power down needs -+ take place after PS power down due to hardware limitation. -+ -+ -+Interrupt -+------------ -+ -+IPU6 interrupt can be generated as MSI or INTA, interrupt will be triggered -+when IS, PS, Buttress event or error happen, driver can get the interrupt -+cause by reading the interrupt status register ``BUTTRESS_REG_ISR_STATUS``, -+driver firstly clear the irq status and then call specific IS or PS irq handler. -+ -+.. c:function:: irqreturn_t ipu6_buttress_isr(int irq, ...) -+ -+Security and firmware authentication -+------------------------------------- -+To address the IPU6 firmware security concerns, the IPU6 firmware needs to -+undergo an authentication process before it is allowed to executed on the IPU6 -+internal processors. Driver will work with Converged Security Engine (CSE) to -+complete authentication process. CSE is responsible of authenticating the -+IPU6 firmware, the authenticated firmware binary is copied into an isolated -+memory region. Firmware authentication process is implemented by CSE following -+an IPC handshake with driver. There are some Buttress registers used by CSE and -+driver to communicate with each other as IPC messages. -+ -+.. c:function:: int ipu6_buttress_authenticate(...) -+ -+Global timer sync -+------------------ -+IPU driver initiates a Hammock Harbor synchronization flow each time it starts -+camera operation. IPU will synchronizes an internal counter in the Buttress -+with a copy of SoC time, this counter keeps the updated time until camera -+operation is stopped. Driver can use this time counter to calibrate the -+timestamp based on the timestamp in response event from firmware. -+ -+.. c:function:: int ipu6_buttress_start_tsc_sync(...) -+ -+ -+DMA and MMU -+============ -+ -+IPU6 has its own scalar processor where the firmware run at, it has -+an internal 32-bits virtual address space. IPU6 has MMU address translation -+hardware to allow that scalar process access the internal memory and external -+system memory through IPU6 virtual address. The address translation is -+based on two levels of page lookup tables stored in system memory which are -+maintained by IPU6 driver. IPU6 driver sets the level-1 page table base address -+to MMU register and allow MMU to lookup the page table. -+ -+IPU6 driver exports its own DMA operations. Driver will update the page table -+entries for each DMA operation and invalidate the MMU TLB after each unmap and -+free. -+ -+.. code-block:: none -+ -+ const struct dma_map_ops ipu6_dma_ops = { -+ .alloc = ipu6_dma_alloc, -+ .free = ipu6_dma_free, -+ .mmap = ipu6_dma_mmap, -+ .map_sg = ipu6_dma_map_sg, -+ .unmap_sg = ipu6_dma_unmap_sg, -+ ... -+ }; -+ -+.. Note:: IPU6 MMU works behind IOMMU, so for each IPU6 DMA ops, driver will -+ call generic PCI DMA ops to ask IOMMU to do the additional mapping -+ if VT-d enabled. -+ -+ -+Firmware file format -+===================== -+ -+IPU6 release the firmware in Code Partition Directory (CPD) file format. The -+CPD firmware contains a CPD header, several CPD entries and CPD components. -+CPD component includes 3 entries - manifest, metadata and module data. Manifest -+and metadata are defined by CSE and used by CSE for authentication. Module data -+is defined by IPU6 which holds the binary data of firmware called package -+directory. IPU6 driver (``ipu6-cpd.c``) parses and validates the CPD firmware -+file and get the package directory binary data of IPU6 firmware, copy it to -+specific DMA buffer and sets its base address to Buttress ``FW_SOURCE_BASE`` -+register, CSE will do authentication for this firmware binary. -+ -+ -+Syscom interface -+================ -+ -+IPU6 driver communicates with firmware via syscom ABI. Syscom is an -+inter-processor communication mechanism between IPU scalar processor and CPU. -+There are a number of resources shared between firmware and software. -+A system memory region where the message queues reside, firmware can access the -+memory region via IPU MMU. Syscom queues are FIFO fixed depth queues with -+configurable elements ``token`` (message). There is also a common IPU MMIO -+registers where the queue read and write indices reside. Software and firmware -+work as producer and consumer of tokens in queue, and update the write and read -+indices separately when sending or receiving each message. -+ -+IPU6 driver must prepare and configure the number of input and output queues, -+configure the count of tokens per queue and the size of per token before -+initiate and start the communication with firmware, firmware and software must -+use same configurations. IPU6 Buttress has a number of firmware boot parameter -+registers which can be used to store the address of configuration and initiate -+the Syscom state, then driver can request firmware to start and run via setting -+the scalar processor control status register. -+ -+ -+Input System -+============== -+ -+IPU6 input system consists of MIPI D-PHY and several CSI receiver controllers, -+it can capture image pixel data from camera sensors or other MIPI CSI output -+devices. -+ -+D-PHYs and CSI-2 ports lane mapping -+----------------------------------- -+ -+IPU6 integrates different D-PHY IPs on different SoCs, on Tiger Lake and Alder -+Lake, IPU6 integrates MCD10 D-PHY, IPU6SE on Jasper Lake integrates JSL D-PHY -+and IPU6EP on Meteor Lake integrates a Synopsys DWC D-PHY. There is an adaption -+layer between D-PHY and CSI receiver controller which includes port -+configuration, PHY wrapper or private test interfaces for D-PHY. There are 3 -+D-PHY drivers ``ipu6-isys-mcd-phy.c``, ``ipu6-isys-jsl-phy.c`` and -+``ipu6-isys-dwc-phy.c`` program the above 3 D-PHYs in IPU6. -+ -+Different IPU6 version has different D-PHY lanes mappings, On Tiger Lake, there -+are 12 data lanes and 8 clock lanes, IPU6 support maximum 8 CSI-2 ports, see -+the ppi mmapping in ``ipu6-isys-mcd-phy.c`` for more information. On Jasper Lake -+and Alder Lake, D-PHY has 8 data lanes and 4 clock lanes, IPU6 support maximum 4 -+CSI-2 ports. For Meteor Lake, D-PHY has 12 data lanes and 6 clock lanes, IPU6 -+support maximum 6 CSI-2 ports. -+ -+.. Note:: Each adjacent CSI ports work as a pair and share the data lanes. -+ For example, for CSI port 0 and 1, CSI port 0 support maximum 4 -+ data lanes, CSI port 1 support maximum 2 data lanes, CSI port 0 -+ with 2 data lanes can work together with CSI port 1 with 2 data lanes. -+ If trying to use CSI port 0 with 4 lanes, CSI port 1 will not be -+ available as the 4 data lanes are shared by CSI port 0 and 1. Same -+ scenario is also applied for CSI port 2/3, 4/5 and 7/8. -+ -+IS firmware ABIs -+---------------- -+ -+IPU6 firmware define a series of ABIs to software. In general, software firstly -+prepare the stream configuration ``struct ipu6_fw_isys_stream_cfg_data_abi`` -+and send the configuration to firmware via sending ``STREAM_OPEN`` command. -+Stream configuration includes input pins and output pins, input pin -+``struct ipu6_fw_isys_input_pin_info_abi`` defines the resolution and data type -+of input source, output pin ``struct ipu6_fw_isys_output_pin_info_abi`` -+defines the output resolution, stride and frame format, etc. Once driver get the -+interrupt from firmware that indicates stream open successfully, driver will -+send the ``STREAM_START`` and ``STREAM_CAPTURE`` command to request firmware to -+start capturing image frames. ``STREAM_CAPTURE`` command queues the buffers to -+firmware with ``struct ipu6_fw_isys_frame_buff_set``, software then wait the -+interrupt and response from firmware, ``PIN_DATA_READY`` means data ready -+on specific output pin and then software return the buffers to user. -+ -+.. Note:: See :ref:`Examples<ipu6_isys_capture_examples>` about how to do -+ capture by IPU6 IS driver. -+ -+ --- -2.43.2 - - -From d883f3386e7185d9404cb25e32df986656a4e82a Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:30 +0800 -Subject: [PATCH 23/33] media: ipu6/isys: support line-based metadata capture - support - -Some camera sensor can output the embedded data in specific -data type. This patch add the support for embedded data capture -in IPU6 IS driver. - -It's based on Sakari's line-based metadata capture support change: -<URL:https://git.linuxtv.org/sailus/media_tree.git/log/?h=metadata> - -Signed-off-by: Hongju Wang <hongju.wang@intel.com> -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c | 5 + - .../media/pci/intel/ipu6/ipu6-isys-queue.c | 44 ++-- - .../media/pci/intel/ipu6/ipu6-isys-subdev.c | 5 + - .../media/pci/intel/ipu6/ipu6-isys-video.c | 201 +++++++++++++++--- - .../media/pci/intel/ipu6/ipu6-isys-video.h | 7 +- - 5 files changed, 216 insertions(+), 46 deletions(-) - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c -index ac9fa3e0d7ab..a6430d531129 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c -@@ -42,6 +42,11 @@ static const u32 csi2_supported_codes[] = { - MEDIA_BUS_FMT_SGBRG8_1X8, - MEDIA_BUS_FMT_SGRBG8_1X8, - MEDIA_BUS_FMT_SRGGB8_1X8, -+ MEDIA_BUS_FMT_META_8, -+ MEDIA_BUS_FMT_META_10, -+ MEDIA_BUS_FMT_META_12, -+ MEDIA_BUS_FMT_META_16, -+ MEDIA_BUS_FMT_META_24, - 0 - }; - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c -index 735d2d642d87..15fa7ed22b2f 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c -@@ -35,11 +35,14 @@ static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, - /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ - if (!*num_planes) { - use_fmt = true; -- *num_planes = av->mpix.num_planes; -+ if (av->vfmt.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) -+ *num_planes = av->vfmt.fmt.pix_mp.num_planes; -+ else if (av->vfmt.type == V4L2_BUF_TYPE_META_CAPTURE) -+ *num_planes = 1; - } - - for (i = 0; i < *num_planes; i++) { -- size = av->mpix.plane_fmt[i].sizeimage; -+ size = ipu6_get_data_size(&av->vfmt, i); - if (use_fmt) { - sizes[i] = size; - } else if (sizes[i] < size) { -@@ -59,16 +62,17 @@ static int ipu6_isys_buf_prepare(struct vb2_buffer *vb) - struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); - struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); - struct device *dev = &av->isys->adev->auxdev.dev; -+ u32 bytesperline = ipu6_get_bytes_per_line(&av->vfmt); -+ u32 height = ipu6_get_frame_height(&av->vfmt); -+ u32 size = ipu6_get_data_size(&av->vfmt, 0); - - dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n", -- av->vdev.name, av->mpix.plane_fmt[0].sizeimage, -- vb2_plane_size(vb, 0)); -+ av->vdev.name, size, vb2_plane_size(vb, 0)); - -- if (av->mpix.plane_fmt[0].sizeimage > vb2_plane_size(vb, 0)) -+ if (size > vb2_plane_size(vb, 0)) - return -EINVAL; - -- vb2_set_plane_payload(vb, 0, av->mpix.plane_fmt[0].bytesperline * -- av->mpix.height); -+ vb2_set_plane_payload(vb, 0, bytesperline * height); - vb->planes[0].data_offset = 0; - - return 0; -@@ -437,18 +441,22 @@ static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq) - return ret; - } - -- if (format.width != av->mpix.width || -- format.height != av->mpix.height) { -- dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n", -- av->mpix.width, av->mpix.height, -- format.width, format.height); -+ if (format.width != ipu6_get_frame_width(&av->vfmt) || -+ format.height != ipu6_get_frame_height(&av->vfmt)) { -+ dev_err(dev, "wrong width or height %ux%u (%ux%u expected)\n", -+ ipu6_get_frame_width(&av->vfmt), -+ ipu6_get_frame_height(&av->vfmt), format.width, -+ format.height); - return -EINVAL; - } - -- if (format.field != av->mpix.field) { -- dev_dbg(dev, "wrong field value 0x%8.8x (0x%8.8x expected)\n", -- av->mpix.field, format.field); -- return -EINVAL; -+ if (av->vfmt.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { -+ if (format.field != av->vfmt.fmt.pix_mp.field) { -+ dev_dbg(dev, -+ "wrong field value 0x%8.8x (%8.8x expected)\n", -+ av->vfmt.fmt.pix_mp.field, format.field); -+ return -EINVAL; -+ } - } - - if (format.code != av->pfmt->code) { -@@ -531,8 +539,8 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) - int nr_queues, ret; - - dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", -- av->vdev.name, av->mpix.width, av->mpix.height, -- av->pfmt->css_pixelformat); -+ av->vdev.name, ipu6_get_frame_width(&av->vfmt), -+ ipu6_get_frame_height(&av->vfmt), av->pfmt->css_pixelformat); - - ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues); - if (ret < 0) { -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c -index 510c5ca34f9f..3c9263ac02a3 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c -@@ -20,25 +20,30 @@ unsigned int ipu6_isys_mbus_code_to_bpp(u32 code) - { - switch (code) { - case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_META_24: - return 24; - case MEDIA_BUS_FMT_RGB565_1X16: - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_YUYV8_1X16: -+ case MEDIA_BUS_FMT_META_16: - return 16; - case MEDIA_BUS_FMT_SBGGR12_1X12: - case MEDIA_BUS_FMT_SGBRG12_1X12: - case MEDIA_BUS_FMT_SGRBG12_1X12: - case MEDIA_BUS_FMT_SRGGB12_1X12: -+ case MEDIA_BUS_FMT_META_12: - return 12; - case MEDIA_BUS_FMT_SBGGR10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SRGGB10_1X10: -+ case MEDIA_BUS_FMT_META_10: - return 10; - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SGBRG8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SRGGB8_1X8: -+ case MEDIA_BUS_FMT_META_8: - return 8; - default: - WARN_ON(1); -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -index 847eac26bcd6..1a023bf1e1a6 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -@@ -85,6 +85,11 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { - IPU6_FW_ISYS_FRAME_FORMAT_RGB565}, - {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, - IPU6_FW_ISYS_FRAME_FORMAT_RGBA888}, -+ {V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8, 0}, -+ {V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10, 0}, -+ {V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12, 0}, -+ {V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16, 0}, -+ {V4L2_META_FMT_GENERIC_CSI2_24, 24, 24, MEDIA_BUS_FMT_META_24, 0}, - }; - - static int video_open(struct file *file) -@@ -181,12 +186,12 @@ static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh, - return 0; - } - --static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh, -- struct v4l2_format *fmt) -+static int vidioc_get_format(struct file *file, void *fh, -+ struct v4l2_format *fmt) - { - struct ipu6_isys_video *av = video_drvdata(file); - -- fmt->fmt.pix_mp = av->mpix; -+ *fmt = av->vfmt; - - return 0; - } -@@ -245,30 +250,114 @@ ipu6_isys_video_try_fmt_vid_mplane(struct ipu6_isys_video *av, - return pfmt; - } - --static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh, -- struct v4l2_format *f) -+static const struct ipu6_isys_pixelformat * -+ipu6_isys_video_try_fmt_meta(struct ipu6_isys_video *av, -+ struct v4l2_meta_format *meta) -+{ -+ const struct ipu6_isys_pixelformat *pfmt = -+ ipu6_isys_get_pixelformat(meta->dataformat); -+ -+ memset(&av->vfmt, 0, sizeof(av->vfmt)); -+ av->vfmt.type = V4L2_BUF_TYPE_META_CAPTURE; -+ av->pfmt = pfmt; -+ -+ meta->dataformat = pfmt->pixelformat; -+ meta->width = clamp(meta->width, IPU6_ISYS_MIN_WIDTH, -+ IPU6_ISYS_MAX_WIDTH); -+ meta->height = clamp(meta->height, IPU6_ISYS_MIN_HEIGHT, -+ IPU6_ISYS_MAX_HEIGHT); -+ -+ if (pfmt->bpp != pfmt->bpp_packed) -+ meta->bytesperline = meta->width * -+ DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); -+ else -+ meta->bytesperline = -+ DIV_ROUND_UP(meta->width * pfmt->bpp, BITS_PER_BYTE); -+ -+ meta->bytesperline = ALIGN(meta->bytesperline, av->isys->line_align); -+ meta->buffersize = -+ max(max(meta->buffersize, meta->bytesperline * meta->height + -+ max(meta->bytesperline, -+ av->isys->pdata->ipdata->isys_dma_overshoot)), 1U); -+ -+ return pfmt; -+} -+ -+static const struct ipu6_isys_pixelformat * -+ipu6_isys_video_try_fmt(struct ipu6_isys_video *av, struct v4l2_format *f) -+{ -+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) -+ return ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp); -+ else if (f->type == V4L2_BUF_TYPE_META_CAPTURE) -+ return ipu6_isys_video_try_fmt_meta(av, &f->fmt.meta); -+ else -+ return &ipu6_isys_pfmts[0]; -+} -+ -+static int vidioc_set_format(struct file *file, void *fh, -+ struct v4l2_format *f) - { - struct ipu6_isys_video *av = video_drvdata(file); - - if (av->aq.vbq.streaming) - return -EBUSY; - -- av->pfmt = ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp); -- av->mpix = f->fmt.pix_mp; -+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && -+ f->type != V4L2_BUF_TYPE_META_CAPTURE) -+ return -EINVAL; -+ -+ av->pfmt = ipu6_isys_video_try_fmt(av, f); -+ av->vfmt = *f; - - return 0; - } - --static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh, -- struct v4l2_format *f) -+static int vidioc_try_format(struct file *file, void *fh, -+ struct v4l2_format *f) - { - struct ipu6_isys_video *av = video_drvdata(file); - -- ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp); -+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && -+ f->type != V4L2_BUF_TYPE_META_CAPTURE) -+ return -EINVAL; -+ -+ ipu6_isys_video_try_fmt(av, f); - - return 0; - } - -+static int vidioc_request_qbufs(struct file *file, void *priv, -+ struct v4l2_requestbuffers *p) -+{ -+ struct ipu6_isys_video *av = video_drvdata(file); -+ int ret; -+ -+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type); -+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type); -+ -+ ret = vb2_queue_change_type(&av->aq.vbq, p->type); -+ if (ret) -+ return ret; -+ -+ return vb2_ioctl_reqbufs(file, priv, p); -+} -+ -+static int vidioc_create_bufs(struct file *file, void *priv, -+ struct v4l2_create_buffers *p) -+{ -+ struct ipu6_isys_video *av = video_drvdata(file); -+ int ret; -+ -+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type); -+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type); -+ -+ ret = vb2_queue_change_type(&av->aq.vbq, p->format.type); -+ if (ret) -+ return ret; -+ -+ return vb2_ioctl_create_bufs(file, priv, p); -+} -+ - static int link_validate(struct media_link *link) - { - struct ipu6_isys_video *av = -@@ -279,6 +368,8 @@ static int link_validate(struct media_link *link) - struct v4l2_mbus_framefmt *s_fmt; - struct media_pad *s_pad; - u32 s_stream; -+ u32 height; -+ u32 width; - int ret = -EPIPE; - - if (!link->source->entity) -@@ -305,11 +396,13 @@ static int link_validate(struct media_link *link) - goto unlock; - } - -- if (s_fmt->width != av->mpix.width || -- s_fmt->height != av->mpix.height || s_fmt->code != av->pfmt->code) { -+ height = ipu6_get_frame_height(&av->vfmt); -+ width = ipu6_get_frame_width(&av->vfmt); -+ if (s_fmt->width != width || s_fmt->height != height || -+ s_fmt->code != av->pfmt->code) { - dev_err(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", -- s_fmt->width, s_fmt->height, s_fmt->code, -- av->mpix.width, av->mpix.height, av->pfmt->code); -+ s_fmt->width, s_fmt->height, s_fmt->code, width, height, -+ av->pfmt->code); - goto unlock; - } - -@@ -393,10 +486,10 @@ static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av, - - output_pin = &cfg->output_pins[output_pins]; - output_pin->input_pin_id = input_pins; -- output_pin->output_res.width = av->mpix.width; -- output_pin->output_res.height = av->mpix.height; -+ output_pin->output_res.width = ipu6_get_frame_width(&av->vfmt); -+ output_pin->output_res.height = ipu6_get_frame_height(&av->vfmt); - -- output_pin->stride = av->mpix.plane_fmt[0].bytesperline; -+ output_pin->stride = ipu6_get_bytes_per_line(&av->vfmt); - if (av->pfmt->bpp != av->pfmt->bpp_packed) - output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC; - else -@@ -663,8 +756,8 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, - - esd = media_entity_to_v4l2_subdev(av->stream->source_entity); - -- av->watermark.width = av->mpix.width; -- av->watermark.height = av->mpix.height; -+ av->watermark.width = ipu6_get_frame_width(&av->vfmt); -+ av->watermark.height = ipu6_get_frame_height(&av->vfmt); - av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift; - av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size; - -@@ -992,11 +1085,15 @@ static const struct v4l2_ioctl_ops ioctl_ops_mplane = { - .vidioc_querycap = ipu6_isys_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt, - .vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes, -- .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, -- .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, -- .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, -- .vidioc_reqbufs = vb2_ioctl_reqbufs, -- .vidioc_create_bufs = vb2_ioctl_create_bufs, -+ .vidioc_g_fmt_vid_cap_mplane = vidioc_get_format, -+ .vidioc_s_fmt_vid_cap_mplane = vidioc_set_format, -+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_format, -+ .vidioc_enum_fmt_meta_cap = ipu6_isys_vidioc_enum_fmt, -+ .vidioc_g_fmt_meta_cap = vidioc_get_format, -+ .vidioc_s_fmt_meta_cap = vidioc_set_format, -+ .vidioc_try_fmt_meta_cap = vidioc_try_format, -+ .vidioc_reqbufs = vidioc_request_qbufs, -+ .vidioc_create_bufs = vidioc_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, -@@ -1199,7 +1296,8 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av) - - mutex_init(&av->mutex); - av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | -- V4L2_CAP_VIDEO_CAPTURE_MPLANE; -+ V4L2_CAP_VIDEO_CAPTURE_MPLANE | -+ V4L2_CAP_META_CAPTURE; - av->vdev.vfl_dir = VFL_DIR_RX; - - ret = ipu6_isys_queue_init(&av->aq); -@@ -1220,8 +1318,8 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av) - av->vdev.queue = &av->aq.vbq; - av->vdev.lock = &av->mutex; - -- ipu6_isys_video_try_fmt_vid_mplane(av, &format.fmt.pix_mp); -- av->mpix = format.fmt.pix_mp; -+ ipu6_isys_video_try_fmt(av, &format); -+ av->vfmt = format; - - set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); - video_set_drvdata(&av->vdev, av); -@@ -1251,3 +1349,52 @@ void ipu6_isys_video_cleanup(struct ipu6_isys_video *av) - media_entity_cleanup(&av->vdev.entity); - mutex_destroy(&av->mutex); - } -+ -+u32 ipu6_get_data_size(struct v4l2_format *vfmt, int plane) -+{ -+ if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) -+ return vfmt->fmt.pix_mp.plane_fmt[plane].sizeimage; -+ else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE) -+ return vfmt->fmt.meta.buffersize; -+ -+ WARN_ON_ONCE(1); -+ -+ return 0; -+} -+ -+u32 ipu6_get_bytes_per_line(struct v4l2_format *vfmt) -+{ -+ if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) -+ return vfmt->fmt.pix_mp.plane_fmt[0].bytesperline; -+ else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE) -+ return vfmt->fmt.meta.bytesperline; -+ -+ WARN_ON_ONCE(1); -+ -+ return 0; -+} -+ -+u32 ipu6_get_frame_width(struct v4l2_format *vfmt) -+{ -+ if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) -+ return vfmt->fmt.pix_mp.width; -+ else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE) -+ return vfmt->fmt.meta.width; -+ -+ WARN_ON_ONCE(1); -+ -+ return 0; -+} -+ -+u32 ipu6_get_frame_height(struct v4l2_format *vfmt) -+{ -+ if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) -+ return vfmt->fmt.pix_mp.height; -+ else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE) -+ return vfmt->fmt.meta.height; -+ -+ WARN_ON_ONCE(1); -+ -+ return 0; -+} -+ -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h -index 21cd33c7e277..2634ec0fd68b 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h -@@ -90,7 +90,7 @@ struct ipu6_isys_video { - struct mutex mutex; - struct media_pad pad; - struct video_device vdev; -- struct v4l2_pix_format_mplane mpix; -+ struct v4l2_format vfmt; - const struct ipu6_isys_pixelformat *pfmt; - struct ipu6_isys *isys; - struct ipu6_isys_stream *stream; -@@ -133,4 +133,9 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, - bool state); - void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state); - -+u32 ipu6_get_data_size(struct v4l2_format *vfmt, int plane); -+u32 ipu6_get_bytes_per_line(struct v4l2_format *vfmt); -+u32 ipu6_get_frame_width(struct v4l2_format *vfmt); -+u32 ipu6_get_frame_height(struct v4l2_format *vfmt); -+ - #endif /* IPU6_ISYS_VIDEO_H */ --- -2.43.2 - - -From 9a6fb311b81433ebbd8e0769bed19958a6a5a5f6 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao <bingbu.cao@intel.com> -Date: Thu, 11 Jan 2024 14:55:31 +0800 -Subject: [PATCH 24/33] media: ipu6/isys: support new v4l2 subdev state APIs - -Add support for the upcoming v4l2-subdev API changes in kernel 6.8. -This patch is based on Sakari's branch: -<URL:https://git.linuxtv.org/sailus/media_tree.git/log/?h=metadata> - -Signed-off-by: Hans de Goede <hdegoede@redhat.com> -Signed-off-by: Bingbu Cao <bingbu.cao@intel.com> ---- - drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c | 8 +++----- - .../media/pci/intel/ipu6/ipu6-isys-subdev.c | 19 +++++++++++-------- - .../media/pci/intel/ipu6/ipu6-isys-subdev.h | 2 -- - .../media/pci/intel/ipu6/ipu6-isys-video.c | 3 +-- - 4 files changed, 15 insertions(+), 17 deletions(-) - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c -index a6430d531129..6f258cf92fc1 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c -@@ -403,12 +403,11 @@ static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd, - if (!sink_ffmt) - return -EINVAL; - -- src_ffmt = v4l2_subdev_state_get_stream_format(state, sel->pad, -- sel->stream); -+ src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); - if (!src_ffmt) - return -EINVAL; - -- crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream); -+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); - if (!crop) - return -EINVAL; - -@@ -453,7 +452,7 @@ static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd, - if (!sink_ffmt) - return -EINVAL; - -- crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream); -+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); - if (!crop) - return -EINVAL; - -@@ -480,7 +479,6 @@ static const struct v4l2_subdev_video_ops csi2_sd_video_ops = { - }; - - static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { -- .init_cfg = ipu6_isys_subdev_init_cfg, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = ipu6_isys_subdev_set_fmt, - .get_selection = ipu6_isys_csi2_get_sel, -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c -index 3c9263ac02a3..aeccd6f93986 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c -@@ -156,8 +156,7 @@ int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, - format->format.field = V4L2_FIELD_NONE; - - /* Store the format and propagate it to the source pad. */ -- fmt = v4l2_subdev_state_get_stream_format(state, format->pad, -- format->stream); -+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); - if (!fmt) - return -EINVAL; - -@@ -182,8 +181,7 @@ int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, - if (ret) - return -EINVAL; - -- crop = v4l2_subdev_state_get_stream_crop(state, other_pad, -- other_stream); -+ crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); - /* reset crop */ - crop->left = 0; - crop->top = 0; -@@ -241,7 +239,7 @@ int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, - return -EINVAL; - - state = v4l2_subdev_lock_and_get_active_state(sd); -- fmt = v4l2_subdev_state_get_stream_format(state, pad, stream); -+ fmt = v4l2_subdev_state_get_format(state, pad, stream); - if (fmt) - *format = *fmt; - v4l2_subdev_unlock_state(state); -@@ -259,7 +257,7 @@ int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, - return -EINVAL; - - state = v4l2_subdev_lock_and_get_active_state(sd); -- rect = v4l2_subdev_state_get_stream_crop(state, pad, stream); -+ rect = v4l2_subdev_state_get_crop(state, pad, stream); - if (rect) - *crop = *rect; - v4l2_subdev_unlock_state(state); -@@ -291,8 +289,8 @@ u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) - return source_stream; - } - --int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state) -+static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) - { - struct v4l2_subdev_route route = { - .sink_pad = 0, -@@ -317,6 +315,10 @@ int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, - return subdev_set_routing(sd, state, routing); - } - -+static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = { -+ .init_state = ipu6_isys_subdev_init_state, -+}; -+ - int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd, - const struct v4l2_subdev_ops *ops, - unsigned int nr_ctrls, -@@ -334,6 +336,7 @@ int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd, - V4L2_SUBDEV_FL_STREAMS; - asd->sd.owner = THIS_MODULE; - asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; -+ asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops; - - asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, - sizeof(*asd->pad), GFP_KERNEL); -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h -index adea2a55761d..f4e32b094b5b 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h -@@ -46,8 +46,6 @@ int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, - struct v4l2_mbus_framefmt *format); - int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, - struct v4l2_rect *crop); --int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state); - int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - enum v4l2_subdev_format_whence which, -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -index 1a023bf1e1a6..62d4043fc2a1 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -@@ -389,8 +389,7 @@ static int link_validate(struct media_link *link) - - v4l2_subdev_lock_state(s_state); - -- s_fmt = v4l2_subdev_state_get_stream_format(s_state, s_pad->index, -- s_stream); -+ s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream); - if (!s_fmt) { - dev_err(dev, "failed to get source pad format\n"); - goto unlock; --- -2.43.2 - - -From 53ca77877d2cc7ecc39bb0ef26a1871a1c26afd1 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Mon, 15 Jan 2024 15:57:06 +0100 -Subject: [PATCH 25/33] media: intel/ipu6: Disable packed bayer v4l2-buffer - formats on TGL - -Using CSI2 packing to store 10bpp bayer data in the v4l2-buffers does not -work on Tiger Lake when testing with an ov01a1s sensor. - -Disable packed bayer formats on Tiger Lake for now. - -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - .../media/pci/intel/ipu6/ipu6-isys-video.c | 65 ++++++++++++------- - 1 file changed, 43 insertions(+), 22 deletions(-) - -diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -index 62d4043fc2a1..c971ffe0b948 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c -@@ -61,6 +61,17 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { - IPU6_FW_ISYS_FRAME_FORMAT_RAW8}, - {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, - IPU6_FW_ISYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, -+ IPU6_FW_ISYS_FRAME_FORMAT_UYVY}, -+ {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, -+ IPU6_FW_ISYS_FRAME_FORMAT_YUYV}, -+ {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, -+ IPU6_FW_ISYS_FRAME_FORMAT_RGB565}, -+ {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, -+ IPU6_FW_ISYS_FRAME_FORMAT_RGBA888}, -+}; -+ -+const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[] = { - {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, - IPU6_FW_ISYS_FRAME_FORMAT_RAW12}, - {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -@@ -77,19 +88,6 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = { - IPU6_FW_ISYS_FRAME_FORMAT_RAW10}, - {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, - IPU6_FW_ISYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, -- IPU6_FW_ISYS_FRAME_FORMAT_UYVY}, -- {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, -- IPU6_FW_ISYS_FRAME_FORMAT_YUYV}, -- {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, -- IPU6_FW_ISYS_FRAME_FORMAT_RGB565}, -- {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, -- IPU6_FW_ISYS_FRAME_FORMAT_RGBA888}, -- {V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8, 0}, -- {V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10, 0}, -- {V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12, 0}, -- {V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16, 0}, -- {V4L2_META_FMT_GENERIC_CSI2_24, 24, 24, MEDIA_BUS_FMT_META_24, 0}, - }; - - static int video_open(struct file *file) -@@ -114,14 +112,27 @@ static int video_release(struct file *file) - return vb2_fop_release(file); - } - -+static const struct ipu6_isys_pixelformat * -+ipu6_isys_get_pixelformat_by_idx(unsigned int idx) -+{ -+ if (idx < ARRAY_SIZE(ipu6_isys_pfmts)) -+ return &ipu6_isys_pfmts[idx]; -+ -+ idx -= ARRAY_SIZE(ipu6_isys_pfmts); -+ -+ if (idx < ARRAY_SIZE(ipu6_isys_pfmts_packed)) -+ return &ipu6_isys_pfmts_packed[idx]; -+ -+ return NULL; -+} -+ - static const struct ipu6_isys_pixelformat * - ipu6_isys_get_pixelformat(u32 pixelformat) - { -+ const struct ipu6_isys_pixelformat *pfmt; - unsigned int i; - -- for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { -- const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i]; -- -+ for (i = 0; (pfmt = ipu6_isys_get_pixelformat_by_idx(i)); i++) { - if (pfmt->pixelformat == pixelformat) - return pfmt; - } -@@ -143,24 +154,34 @@ int ipu6_isys_vidioc_querycap(struct file *file, void *fh, - int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh, - struct v4l2_fmtdesc *f) - { -- unsigned int i, found = 0; -+ struct ipu6_isys_video *av = video_drvdata(file); -+ const struct ipu6_isys_pixelformat *fmt; -+ unsigned int i, nfmts, found = 0; -+ -+ nfmts = ARRAY_SIZE(ipu6_isys_pfmts); -+ /* Disable packed formats on TGL for now, TGL has 8 CSI ports */ -+ if (av->isys->pdata->ipdata->csi2.nports != 8) -+ nfmts += ARRAY_SIZE(ipu6_isys_pfmts_packed); - -- if (f->index >= ARRAY_SIZE(ipu6_isys_pfmts)) -+ if (f->index >= nfmts) - return -EINVAL; - - if (!f->mbus_code) { -+ fmt = ipu6_isys_get_pixelformat_by_idx(f->index); - f->flags = 0; -- f->pixelformat = ipu6_isys_pfmts[f->index].pixelformat; -+ f->pixelformat = fmt->pixelformat; - return 0; - } - -- for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) { -- if (f->mbus_code != ipu6_isys_pfmts[i].code) -+ for (i = 0; i < nfmts; i++) { -+ fmt = ipu6_isys_get_pixelformat_by_idx(i); -+ -+ if (f->mbus_code != fmt->code) - continue; - - if (f->index == found) { - f->flags = 0; -- f->pixelformat = ipu6_isys_pfmts[i].pixelformat; -+ f->pixelformat = fmt->pixelformat; - return 0; - } - found++; --- -2.43.2 - - -From ed407043f03e9af2b09ab8ad449c2716ce7fde01 Mon Sep 17 00:00:00 2001 +From b5a82464677815dcbe6a06b0ee92ed065b1ec275 Mon Sep 17 00:00:00 2001 From: Hans de Goede <hdegoede@redhat.com> Date: Mon, 6 Nov 2023 12:13:42 +0100 -Subject: [PATCH 26/33] media: Add ov01a1s driver +Subject: [PATCH 1/3] media: Add ov01a1s driver Add ov01a1s driver from: https://github.com/intel/ipu6-drivers/ @@ -16257,10 +15,10 @@ Signed-off-by: Hans de Goede <hdegoede@redhat.com> create mode 100644 drivers/media/i2c/ov01a1s.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index 4c3435921f19..08f934740980 100644 +index c6d3ee472d81..59e0d304bef2 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig -@@ -313,6 +313,15 @@ config VIDEO_OV01A10 +@@ -315,6 +315,15 @@ config VIDEO_OV01A10 To compile this driver as a module, choose M here: the module will be called ov01a10. @@ -17486,70 +1244,12 @@ index 000000000000..0dcce8b492b4 +MODULE_DESCRIPTION("OmniVision OV01A1S sensor driver"); +MODULE_LICENSE("GPL v2"); -- -2.43.2 - - -From 9f58ae728245ad7ac604737ab16781d7ccb2006e Mon Sep 17 00:00:00 2001 -From: Florian Klink <flokli@flokli.de> -Date: Sun, 17 Mar 2024 14:24:05 +0200 -Subject: [PATCH 27/33] ov01a1s.c: support Linux 6.8.0 - -Used https://github.com/intel/ipu6-drivers/pull/213 as an inspiration. ---- - drivers/media/i2c/ov01a1s.c | 13 ++++++++++--- - 1 file changed, 10 insertions(+), 3 deletions(-) - -diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c -index 0dcce8b492b4..923b12b2a948 100644 ---- a/drivers/media/i2c/ov01a1s.c -+++ b/drivers/media/i2c/ov01a1s.c -@@ -832,8 +832,10 @@ static int ov01a1s_set_format(struct v4l2_subdev *sd, - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; --#else -+#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; -+#else -+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; - #endif - } else { - ov01a1s->cur_mode = mode; -@@ -871,9 +873,11 @@ static int ov01a1s_get_format(struct v4l2_subdev *sd, - #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) - fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd, cfg, - fmt->pad); --#else -+#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) - fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd, - sd_state, fmt->pad); -+#else -+ fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); - #endif - else - ov01a1s_update_pad_format(ov01a1s->cur_mode, &fmt->format); -@@ -929,9 +933,12 @@ static int ov01a1s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) - #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) - ov01a1s_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->pad, 0)); --#else -+#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) - ov01a1s_update_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); -+#else -+ ov01a1s_update_pad_format(&supported_modes[0], -+ v4l2_subdev_state_get_format(fh->state, 0)); - #endif - mutex_unlock(&ov01a1s->mutex); - --- -2.43.2 - +2.45.2 -From 80bee1ca899ebfa4126d1e69ea821a2c30aba00c Mon Sep 17 00:00:00 2001 +From 5cdf8b2b159a7cfef03b67b7fd51da967345090c Mon Sep 17 00:00:00 2001 From: Hans de Goede <hdegoede@redhat.com> Date: Mon, 6 Nov 2023 12:33:56 +0100 -Subject: [PATCH 28/33] media: ov01a1s: Remove non upstream iVSC support +Subject: [PATCH 2/3] media: ov01a1s: Remove non upstream iVSC support Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- @@ -17557,7 +1257,7 @@ Signed-off-by: Hans de Goede <hdegoede@redhat.com> 1 file changed, 71 deletions(-) diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c -index 923b12b2a948..22b406bdeae9 100644 +index 0dcce8b492b4..c97c1a661022 100644 --- a/drivers/media/i2c/ov01a1s.c +++ b/drivers/media/i2c/ov01a1s.c @@ -17,9 +17,6 @@ @@ -17683,7 +1383,7 @@ index 923b12b2a948..22b406bdeae9 100644 return ret; } -@@ -1051,18 +992,6 @@ static int ov01a1s_parse_power(struct ov01a1s *ov01a1s) +@@ -1044,18 +985,6 @@ static int ov01a1s_parse_power(struct ov01a1s *ov01a1s) { int ret = 0; @@ -17703,375 +1403,61 @@ index 923b12b2a948..22b406bdeae9 100644 ret = ov01a1s_parse_gpio(ov01a1s); #elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC) -- -2.43.2 - - -From e624515c64d782b452a4676c1e117815267559ae Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Tue, 23 Jan 2024 14:58:35 +0100 -Subject: [PATCH 29/33] media: hi556: Return -EPROBE_DEFER if no endpoint is - found - -With ipu bridge, endpoints may only be created when ipu bridge has -initialised. This may happen after the sensor driver has first probed. - -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - drivers/media/i2c/hi556.c | 13 +++++++------ - 1 file changed, 7 insertions(+), 6 deletions(-) - -diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c -index 38c77d515786..96bae9914d52 100644 ---- a/drivers/media/i2c/hi556.c -+++ b/drivers/media/i2c/hi556.c -@@ -1206,8 +1206,13 @@ static int hi556_check_hwcfg(struct device *dev) - int ret = 0; - unsigned int i, j; - -- if (!fwnode) -- return -ENXIO; -+ /* -+ * Sometimes the fwnode graph is initialized by the bridge driver, -+ * wait for this. -+ */ -+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL); -+ if (!ep) -+ return -EPROBE_DEFER; - - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); - if (ret) { -@@ -1220,10 +1225,6 @@ static int hi556_check_hwcfg(struct device *dev) - return -EINVAL; - } - -- ep = fwnode_graph_get_next_endpoint(fwnode, NULL); -- if (!ep) -- return -ENXIO; -- - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - fwnode_handle_put(ep); - if (ret) --- -2.43.2 - - -From b127d1003050fb894ea764b600d5f399af413b68 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Tue, 23 Jan 2024 14:48:26 +0100 -Subject: [PATCH 30/33] media: hi556: Add support for reset GPIO - -On some ACPI platforms, such as Chromebooks the ACPI methods to -change the power-state (_PS0 and _PS3) fully take care of powering -on/off the sensor. - -On other ACPI platforms, such as e.g. various HP models with IPU6 + -hi556 sensor, the sensor driver must control the reset GPIO itself. - -Add support for having the driver control an optional reset GPIO. - -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - drivers/media/i2c/hi556.c | 45 ++++++++++++++++++++++++++++++++++++++- - 1 file changed, 44 insertions(+), 1 deletion(-) - -diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c -index 96bae9914d52..f5a39b83598b 100644 ---- a/drivers/media/i2c/hi556.c -+++ b/drivers/media/i2c/hi556.c -@@ -4,6 +4,7 @@ - #include <asm/unaligned.h> - #include <linux/acpi.h> - #include <linux/delay.h> -+#include <linux/gpio/consumer.h> - #include <linux/i2c.h> - #include <linux/module.h> - #include <linux/pm_runtime.h> -@@ -633,6 +634,9 @@ struct hi556 { - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *exposure; - -+ /* GPIOs, clocks, etc. */ -+ struct gpio_desc *reset_gpio; -+ - /* Current mode */ - const struct hi556_mode *cur_mode; - -@@ -1276,6 +1280,25 @@ static void hi556_remove(struct i2c_client *client) - mutex_destroy(&hi556->mutex); - } - -+static int hi556_suspend(struct device *dev) -+{ -+ struct v4l2_subdev *sd = dev_get_drvdata(dev); -+ struct hi556 *hi556 = to_hi556(sd); -+ -+ gpiod_set_value_cansleep(hi556->reset_gpio, 1); -+ return 0; -+} -+ -+static int hi556_resume(struct device *dev) -+{ -+ struct v4l2_subdev *sd = dev_get_drvdata(dev); -+ struct hi556 *hi556 = to_hi556(sd); -+ -+ gpiod_set_value_cansleep(hi556->reset_gpio, 0); -+ usleep_range(5000, 5500); -+ return 0; -+} -+ - static int hi556_probe(struct i2c_client *client) - { - struct hi556 *hi556; -@@ -1295,12 +1318,24 @@ static int hi556_probe(struct i2c_client *client) - - v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops); - -+ hi556->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", -+ GPIOD_OUT_HIGH); -+ if (IS_ERR(hi556->reset_gpio)) -+ return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio), -+ "failed to get reset GPIO\n"); -+ - full_power = acpi_dev_state_d0(&client->dev); - if (full_power) { -+ /* Ensure non ACPI managed resources are enabled */ -+ ret = hi556_resume(&client->dev); -+ if (ret) -+ return dev_err_probe(&client->dev, ret, -+ "failed to power on sensor\n"); -+ - ret = hi556_identify_module(hi556); - if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); -- return ret; -+ goto probe_error_power_off; - } - } - -@@ -1345,9 +1380,16 @@ static int hi556_probe(struct i2c_client *client) - v4l2_ctrl_handler_free(hi556->sd.ctrl_handler); - mutex_destroy(&hi556->mutex); - -+probe_error_power_off: -+ if (full_power) -+ hi556_suspend(&client->dev); -+ - return ret; - } - -+static DEFINE_RUNTIME_DEV_PM_OPS(hi556_pm_ops, hi556_suspend, hi556_resume, -+ NULL); -+ - #ifdef CONFIG_ACPI - static const struct acpi_device_id hi556_acpi_ids[] = { - {"INT3537"}, -@@ -1361,6 +1403,7 @@ static struct i2c_driver hi556_i2c_driver = { - .driver = { - .name = "hi556", - .acpi_match_table = ACPI_PTR(hi556_acpi_ids), -+ .pm = pm_sleep_ptr(&hi556_pm_ops), - }, - .probe = hi556_probe, - .remove = hi556_remove, --- -2.43.2 - - -From ee651202ba2ca38da067b5379edd7b4f339cf7a8 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Tue, 23 Jan 2024 14:54:22 +0100 -Subject: [PATCH 31/33] media: hi556: Add support for external clock - -On some ACPI platforms, such as Chromebooks the ACPI methods to -change the power-state (_PS0 and _PS3) fully take care of powering -on/off the sensor. - -On other ACPI platforms, such as e.g. various HP models with IPU6 + -hi556 sensor, the sensor driver must control the sensor's clock itself. - -Add support for having the driver control an optional clock. - -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - drivers/media/i2c/hi556.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c -index f5a39b83598b..b783e0f56687 100644 ---- a/drivers/media/i2c/hi556.c -+++ b/drivers/media/i2c/hi556.c -@@ -3,6 +3,7 @@ - - #include <asm/unaligned.h> - #include <linux/acpi.h> -+#include <linux/clk.h> - #include <linux/delay.h> - #include <linux/gpio/consumer.h> - #include <linux/i2c.h> -@@ -636,6 +637,7 @@ struct hi556 { - - /* GPIOs, clocks, etc. */ - struct gpio_desc *reset_gpio; -+ struct clk *clk; - - /* Current mode */ - const struct hi556_mode *cur_mode; -@@ -1286,6 +1288,7 @@ static int hi556_suspend(struct device *dev) - struct hi556 *hi556 = to_hi556(sd); - - gpiod_set_value_cansleep(hi556->reset_gpio, 1); -+ clk_disable_unprepare(hi556->clk); - return 0; - } - -@@ -1293,6 +1296,11 @@ static int hi556_resume(struct device *dev) - { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct hi556 *hi556 = to_hi556(sd); -+ int ret; -+ -+ ret = clk_prepare_enable(hi556->clk); -+ if (ret) -+ return ret; - - gpiod_set_value_cansleep(hi556->reset_gpio, 0); - usleep_range(5000, 5500); -@@ -1324,6 +1332,11 @@ static int hi556_probe(struct i2c_client *client) - return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio), - "failed to get reset GPIO\n"); - -+ hi556->clk = devm_clk_get_optional(&client->dev, "clk"); -+ if (IS_ERR(hi556->clk)) -+ return dev_err_probe(&client->dev, PTR_ERR(hi556->clk), -+ "failed to get clock\n"); -+ - full_power = acpi_dev_state_d0(&client->dev); - if (full_power) { - /* Ensure non ACPI managed resources are enabled */ --- -2.43.2 - - -From 16be71996d451b8137ba63070e760448814c11a1 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Wed, 24 Jan 2024 18:45:02 +0100 -Subject: [PATCH 32/33] media: hi556: Add support for avdd regulator - -On some ACPI platforms, such as Chromebooks the ACPI methods to -change the power-state (_PS0 and _PS3) fully take care of powering -on/off the sensor. - -On other ACPI platforms, such as e.g. various HP models with IPU6 + -hi556 sensor, the sensor driver must control the avdd regulator itself. - -Add support for having the driver control the sensor's avdd regulator. -Note this relies on the regulator-core providing a dummy regulator -(which it does by default) on platforms where Linux is not aware of -the avdd regulator. - -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - drivers/media/i2c/hi556.c | 24 ++++++++++++++++++++++++ - 1 file changed, 24 insertions(+) - -diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c -index b783e0f56687..5641c249d4b1 100644 ---- a/drivers/media/i2c/hi556.c -+++ b/drivers/media/i2c/hi556.c -@@ -9,6 +9,7 @@ - #include <linux/i2c.h> - #include <linux/module.h> - #include <linux/pm_runtime.h> -+#include <linux/regulator/consumer.h> - #include <media/v4l2-ctrls.h> - #include <media/v4l2-device.h> - #include <media/v4l2-fwnode.h> -@@ -638,6 +639,7 @@ struct hi556 { - /* GPIOs, clocks, etc. */ - struct gpio_desc *reset_gpio; - struct clk *clk; -+ struct regulator *avdd; - - /* Current mode */ - const struct hi556_mode *cur_mode; -@@ -1286,8 +1288,17 @@ static int hi556_suspend(struct device *dev) - { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct hi556 *hi556 = to_hi556(sd); -+ int ret; - - gpiod_set_value_cansleep(hi556->reset_gpio, 1); -+ -+ ret = regulator_disable(hi556->avdd); -+ if (ret) { -+ dev_err(dev, "failed to disable avdd: %d\n", ret); -+ gpiod_set_value_cansleep(hi556->reset_gpio, 0); -+ return ret; -+ } -+ - clk_disable_unprepare(hi556->clk); - return 0; - } -@@ -1302,6 +1313,13 @@ static int hi556_resume(struct device *dev) - if (ret) - return ret; - -+ ret = regulator_enable(hi556->avdd); -+ if (ret) { -+ dev_err(dev, "failed to enable avdd: %d\n", ret); -+ clk_disable_unprepare(hi556->clk); -+ return ret; -+ } -+ - gpiod_set_value_cansleep(hi556->reset_gpio, 0); - usleep_range(5000, 5500); - return 0; -@@ -1337,6 +1355,12 @@ static int hi556_probe(struct i2c_client *client) - return dev_err_probe(&client->dev, PTR_ERR(hi556->clk), - "failed to get clock\n"); - -+ /* The regulator core will provide a "dummy" regulator if necessary */ -+ hi556->avdd = devm_regulator_get(&client->dev, "avdd"); -+ if (IS_ERR(hi556->avdd)) -+ return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd), -+ "failed to get avdd regulator\n"); -+ - full_power = acpi_dev_state_d0(&client->dev); - if (full_power) { - /* Ensure non ACPI managed resources are enabled */ --- -2.43.2 - +2.45.2 -From 6bd6e73829cf264120f629c88c552c4eb59c7eee Mon Sep 17 00:00:00 2001 +From 5ccd06ac4351f3eb8146c1109446fc80aeaa873f Mon Sep 17 00:00:00 2001 From: Florian Klink <flokli@flokli.de> -Date: Sun, 17 Mar 2024 17:07:53 +0200 -Subject: [PATCH 33/33] media: intel/ipu6: fix firmware paths +Date: Sun, 17 Mar 2024 14:24:05 +0200 +Subject: [PATCH 3/3] ov01a1s.c: support Linux 6.8.0 -linux-firmware ships them in intel/ipu, not intel/. +Used https://github.com/intel/ipu6-drivers/pull/213 as an inspiration. --- - drivers/media/pci/intel/ipu6/ipu6.h | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) + drivers/media/i2c/ov01a1s.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) -diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h -index 04e7e7e61ca5..da8a95a9edf8 100644 ---- a/drivers/media/pci/intel/ipu6/ipu6.h -+++ b/drivers/media/pci/intel/ipu6/ipu6.h -@@ -24,10 +24,10 @@ struct ipu6_bus_device; - #define IPU6_NAME "intel-ipu6" - #define IPU6_MEDIA_DEV_MODEL_NAME "ipu6" - --#define IPU6SE_FIRMWARE_NAME "intel/ipu6se_fw.bin" --#define IPU6EP_FIRMWARE_NAME "intel/ipu6ep_fw.bin" --#define IPU6_FIRMWARE_NAME "intel/ipu6_fw.bin" --#define IPU6EPMTL_FIRMWARE_NAME "intel/ipu6epmtl_fw.bin" -+#define IPU6SE_FIRMWARE_NAME "intel/ipu/ipu6se_fw.bin" -+#define IPU6EP_FIRMWARE_NAME "intel/ipu/ipu6ep_fw.bin" -+#define IPU6_FIRMWARE_NAME "intel/ipu/ipu6_fw.bin" -+#define IPU6EPMTL_FIRMWARE_NAME "intel/ipu/ipu6epmtl_fw.bin" +diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c +index c97c1a661022..22b406bdeae9 100644 +--- a/drivers/media/i2c/ov01a1s.c ++++ b/drivers/media/i2c/ov01a1s.c +@@ -773,8 +773,10 @@ static int ov01a1s_set_format(struct v4l2_subdev *sd, + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +-#else ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; ++#else ++ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + #endif + } else { + ov01a1s->cur_mode = mode; +@@ -812,9 +814,11 @@ static int ov01a1s_get_format(struct v4l2_subdev *sd, + #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd, cfg, + fmt->pad); +-#else ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) + fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd, + sd_state, fmt->pad); ++#else ++ fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); + #endif + else + ov01a1s_update_pad_format(ov01a1s->cur_mode, &fmt->format); +@@ -870,9 +874,12 @@ static int ov01a1s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) + #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) + ov01a1s_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); +-#else ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) + ov01a1s_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->state, 0)); ++#else ++ ov01a1s_update_pad_format(&supported_modes[0], ++ v4l2_subdev_state_get_format(fh->state, 0)); + #endif + mutex_unlock(&ov01a1s->mutex); - enum ipu6_version { - IPU6_VER_INVALID = 0, -- -2.43.2 +2.45.2 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/0001-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch index 724b67033ff4..5340901edb94 100644 --- a/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch +++ b/users/flokli/ipu6-softisp/libcamera/0001-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch @@ -1,7 +1,7 @@ -From e9580d30a1a79fce1ebd72ae74ceb4a3d1cf8fbb Mon Sep 17 00:00:00 2001 +From 0e94883c2f4f6164d40d4ea355449ba0864dc4f9 Mon Sep 17 00:00:00 2001 From: Hans de Goede <hdegoede@redhat.com> Date: Tue, 19 Dec 2023 11:16:26 +0100 -Subject: [PATCH 16/21] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer +Subject: [PATCH 1/3] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer order DNU The ov01a1s sensor has the following bayer pattern (4x4 tile repeating): @@ -23,15 +23,15 @@ Signed-off-by: Hans de Goede <hdegoede@redhat.com> 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/sensor/camera_sensor.cpp | 3 +++ src/libcamera/v4l2_pixelformat.cpp | 4 ++++ - src/libcamera/v4l2_subdevice.cpp | 1 + - 10 files changed, 48 insertions(+), 2 deletions(-) + src/libcamera/v4l2_subdevice.cpp | 7 +++++++ + 10 files changed, 54 insertions(+), 2 deletions(-) diff --git a/include/libcamera/internal/bayer_format.h b/include/libcamera/internal/bayer_format.h -index 78ba3969..e77106c3 100644 +index 5c14bb5f..49b7f417 100644 --- a/include/libcamera/internal/bayer_format.h +++ b/include/libcamera/internal/bayer_format.h @@ -27,7 +27,8 @@ public: @@ -45,10 +45,10 @@ index 78ba3969..e77106c3 100644 enum class Packing : uint16_t { diff --git a/include/linux/drm_fourcc.h b/include/linux/drm_fourcc.h -index 1496e097..750ae8c9 100644 +index b4e1a092..070696bc 100644 --- a/include/linux/drm_fourcc.h +++ b/include/linux/drm_fourcc.h -@@ -405,6 +405,8 @@ extern "C" { +@@ -447,6 +447,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') @@ -58,19 +58,19 @@ index 1496e097..750ae8c9 100644 /* 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 +index f05f747e..1b4a65db 100644 --- a/include/linux/media-bus-format.h +++ b/include/linux/media-bus-format.h -@@ -112,7 +112,7 @@ +@@ -121,7 +121,7 @@ #define MEDIA_BUS_FMT_YUV16_1X48 0x202a #define MEDIA_BUS_FMT_UYYVYY16_0_5X48 0x202b -/* Bayer - next is 0x3021 */ -+/* Bayer - next is 0x3022 */ ++/* 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 @@ +@@ -154,6 +154,8 @@ #define MEDIA_BUS_FMT_SGBRG16_1X16 0x301e #define MEDIA_BUS_FMT_SGRBG16_1X16 0x301f #define MEDIA_BUS_FMT_SRGGB16_1X16 0x3020 @@ -80,10 +80,10 @@ index 0dfc11ee..c5fbda0e 100644 /* 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 +index 0b5482a0..a51d6755 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h -@@ -678,6 +678,9 @@ struct v4l2_pix_format { +@@ -704,6 +704,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.. */ @@ -94,10 +94,10 @@ index bfb315d6..13c6c9d3 100644 /* 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 +index 014f716d..325887a2 100644 --- a/src/libcamera/bayer_format.cpp +++ b/src/libcamera/bayer_format.cpp -@@ -108,6 +108,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{ +@@ -112,6 +112,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{ { formats::SGRBG10, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10) } }, { { BayerFormat::RGGB, 10, BayerFormat::Packing::None }, { formats::SRGGB10, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10) } }, @@ -106,7 +106,7 @@ index 3bf15fb4..ae227540 100644 { { BayerFormat::BGGR, 10, BayerFormat::Packing::CSI2 }, { formats::SBGGR10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P) } }, { { BayerFormat::GBRG, 10, BayerFormat::Packing::CSI2 }, -@@ -116,6 +118,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{ +@@ -120,6 +122,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{ { formats::SGRBG10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P) } }, { { BayerFormat::RGGB, 10, BayerFormat::Packing::CSI2 }, { formats::SRGGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P) } }, @@ -115,7 +115,7 @@ index 3bf15fb4..ae227540 100644 { { BayerFormat::BGGR, 10, BayerFormat::Packing::IPU3 }, { formats::SBGGR10_IPU3, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10) } }, { { BayerFormat::GBRG, 10, BayerFormat::Packing::IPU3 }, -@@ -193,6 +197,7 @@ const std::unordered_map<unsigned int, BayerFormat> mbusCodeToBayer{ +@@ -211,6 +215,7 @@ const std::unordered_map<unsigned int, BayerFormat> mbusCodeToBayer{ { MEDIA_BUS_FMT_SGBRG10_1X10, { BayerFormat::GBRG, 10, BayerFormat::Packing::None } }, { MEDIA_BUS_FMT_SGRBG10_1X10, { BayerFormat::GRBG, 10, BayerFormat::Packing::None } }, { MEDIA_BUS_FMT_SRGGB10_1X10, { BayerFormat::RGGB, 10, BayerFormat::Packing::None } }, @@ -123,25 +123,11 @@ index 3bf15fb4..ae227540 100644 { 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 +index cf41f2c2..13ac3253 100644 --- a/src/libcamera/formats.cpp +++ b/src/libcamera/formats.cpp -@@ -599,6 +599,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{ +@@ -639,6 +639,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{ .pixelsPerGroup = 2, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, @@ -158,7 +144,7 @@ index 447e6238..aef7d598 100644 { formats::SBGGR10_CSI2P, { .name = "SBGGR10_CSI2P", .format = formats::SBGGR10_CSI2P, -@@ -639,6 +649,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{ +@@ -679,6 +689,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{ .pixelsPerGroup = 4, .planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }}, } }, @@ -176,10 +162,10 @@ index 447e6238..aef7d598 100644 .name = "SBGGR12", .format = formats::SBGGR12, diff --git a/src/libcamera/formats.yaml b/src/libcamera/formats.yaml -index 539ac0b3..0786a900 100644 +index fe027a7c..394f980c 100644 --- a/src/libcamera/formats.yaml +++ b/src/libcamera/formats.yaml -@@ -100,6 +100,8 @@ formats: +@@ -107,6 +107,8 @@ formats: fourcc: DRM_FORMAT_SGBRG10 - SBGGR10: fourcc: DRM_FORMAT_SBGGR10 @@ -188,7 +174,7 @@ index 539ac0b3..0786a900 100644 - SRGGB12: fourcc: DRM_FORMAT_SRGGB12 -@@ -144,6 +146,9 @@ formats: +@@ -151,6 +153,9 @@ formats: - SBGGR10_CSI2P: fourcc: DRM_FORMAT_SBGGR10 mod: MIPI_FORMAT_MOD_CSI2_PACKED @@ -198,11 +184,25 @@ index 539ac0b3..0786a900 100644 - SRGGB12_CSI2P: fourcc: DRM_FORMAT_SRGGB12 +diff --git a/src/libcamera/sensor/camera_sensor.cpp b/src/libcamera/sensor/camera_sensor.cpp +index c6d7f801..77c396b9 100644 +--- a/src/libcamera/sensor/camera_sensor.cpp ++++ b/src/libcamera/sensor/camera_sensor.cpp +@@ -526,6 +526,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/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp -index 5551c62e..53078d99 100644 +index 70568335..a2fbd644 100644 --- a/src/libcamera/v4l2_pixelformat.cpp +++ b/src/libcamera/v4l2_pixelformat.cpp -@@ -153,6 +153,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{ +@@ -159,6 +159,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{ { formats::SGRBG10, "10-bit Bayer GRGR/BGBG" } }, { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), { formats::SRGGB10, "10-bit Bayer RGRG/GBGB" } }, @@ -211,7 +211,7 @@ index 5551c62e..53078d99 100644 { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), { formats::SBGGR10_CSI2P, "10-bit Bayer BGBG/GRGR Packed" } }, { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), -@@ -161,6 +163,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{ +@@ -167,6 +169,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{ { formats::SGRBG10_CSI2P, "10-bit Bayer GRGR/BGBG Packed" } }, { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), { formats::SRGGB10_CSI2P, "10-bit Bayer RGRG/GBGB Packed" } }, @@ -221,17 +221,23 @@ index 5551c62e..53078d99 100644 { 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 +index 6da77775..0ba8c0de 100644 --- a/src/libcamera/v4l2_subdevice.cpp +++ b/src/libcamera/v4l2_subdevice.cpp -@@ -128,6 +128,7 @@ const std::map<uint32_t, V4L2SubdeviceFormatInfo> formatInfoMap = { - { MEDIA_BUS_FMT_SGBRG10_1X10, { 10, "SGBRG10_1X10", PixelFormatInfo::ColourEncodingRAW } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, { 10, "SGRBG10_1X10", PixelFormatInfo::ColourEncodingRAW } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, { 10, "SRGGB10_1X10", PixelFormatInfo::ColourEncodingRAW } }, -+ { MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { 10, "SIGIG_GBGR_IGIG_GRGB10_1X10", PixelFormatInfo::ColourEncodingRAW } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, { 12, "SBGGR12_1X12", PixelFormatInfo::ColourEncodingRAW } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12", PixelFormatInfo::ColourEncodingRAW } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12", PixelFormatInfo::ColourEncodingRAW } }, +@@ -595,6 +595,13 @@ const std::map<uint32_t, MediaBusFormatInfo> mediaBusFormatInfo{ + .bitsPerPixel = 10, + .colourEncoding = PixelFormatInfo::ColourEncodingRAW, + } }, ++ { MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { ++ .name = "SIGIG_GBGR_IGIG_GRGB10_1X10", ++ .code = MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, ++ .type = MediaBusFormatInfo::Type::Image, ++ .bitsPerPixel = 10, ++ .colourEncoding = PixelFormatInfo::ColourEncodingRAW, ++ } }, + { MEDIA_BUS_FMT_SBGGR12_1X12, { + .name = "SBGGR12_1X12", + .code = MEDIA_BUS_FMT_SBGGR12_1X12, -- -2.43.2 +2.45.2 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 deleted file mode 100644 index b640ddaa24cd..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch +++ /dev/null @@ -1,82 +0,0 @@ -From d86746fc1739f678e4bafe43f5047cba9b6b053e Mon Sep 17 00:00:00 2001 -From: Andrey Konovalov <andrey.konovalov@linaro.org> -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. - -Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - src/libcamera/pipeline/simple/simple.cpp | 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..a84f6760 100644 ---- a/src/libcamera/pipeline/simple/simple.cpp -+++ b/src/libcamera/pipeline/simple/simple.cpp -@@ -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 = adjustSize(cfg.size, pipeConfig_->outputSizes); - LOG(SimplePipeline, Debug) - << "Adjusting size from " << cfg.size -- << " to " << pipeConfig_->captureSize; -- cfg.size = pipeConfig_->captureSize; -+ << " to " << adjustedSize; -+ cfg.size = adjustedSize; - status = Adjusted; - } - --- -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 deleted file mode 100644 index 450a0a21f19a..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch +++ /dev/null @@ -1,350 +0,0 @@ -From 96e50c6a43352a9cb81d558fea27e580f2b26585 Mon Sep 17 00:00:00 2001 -From: Andrey Konovalov <andrey.konovalov@linaro.org> -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. - -Move dma_heaps.h and dma_heaps.cpp to common directories. Update -the build files and RPi vc4 pipeline handler accordingly. - -Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> -Reviewed-by: Naushir Patuck <naush@raspberrypi.com> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> ---- - .../libcamera/internal}/dma_heaps.h | 4 - - include/libcamera/internal/meson.build | 1 + - 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%) - 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% -rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.h -rename to include/libcamera/internal/dma_heaps.h -index 0a4a8d86..cff8f140 100644 ---- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h -+++ b/include/libcamera/internal/dma_heaps.h -@@ -13,8 +13,6 @@ - - namespace libcamera { - --namespace RPi { -- - class DmaHeap - { - public: -@@ -27,6 +25,4 @@ private: - UniqueFD dmaHeapHandle_; - }; - --} /* namespace RPi */ -- - } /* namespace libcamera */ -diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build -index 7f1f3440..33eb0fb3 100644 ---- a/include/libcamera/internal/meson.build -+++ b/include/libcamera/internal/meson.build -@@ -25,6 +25,7 @@ libcamera_internal_headers = files([ - 'device_enumerator.h', - 'device_enumerator_sysfs.h', - 'device_enumerator_udev.h', -+ 'dma_heaps.h', - 'formats.h', - 'framebuffer.h', - 'ipa_manager.h', -diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp -new file mode 100644 -index 00000000..38ef175a ---- /dev/null -+++ b/src/libcamera/dma_heaps.cpp -@@ -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" -+ -+#include <array> -+#include <fcntl.h> -+#include <sys/ioctl.h> -+#include <unistd.h> -+ -+#include <linux/dma-buf.h> -+#include <linux/dma-heap.h> -+ -+#include <libcamera/base/log.h> -+ -+/** -+ * \file dma_heaps.cpp -+ * \brief CMA dma-heap allocator -+ */ -+ -+/* -+ * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma -+ * to only have to worry about importing. -+ * -+ * Annoyingly, should the cma heap size be specified on the kernel command line -+ * instead of DT, the heap gets named "reserved" instead. -+ */ -+static constexpr std::array<const char *, 2> heapNames = { -+ "/dev/dma_heap/linux,cma", -+ "/dev/dma_heap/reserved" -+}; -+ -+namespace libcamera { -+ -+LOG_DEFINE_CATEGORY(DmaHeap) -+ -+/** -+ * \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"; -+} -+ -+/** -+ * \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 -+++ b/src/libcamera/meson.build -@@ -17,6 +17,7 @@ libcamera_sources = files([ - 'delayed_controls.cpp', - 'device_enumerator.cpp', - 'device_enumerator_sysfs.cpp', -+ 'dma_heaps.cpp', - 'fence.cpp', - 'formats.cpp', - 'framebuffer.cpp', -diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp -deleted file mode 100644 -index 317b1fc1..00000000 ---- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp -+++ /dev/null -@@ -1,90 +0,0 @@ --/* SPDX-License-Identifier: LGPL-2.1-or-later */ --/* -- * Copyright (C) 2020, Raspberry Pi Ltd -- * -- * dma_heaps.h - Helper class for dma-heap allocations. -- */ -- --#include "dma_heaps.h" -- --#include <array> --#include <fcntl.h> --#include <linux/dma-buf.h> --#include <linux/dma-heap.h> --#include <sys/ioctl.h> --#include <unistd.h> -- --#include <libcamera/base/log.h> -- --/* -- * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma -- * to only have to worry about importing. -- * -- * Annoyingly, should the cma heap size be specified on the kernel command line -- * instead of DT, the heap gets named "reserved" instead. -- */ --static constexpr std::array<const char *, 2> heapNames = { -- "/dev/dma_heap/linux,cma", -- "/dev/dma_heap/reserved" --}; -- --namespace libcamera { -- --LOG_DECLARE_CATEGORY(RPI) -- --namespace RPi { -- --DmaHeap::DmaHeap() --{ -- for (const char *name : heapNames) { -- int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); -- if (ret < 0) { -- ret = errno; -- LOG(RPI, Debug) << "Failed to open " << name << ": " -- << strerror(ret); -- continue; -- } -- -- dmaHeapHandle_ = UniqueFD(ret); -- break; -- } -- -- if (!dmaHeapHandle_.isValid()) -- LOG(RPI, Error) << "Could not open any dmaHeap device"; --} -- --DmaHeap::~DmaHeap() = default; -- --UniqueFD DmaHeap::alloc(const char *name, std::size_t size) --{ -- int ret; -- -- if (!name) -- return {}; -- -- struct dma_heap_allocation_data alloc = {}; -- -- alloc.len = size; -- alloc.fd_flags = O_CLOEXEC | O_RDWR; -- -- ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc); -- if (ret < 0) { -- LOG(RPI, Error) << "dmaHeap allocation failure for " -- << name; -- return {}; -- } -- -- UniqueFD allocFd(alloc.fd); -- ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name); -- if (ret < 0) { -- LOG(RPI, Error) << "dmaHeap naming failure for " -- << name; -- return {}; -- } -- -- return allocFd; --} -- --} /* namespace RPi */ -- --} /* namespace libcamera */ -diff --git a/src/libcamera/pipeline/rpi/vc4/meson.build b/src/libcamera/pipeline/rpi/vc4/meson.build -index cdb049c5..386e2296 100644 ---- a/src/libcamera/pipeline/rpi/vc4/meson.build -+++ b/src/libcamera/pipeline/rpi/vc4/meson.build -@@ -1,7 +1,6 @@ - # SPDX-License-Identifier: CC0-1.0 - - libcamera_sources += files([ -- 'dma_heaps.cpp', - 'vc4.cpp', - ]) - -diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp -index 26102ea7..3a42e75e 100644 ---- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp -+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp -@@ -12,12 +12,11 @@ - #include <libcamera/formats.h> - - #include "libcamera/internal/device_enumerator.h" -+#include "libcamera/internal/dma_heaps.h" - - #include "../common/pipeline_base.h" - #include "../common/rpi_stream.h" - --#include "dma_heaps.h" -- - using namespace std::chrono_literals; - - namespace libcamera { -@@ -87,7 +86,7 @@ public: - RPi::Device<Isp, 4> isp_; - - /* DMAHEAP allocation helper. */ -- RPi::DmaHeap dmaHeap_; -+ DmaHeap dmaHeap_; - SharedFD lsTable_; - - struct Config { --- -2.43.2 - diff --git a/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch b/users/flokli/ipu6-softisp/libcamera/0002-ov01a1s-HACK.patch index 343f04c8502b..61316d10427f 100644 --- a/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch +++ b/users/flokli/ipu6-softisp/libcamera/0002-ov01a1s-HACK.patch @@ -1,30 +1,30 @@ -From 2bde6e420571c6dc0ff25246620b4c987987f6be Mon Sep 17 00:00:00 2001 +From 5895f4ed8163780446665b99b8d5dc31d6f2b791 Mon Sep 17 00:00:00 2001 From: Hans de Goede <hdegoede@redhat.com> Date: Tue, 19 Dec 2023 15:45:51 +0100 -Subject: [PATCH 20/21] ov01a1s HACK +Subject: [PATCH 2/3] ov01a1s HACK Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- - src/libcamera/camera_sensor.cpp | 6 ++++++ - src/libcamera/software_isp/debayer_cpu.cpp | 8 ++++++++ + src/libcamera/sensor/camera_sensor.cpp | 6 ++++++ + src/libcamera/software_isp/debayer_cpu.cpp | 7 +++++++ src/libcamera/software_isp/swstats_cpu.cpp | 4 ++++ - 3 files changed, 18 insertions(+) + 3 files changed, 17 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 @@ +diff --git a/src/libcamera/sensor/camera_sensor.cpp b/src/libcamera/sensor/camera_sensor.cpp +index 77c396b9..628b12f4 100644 +--- a/src/libcamera/sensor/camera_sensor.cpp ++++ b/src/libcamera/sensor/camera_sensor.cpp +@@ -33,6 +33,9 @@ + */ namespace libcamera { - ++ +// HACK HACK +bool is_ov01a1s = false; -+ + LOG_DEFINE_CATEGORY(CameraSensor) - /** -@@ -426,6 +429,9 @@ int CameraSensor::initProperties() +@@ -442,6 +445,9 @@ int CameraSensor::initProperties() model_ = subdev_->model(); properties_.set(properties::Model, utils::toAscii(model_)); @@ -35,7 +35,7 @@ index f19f72ea..7ad4b9ef 100644 int ret = generateId(); if (ret) diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp -index 3be3cdfe..d6599805 100644 +index 8254bbe9..10ea29b1 100644 --- a/src/libcamera/software_isp/debayer_cpu.cpp +++ b/src/libcamera/software_isp/debayer_cpu.cpp @@ -23,6 +23,7 @@ @@ -46,7 +46,7 @@ index 3be3cdfe..d6599805 100644 /** * \class DebayerCpu * \brief Class for debayering on the CPU -@@ -262,6 +263,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf +@@ -275,6 +276,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat); @@ -56,7 +56,7 @@ index 3be3cdfe..d6599805 100644 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 +@@ -343,6 +347,9 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat); @@ -64,12 +64,10 @@ index 3be3cdfe..d6599805 100644 + 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 +index 815c4d4f..0b310f80 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -19,6 +19,7 @@ @@ -80,7 +78,7 @@ index be310f56..cda1894a 100644 /** * \class SwStatsCpu * \brief Class for gathering statistics on the CPU -@@ -271,6 +272,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg) +@@ -367,6 +368,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg) BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputCfg.pixelFormat); @@ -91,5 +89,5 @@ index be310f56..cda1894a 100644 setupStandardBayerOrder(bayerFormat.order) == 0) { switch (bayerFormat.bitDepth) { -- -2.43.2 +2.45.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/0003-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch index a3af38c93c79..f250617bd3ad 100644 --- a/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch +++ b/users/flokli/ipu6-softisp/libcamera/0003-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch @@ -1,7 +1,7 @@ -From a21bb26dcfcc00425f031421b87576f9c81e4824 Mon Sep 17 00:00:00 2001 +From 06add438e4fc53faca6e016bd582df0e7ac5a271 Mon Sep 17 00:00:00 2001 From: Hans de Goede <hdegoede@redhat.com> Date: Wed, 24 Jan 2024 20:44:29 +0100 -Subject: [PATCH 21/21] libcamera: debayer_cpu: Make the minimum size 1280x720 +Subject: [PATCH 3/3] 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. @@ -12,31 +12,31 @@ Signed-off-by: Hans de Goede <hdegoede@redhat.com> 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 +index 10ea29b1..a354138b 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) +@@ -805,10 +805,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); +- return SizeRange(Size(patternSize.width, patternSize.height), +- Size((inputSize.width - 2 * patternSize.width) & ~(patternSize.width - 1), +- (inputSize.height - 2 * borderHeight) & ~(patternSize.height - 1)), +- patternSize.width, patternSize.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) ++ * Size(patternSize.width, patternSize.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); ++ unsigned int w = (inputSize.width - 2 * patternSize.width) & ~(patternSize.width - 1); ++ unsigned int h = (inputSize.height - 2 * patternSize.height) & ~(patternSize.height - 1); + return SizeRange(Size(std::min(w, 1280u), std::min(h, 720u)), + Size(w, h), -+ pattern_size.width, pattern_size.height); ++ patternSize.width, patternSize.height); } } /* namespace libcamera */ -- -2.43.2 +2.45.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 deleted file mode 100644 index 6e5ef9445a4b..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch +++ /dev/null @@ -1,169 +0,0 @@ -From 5df9bc3b2a3d86bcc8504896cc87d7fcb5aea3a4 Mon Sep 17 00:00:00 2001 -From: Andrey Konovalov <andrey.konovalov@linaro.org> -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) -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. - -Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - include/libcamera/internal/dma_heaps.h | 12 ++++- - src/libcamera/dma_heaps.cpp | 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..80bf29e7 100644 ---- a/include/libcamera/internal/dma_heaps.h -+++ b/include/libcamera/internal/dma_heaps.h -@@ -9,6 +9,7 @@ - - #include <stddef.h> - -+#include <libcamera/base/flags.h> - #include <libcamera/base/unique_fd.h> - - namespace libcamera { -@@ -16,7 +17,14 @@ namespace libcamera { - class DmaHeap - { - public: -- DmaHeap(); -+ enum class DmaHeapFlag { -+ Cma = 1 << 0, -+ System = 1 << 1, -+ }; -+ -+ using DmaHeapFlags = Flags<DmaHeapFlag>; -+ -+ DmaHeap(DmaHeapFlags flags = DmaHeapFlag::Cma); - ~DmaHeap(); - bool isValid() const { return dmaHeapHandle_.isValid(); } - UniqueFD alloc(const char *name, std::size_t size); -@@ -25,4 +33,6 @@ private: - UniqueFD dmaHeapHandle_; - }; - -+LIBCAMERA_FLAGS_ENABLE_OPERATORS(DmaHeap::DmaHeapFlag) -+ - } /* namespace libcamera */ -diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp -index 38ef175a..d0e33ce6 100644 ---- a/src/libcamera/dma_heaps.cpp -+++ b/src/libcamera/dma_heaps.cpp -@@ -19,9 +19,11 @@ - - /** - * \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 - * to only have to worry about importing. -@@ -29,42 +31,77 @@ - * Annoyingly, should the cma heap size be specified on the kernel command line - * instead of DT, the heap gets named "reserved" instead. - */ --static constexpr std::array<const char *, 2> heapNames = { -- "/dev/dma_heap/linux,cma", -- "/dev/dma_heap/reserved" -+ -+/** -+ * \struct DmaHeapInfo -+ * \brief Tells what type of dma-heap the dma-heap represented by the device node name is -+ * \var DmaHeapInfo::flag -+ * \brief The type of the dma-heap -+ * \var DmaHeapInfo::name -+ * \brief The dma-heap's device node name -+ */ -+struct DmaHeapInfo { -+ DmaHeap::DmaHeapFlag flag; -+ const char *name; - }; - --namespace libcamera { -+static constexpr std::array<DmaHeapInfo, 3> heapInfos = { -+ { /* CMA heap names first */ -+ { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma" }, -+ { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved" }, -+ { DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system" } } -+}; - - LOG_DEFINE_CATEGORY(DmaHeap) - - /** - * \class DmaHeap -- * \brief Helper class for CMA dma-heap allocations -+ * \brief Helper class for dma-heap allocations - */ - - /** -- * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor -+ * \enum DmaHeap::DmaHeapFlag -+ * \brief Type of the dma-heap -+ * \var DmaHeap::Cma -+ * \brief Allocate from a CMA dma-heap -+ * \var DmaHeap::System -+ * \brief Allocate from the system dma-heap -+ */ -+ -+/** -+ * \typedef DmaHeap::DmaHeapFlags -+ * \brief A bitwise combination of DmaHeap::DmaHeapFlag values -+ */ -+ -+/** -+ * \brief Construct a DmaHeap that owns a CMA or system dma-heap file descriptor -+ * \param [in] flags The type(s) of the dma-heap(s) to allocate from - * -- * Goes through the internal list of possible names of the CMA dma-heap devices -- * until a CMA dma-heap device is successfully opened. If it fails to open any -- * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap -- * object owns a wrapped dma-heap file descriptor. -+ * By default \a flags are set to DmaHeap::DmaHeapFlag::Cma. The constructor goes -+ * through the internal list of possible names of the CMA and system dma-heap devices -+ * until the dma-heap device of the requested type is successfully opened. If more -+ * than one dma-heap type is specified in flags the CMA heap is tried first. If it -+ * fails to open any dma-heap device an invalid DmaHeap object is constructed. -+ * A valid DmaHeap object owns a wrapped dma-heap file descriptor. - * - * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it. - */ --DmaHeap::DmaHeap() -+DmaHeap::DmaHeap(DmaHeapFlags flags) - { -- for (const char *name : heapNames) { -- int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); -+ for (const auto &info : heapInfos) { -+ if (!(flags & info.flag)) -+ continue; -+ -+ 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; - } - -+ LOG(DmaHeap, Debug) << "Using " << info.name; - dmaHeapHandle_ = UniqueFD(ret); - break; - } --- -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 deleted file mode 100644 index 48f10aa47a99..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch +++ /dev/null @@ -1,69 +0,0 @@ -From a6777760a2121f02808baecea504ac0e242f860b Mon Sep 17 00:00:00 2001 -From: Andrey Konovalov <andrey.konovalov@linaro.org> -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. - -Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> ---- - include/libcamera/internal/meson.build | 1 + - .../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 ---- a/include/libcamera/internal/meson.build -+++ b/include/libcamera/internal/meson.build -@@ -39,6 +39,7 @@ libcamera_internal_headers = files([ - 'process.h', - 'pub_key.h', - 'request.h', -+ 'shared_mem_object.h', - 'source_paths.h', - 'sysfs.h', - 'v4l2_device.h', -diff --git a/src/libcamera/pipeline/rpi/common/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h -similarity index 97% -rename from src/libcamera/pipeline/rpi/common/shared_mem_object.h -rename to include/libcamera/internal/shared_mem_object.h -index aa56c220..98636b44 100644 ---- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h -+++ b/include/libcamera/internal/shared_mem_object.h -@@ -6,8 +6,8 @@ - */ - #pragma once - --#include <cstddef> - #include <fcntl.h> -+#include <stddef.h> - #include <string> - #include <sys/mman.h> - #include <sys/stat.h> -@@ -19,8 +19,6 @@ - - namespace libcamera { - --namespace RPi { -- - template<class T> - class SharedMemObject - { -@@ -123,6 +121,4 @@ private: - T *obj_; - }; - --} /* namespace RPi */ -- - } /* namespace libcamera */ --- -2.43.2 - 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 deleted file mode 100644 index d2143febf740..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch +++ /dev/null @@ -1,403 +0,0 @@ -From f94af21adc1889706127d07c5425f44c9cec9a95 Mon Sep 17 00:00:00 2001 -From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com> -Date: Mon, 11 Mar 2024 15:15:09 +0100 -Subject: [PATCH 05/21] libcamera: shared_mem_object: reorganize the code and - document the SharedMemObject class - -Split the parts which doesn't otherwise depend on the type T or -arguments Args out of the SharedMemObject class into a new -SharedMem class. - -Doxygen documentation by Dennis Bonke and Andrei Konovalov. - -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Co-developed-by: Dennis Bonke <admin@dennisbonke.com> -Signed-off-by: Dennis Bonke <admin@dennisbonke.com> -Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> ---- - .../libcamera/internal/shared_mem_object.h | 101 ++++++---- - src/libcamera/meson.build | 1 + - src/libcamera/shared_mem_object.cpp | 190 ++++++++++++++++++ - 3 files changed, 253 insertions(+), 39 deletions(-) - create mode 100644 src/libcamera/shared_mem_object.cpp - -diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h -index 98636b44..43b07c9d 100644 ---- a/include/libcamera/internal/shared_mem_object.h -+++ b/include/libcamera/internal/shared_mem_object.h -@@ -6,12 +6,9 @@ - */ - #pragma once - --#include <fcntl.h> - #include <stddef.h> - #include <string> - #include <sys/mman.h> --#include <sys/stat.h> --#include <unistd.h> - #include <utility> - - #include <libcamera/base/class.h> -@@ -19,58 +16,92 @@ - - namespace libcamera { - -+class SharedMem -+{ -+public: -+ SharedMem() -+ : mem_(nullptr) -+ { -+ } -+ -+ SharedMem(const std::string &name, std::size_t size); -+ -+ SharedMem(SharedMem &&rhs) -+ { -+ this->name_ = std::move(rhs.name_); -+ this->fd_ = std::move(rhs.fd_); -+ this->mem_ = rhs.mem_; -+ rhs.mem_ = nullptr; -+ } -+ -+ virtual ~SharedMem() -+ { -+ if (mem_) -+ munmap(mem_, size_); -+ } -+ -+ /* Make SharedMem non-copyable for now. */ -+ LIBCAMERA_DISABLE_COPY(SharedMem) -+ -+ SharedMem &operator=(SharedMem &&rhs) -+ { -+ this->name_ = std::move(rhs.name_); -+ this->fd_ = std::move(rhs.fd_); -+ this->mem_ = rhs.mem_; -+ rhs.mem_ = nullptr; -+ return *this; -+ } -+ -+ const SharedFD &fd() const -+ { -+ return fd_; -+ } -+ -+ void *mem() const -+ { -+ return mem_; -+ } -+ -+private: -+ std::string name_; -+ SharedFD fd_; -+ size_t size_; -+protected: -+ void *mem_; -+}; -+ - template<class T> --class SharedMemObject -+class SharedMemObject : public SharedMem - { - public: - static constexpr std::size_t SIZE = sizeof(T); - - SharedMemObject() -- : obj_(nullptr) -+ : SharedMem(), obj_(nullptr) - { - } - - template<class... Args> - SharedMemObject(const std::string &name, Args &&...args) -- : name_(name), obj_(nullptr) -+ : SharedMem(name, SIZE), obj_(nullptr) - { -- void *mem; -- int ret; -- -- ret = memfd_create(name_.c_str(), MFD_CLOEXEC); -- if (ret < 0) -- return; -- -- fd_ = SharedFD(std::move(ret)); -- if (!fd_.isValid()) -- return; -- -- ret = ftruncate(fd_.get(), SIZE); -- if (ret < 0) -+ if (mem_ == nullptr) - return; - -- mem = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, -- fd_.get(), 0); -- if (mem == MAP_FAILED) -- return; -- -- obj_ = new (mem) T(std::forward<Args>(args)...); -+ obj_ = new (mem_) T(std::forward<Args>(args)...); - } - - SharedMemObject(SharedMemObject<T> &&rhs) -+ : SharedMem(std::move(rhs)) - { -- this->name_ = std::move(rhs.name_); -- this->fd_ = std::move(rhs.fd_); - this->obj_ = rhs.obj_; - rhs.obj_ = nullptr; - } - - ~SharedMemObject() - { -- if (obj_) { -+ if (obj_) - obj_->~T(); -- munmap(obj_, SIZE); -- } - } - - /* Make SharedMemObject non-copyable for now. */ -@@ -78,8 +109,7 @@ public: - - SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs) - { -- this->name_ = std::move(rhs.name_); -- this->fd_ = std::move(rhs.fd_); -+ SharedMem::operator=(std::move(rhs)); - this->obj_ = rhs.obj_; - rhs.obj_ = nullptr; - return *this; -@@ -105,19 +135,12 @@ public: - return *obj_; - } - -- const SharedFD &fd() const -- { -- return fd_; -- } -- - explicit operator bool() const - { - return !!obj_; - } - - private: -- std::string name_; -- SharedFD fd_; - T *obj_; - }; - -diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build -index 3c5e43df..94a95ae3 100644 ---- a/src/libcamera/meson.build -+++ b/src/libcamera/meson.build -@@ -41,6 +41,7 @@ libcamera_sources = files([ - 'process.cpp', - 'pub_key.cpp', - 'request.cpp', -+ 'shared_mem_object.cpp', - 'source_paths.cpp', - 'stream.cpp', - 'sysfs.cpp', -diff --git a/src/libcamera/shared_mem_object.cpp b/src/libcamera/shared_mem_object.cpp -new file mode 100644 -index 00000000..44fe74c2 ---- /dev/null -+++ b/src/libcamera/shared_mem_object.cpp -@@ -0,0 +1,190 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Raspberry Pi Ltd -+ * -+ * shared_mem_object.cpp - Helper class for shared memory allocations -+ */ -+ -+#include "libcamera/internal/shared_mem_object.h" -+ -+#include <sys/types.h> -+#include <unistd.h> -+ -+/** -+ * \file shared_mem_object.cpp -+ * \brief Helper class for shared memory allocations -+ */ -+ -+namespace libcamera { -+ -+/** -+ * \class SharedMem -+ * \brief Helper class for allocating shared memory -+ * -+ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries. -+ * -+ * SharedMem allocates the shared memory of the given size and maps it. -+ * To check that the shared memory was allocated and mapped successfully, one -+ * needs to verify that the pointer to the shared memory returned by SharedMem::mem() -+ * is not nullptr. -+ * -+ * To access the shared memory from another process the SharedFD should be passed -+ * to that process, and then the shared memory should be mapped into that process -+ * address space by calling mmap(). -+ * -+ * A single memfd is created for every SharedMem. If there is a need to allocate -+ * a large number of objects in shared memory, these objects should be grouped -+ * together and use the shared memory allocated by a single SharedMem object if -+ * possible. This will help to minimize the number of created memfd's. -+ */ -+ -+/** -+ * \fn SharedMem::SharedMem(const std::string &name, std::size_t size) -+ * \brief Constructor for the SharedMem -+ * \param[in] name Name of the SharedMem -+ * \param[in] size Size of the shared memory to allocate and map -+ */ -+ -+/** -+ * \fn SharedMem::SharedMem(SharedMem &&rhs) -+ * \brief Move constructor for SharedMem -+ * \param[in] rhs The object to move -+ */ -+ -+/** -+ * \fn SharedMem::~SharedMem() -+ * \brief SharedMem destructor -+ * -+ * Unmaps the allocated shared memory. Decrements the shared memory descriptor use -+ * count. -+ */ -+ -+/** -+ * \fn SharedMem &SharedMem::operator=(SharedMem &&rhs) -+ * \brief Move constructor for SharedMem -+ * \param[in] rhs The object to move -+ */ -+ -+/** -+ * \fn const SharedFD &SharedMem::fd() const -+ * \brief Gets the file descriptor for the underlying shared memory -+ * \return The file descriptor -+ */ -+ -+/** -+ * \fn void *SharedMem::mem() const -+ * \brief Gets the pointer to the underlying shared memory -+ * \return The pointer to the shared memory -+ */ -+ -+SharedMem::SharedMem(const std::string &name, std::size_t size) -+ : name_(name), size_(size), mem_(nullptr) -+{ -+ int fd = memfd_create(name_.c_str(), MFD_CLOEXEC); -+ if (fd < 0) -+ return; -+ -+ fd_ = SharedFD(std::move(fd)); -+ if (!fd_.isValid()) -+ return; -+ -+ if (ftruncate(fd_.get(), size_) < 0) -+ return; -+ -+ mem_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, -+ fd_.get(), 0); -+ if (mem_ == MAP_FAILED) -+ mem_ = nullptr; -+} -+ -+/** -+ * \var SharedMem::mem_ -+ * \brief Pointer to the shared memory allocated -+ */ -+ -+/** -+ * \class SharedMemObject -+ * \brief Helper class for allocating objects in shared memory -+ * -+ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries. -+ * -+ * Given the type of the object to be created in shared memory and the arguments -+ * to pass to this object's constructor, SharedMemObject allocates the shared memory -+ * of the size of the object and constructs the object in this memory. To ensure -+ * that the SharedMemObject was created successfully, one needs to verify that the -+ * overloaded bool() operator returns true. The object created in the shared memory -+ * can be accessed using the SharedMemObject::operator*() indirection operator. Its -+ * members can be accessed with the SharedMemObject::operator->() member of pointer -+ * operator. -+ * -+ * To access the object from another process the SharedFD should be passed to that -+ * process, and the shared memory should be mapped by calling mmap(). -+ * -+ * A single memfd is created for every SharedMemObject. If there is a need to allocate -+ * a large number of objects in shared memory, these objects should be grouped into a -+ * single large object to keep the number of created memfd's reasonably small. -+ */ -+ -+/** -+ * \var SharedMemObject::SIZE -+ * \brief The size of the object that is going to be stored here -+ */ -+ -+/** -+ * \fn SharedMemObject< T >::SharedMemObject(const std::string &name, Args &&...args) -+ * \brief Constructor for the SharedMemObject -+ * \param[in] name Name of the SharedMemObject -+ * \param[in] args Args to pass to the constructor of the object in shared memory -+ */ -+ -+/** -+ * \fn SharedMemObject::SharedMemObject(SharedMemObject<T> &&rhs) -+ * \brief Move constructor for SharedMemObject -+ * \param[in] rhs The object to move -+ */ -+ -+/** -+ * \fn SharedMemObject::~SharedMemObject() -+ * \brief SharedMemObject destructor -+ * -+ * Destroys the object created in the shared memory and then unmaps the shared memory. -+ * Decrements the shared memory descriptor use count. -+ */ -+ -+/** -+ * \fn SharedMemObject::operator=(SharedMemObject<T> &&rhs) -+ * \brief Operator= for SharedMemObject -+ * \param[in] rhs The SharedMemObject object to take the data from -+ */ -+ -+/** -+ * \fn SharedMemObject::operator->() -+ * \brief Operator-> for SharedMemObject -+ * \return The pointer to the object -+ */ -+ -+/** -+ * \fn const T *SharedMemObject::operator->() const -+ * \brief Operator-> for SharedMemObject -+ * \return The pointer to the const object -+ */ -+ -+/** -+ * \fn SharedMemObject::operator*() -+ * \brief Operator* for SharedMemObject -+ * \return The reference to the object -+ */ -+ -+/** -+ * \fn const T &SharedMemObject::operator*() const -+ * \brief Operator* for SharedMemObject -+ * \return Const reference to the object -+ */ -+ -+/** -+ * \fn SharedMemObject::operator bool() -+ * \brief Operator bool() for SharedMemObject -+ * \return True if the object was created OK in the shared memory, false otherwise -+ */ -+ -+} // namespace libcamera --- -2.43.2 - 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 deleted file mode 100644 index 9f80b69f168c..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch +++ /dev/null @@ -1,523 +0,0 @@ -From 4259b01930333c6666a185d923e6e68ec915a4fd Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Mon, 11 Mar 2024 15:15:10 +0100 -Subject: [PATCH 06/21] libcamera: software_isp: Add SwStatsCpu class - -Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use. - -This implementation offers a configure function + functions to gather -statistics on a line by line basis. This allows CPU based software -debayering to call into interlace debayering and statistics gathering -on a line by line bases while the input data is still hot in the cache. - -This implementation also allows specifying a window over which to gather -statistics instead of processing the whole frame. - -Doxygen documentation by Dennis Bonke. - -Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> -Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Co-developed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Pavel Machek <pavel@ucw.cz> -Co-developed-by: Dennis Bonke <admin@dennisbonke.com> -Signed-off-by: Dennis Bonke <admin@dennisbonke.com> -Co-developed-by: Marttico <g.martti@gmail.com> -Signed-off-by: Marttico <g.martti@gmail.com> -Co-developed-by: Toon Langendam <t.langendam@gmail.com> -Signed-off-by: Toon Langendam <t.langendam@gmail.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - include/libcamera/internal/meson.build | 1 + - .../internal/software_isp/meson.build | 5 + - .../internal/software_isp/swisp_stats.h | 38 ++++ - src/libcamera/meson.build | 1 + - src/libcamera/software_isp/meson.build | 12 + - src/libcamera/software_isp/swstats_cpu.cpp | 208 ++++++++++++++++++ - src/libcamera/software_isp/swstats_cpu.h | 159 +++++++++++++ - 7 files changed, 424 insertions(+) - create mode 100644 include/libcamera/internal/software_isp/meson.build - create mode 100644 include/libcamera/internal/software_isp/swisp_stats.h - create mode 100644 src/libcamera/software_isp/meson.build - create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp - create mode 100644 src/libcamera/software_isp/swstats_cpu.h - -diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build -index 5807dfd9..160fdc37 100644 ---- a/include/libcamera/internal/meson.build -+++ b/include/libcamera/internal/meson.build -@@ -50,3 +50,4 @@ libcamera_internal_headers = files([ - ]) - - subdir('converter') -+subdir('software_isp') -diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build -new file mode 100644 -index 00000000..66c9c3fb ---- /dev/null -+++ b/include/libcamera/internal/software_isp/meson.build -@@ -0,0 +1,5 @@ -+# SPDX-License-Identifier: CC0-1.0 -+ -+libcamera_internal_headers += files([ -+ 'swisp_stats.h', -+]) -diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h -new file mode 100644 -index 00000000..afe42c9a ---- /dev/null -+++ b/include/libcamera/internal/software_isp/swisp_stats.h -@@ -0,0 +1,38 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Linaro Ltd -+ * -+ * swisp_stats.h - Statistics data format used by the software ISP and software IPA -+ */ -+ -+#pragma once -+ -+namespace libcamera { -+ -+/** -+ * \brief Struct that holds the statistics for the Software ISP. -+ */ -+struct SwIspStats { -+ /** -+ * \brief Holds the sum of all sampled red pixels. -+ */ -+ unsigned long sumR_; -+ /** -+ * \brief Holds the sum of all sampled green pixels. -+ */ -+ unsigned long sumG_; -+ /** -+ * \brief Holds the sum of all sampled blue pixels. -+ */ -+ unsigned long sumB_; -+ /** -+ * \brief Number of bins in the yHistogram. -+ */ -+ static constexpr unsigned int kYHistogramSize = 16; -+ /** -+ * \brief A histogram of luminance values. -+ */ -+ std::array<unsigned int, kYHistogramSize> yHistogram; -+}; -+ -+} /* namespace libcamera */ -diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build -index 94a95ae3..91e4cc60 100644 ---- a/src/libcamera/meson.build -+++ b/src/libcamera/meson.build -@@ -71,6 +71,7 @@ subdir('converter') - subdir('ipa') - subdir('pipeline') - subdir('proxy') -+subdir('software_isp') - - null_dep = dependency('', required : false) - -diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build -new file mode 100644 -index 00000000..fcfff74a ---- /dev/null -+++ b/src/libcamera/software_isp/meson.build -@@ -0,0 +1,12 @@ -+# SPDX-License-Identifier: CC0-1.0 -+ -+softisp_enabled = pipelines.contains('simple') -+summary({'SoftISP support' : softisp_enabled}, section : 'Configuration') -+ -+if not (softisp_enabled) -+ subdir_done() -+endif -+ -+libcamera_sources += files([ -+ 'swstats_cpu.cpp', -+]) -diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp -new file mode 100644 -index 00000000..448d0e4c ---- /dev/null -+++ b/src/libcamera/software_isp/swstats_cpu.cpp -@@ -0,0 +1,208 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Linaro Ltd -+ * Copyright (C) 2023, Red Hat Inc. -+ * -+ * Authors: -+ * Hans de Goede <hdegoede@redhat.com> -+ * -+ * swstats_cpu.cpp - CPU based software statistics implementation -+ */ -+ -+#include "swstats_cpu.h" -+ -+#include <libcamera/base/log.h> -+ -+#include <libcamera/stream.h> -+ -+#include "libcamera/internal/bayer_format.h" -+ -+namespace libcamera { -+ -+/** -+ * \class SwStatsCpu -+ * \brief Class for gathering statistics on the CPU -+ * -+ * CPU based software ISP statistics implementation. -+ * -+ * This class offers a configure function + functions to gather statistics -+ * on a line by line basis. This allows CPU based software debayering to -+ * interlace debayering and statistics gathering on a line by line basis -+ * while the input data is still hot in the cache. -+ * -+ * It is also possible to specify a window over which to gather -+ * statistics instead of processing the whole frame. -+ */ -+ -+LOG_DEFINE_CATEGORY(SwStatsCpu) -+ -+SwStatsCpu::SwStatsCpu() -+{ -+ sharedStats_ = SharedMemObject<SwIspStats>("softIsp_stats"); -+ if (!sharedStats_.fd().isValid()) -+ LOG(SwStatsCpu, Error) -+ << "Failed to create shared memory for statistics"; -+} -+ -+static const unsigned int kRedYMul = 77; /* 0.299 * 256 */ -+static const unsigned int kGreenYMul = 150; /* 0.587 * 256 */ -+static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */ -+ -+#define SWSTATS_START_LINE_STATS(pixel_t) \ -+ pixel_t r, g, g2, b; \ -+ unsigned int yVal; \ -+ \ -+ unsigned int sumR = 0; \ -+ unsigned int sumG = 0; \ -+ unsigned int sumB = 0; -+ -+#define SWSTATS_ACCUMULATE_LINE_STATS(div) \ -+ sumR += r; \ -+ sumG += g; \ -+ sumB += b; \ -+ \ -+ yVal = r * kRedYMul; \ -+ yVal += g * kGreenYMul; \ -+ yVal += b * kBlueYMul; \ -+ stats_.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++; -+ -+#define SWSTATS_FINISH_LINE_STATS() \ -+ stats_.sumR_ += sumR; \ -+ stats_.sumG_ += sumG; \ -+ stats_.sumB_ += sumB; -+ -+void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[]) -+{ -+ const uint8_t *src0 = src[1] + window_.x * 5 / 4; -+ const uint8_t *src1 = src[2] + window_.x * 5 / 4; -+ const int widthInBytes = window_.width * 5 / 4; -+ -+ if (swapLines_) -+ std::swap(src0, src1); -+ -+ SWSTATS_START_LINE_STATS(uint8_t) -+ -+ /* x += 5 sample every other 2x2 block */ -+ for (int x = 0; x < widthInBytes; x += 5) { -+ /* BGGR */ -+ b = src0[x]; -+ g = src0[x + 1]; -+ g2 = src1[x]; -+ r = src1[x + 1]; -+ g = (g + g2) / 2; -+ /* Data is already 8 bits, divide by 1 */ -+ SWSTATS_ACCUMULATE_LINE_STATS(1) -+ } -+ -+ SWSTATS_FINISH_LINE_STATS() -+} -+ -+void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[]) -+{ -+ const uint8_t *src0 = src[1] + window_.x * 5 / 4; -+ const uint8_t *src1 = src[2] + window_.x * 5 / 4; -+ const int widthInBytes = window_.width * 5 / 4; -+ -+ if (swapLines_) -+ std::swap(src0, src1); -+ -+ SWSTATS_START_LINE_STATS(uint8_t) -+ -+ /* x += 5 sample every other 2x2 block */ -+ for (int x = 0; x < widthInBytes; x += 5) { -+ /* GBRG */ -+ g = src0[x]; -+ b = src0[x + 1]; -+ r = src1[x]; -+ g2 = src1[x + 1]; -+ g = (g + g2) / 2; -+ /* Data is already 8 bits, divide by 1 */ -+ SWSTATS_ACCUMULATE_LINE_STATS(1) -+ } -+ -+ SWSTATS_FINISH_LINE_STATS() -+} -+ -+/** -+ * \brief Reset state to start statistics gathering for a new frame. -+ * -+ * This may only be called after a successful setWindow() call. -+ */ -+void SwStatsCpu::startFrame(void) -+{ -+ stats_.sumR_ = 0; -+ stats_.sumB_ = 0; -+ stats_.sumG_ = 0; -+ stats_.yHistogram.fill(0); -+} -+ -+/** -+ * \brief Finish statistics calculation for the current frame. -+ * -+ * This may only be called after a successful setWindow() call. -+ */ -+void SwStatsCpu::finishFrame(void) -+{ -+ *sharedStats_ = stats_; -+ statsReady.emit(0); -+} -+ -+/** -+ * \brief Configure the statistics object for the passed in input format. -+ * \param[in] inputCfg The input format -+ * -+ * \return 0 on success, a negative errno value on failure -+ */ -+int SwStatsCpu::configure(const StreamConfiguration &inputCfg) -+{ -+ BayerFormat bayerFormat = -+ BayerFormat::fromPixelFormat(inputCfg.pixelFormat); -+ -+ if (bayerFormat.bitDepth == 10 && -+ bayerFormat.packing == BayerFormat::Packing::CSI2) { -+ patternSize_.height = 2; -+ patternSize_.width = 4; /* 5 bytes per *4* pixels */ -+ /* Skip every 3th and 4th line, sample every other 2x2 block */ -+ ySkipMask_ = 0x02; -+ xShift_ = 0; -+ -+ switch (bayerFormat.order) { -+ case BayerFormat::BGGR: -+ case BayerFormat::GRBG: -+ stats0_ = &SwStatsCpu::statsBGGR10PLine0; -+ swapLines_ = bayerFormat.order == BayerFormat::GRBG; -+ return 0; -+ case BayerFormat::GBRG: -+ case BayerFormat::RGGB: -+ stats0_ = &SwStatsCpu::statsGBRG10PLine0; -+ swapLines_ = bayerFormat.order == BayerFormat::RGGB; -+ return 0; -+ default: -+ break; -+ } -+ } -+ -+ LOG(SwStatsCpu, Info) -+ << "Unsupported input format " << inputCfg.pixelFormat.toString(); -+ return -EINVAL; -+} -+ -+/** -+ * \brief Specify window coordinates over which to gather statistics. -+ * \param[in] window The window object. -+ */ -+void SwStatsCpu::setWindow(Rectangle window) -+{ -+ window_ = window; -+ -+ window_.x &= ~(patternSize_.width - 1); -+ window_.x += xShift_; -+ window_.y &= ~(patternSize_.height - 1); -+ -+ /* width_ - xShift_ to make sure the window fits */ -+ window_.width -= xShift_; -+ window_.width &= ~(patternSize_.width - 1); -+ window_.height &= ~(patternSize_.height - 1); -+} -+ -+} /* namespace libcamera */ -diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h -new file mode 100644 -index 00000000..0ac9ae71 ---- /dev/null -+++ b/src/libcamera/software_isp/swstats_cpu.h -@@ -0,0 +1,159 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Linaro Ltd -+ * Copyright (C) 2023, Red Hat Inc. -+ * -+ * Authors: -+ * Hans de Goede <hdegoede@redhat.com> -+ * -+ * swstats_cpu.h - CPU based software statistics implementation -+ */ -+ -+#pragma once -+ -+#include <stdint.h> -+ -+#include <libcamera/base/signal.h> -+ -+#include <libcamera/geometry.h> -+ -+#include "libcamera/internal/shared_mem_object.h" -+#include "libcamera/internal/software_isp/swisp_stats.h" -+ -+namespace libcamera { -+ -+class PixelFormat; -+struct StreamConfiguration; -+ -+class SwStatsCpu -+{ -+public: -+ SwStatsCpu(); -+ ~SwStatsCpu() = default; -+ -+ /** -+ * \brief Gets whether the statistics object is valid. -+ * -+ * \return true if it's valid, false otherwise -+ */ -+ bool isValid() const { return sharedStats_.fd().isValid(); } -+ -+ /** -+ * \brief Get the file descriptor for the statistics. -+ * -+ * \return the file descriptor -+ */ -+ const SharedFD &getStatsFD() { return sharedStats_.fd(); } -+ -+ /** -+ * \brief Get the pattern size. -+ * -+ * For some input-formats, e.g. Bayer data, processing is done multiple lines -+ * and/or columns at a time. Get width and height at which the (bayer) pattern -+ * repeats. Window values are rounded down to a multiple of this and the height -+ * also indicates if processLine2() should be called or not. -+ * This may only be called after a successful configure() call. -+ * -+ * \return the pattern size -+ */ -+ const Size &patternSize() { return patternSize_; } -+ -+ int configure(const StreamConfiguration &inputCfg); -+ void setWindow(Rectangle window); -+ void startFrame(); -+ void finishFrame(); -+ -+ /** -+ * \brief Process line 0. -+ * \param[in] y The y coordinate. -+ * \param[in] src The input data. -+ * -+ * This function processes line 0 for input formats with patternSize height == 1. -+ * It'll process line 0 and 1 for input formats with patternSize height >= 2. -+ * This function may only be called after a successful setWindow() call. -+ */ -+ void processLine0(unsigned int y, const uint8_t *src[]) -+ { -+ if ((y & ySkipMask_) || y < (unsigned int)window_.y || -+ y >= (window_.y + window_.height)) -+ return; -+ -+ (this->*stats0_)(src); -+ } -+ -+ /** -+ * \brief Process line 2 and 3. -+ * \param[in] y The y coordinate. -+ * \param[in] src The input data. -+ * -+ * This function processes line 2 and 3 for input formats with patternSize height == 4. -+ * This function may only be called after a successful setWindow() call. -+ */ -+ void processLine2(unsigned int y, const uint8_t *src[]) -+ { -+ if ((y & ySkipMask_) || y < (unsigned int)window_.y || -+ y >= (window_.y + window_.height)) -+ return; -+ -+ (this->*stats2_)(src); -+ } -+ -+ /** -+ * \brief Signals that the statistics are ready. -+ * -+ * The int parameter isn't actually used. -+ */ -+ Signal<int> statsReady; -+ -+private: -+ /** -+ * \brief Called when there is data to get statistics from. -+ * \param[in] src The input data -+ * -+ * These functions take an array of (patternSize_.height + 1) src -+ * pointers each pointing to a line in the source image. The middle -+ * element of the array will point to the actual line being processed. -+ * Earlier element(s) will point to the previous line(s) and later -+ * element(s) to the next line(s). -+ * -+ * See the documentation of DebayerCpu::debayerFn for more details. -+ */ -+ using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]); -+ -+ void statsBGGR10PLine0(const uint8_t *src[]); -+ void statsGBRG10PLine0(const uint8_t *src[]); -+ -+ /* Variables set by configure(), used every line */ -+ statsProcessFn stats0_; -+ statsProcessFn stats2_; -+ bool swapLines_; -+ -+ /** -+ * \brief Skip lines where this bitmask is set in y. -+ */ -+ unsigned int ySkipMask_; -+ -+ /** -+ * \brief Statistics window, set by setWindow(), used ever line. -+ */ -+ Rectangle window_; -+ -+ /** -+ * \brief The size of the bayer pattern. -+ * -+ * Valid sizes are: 2x2, 4x2 or 4x4. -+ */ -+ Size patternSize_; -+ -+ /** -+ * \brief The offset of x, applied to window_.x for bayer variants. -+ * -+ * This can either be 0 or 1. -+ */ -+ unsigned int xShift_; -+ -+ SharedMemObject<SwIspStats> sharedStats_; -+ SwIspStats stats_; -+}; -+ -+} /* namespace libcamera */ --- -2.43.2 - 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 deleted file mode 100644 index 7c7170989666..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch +++ /dev/null @@ -1,255 +0,0 @@ -From 25e6893e46bd2174f6913eea79817988d9280706 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -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 <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> -Co-developed-by: Dennis Bonke <admin@dennisbonke.com> -Signed-off-by: Dennis Bonke <admin@dennisbonke.com> -Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - .../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 <hdegoede@redhat.com> -+ * -+ * debayer_params.h - DebayerParams header -+ */ -+ -+#pragma once -+ -+namespace libcamera { -+ -+/** -+ * \brief Struct to hold the debayer parameters. -+ */ -+struct DebayerParams { -+ /** -+ * \brief const value for 1.0 gain -+ */ -+ static constexpr unsigned int kGain10 = 256; -+ -+ /** -+ * \brief Red Gain -+ * -+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc. -+ */ -+ unsigned int gainR; -+ /** -+ * \brief Green Gain -+ * -+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc. -+ */ -+ unsigned int gainG; -+ /** -+ * \brief Blue Gain -+ * -+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc. -+ */ -+ unsigned int gainB; -+ /** -+ * \brief Gamma correction, 1.0 is no correction -+ */ -+ float gamma; -+}; -+ -+} /* namespace libcamera */ -diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build -index 66c9c3fb..a620e16d 100644 ---- a/include/libcamera/internal/software_isp/meson.build -+++ b/include/libcamera/internal/software_isp/meson.build -@@ -1,5 +1,6 @@ - # SPDX-License-Identifier: CC0-1.0 - - libcamera_internal_headers += files([ -+ 'debayer_params.h', - 'swisp_stats.h', - ]) -diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp -new file mode 100644 -index 00000000..64f0b5a0 ---- /dev/null -+++ b/src/libcamera/software_isp/debayer.cpp -@@ -0,0 +1,29 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Linaro Ltd -+ * Copyright (C) 2023, Red Hat Inc. -+ * -+ * Authors: -+ * Hans de Goede <hdegoede@redhat.com> -+ * -+ * debayer.cpp - debayer base class -+ */ -+ -+#include "debayer.h" -+ -+namespace libcamera { -+ -+/** -+ * \class Debayer -+ * \brief Base debayering class -+ * -+ * Base class that provides functions for setting up the debayering process. -+ */ -+ -+LOG_DEFINE_CATEGORY(Debayer) -+ -+Debayer::~Debayer() -+{ -+} -+ -+} /* namespace libcamera */ -diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h -new file mode 100644 -index 00000000..8880ff99 ---- /dev/null -+++ b/src/libcamera/software_isp/debayer.h -@@ -0,0 +1,104 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Linaro Ltd -+ * Copyright (C) 2023, Red Hat Inc. -+ * -+ * Authors: -+ * Hans de Goede <hdegoede@redhat.com> -+ * -+ * debayer.h - debayering base class -+ */ -+ -+#pragma once -+ -+#include <stdint.h> -+ -+#include <libcamera/base/log.h> -+#include <libcamera/base/signal.h> -+ -+#include <libcamera/geometry.h> -+#include <libcamera/stream.h> -+ -+#include "libcamera/internal/software_isp/debayer_params.h" -+ -+namespace libcamera { -+ -+class FrameBuffer; -+ -+LOG_DECLARE_CATEGORY(Debayer) -+ -+class Debayer -+{ -+public: -+ virtual ~Debayer() = 0; -+ -+ /** -+ * \brief Configure the debayer object according to the passed in parameters. -+ * \param[in] inputCfg The input configuration. -+ * \param[in] outputCfgs The output configurations. -+ * -+ * \return 0 on success, a negative errno on failure. -+ */ -+ virtual int configure(const StreamConfiguration &inputCfg, -+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0; -+ -+ /** -+ * \brief Get the width and height at which the bayer pattern repeats. -+ * \param[in] inputFormat The input format. -+ * -+ * 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<PixelFormat> formats(PixelFormat inputFormat) = 0; -+ -+ /** -+ * \brief Get the stride and the frame size. -+ * \param[in] outputFormat The output format. -+ * \param[in] size The output size. -+ * -+ * \return a tuple of the stride and the frame size, or a tuple with 0,0 if there is no valid output config. -+ */ -+ virtual std::tuple<unsigned int, unsigned int> -+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0; -+ -+ /** -+ * \brief Process the bayer data into the requested format. -+ * \param[in] input The input buffer. -+ * \param[in] output The output buffer. -+ * \param[in] params The parameters to be used in debayering. -+ * -+ * \note DebayerParams is passed by value deliberately so that a copy is passed -+ * when this is run in another thread by invokeMethod(). -+ */ -+ virtual void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params) = 0; -+ -+ /** -+ * \brief Get the supported output sizes for the given input format and size. -+ * \param[in] inputFormat The input format. -+ * \param[in] inputSize The input size. -+ * -+ * \return The valid size ranges or an empty range if there are none. -+ */ -+ virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0; -+ -+ /** -+ * \brief Signals when the input buffer is ready. -+ */ -+ Signal<FrameBuffer *> inputBufferReady; -+ -+ /** -+ * \brief Signals when the output buffer is ready. -+ */ -+ Signal<FrameBuffer *> outputBufferReady; -+}; -+ -+} /* namespace libcamera */ -diff --git a/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/0008-libcamera-software_isp-Add-DebayerCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch deleted file mode 100644 index f549769f2fde..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch +++ /dev/null @@ -1,825 +0,0 @@ -From 5f57a52ea1054cac73344d83ff605cba0df0d279 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -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 <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Co-developed-by: Dennis Bonke <admin@dennisbonke.com> -Signed-off-by: Dennis Bonke <admin@dennisbonke.com> -Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Co-developed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> ---- - 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 <hdegoede@redhat.com> -+ * -+ * debayer_cpu.cpp - CPU based debayering class -+ */ -+ -+#include "debayer_cpu.h" -+ -+#include <math.h> -+#include <stdlib.h> -+#include <time.h> -+ -+#include <libcamera/formats.h> -+ -+#include "libcamera/internal/bayer_format.h" -+#include "libcamera/internal/framebuffer.h" -+#include "libcamera/internal/mapped_framebuffer.h" -+ -+namespace libcamera { -+ -+/** -+ * \class DebayerCpu -+ * \brief Class for debayering on the CPU -+ * -+ * Implementation for CPU based debayering -+ */ -+ -+/** -+ * \brief Constructs a DebayerCpu object. -+ * \param[in] stats Pointer to the stats object to use. -+ */ -+DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats) -+ : stats_(std::move(stats)), gamma_correction_(1.0) -+{ -+#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<PixelFormat>({ formats::RGB888 }); -+ return 0; -+ } -+ -+ LOG(Debayer, Info) -+ << "Unsupported input format " << inputFormat.toString(); -+ return -EINVAL; -+} -+ -+int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config) -+{ -+ if (outputFormat == formats::RGB888) { -+ config.bpp = 24; -+ return 0; -+ } -+ -+ LOG(Debayer, Info) -+ << "Unsupported output format " << outputFormat.toString(); -+ return -EINVAL; -+} -+ -+/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */ -+int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat) -+{ -+ BayerFormat bayerFormat = -+ BayerFormat::fromPixelFormat(inputFormat); -+ -+ if (bayerFormat.bitDepth == 10 && -+ bayerFormat.packing == BayerFormat::Packing::CSI2) { -+ switch (bayerFormat.order) { -+ case BayerFormat::BGGR: -+ debayer0_ = &DebayerCpu::debayer10P_BGBG_BGR888; -+ debayer1_ = &DebayerCpu::debayer10P_GRGR_BGR888; -+ return 0; -+ case BayerFormat::GBRG: -+ debayer0_ = &DebayerCpu::debayer10P_GBGB_BGR888; -+ debayer1_ = &DebayerCpu::debayer10P_RGRG_BGR888; -+ return 0; -+ case BayerFormat::GRBG: -+ debayer0_ = &DebayerCpu::debayer10P_GRGR_BGR888; -+ debayer1_ = &DebayerCpu::debayer10P_BGBG_BGR888; -+ return 0; -+ case BayerFormat::RGGB: -+ debayer0_ = &DebayerCpu::debayer10P_RGRG_BGR888; -+ debayer1_ = &DebayerCpu::debayer10P_GBGB_BGR888; -+ return 0; -+ default: -+ break; -+ } -+ } -+ -+ LOG(Debayer, Error) << "Unsupported input output format combination"; -+ return -EINVAL; -+} -+ -+int DebayerCpu::configure(const StreamConfiguration &inputCfg, -+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) -+{ -+ if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0) -+ return -EINVAL; -+ -+ if (stats_->configure(inputCfg) != 0) -+ return -EINVAL; -+ -+ const Size &stats_pattern_size = stats_->patternSize(); -+ if (inputConfig_.patternSize.width != stats_pattern_size.width || -+ inputConfig_.patternSize.height != stats_pattern_size.height) { -+ LOG(Debayer, Error) -+ << "mismatching stats and debayer pattern sizes for " -+ << inputCfg.pixelFormat.toString(); -+ return -EINVAL; -+ } -+ -+ inputConfig_.stride = inputCfg.stride; -+ -+ if (outputCfgs.size() != 1) { -+ LOG(Debayer, Error) -+ << "Unsupported number of output streams: " -+ << outputCfgs.size(); -+ return -EINVAL; -+ } -+ -+ const StreamConfiguration &outputCfg = outputCfgs[0]; -+ SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size); -+ std::tie(outputConfig_.stride, outputConfig_.frameSize) = -+ strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size); -+ -+ if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) { -+ LOG(Debayer, Error) -+ << "Invalid output size/stride: " -+ << "\n " << outputCfg.size << " (" << outSizeRange << ")" -+ << "\n " << outputCfg.stride << " (" << outputConfig_.stride << ")"; -+ return -EINVAL; -+ } -+ -+ if (setDebayerFunctions(inputCfg.pixelFormat, outputCfg.pixelFormat) != 0) -+ return -EINVAL; -+ -+ window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) & -+ ~(inputConfig_.patternSize.width - 1); -+ window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) & -+ ~(inputConfig_.patternSize.height - 1); -+ window_.width = outputCfg.size.width; -+ window_.height = outputCfg.size.height; -+ -+ /* Don't pass x,y since process() already adjusts src before passing it */ -+ stats_->setWindow(Rectangle(window_.size())); -+ -+ /* 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<PixelFormat> DebayerCpu::formats(PixelFormat inputFormat) -+{ -+ DebayerCpu::DebayerInputConfig config; -+ -+ if (getInputConfig(inputFormat, config) != 0) -+ return std::vector<PixelFormat>(); -+ -+ return config.outputFormats; -+} -+ -+std::tuple<unsigned int, unsigned int> -+DebayerCpu::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) -+{ -+ DebayerCpu::DebayerOutputConfig config; -+ -+ if (getOutputConfig(outputFormat, config) != 0) -+ return std::make_tuple(0, 0); -+ -+ /* round up to multiple of 8 for 64 bits alignment */ -+ unsigned int stride = (size.width * config.bpp / 8 + 7) & ~7; -+ -+ return std::make_tuple(stride, stride * size.height); -+} -+ -+void DebayerCpu::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 <hdegoede@redhat.com> -+ * -+ * debayer_cpu.h - CPU based debayering header -+ */ -+ -+#pragma once -+ -+#include <memory> -+#include <stdint.h> -+#include <vector> -+ -+#include <libcamera/base/object.h> -+ -+#include "debayer.h" -+#include "swstats_cpu.h" -+ -+namespace libcamera { -+ -+class DebayerCpu : public Debayer, public Object -+{ -+public: -+ DebayerCpu(std::unique_ptr<SwStatsCpu> stats); -+ ~DebayerCpu(); -+ -+ int configure(const StreamConfiguration &inputCfg, -+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs); -+ Size patternSize(PixelFormat inputFormat); -+ std::vector<PixelFormat> formats(PixelFormat input); -+ std::tuple<unsigned int, unsigned int> -+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size); -+ void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params); -+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize); -+ -+ /** -+ * \brief Get the file descriptor for the statistics. -+ * -+ * \return the file descriptor pointing to the statistics. -+ */ -+ const SharedFD &getStatsFD() { return stats_->getStatsFD(); } -+ -+ /** -+ * \brief Get the output frame size. -+ * -+ * \return The output frame size. -+ */ -+ unsigned int frameSize() { return outputConfig_.frameSize; } -+ -+private: -+ /** -+ * \brief Called to debayer 1 line of Bayer input data to output format -+ * \param[out] dst Pointer to the start of the output line to write -+ * \param[in] src The input data -+ * -+ * Input data is an array of (patternSize_.height + 1) src -+ * pointers each pointing to a line in the Bayer source. The middle -+ * element of the array will point to the actual line being processed. -+ * Earlier element(s) will point to the previous line(s) and later -+ * element(s) to the next line(s). -+ * -+ * These functions take an array of src pointers, rather than -+ * a single src pointer + a stride for the source, so that when the src -+ * is slow uncached memory it can be copied to faster memory before -+ * debayering. Debayering a standard 2x2 Bayer pattern requires access -+ * to the previous and next src lines for interpolating the missing -+ * colors. To allow copying the src lines only once 3 temporary buffers -+ * each holding a single line are used, re-using the oldest buffer for -+ * the next line and the pointers are swizzled so that: -+ * src[0] = previous-line, src[1] = currrent-line, src[2] = next-line. -+ * This way the 3 pointers passed to the debayer functions form -+ * a sliding window over the src avoiding the need to copy each -+ * line more than once. -+ * -+ * Similarly for bayer patterns which repeat every 4 lines, 5 src -+ * pointers are passed holding: src[0] = 2-lines-up, src[1] = 1-line-up -+ * src[2] = current-line, src[3] = 1-line-down, src[4] = 2-lines-down. -+ */ -+ using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]); -+ -+ /* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */ -+ void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); -+ void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); -+ void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]); -+ void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]); -+ -+ struct DebayerInputConfig { -+ Size patternSize; -+ unsigned int bpp; /* Memory used per pixel, not precision */ -+ unsigned int stride; -+ std::vector<PixelFormat> outputFormats; -+ }; -+ -+ struct DebayerOutputConfig { -+ unsigned int bpp; /* Memory used per pixel, not precision */ -+ unsigned int stride; -+ unsigned int frameSize; -+ }; -+ -+ int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config); -+ int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config); -+ int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat); -+ void setupInputMemcpy(const uint8_t *linePointers[]); -+ void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src); -+ void memcpyNextLine(const uint8_t *linePointers[]); -+ void process2(const uint8_t *src, uint8_t *dst); -+ void process4(const uint8_t *src, uint8_t *dst); -+ -+ static constexpr unsigned int kGammaLookupSize = 1024; -+ static constexpr unsigned int kRGBLookupSize = 256; -+ /* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */ -+ static constexpr unsigned int kMaxLineBuffers = 5; -+ -+ std::array<uint8_t, kGammaLookupSize> gamma_; -+ std::array<uint8_t, kRGBLookupSize> red_; -+ std::array<uint8_t, kRGBLookupSize> green_; -+ std::array<uint8_t, kRGBLookupSize> blue_; -+ debayerFn debayer0_; -+ debayerFn debayer1_; -+ debayerFn debayer2_; -+ debayerFn debayer3_; -+ Rectangle window_; -+ DebayerInputConfig inputConfig_; -+ DebayerOutputConfig outputConfig_; -+ std::unique_ptr<SwStatsCpu> stats_; -+ uint8_t *lineBuffers_[kMaxLineBuffers]; -+ unsigned int lineBufferLength_; -+ unsigned int lineBufferPadding_; -+ unsigned int lineBufferIndex_; -+ bool enableInputMemcpy_; -+ float gamma_correction_; -+ unsigned int measuredFrames_; -+ int64_t frameProcessTime_; -+ /* Skip 30 frames for things to stabilize then measure 30 frames */ -+ static constexpr unsigned int kFramesToSkip = 30; -+ static constexpr unsigned int kLastFrameToMeasure = 60; -+}; -+ -+} /* namespace libcamera */ -diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build -index 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/0009-libcamera-ipa-add-Soft-IPA.patch b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch deleted file mode 100644 index 40f9403ba984..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch +++ /dev/null @@ -1,506 +0,0 @@ -From 5261c801d8425fa82bcbd3da0199d06153eb5bd7 Mon Sep 17 00:00:00 2001 -From: Andrey Konovalov <andrey.konovalov@linaro.org> -Date: Mon, 11 Mar 2024 15:15:13 +0100 -Subject: [PATCH 09/21] libcamera: ipa: add Soft IPA - -Define the Soft IPA main and event interfaces, add the Soft IPA -implementation. - -The current src/ipa/meson.build assumes the IPA name to match the -pipeline name. For this reason "-Dipas=simple" is used for the -Soft IPA module. - -Auto exposure/gain and AWB implementation by Dennis, Toon and Martti. - -Auto exposure/gain targets a Mean Sample Value of 2.5 following -the MSV calculation algorithm from: -https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf - -Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Co-developed-by: Dennis Bonke <admin@dennisbonke.com> -Signed-off-by: Dennis Bonke <admin@dennisbonke.com> -Co-developed-by: Marttico <g.martti@gmail.com> -Signed-off-by: Marttico <g.martti@gmail.com> -Co-developed-by: Toon Langendam <t.langendam@gmail.com> -Signed-off-by: Toon Langendam <t.langendam@gmail.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - Documentation/Doxyfile.in | 1 + - include/libcamera/ipa/meson.build | 1 + - include/libcamera/ipa/soft.mojom | 28 +++ - meson_options.txt | 2 +- - src/ipa/simple/data/meson.build | 9 + - src/ipa/simple/data/soft.conf | 3 + - src/ipa/simple/meson.build | 25 +++ - src/ipa/simple/soft_simple.cpp | 326 ++++++++++++++++++++++++++++++ - 8 files changed, 394 insertions(+), 1 deletion(-) - create mode 100644 include/libcamera/ipa/soft.mojom - create mode 100644 src/ipa/simple/data/meson.build - create mode 100644 src/ipa/simple/data/soft.conf - create mode 100644 src/ipa/simple/meson.build - create mode 100644 src/ipa/simple/soft_simple.cpp - -diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in -index a86ea6c1..2be8d47b 100644 ---- a/Documentation/Doxyfile.in -+++ b/Documentation/Doxyfile.in -@@ -44,6 +44,7 @@ EXCLUDE = @TOP_SRCDIR@/include/libcamera/base/span.h \ - @TOP_SRCDIR@/src/libcamera/pipeline/ \ - @TOP_SRCDIR@/src/libcamera/tracepoints.cpp \ - @TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \ -+ @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \ - @TOP_BUILDDIR@/src/libcamera/proxy/ - - EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \ -diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build -index f3b4881c..3352d08f 100644 ---- a/include/libcamera/ipa/meson.build -+++ b/include/libcamera/ipa/meson.build -@@ -65,6 +65,7 @@ pipeline_ipa_mojom_mapping = { - 'ipu3': 'ipu3.mojom', - 'rkisp1': 'rkisp1.mojom', - 'rpi/vc4': 'raspberrypi.mojom', -+ 'simple': 'soft.mojom', - 'vimc': 'vimc.mojom', - } - -diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom -new file mode 100644 -index 00000000..c249bd75 ---- /dev/null -+++ b/include/libcamera/ipa/soft.mojom -@@ -0,0 +1,28 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+ -+/* -+ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry. -+ */ -+ -+module ipa.soft; -+ -+import "include/libcamera/ipa/core.mojom"; -+ -+interface IPASoftInterface { -+ init(libcamera.IPASettings settings, -+ libcamera.SharedFD fdStats, -+ libcamera.SharedFD fdParams, -+ libcamera.ControlInfoMap sensorCtrlInfoMap) -+ => (int32 ret); -+ start() => (int32 ret); -+ stop(); -+ configure(libcamera.ControlInfoMap sensorCtrlInfoMap) -+ => (int32 ret); -+ -+ [async] processStats(libcamera.ControlList sensorControls); -+}; -+ -+interface IPASoftEventInterface { -+ setSensorControls(libcamera.ControlList sensorControls); -+ setIspParams(int32 dummy); -+}; -diff --git a/meson_options.txt b/meson_options.txt -index 5fdc7be8..94372e47 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -27,7 +27,7 @@ option('gstreamer', - - option('ipas', - type : 'array', -- choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'], -+ choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'], - description : 'Select which IPA modules to build') - - option('lc-compliance', -diff --git a/src/ipa/simple/data/meson.build b/src/ipa/simple/data/meson.build -new file mode 100644 -index 00000000..33548cc6 ---- /dev/null -+++ b/src/ipa/simple/data/meson.build -@@ -0,0 +1,9 @@ -+# SPDX-License-Identifier: CC0-1.0 -+ -+conf_files = files([ -+ 'soft.conf', -+]) -+ -+install_data(conf_files, -+ install_dir : ipa_data_dir / 'soft', -+ install_tag : 'runtime') -diff --git a/src/ipa/simple/data/soft.conf b/src/ipa/simple/data/soft.conf -new file mode 100644 -index 00000000..0c70e7c0 ---- /dev/null -+++ b/src/ipa/simple/data/soft.conf -@@ -0,0 +1,3 @@ -+# SPDX-License-Identifier: LGPL-2.1-or-later -+# -+# Dummy configuration file for the soft IPA. -diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build -new file mode 100644 -index 00000000..3e863db7 ---- /dev/null -+++ b/src/ipa/simple/meson.build -@@ -0,0 +1,25 @@ -+# SPDX-License-Identifier: CC0-1.0 -+ -+ipa_name = 'ipa_soft_simple' -+ -+mod = shared_module(ipa_name, -+ ['soft_simple.cpp', libcamera_generated_ipa_headers], -+ name_prefix : '', -+ include_directories : [ipa_includes, libipa_includes], -+ dependencies : libcamera_private, -+ link_with : libipa, -+ install : true, -+ install_dir : ipa_install_dir) -+ -+if ipa_sign_module -+ custom_target(ipa_name + '.so.sign', -+ input : mod, -+ output : ipa_name + '.so.sign', -+ command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'], -+ install : false, -+ build_by_default : true) -+endif -+ -+subdir('data') -+ -+ipa_names += ipa_name -diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp -new file mode 100644 -index 00000000..312df4ba ---- /dev/null -+++ b/src/ipa/simple/soft_simple.cpp -@@ -0,0 +1,326 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Linaro Ltd -+ * -+ * soft_simple.cpp - Simple Software Image Processing Algorithm module -+ */ -+ -+#include <sys/mman.h> -+ -+#include <libcamera/base/file.h> -+#include <libcamera/base/log.h> -+#include <libcamera/base/shared_fd.h> -+ -+#include <libcamera/control_ids.h> -+#include <libcamera/controls.h> -+ -+#include <libcamera/ipa/ipa_interface.h> -+#include <libcamera/ipa/ipa_module_info.h> -+#include <libcamera/ipa/soft_ipa_interface.h> -+ -+#include "libcamera/internal/camera_sensor.h" -+#include "libcamera/internal/software_isp/debayer_params.h" -+#include "libcamera/internal/software_isp/swisp_stats.h" -+ -+namespace libcamera { -+ -+LOG_DEFINE_CATEGORY(IPASoft) -+ -+namespace ipa::soft { -+ -+class IPASoftSimple : public ipa::soft::IPASoftInterface -+{ -+public: -+ IPASoftSimple() -+ : params_(static_cast<DebayerParams *>(MAP_FAILED)), -+ stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0) -+ { -+ } -+ -+ ~IPASoftSimple() -+ { -+ if (stats_ != MAP_FAILED) -+ munmap(stats_, sizeof(SwIspStats)); -+ if (params_ != MAP_FAILED) -+ munmap(params_, sizeof(DebayerParams)); -+ } -+ -+ int init(const IPASettings &settings, -+ const SharedFD &fdStats, -+ const SharedFD &fdParams, -+ const ControlInfoMap &sensorInfoMap) override; -+ int configure(const ControlInfoMap &sensorInfoMap) override; -+ -+ int start() override; -+ void stop() override; -+ -+ void processStats(const ControlList &sensorControls) override; -+ -+private: -+ void updateExposure(double exposureMSV); -+ -+ SharedFD fdStats_; -+ SharedFD fdParams_; -+ DebayerParams *params_; -+ SwIspStats *stats_; -+ -+ int32_t exposure_min_, exposure_max_; -+ int32_t again_min_, again_max_; -+ int32_t again_, exposure_; -+ unsigned int ignore_updates_; -+}; -+ -+int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings, -+ const SharedFD &fdStats, -+ const SharedFD &fdParams, -+ const ControlInfoMap &sensorInfoMap) -+{ -+ fdStats_ = fdStats; -+ if (!fdStats_.isValid()) { -+ LOG(IPASoft, Error) << "Invalid Statistics handle"; -+ return -ENODEV; -+ } -+ -+ fdParams_ = fdParams; -+ if (!fdParams_.isValid()) { -+ LOG(IPASoft, Error) << "Invalid Parameters handle"; -+ return -ENODEV; -+ } -+ -+ params_ = static_cast<DebayerParams *>(mmap(nullptr, sizeof(DebayerParams), -+ PROT_WRITE, MAP_SHARED, -+ fdParams_.get(), 0)); -+ if (params_ == MAP_FAILED) { -+ LOG(IPASoft, Error) << "Unable to map Parameters"; -+ return -errno; -+ } -+ -+ stats_ = static_cast<SwIspStats *>(mmap(nullptr, sizeof(SwIspStats), -+ PROT_READ, MAP_SHARED, -+ fdStats_.get(), 0)); -+ if (stats_ == MAP_FAILED) { -+ LOG(IPASoft, Error) << "Unable to map Statistics"; -+ return -errno; -+ } -+ -+ if (sensorInfoMap.find(V4L2_CID_EXPOSURE) == sensorInfoMap.end()) { -+ LOG(IPASoft, Error) << "Don't have exposure control"; -+ return -EINVAL; -+ } -+ -+ if (sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN) == sensorInfoMap.end()) { -+ LOG(IPASoft, Error) << "Don't have gain control"; -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap) -+{ -+ const ControlInfo &exposure_info = sensorInfoMap.find(V4L2_CID_EXPOSURE)->second; -+ const ControlInfo &gain_info = sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN)->second; -+ -+ exposure_min_ = exposure_info.min().get<int32_t>(); -+ exposure_max_ = exposure_info.max().get<int32_t>(); -+ if (!exposure_min_) { -+ LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear"; -+ exposure_min_ = 1; -+ } -+ -+ again_min_ = gain_info.min().get<int32_t>(); -+ again_max_ = gain_info.max().get<int32_t>(); -+ /* -+ * The camera sensor gain (g) is usually not equal to the value written -+ * into the gain register (x). But the way how the AGC algorithm changes -+ * the gain value to make the total exposure closer to the optimum assumes -+ * that g(x) is not too far from linear function. If the minimal gain is 0, -+ * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c). -+ * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near -+ * one edge, and very small near the other) we limit the range of the gain -+ * values used. -+ */ -+ if (!again_min_) { -+ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear"; -+ again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2); -+ } -+ -+ LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_ -+ << ", gain " << again_min_ << "-" << again_max_; -+ -+ return 0; -+} -+ -+int IPASoftSimple::start() -+{ -+ return 0; -+} -+ -+void IPASoftSimple::stop() -+{ -+} -+ -+/* -+ * The number of bins to use for the optimal exposure calculations. -+ */ -+static constexpr unsigned int kExposureBinsCount = 5; -+/* -+ * The exposure is optimal when the mean sample value of the histogram is -+ * in the middle of the range. -+ */ -+static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; -+/* -+ * The below value implements the hysteresis for the exposure adjustment. -+ * It is small enough to have the exposure close to the optimal, and is big -+ * enough to prevent the exposure from wobbling around the optimal value. -+ */ -+static constexpr float kExposureSatisfactory = 0.2; -+ -+void IPASoftSimple::processStats(const ControlList &sensorControls) -+{ -+ /* -+ * Calculate red and blue gains for AWB. -+ * Clamp max gain at 4.0, this also avoids 0 division. -+ */ -+ if (stats_->sumR_ <= stats_->sumG_ / 4) -+ params_->gainR = 1024; -+ else -+ params_->gainR = 256 * stats_->sumG_ / stats_->sumR_; -+ -+ if (stats_->sumB_ <= stats_->sumG_ / 4) -+ params_->gainB = 1024; -+ else -+ params_->gainB = 256 * stats_->sumG_ / stats_->sumB_; -+ -+ /* Green gain and gamma values are fixed */ -+ params_->gainG = 256; -+ params_->gamma = 0.5; -+ -+ setIspParams.emit(0); -+ -+ /* -+ * AE / AGC, use 2 frames delay to make sure that the exposure and -+ * the gain set have applied to the camera sensor. -+ */ -+ if (ignore_updates_ > 0) { -+ --ignore_updates_; -+ return; -+ } -+ -+ /* -+ * Calculate Mean Sample Value (MSV) according to formula from: -+ * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf -+ */ -+ constexpr unsigned int yHistValsPerBin = -+ SwIspStats::kYHistogramSize / kExposureBinsCount; -+ constexpr unsigned int yHistValsPerBinMod = -+ SwIspStats::kYHistogramSize / -+ (SwIspStats::kYHistogramSize % kExposureBinsCount + 1); -+ int ExposureBins[kExposureBinsCount] = {}; -+ unsigned int denom = 0; -+ unsigned int num = 0; -+ -+ for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) { -+ unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin; -+ ExposureBins[idx] += stats_->yHistogram[i]; -+ } -+ -+ for (unsigned int i = 0; i < kExposureBinsCount; i++) { -+ LOG(IPASoft, Debug) << i << ": " << ExposureBins[i]; -+ denom += ExposureBins[i]; -+ num += ExposureBins[i] * (i + 1); -+ } -+ -+ float exposureMSV = (float)num / denom; -+ -+ /* sanity check */ -+ if (!sensorControls.contains(V4L2_CID_EXPOSURE) || -+ !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) { -+ LOG(IPASoft, Error) << "Control(s) missing"; -+ return; -+ } -+ -+ ControlList ctrls(sensorControls); -+ -+ exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>(); -+ again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>(); -+ -+ updateExposure(exposureMSV); -+ -+ ctrls.set(V4L2_CID_EXPOSURE, exposure_); -+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_); -+ -+ ignore_updates_ = 2; -+ -+ setSensorControls.emit(ctrls); -+ -+ LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV -+ << " exp " << exposure_ << " again " << again_ -+ << " gain R/B " << params_->gainR << "/" << params_->gainB; -+} -+ -+void IPASoftSimple::updateExposure(double exposureMSV) -+{ -+ /* DENOMINATOR of 10 gives ~10% increment/decrement; DENOMINATOR of 5 - about ~20% */ -+ static constexpr uint8_t kExpDenominator = 10; -+ static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1; -+ static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1; -+ -+ int next; -+ -+ if (exposureMSV < kExposureOptimal - kExposureSatisfactory) { -+ next = exposure_ * kExpNumeratorUp / kExpDenominator; -+ if (next - exposure_ < 1) -+ exposure_ += 1; -+ else -+ exposure_ = next; -+ if (exposure_ >= exposure_max_) { -+ next = again_ * kExpNumeratorUp / kExpDenominator; -+ if (next - again_ < 1) -+ again_ += 1; -+ else -+ again_ = next; -+ } -+ } -+ -+ if (exposureMSV > kExposureOptimal + kExposureSatisfactory) { -+ if (exposure_ == exposure_max_ && again_ != again_min_) { -+ next = again_ * kExpNumeratorDown / kExpDenominator; -+ if (again_ - next < 1) -+ again_ -= 1; -+ else -+ again_ = next; -+ } else { -+ next = exposure_ * kExpNumeratorDown / kExpDenominator; -+ if (exposure_ - next < 1) -+ exposure_ -= 1; -+ else -+ exposure_ = next; -+ } -+ } -+ -+ exposure_ = std::clamp(exposure_, exposure_min_, exposure_max_); -+ again_ = std::clamp(again_, again_min_, again_max_); -+} -+ -+} /* namespace ipa::soft */ -+ -+/* -+ * External IPA module interface -+ */ -+extern "C" { -+const struct IPAModuleInfo ipaModuleInfo = { -+ IPA_MODULE_API_VERSION, -+ 0, -+ "SimplePipelineHandler", -+ "simple", -+}; -+ -+IPAInterface *ipaCreate() -+{ -+ return new ipa::soft::IPASoftSimple(); -+} -+ -+} /* extern "C" */ -+ -+} /* namespace libcamera */ --- -2.43.2 - diff --git a/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch deleted file mode 100644 index 9f2d66c2f8b6..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch +++ /dev/null @@ -1,507 +0,0 @@ -From ad41ea12fe4b8ca0ace20781c775a63ed0d66f4c Mon Sep 17 00:00:00 2001 -From: Andrey Konovalov <andrey.konovalov@linaro.org> -Date: Mon, 11 Mar 2024 15:15:14 +0100 -Subject: [PATCH 10/21] libcamera: introduce SoftwareIsp - -Doxygen documentation by Dennis Bonke. - -Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Co-developed-by: Dennis Bonke <admin@dennisbonke.com> -Signed-off-by: Dennis Bonke <admin@dennisbonke.com> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - .../internal/software_isp/meson.build | 1 + - .../internal/software_isp/software_isp.h | 98 +++++ - src/libcamera/software_isp/meson.build | 1 + - src/libcamera/software_isp/software_isp.cpp | 349 ++++++++++++++++++ - 4 files changed, 449 insertions(+) - create mode 100644 include/libcamera/internal/software_isp/software_isp.h - create mode 100644 src/libcamera/software_isp/software_isp.cpp - -diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build -index a620e16d..508ddddc 100644 ---- a/include/libcamera/internal/software_isp/meson.build -+++ b/include/libcamera/internal/software_isp/meson.build -@@ -2,5 +2,6 @@ - - libcamera_internal_headers += files([ - 'debayer_params.h', -+ 'software_isp.h', - 'swisp_stats.h', - ]) -diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h -new file mode 100644 -index 00000000..8d25e979 ---- /dev/null -+++ b/include/libcamera/internal/software_isp/software_isp.h -@@ -0,0 +1,98 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Linaro Ltd -+ * -+ * software_isp.h - Simple software ISP implementation -+ */ -+ -+#pragma once -+ -+#include <functional> -+#include <initializer_list> -+#include <map> -+#include <memory> -+#include <string> -+#include <tuple> -+#include <vector> -+ -+#include <libcamera/base/class.h> -+#include <libcamera/base/log.h> -+#include <libcamera/base/signal.h> -+#include <libcamera/base/thread.h> -+ -+#include <libcamera/geometry.h> -+#include <libcamera/pixel_format.h> -+ -+#include <libcamera/ipa/soft_ipa_interface.h> -+#include <libcamera/ipa/soft_ipa_proxy.h> -+ -+#include "libcamera/internal/dma_heaps.h" -+#include "libcamera/internal/pipeline_handler.h" -+#include "libcamera/internal/shared_mem_object.h" -+#include "libcamera/internal/software_isp/debayer_params.h" -+ -+namespace libcamera { -+ -+class DebayerCpu; -+class FrameBuffer; -+class PixelFormat; -+struct StreamConfiguration; -+ -+LOG_DECLARE_CATEGORY(SoftwareIsp) -+ -+class SoftwareIsp -+{ -+public: -+ SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls); -+ ~SoftwareIsp(); -+ -+ int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; } -+ -+ bool isValid() const; -+ -+ std::vector<PixelFormat> formats(PixelFormat input); -+ -+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize); -+ -+ std::tuple<unsigned int, unsigned int> -+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size); -+ -+ int configure(const StreamConfiguration &inputCfg, -+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs, -+ const ControlInfoMap &sensorControls); -+ -+ int exportBuffers(unsigned int output, unsigned int count, -+ std::vector<std::unique_ptr<FrameBuffer>> *buffers); -+ -+ void processStats(const ControlList &sensorControls); -+ -+ int start(); -+ void stop(); -+ -+ int queueBuffers(FrameBuffer *input, -+ const std::map<unsigned int, FrameBuffer *> &outputs); -+ -+ void process(FrameBuffer *input, FrameBuffer *output); -+ -+ Signal<FrameBuffer *> inputBufferReady; -+ Signal<FrameBuffer *> outputBufferReady; -+ Signal<int> ispStatsReady; -+ Signal<const ControlList &> setSensorControls; -+ -+private: -+ void saveIspParams(int dummy); -+ void setSensorCtrls(const ControlList &sensorControls); -+ void statsReady(int dummy); -+ void inputReady(FrameBuffer *input); -+ void outputReady(FrameBuffer *output); -+ -+ std::unique_ptr<DebayerCpu> debayer_; -+ Thread ispWorkerThread_; -+ SharedMemObject<DebayerParams> sharedParams_; -+ DebayerParams debayerParams_; -+ DmaHeap dmaHeap_; -+ -+ std::unique_ptr<ipa::soft::IPAProxySoft> ipa_; -+}; -+ -+} /* namespace libcamera */ -diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build -index 71b46539..e9266e54 100644 ---- a/src/libcamera/software_isp/meson.build -+++ b/src/libcamera/software_isp/meson.build -@@ -10,5 +10,6 @@ endif - libcamera_sources += files([ - 'debayer.cpp', - 'debayer_cpu.cpp', -+ 'software_isp.cpp', - 'swstats_cpu.cpp', - ]) -diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp -new file mode 100644 -index 00000000..388b4496 ---- /dev/null -+++ b/src/libcamera/software_isp/software_isp.cpp -@@ -0,0 +1,349 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2023, Linaro Ltd -+ * -+ * software_isp.cpp - Simple software ISP implementation -+ */ -+ -+#include "libcamera/internal/software_isp/software_isp.h" -+ -+#include <sys/mman.h> -+#include <sys/types.h> -+#include <unistd.h> -+ -+#include <libcamera/formats.h> -+#include <libcamera/stream.h> -+ -+#include "libcamera/internal/bayer_format.h" -+#include "libcamera/internal/framebuffer.h" -+#include "libcamera/internal/ipa_manager.h" -+#include "libcamera/internal/mapped_framebuffer.h" -+ -+#include "debayer_cpu.h" -+ -+/** -+ * \file software_isp.cpp -+ * \brief Simple software ISP implementation -+ */ -+ -+namespace libcamera { -+ -+LOG_DEFINE_CATEGORY(SoftwareIsp) -+ -+/** -+ * \class SoftwareIsp -+ * \brief Class for the Software ISP -+ */ -+ -+/** -+ * \var SoftwareIsp::inputBufferReady -+ * \brief A signal emitted when the input frame buffer completes -+ */ -+ -+/** -+ * \var SoftwareIsp::outputBufferReady -+ * \brief A signal emitted when the output frame buffer completes -+ */ -+ -+/** -+ * \var SoftwareIsp::ispStatsReady -+ * \brief A signal emitted when the statistics for IPA are ready -+ * -+ * The int parameter isn't actually used. -+ */ -+ -+/** -+ * \var SoftwareIsp::setSensorControls -+ * \brief A signal emitted when the values to write to the sensor controls are ready -+ */ -+ -+/** -+ * \brief Constructs SoftwareIsp object -+ * \param[in] pipe The pipeline handler in use -+ * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor -+ */ -+SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls) -+ : debayer_(nullptr), -+ debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f }, -+ dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System) -+{ -+ if (!dmaHeap_.isValid()) { -+ LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object"; -+ return; -+ } -+ -+ sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params"); -+ if (!sharedParams_) { -+ LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters"; -+ return; -+ } -+ -+ auto stats = std::make_unique<SwStatsCpu>(); -+ if (!stats->isValid()) { -+ LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object"; -+ return; -+ } -+ stats->statsReady.connect(this, &SoftwareIsp::statsReady); -+ -+ debayer_ = std::make_unique<DebayerCpu>(std::move(stats)); -+ debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady); -+ debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady); -+ -+ ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0); -+ if (!ipa_) { -+ LOG(SoftwareIsp, Error) -+ << "Creating IPA for software ISP failed"; -+ debayer_.reset(); -+ return; -+ } -+ -+ int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" }, -+ debayer_->getStatsFD(), -+ sharedParams_.fd(), -+ sensorControls); -+ if (ret) { -+ LOG(SoftwareIsp, Error) << "IPA init failed"; -+ debayer_.reset(); -+ return; -+ } -+ -+ ipa_->setIspParams.connect(this, &SoftwareIsp::saveIspParams); -+ ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls); -+ -+ debayer_->moveToThread(&ispWorkerThread_); -+} -+ -+SoftwareIsp::~SoftwareIsp() -+{ -+ /* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */ -+ debayer_.reset(); -+} -+ -+/** -+ * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename) -+ * \brief Load a configuration from a file -+ * \param[in] filename The file to load the configuration data from -+ * -+ * Currently is a stub doing nothing and always returning "success". -+ * -+ * \return 0 on success -+ */ -+ -+/** -+ * \brief Process the statistics gathered -+ * \param[in] sensorControls The sensor controls -+ * -+ * Requests the IPA to calculate new parameters for ISP and new control -+ * values for the sensor. -+ */ -+void SoftwareIsp::processStats(const ControlList &sensorControls) -+{ -+ ASSERT(ipa_); -+ ipa_->processStats(sensorControls); -+} -+ -+/** -+ * \brief Check the validity of Software Isp object -+ * \return True if Software Isp is valid, false otherwise -+ */ -+bool SoftwareIsp::isValid() const -+{ -+ return !!debayer_; -+} -+ -+/** -+ * \brief Get the output formats supported for the given input format -+ * \param[in] inputFormat The input format -+ * \return All the supported output formats or an empty vector if there are none -+ */ -+std::vector<PixelFormat> SoftwareIsp::formats(PixelFormat inputFormat) -+{ -+ ASSERT(debayer_ != nullptr); -+ -+ return debayer_->formats(inputFormat); -+} -+ -+/** -+ * \brief Get the supported output sizes for the given input format and size -+ * \param[in] inputFormat The input format -+ * \param[in] inputSize The input frame size -+ * \return The valid size range or an empty range if there are none -+ */ -+SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize) -+{ -+ ASSERT(debayer_ != nullptr); -+ -+ return debayer_->sizes(inputFormat, inputSize); -+} -+ -+/** -+ * Get the output stride and the frame size in bytes for the given output format and size -+ * \param[in] outputFormat The output format -+ * \param[in] size The output size (width and height in pixels) -+ * \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0 -+ * if there is no valid output config -+ */ -+std::tuple<unsigned int, unsigned int> -+SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) -+{ -+ ASSERT(debayer_ != nullptr); -+ -+ return debayer_->strideAndFrameSize(outputFormat, size); -+} -+ -+/** -+ * \brief Configure the SoftwareIsp object according to the passed in parameters -+ * \param[in] inputCfg The input configuration -+ * \param[in] outputCfgs The output configurations -+ * \param[in] sensorControls ControlInfoMap of the controls supported by the sensor -+ * \return 0 on success, a negative errno on failure -+ */ -+int SoftwareIsp::configure(const StreamConfiguration &inputCfg, -+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs, -+ const ControlInfoMap &sensorControls) -+{ -+ ASSERT(ipa_ != nullptr && debayer_ != nullptr); -+ -+ int ret = ipa_->configure(sensorControls); -+ if (ret < 0) -+ return ret; -+ -+ return debayer_->configure(inputCfg, outputCfgs); -+} -+ -+/** -+ * \brief Export the buffers from the Software ISP -+ * \param[in] output Output stream index exporting the buffers -+ * \param[in] count Number of buffers to allocate -+ * \param[out] buffers Vector to store the allocated buffers -+ * \return The number of allocated buffers on success or a negative error code -+ * otherwise -+ */ -+int SoftwareIsp::exportBuffers(unsigned int output, unsigned int count, -+ std::vector<std::unique_ptr<FrameBuffer>> *buffers) -+{ -+ ASSERT(debayer_ != nullptr); -+ -+ /* single output for now */ -+ if (output >= 1) -+ return -EINVAL; -+ -+ for (unsigned int i = 0; i < count; i++) { -+ const std::string name = "frame-" + std::to_string(i); -+ const size_t frameSize = debayer_->frameSize(); -+ -+ FrameBuffer::Plane outPlane; -+ outPlane.fd = SharedFD(dmaHeap_.alloc(name.c_str(), frameSize)); -+ if (!outPlane.fd.isValid()) { -+ LOG(SoftwareIsp, Error) -+ << "failed to allocate a dma_buf"; -+ return -ENOMEM; -+ } -+ outPlane.offset = 0; -+ outPlane.length = frameSize; -+ -+ std::vector<FrameBuffer::Plane> planes{ outPlane }; -+ buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes))); -+ } -+ -+ return count; -+} -+ -+/** -+ * \brief Queue buffers to Software ISP -+ * \param[in] input The input framebuffer -+ * \param[in] outputs The container holding the output stream indexes and -+ * their respective frame buffer outputs -+ * \return 0 on success, a negative errno on failure -+ */ -+int SoftwareIsp::queueBuffers(FrameBuffer *input, -+ const std::map<unsigned int, FrameBuffer *> &outputs) -+{ -+ unsigned int mask = 0; -+ -+ /* -+ * Validate the outputs as a sanity check: at least one output is -+ * required, all outputs must reference a valid stream and no two -+ * outputs can reference the same stream. -+ */ -+ if (outputs.empty()) -+ return -EINVAL; -+ -+ for (auto [index, buffer] : outputs) { -+ if (!buffer) -+ return -EINVAL; -+ if (index >= 1) /* only single stream atm */ -+ return -EINVAL; -+ if (mask & (1 << index)) -+ return -EINVAL; -+ -+ mask |= 1 << index; -+ } -+ -+ process(input, outputs.at(0)); -+ -+ return 0; -+} -+ -+/** -+ * \brief Starts the Software ISP streaming operation -+ * \return 0 on success, any other value indicates an error -+ */ -+int SoftwareIsp::start() -+{ -+ int ret = ipa_->start(); -+ if (ret) -+ return ret; -+ -+ ispWorkerThread_.start(); -+ return 0; -+} -+ -+/** -+ * \brief Stops the Software ISP streaming operation -+ */ -+void SoftwareIsp::stop() -+{ -+ ispWorkerThread_.exit(); -+ ispWorkerThread_.wait(); -+ -+ ipa_->stop(); -+} -+ -+/** -+ * \brief Passes the input framebuffer to the ISP worker to process -+ * \param[in] input The input framebuffer -+ * \param[out] output The framebuffer to write the processed frame to -+ */ -+void SoftwareIsp::process(FrameBuffer *input, FrameBuffer *output) -+{ -+ debayer_->invokeMethod(&DebayerCpu::process, -+ ConnectionTypeQueued, input, output, debayerParams_); -+} -+ -+void SoftwareIsp::saveIspParams([[maybe_unused]] int dummy) -+{ -+ debayerParams_ = *sharedParams_; -+} -+ -+void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls) -+{ -+ setSensorControls.emit(sensorControls); -+} -+ -+void SoftwareIsp::statsReady(int dummy) -+{ -+ ispStatsReady.emit(dummy); -+} -+ -+void SoftwareIsp::inputReady(FrameBuffer *input) -+{ -+ inputBufferReady.emit(input); -+} -+ -+void SoftwareIsp::outputReady(FrameBuffer *output) -+{ -+ outputBufferReady.emit(output); -+} -+ -+} /* namespace libcamera */ --- -2.43.2 - 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 deleted file mode 100644 index 5c2237a8eb01..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch +++ /dev/null @@ -1,240 +0,0 @@ -From 050440eed6ab90686df217f5ff7dea0b241e3898 Mon Sep 17 00:00:00 2001 -From: Andrey Konovalov <andrey.konovalov@linaro.org> -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 <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - src/libcamera/pipeline/simple/simple.cpp | 63 ++++++++++++------------ - 1 file changed, 32 insertions(+), 31 deletions(-) - -diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp -index a84f6760..78854ef8 100644 ---- a/src/libcamera/pipeline/simple/simple.cpp -+++ b/src/libcamera/pipeline/simple/simple.cpp -@@ -269,17 +269,18 @@ public: - std::vector<Configuration> configs_; - std::map<PixelFormat, std::vector<const Configuration *>> formats_; - -+ std::vector<std::unique_ptr<FrameBuffer>> conversionBuffers_; -+ std::queue<std::map<unsigned int, FrameBuffer *>> conversionQueue_; -+ bool useConversion_; -+ - std::unique_ptr<Converter> converter_; -- std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_; -- bool useConverter_; -- std::queue<std::map<unsigned int, FrameBuffer *>> converterQueue_; - - private: - void tryPipeline(unsigned int code, const Size &size); - static std::vector<const MediaPad *> routedSourcePads(MediaPad *sink); - -- void converterInputDone(FrameBuffer *buffer); -- void converterOutputDone(FrameBuffer *buffer); -+ void conversionInputDone(FrameBuffer *buffer); -+ void conversionOutputDone(FrameBuffer *buffer); - }; - - class SimpleCameraConfiguration : public CameraConfiguration -@@ -503,8 +504,8 @@ int SimpleCameraData::init() - << "Failed to create converter, disabling format conversion"; - converter_.reset(); - } else { -- converter_->inputBufferReady.connect(this, &SimpleCameraData::converterInputDone); -- converter_->outputBufferReady.connect(this, &SimpleCameraData::converterOutputDone); -+ converter_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone); -+ converter_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone); - } - } - -@@ -740,7 +741,7 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) - * point converting an erroneous buffer. - */ - if (buffer->metadata().status != FrameMetadata::FrameSuccess) { -- if (!useConverter_) { -+ if (!useConversion_) { - /* No conversion, just complete the request. */ - Request *request = buffer->request(); - pipe->completeBuffer(request, buffer); -@@ -756,16 +757,16 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) - if (buffer->metadata().status != FrameMetadata::FrameCancelled) - video_->queueBuffer(buffer); - -- if (converterQueue_.empty()) -+ if (conversionQueue_.empty()) - return; - - Request *request = nullptr; -- for (auto &item : converterQueue_.front()) { -+ for (auto &item : conversionQueue_.front()) { - FrameBuffer *outputBuffer = item.second; - request = outputBuffer->request(); - pipe->completeBuffer(request, outputBuffer); - } -- converterQueue_.pop(); -+ conversionQueue_.pop(); - - if (request) - pipe->completeRequest(request); -@@ -782,9 +783,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) - */ - Request *request = buffer->request(); - -- if (useConverter_ && !converterQueue_.empty()) { -+ if (useConversion_ && !conversionQueue_.empty()) { - const std::map<unsigned int, FrameBuffer *> &outputs = -- converterQueue_.front(); -+ conversionQueue_.front(); - if (!outputs.empty()) { - FrameBuffer *outputBuffer = outputs.begin()->second; - if (outputBuffer) -@@ -801,14 +802,14 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) - * conversion is needed. If there's no queued request, just requeue the - * captured buffer for capture. - */ -- if (useConverter_) { -- if (converterQueue_.empty()) { -+ if (useConversion_) { -+ if (conversionQueue_.empty()) { - video_->queueBuffer(buffer); - return; - } - -- converter_->queueBuffers(buffer, converterQueue_.front()); -- converterQueue_.pop(); -+ converter_->queueBuffers(buffer, conversionQueue_.front()); -+ conversionQueue_.pop(); - return; - } - -@@ -817,13 +818,13 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer) - pipe->completeRequest(request); - } - --void SimpleCameraData::converterInputDone(FrameBuffer *buffer) -+void SimpleCameraData::conversionInputDone(FrameBuffer *buffer) - { - /* Queue the input buffer back for capture. */ - video_->queueBuffer(buffer); - } - --void SimpleCameraData::converterOutputDone(FrameBuffer *buffer) -+void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer) - { - SimplePipelineHandler *pipe = SimpleCameraData::pipe(); - -@@ -1189,14 +1190,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c) - - /* Configure the converter if needed. */ - std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs; -- data->useConverter_ = config->needConversion(); -+ data->useConversion_ = config->needConversion(); - - for (unsigned int i = 0; i < config->size(); ++i) { - StreamConfiguration &cfg = config->at(i); - - cfg.setStream(&data->streams_[i]); - -- if (data->useConverter_) -+ if (data->useConversion_) - outputCfgs.push_back(cfg); - } - -@@ -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<FrameBuffer> &buffer : data->converterBuffers_) -+ for (std::unique_ptr<FrameBuffer> &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-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 deleted file mode 100644 index 378a43604f9a..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch +++ /dev/null @@ -1,302 +0,0 @@ -From d64b0fca22ef25b8a14d7fc97dfab64eb1c4f21a Mon Sep 17 00:00:00 2001 -From: Andrey Konovalov <andrey.konovalov@linaro.org> -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 <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - src/libcamera/pipeline/simple/simple.cpp | 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<std::pair<const char *, unsigned int>> converters; -+ /* -+ * Using Software ISP is to be enabled per driver. -+ * The Software ISP can't be used together with the converters. -+ */ -+ bool swIspEnabled; - }; - - namespace { - - static const SimplePipelineInfo supportedDevices[] = { -- { "dcmipp", {} }, -- { "imx7-csi", { { "pxp", 1 } } }, -- { "j721e-csi2rx", {} }, -- { "mxc-isi", {} }, -- { "qcom-camss", {} }, -- { "sun6i-csi", {} }, -+ { "dcmipp", {}, false }, -+ { "imx7-csi", { { "pxp", 1 } }, false }, -+ { "j721e-csi2rx", {}, false }, -+ { "mxc-isi", {}, false }, -+ { "qcom-camss", {}, true }, -+ { "sun6i-csi", {}, false }, - }; - - } /* namespace */ -@@ -274,6 +280,7 @@ public: - bool useConversion_; - - std::unique_ptr<Converter> converter_; -+ std::unique_ptr<SoftwareIsp> 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<const MediaEntity *, EntityData> 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<SoftwareIsp>(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<const MediaPad *> 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<MediaEntity *> sensors = locateSensors(); - if (sensors.empty()) { --- -2.43.2 - 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 deleted file mode 100644 index 1a57d690ff91..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch +++ /dev/null @@ -1,203 +0,0 @@ -From aabc53453d542495d9da25411f57308c01f2bc28 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -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 <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> -Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - 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 <libcamera/geometry.h> - -+#include "libcamera/internal/bayer_format.h" - #include "libcamera/internal/shared_mem_object.h" - #include "libcamera/internal/software_isp/swisp_stats.h" - -@@ -120,6 +121,14 @@ private: - */ - using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]); - -+ int setupStandardBayerOrder(BayerFormat::Order order); -+ /* Bayer 8 bpp unpacked */ -+ void statsBGGR8Line0(const uint8_t *src[]); -+ /* Bayer 10 bpp unpacked */ -+ void statsBGGR10Line0(const uint8_t *src[]); -+ /* Bayer 12 bpp unpacked */ -+ void statsBGGR12Line0(const uint8_t *src[]); -+ /* Bayer 10 bpp packed */ - void statsBGGR10PLine0(const uint8_t *src[]); - void statsGBRG10PLine0(const uint8_t *src[]); - --- -2.43.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 deleted file mode 100644 index c7edf498280e..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch +++ /dev/null @@ -1,234 +0,0 @@ -From 5f3647bd4f12dd62256a425c49fd18a0f5990930 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -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 <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> -Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - 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<PixelFormat>({ 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 <libcamera/base/object.h> - -+#include "libcamera/internal/bayer_format.h" -+ - #include "debayer.h" - #include "swstats_cpu.h" - -@@ -82,6 +84,15 @@ private: - */ - using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]); - -+ /* 8-bit raw bayer format */ -+ void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); -+ void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); -+ /* unpacked 10-bit raw bayer format */ -+ void debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); -+ void debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); -+ /* unpacked 12-bit raw bayer format */ -+ void debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); -+ void debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); - /* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */ - void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]); - void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]); -@@ -103,6 +114,7 @@ private: - - int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config); - int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config); -+ int setupStandardBayerOrder(BayerFormat::Order order); - int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat); - void setupInputMemcpy(const uint8_t *linePointers[]); - void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src); -@@ -131,6 +143,7 @@ private: - unsigned int lineBufferLength_; - unsigned int lineBufferPadding_; - unsigned int lineBufferIndex_; -+ unsigned int xShift_; /* Offset of 0/1 applied to window_.x */ - bool enableInputMemcpy_; - float gamma_correction_; - unsigned int measuredFrames_; --- -2.43.2 - 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 deleted file mode 100644 index 0abca2ea82d9..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch +++ /dev/null @@ -1,127 +0,0 @@ -From 186db51d54bcbd4d5096bea1e4396966c2dad001 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -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 <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s -Tested-by: Pavel Machek <pavel@ucw.cz> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> -Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> ---- - 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<PixelFormat>({ formats::RGB888 }); -+ config.outputFormats = std::vector<PixelFormat>({ 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<PixelFormat>({ formats::RGB888 }); -+ config.outputFormats = std::vector<PixelFormat>({ 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/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch deleted file mode 100644 index 2343e9c46fe8..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch +++ /dev/null @@ -1,132 +0,0 @@ -From 6c509a3d144d46a11454d32d128d16e16602b50f Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Mon, 11 Mar 2024 15:15:20 +0100 -Subject: [PATCH 17/21] libcamera: Add "Software ISP benchmarking" - documentation - -Add a "Software ISP benchmarking" documentation section which describes -the performance/power consumption measurements used during -the Software ISP's development. - -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> -Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> ---- - Documentation/index.rst | 1 + - Documentation/meson.build | 1 + - Documentation/software-isp-benchmarking.rst | 82 +++++++++++++++++++++ - 3 files changed, 84 insertions(+) - create mode 100644 Documentation/software-isp-benchmarking.rst - -diff --git a/Documentation/index.rst b/Documentation/index.rst -index 63fac72d..5442ae75 100644 ---- a/Documentation/index.rst -+++ b/Documentation/index.rst -@@ -24,3 +24,4 @@ - Lens driver requirements <lens_driver_requirements> - Python Bindings <python-bindings> - Camera Sensor Model <camera-sensor-model> -+ SoftwareISP Benchmarking <software-isp-benchmarking> -diff --git a/Documentation/meson.build b/Documentation/meson.build -index 7a58fec8..3872e0a8 100644 ---- a/Documentation/meson.build -+++ b/Documentation/meson.build -@@ -80,6 +80,7 @@ if sphinx.found() - 'lens_driver_requirements.rst', - 'python-bindings.rst', - 'sensor_driver_requirements.rst', -+ 'software-isp-benchmarking.rst', - '../README.rst', - ] - -diff --git a/Documentation/software-isp-benchmarking.rst b/Documentation/software-isp-benchmarking.rst -new file mode 100644 -index 00000000..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/0018-libcamera-software_isp-Apply-black-level-compensatio.patch b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch deleted file mode 100644 index c746b74dba67..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch +++ /dev/null @@ -1,396 +0,0 @@ -From bb608d177135d74e3c98b8a61fb459ebe254bca5 Mon Sep 17 00:00:00 2001 -From: Milan Zamazal <mzamazal@redhat.com> -Date: Mon, 11 Mar 2024 15:15:21 +0100 -Subject: [PATCH 18/21] libcamera: software_isp: Apply black level compensation - -Black may not be represented as 0 pixel value for given hardware, it may be -higher. If this is not compensated then various problems may occur such as low -contrast or suboptimal exposure. - -The black pixel value can be either retrieved from a tuning file for the given -hardware, or automatically on fly. The former is the right and correct method, -while the latter can be used when a tuning file is not available for the given -hardware. Since there is currently no support for tuning files in software ISP, -the automatic, hardware independent way, is always used. Support for tuning -files should be added in future but it will require more work than this patch. - -The patch looks at the image histogram and assumes that black starts when pixel -values start occurring on the left. A certain amount of the darkest pixels is -ignored; it doesn't matter whether they represent various kinds of noise or are -real, they are better to omit in any case to make the image looking better. It -also doesn't matter whether the darkest pixels occur around the supposed black -level or are spread between 0 and the black level, the difference is not -important. - -An arbitrary threshold of 2% darkest pixels is applied; there is no magic about -that value. - -The patch assumes that the black values for different colors are the same and -doesn't attempt any other non-primitive enhancements. It cannot completely -replace tuning files and simplicity, while providing visible benefit, is its -goal. Anything more sophisticated is left for future patches. - -A possible cheap enhancement, if needed, could be setting exposure + gain to -minimum values temporarily, before setting the black level. In theory, the -black level should be fixed but it may not be reached in all images. For this -reason, the patch updates black level only if the observed value is lower than -the current one; it should be never increased. - -The purpose of the patch is to compensate for hardware properties. General -image contrast enhancements are out of scope of this patch. - -Stats are still gathered as an uncorrected histogram, to avoid any confusion and -to represent the raw image data. Exposure must be determined after the black -level correction -- it has no influence on the sub-black area and must be -correct after applying the black level correction. The granularity of the -histogram is increased from 16 to 64 to provide a better precision (there is no -theory behind either of those numbers). - -Reviewed-by: Hans de Goede <hdegoede@redhat.com> -Signed-off-by: Milan Zamazal <mzamazal@redhat.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> ---- - .../internal/software_isp/debayer_params.h | 4 + - .../internal/software_isp/swisp_stats.h | 10 ++- - src/ipa/simple/black_level.cpp | 86 +++++++++++++++++++ - src/ipa/simple/black_level.h | 28 ++++++ - src/ipa/simple/meson.build | 7 +- - src/ipa/simple/soft_simple.cpp | 28 ++++-- - src/libcamera/software_isp/debayer_cpu.cpp | 13 ++- - src/libcamera/software_isp/debayer_cpu.h | 1 + - src/libcamera/software_isp/software_isp.cpp | 2 +- - 9 files changed, 162 insertions(+), 17 deletions(-) - create mode 100644 src/ipa/simple/black_level.cpp - create mode 100644 src/ipa/simple/black_level.h - -diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h -index 98965fa1..5e38e08b 100644 ---- a/include/libcamera/internal/software_isp/debayer_params.h -+++ b/include/libcamera/internal/software_isp/debayer_params.h -@@ -43,6 +43,10 @@ struct DebayerParams { - * \brief Gamma correction, 1.0 is no correction - */ - float gamma; -+ /** -+ * \brief Level of the black point, 0..255, 0 is no correction. -+ */ -+ unsigned int blackLevel; - }; - - } /* namespace libcamera */ -diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h -index afe42c9a..25cd5abd 100644 ---- a/include/libcamera/internal/software_isp/swisp_stats.h -+++ b/include/libcamera/internal/software_isp/swisp_stats.h -@@ -7,6 +7,8 @@ - - #pragma once - -+#include <array> -+ - namespace libcamera { - - /** -@@ -28,11 +30,15 @@ struct SwIspStats { - /** - * \brief Number of bins in the yHistogram. - */ -- static constexpr unsigned int kYHistogramSize = 16; -+ static constexpr unsigned int kYHistogramSize = 64; -+ /** -+ * \brief Type of the histogram. -+ */ -+ using histogram = std::array<unsigned int, kYHistogramSize>; - /** - * \brief A histogram of luminance values. - */ -- std::array<unsigned int, kYHistogramSize> yHistogram; -+ histogram yHistogram; - }; - - } /* namespace libcamera */ -diff --git a/src/ipa/simple/black_level.cpp b/src/ipa/simple/black_level.cpp -new file mode 100644 -index 00000000..8d52201b ---- /dev/null -+++ b/src/ipa/simple/black_level.cpp -@@ -0,0 +1,86 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2024, Red Hat Inc. -+ * -+ * black_level.cpp - black level handling -+ */ -+ -+#include "black_level.h" -+ -+#include <numeric> -+ -+#include <libcamera/base/log.h> -+ -+namespace libcamera { -+ -+LOG_DEFINE_CATEGORY(IPASoftBL) -+ -+/** -+ * \class BlackLevel -+ * \brief Object providing black point level for software ISP -+ * -+ * Black level can be provided in hardware tuning files or, if no tuning file is -+ * available for the given hardware, guessed automatically, with less accuracy. -+ * As tuning files are not yet implemented for software ISP, BlackLevel -+ * currently provides only guessed black levels. -+ * -+ * This class serves for tracking black level as a property of the underlying -+ * hardware, not as means of enhancing a particular scene or image. -+ * -+ * The class is supposed to be instantiated for the given camera stream. -+ * The black level can be retrieved using BlackLevel::get() method. It is -+ * initially 0 and may change when updated using BlackLevel::update() method. -+ */ -+ -+BlackLevel::BlackLevel() -+ : blackLevel_(255), blackLevelSet_(false) -+{ -+} -+ -+/** -+ * \brief Return the current black level -+ * -+ * \return The black level, in the range from 0 (minimum) to 255 (maximum). -+ * If the black level couldn't be determined yet, return 0. -+ */ -+unsigned int BlackLevel::get() const -+{ -+ return blackLevelSet_ ? blackLevel_ : 0; -+} -+ -+/** -+ * \brief Update black level from the provided histogram -+ * \param[in] yHistogram The histogram to be used for updating black level -+ * -+ * The black level is property of the given hardware, not image. It is updated -+ * only if it has not been yet set or if it is lower than the lowest value seen -+ * so far. -+ */ -+void BlackLevel::update(SwIspStats::histogram &yHistogram) -+{ -+ // The constant is selected to be "good enough", not overly conservative or -+ // aggressive. There is no magic about the given value. -+ constexpr float ignoredPercentage_ = 0.02; -+ const unsigned int total = -+ std::accumulate(begin(yHistogram), end(yHistogram), 0); -+ const unsigned int pixelThreshold = ignoredPercentage_ * total; -+ const unsigned int currentBlackIdx = -+ blackLevel_ / (256 / SwIspStats::kYHistogramSize); -+ -+ for (unsigned int i = 0, seen = 0; -+ i < currentBlackIdx && i < SwIspStats::kYHistogramSize; -+ i++) { -+ seen += yHistogram[i]; -+ if (seen >= pixelThreshold) { -+ blackLevel_ = i * (256 / SwIspStats::kYHistogramSize); -+ blackLevelSet_ = true; -+ LOG(IPASoftBL, Debug) -+ << "Auto-set black level: " -+ << i << "/" << SwIspStats::kYHistogramSize -+ << " (" << 100 * (seen - yHistogram[i]) / total << "% below, " -+ << 100 * seen / total << "% at or below)"; -+ break; -+ } -+ }; -+} -+} // namespace libcamera -diff --git a/src/ipa/simple/black_level.h b/src/ipa/simple/black_level.h -new file mode 100644 -index 00000000..b3785db0 ---- /dev/null -+++ b/src/ipa/simple/black_level.h -@@ -0,0 +1,28 @@ -+/* SPDX-License-Identifier: LGPL-2.1-or-later */ -+/* -+ * Copyright (C) 2024, Red Hat Inc. -+ * -+ * black_level.h - black level handling -+ */ -+ -+#pragma once -+ -+#include <array> -+ -+#include "libcamera/internal/software_isp/swisp_stats.h" -+ -+namespace libcamera { -+ -+class BlackLevel -+{ -+public: -+ BlackLevel(); -+ unsigned int get() const; -+ void update(std::array<unsigned int, SwIspStats::kYHistogramSize> &yHistogram); -+ -+private: -+ unsigned int blackLevel_; -+ bool blackLevelSet_; -+}; -+ -+} // namespace libcamera -diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build -index 3e863db7..44b5f1d7 100644 ---- a/src/ipa/simple/meson.build -+++ b/src/ipa/simple/meson.build -@@ -2,8 +2,13 @@ - - ipa_name = 'ipa_soft_simple' - -+soft_simple_sources = files([ -+ 'soft_simple.cpp', -+ 'black_level.cpp', -+]) -+ - mod = shared_module(ipa_name, -- ['soft_simple.cpp', libcamera_generated_ipa_headers], -+ [soft_simple_sources, libcamera_generated_ipa_headers], - name_prefix : '', - include_directories : [ipa_includes, libipa_includes], - dependencies : libcamera_private, -diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp -index 312df4ba..ac027568 100644 ---- a/src/ipa/simple/soft_simple.cpp -+++ b/src/ipa/simple/soft_simple.cpp -@@ -22,6 +22,8 @@ - #include "libcamera/internal/software_isp/debayer_params.h" - #include "libcamera/internal/software_isp/swisp_stats.h" - -+#include "black_level.h" -+ - namespace libcamera { - - LOG_DEFINE_CATEGORY(IPASoft) -@@ -33,7 +35,8 @@ class IPASoftSimple : public ipa::soft::IPASoftInterface - public: - IPASoftSimple() - : params_(static_cast<DebayerParams *>(MAP_FAILED)), -- stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0) -+ stats_(static_cast<SwIspStats *>(MAP_FAILED)), -+ blackLevel_(BlackLevel()), ignore_updates_(0) - { - } - -@@ -63,6 +66,7 @@ private: - SharedFD fdParams_; - DebayerParams *params_; - SwIspStats *stats_; -+ BlackLevel blackLevel_; - - int32_t exposure_min_, exposure_max_; - int32_t again_min_, again_max_; -@@ -196,6 +200,10 @@ void IPASoftSimple::processStats(const ControlList &sensorControls) - params_->gainG = 256; - params_->gamma = 0.5; - -+ if (ignore_updates_ > 0) -+ blackLevel_.update(stats_->yHistogram); -+ params_->blackLevel = blackLevel_.get(); -+ - setIspParams.emit(0); - - /* -@@ -211,18 +219,19 @@ void IPASoftSimple::processStats(const ControlList &sensorControls) - * Calculate Mean Sample Value (MSV) according to formula from: - * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf - */ -- constexpr unsigned int yHistValsPerBin = -- SwIspStats::kYHistogramSize / kExposureBinsCount; -- constexpr unsigned int yHistValsPerBinMod = -- SwIspStats::kYHistogramSize / -- (SwIspStats::kYHistogramSize % kExposureBinsCount + 1); -+ const unsigned int blackLevelHistIdx = -+ params_->blackLevel / (256 / SwIspStats::kYHistogramSize); -+ const unsigned int histogramSize = SwIspStats::kYHistogramSize - blackLevelHistIdx; -+ const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount; -+ const unsigned int yHistValsPerBinMod = -+ histogramSize / (histogramSize % kExposureBinsCount + 1); - int ExposureBins[kExposureBinsCount] = {}; - unsigned int denom = 0; - unsigned int num = 0; - -- for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) { -+ for (unsigned int i = 0; i < histogramSize; i++) { - unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin; -- ExposureBins[idx] += stats_->yHistogram[i]; -+ ExposureBins[idx] += stats_->yHistogram[blackLevelHistIdx + i]; - } - - for (unsigned int i = 0; i < kExposureBinsCount; i++) { -@@ -256,7 +265,8 @@ void IPASoftSimple::processStats(const ControlList &sensorControls) - - LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV - << " exp " << exposure_ << " again " << again_ -- << " gain R/B " << params_->gainR << "/" << params_->gainB; -+ << " gain R/B " << params_->gainR << "/" << params_->gainB -+ << " black level " << params_->blackLevel; - } - - void IPASoftSimple::updateExposure(double exposureMSV) -diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp -index a1692693..3be3cdfe 100644 ---- a/src/libcamera/software_isp/debayer_cpu.cpp -+++ b/src/libcamera/software_isp/debayer_cpu.cpp -@@ -35,7 +35,7 @@ namespace libcamera { - * \param[in] stats Pointer to the stats object to use. - */ - DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats) -- : stats_(std::move(stats)), gamma_correction_(1.0) -+ : stats_(std::move(stats)), gamma_correction_(1.0), blackLevel_(0) - { - #ifdef __x86_64__ - enableInputMemcpy_ = false; -@@ -683,11 +683,16 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams - } - - /* Apply DebayerParams */ -- if (params.gamma != gamma_correction_) { -- for (unsigned int i = 0; i < kGammaLookupSize; i++) -- gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma); -+ if (params.gamma != gamma_correction_ || params.blackLevel != blackLevel_) { -+ const unsigned int blackIndex = -+ params.blackLevel * kGammaLookupSize / 256; -+ std::fill(gamma_.begin(), gamma_.begin() + blackIndex, 0); -+ const float divisor = kGammaLookupSize - blackIndex - 1.0; -+ for (unsigned int i = blackIndex; i < kGammaLookupSize; i++) -+ gamma_[i] = UINT8_MAX * powf((i - blackIndex) / divisor, params.gamma); - - gamma_correction_ = params.gamma; -+ blackLevel_ = params.blackLevel; - } - - if (swapRedBlueGains_) -diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h -index 5f44fc65..ea02f909 100644 ---- a/src/libcamera/software_isp/debayer_cpu.h -+++ b/src/libcamera/software_isp/debayer_cpu.h -@@ -147,6 +147,7 @@ private: - bool enableInputMemcpy_; - bool swapRedBlueGains_; - float gamma_correction_; -+ unsigned int blackLevel_; - unsigned int measuredFrames_; - int64_t frameProcessTime_; - /* Skip 30 frames for things to stabilize then measure 30 frames */ -diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp -index 388b4496..9b49be41 100644 ---- a/src/libcamera/software_isp/software_isp.cpp -+++ b/src/libcamera/software_isp/software_isp.cpp -@@ -64,7 +64,7 @@ LOG_DEFINE_CATEGORY(SoftwareIsp) - */ - SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls) - : debayer_(nullptr), -- debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f }, -+ debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 }, - dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System) - { - if (!dmaHeap_.isValid()) { --- -2.43.2 - 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 deleted file mode 100644 index 5b562c603c52..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch +++ /dev/null @@ -1,239 +0,0 @@ -From b0c07674abecb05dc0af93a4b749971f057bc3c6 Mon Sep 17 00:00:00 2001 -From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com> -Date: Mon, 11 Mar 2024 15:15:22 +0100 -Subject: [PATCH 19/21] libcamera: Soft IPA: use CameraSensorHelper for - analogue gain - -Use CameraSensorHelper to convert the analogue gain code read from the -camera sensor into real analogue gain value. In the future this makes -it possible to use faster AE/AGC algorithm. For now the same AE/AGC -algorithm is used, but even then the CameraSensorHelper lets us use the -full range of analogue gain values. - -If there is no CameraSensorHelper for the camera sensor in use, a -warning log message is printed, and the AE/AGC works exactly as before -this change. - -Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com> -Reviewed-by: Hans de Goede <hdegoede@redhat.com> -Signed-off-by: Hans de Goede <hdegoede@redhat.com> -Reviewed-by: Milan Zamazal <mzamazal@redhat.com> ---- - .../internal/software_isp/software_isp.h | 3 +- - src/ipa/simple/soft_simple.cpp | 77 ++++++++++++------- - src/libcamera/pipeline/simple/simple.cpp | 2 +- - src/libcamera/software_isp/software_isp.cpp | 8 +- - 4 files changed, 57 insertions(+), 33 deletions(-) - -diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h -index 8d25e979..2a6db7ba 100644 ---- a/include/libcamera/internal/software_isp/software_isp.h -+++ b/include/libcamera/internal/software_isp/software_isp.h -@@ -26,6 +26,7 @@ - #include <libcamera/ipa/soft_ipa_interface.h> - #include <libcamera/ipa/soft_ipa_proxy.h> - -+#include "libcamera/internal/camera_sensor.h" - #include "libcamera/internal/dma_heaps.h" - #include "libcamera/internal/pipeline_handler.h" - #include "libcamera/internal/shared_mem_object.h" -@@ -43,7 +44,7 @@ LOG_DECLARE_CATEGORY(SoftwareIsp) - class SoftwareIsp - { - public: -- SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls); -+ SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor); - ~SoftwareIsp(); - - int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; } -diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp -index ac027568..e4d64762 100644 ---- a/src/ipa/simple/soft_simple.cpp -+++ b/src/ipa/simple/soft_simple.cpp -@@ -22,6 +22,8 @@ - #include "libcamera/internal/software_isp/debayer_params.h" - #include "libcamera/internal/software_isp/swisp_stats.h" - -+#include "libipa/camera_sensor_helper.h" -+ - #include "black_level.h" - - namespace libcamera { -@@ -67,18 +69,27 @@ private: - DebayerParams *params_; - SwIspStats *stats_; - BlackLevel blackLevel_; -+ std::unique_ptr<CameraSensorHelper> camHelper_; - - int32_t exposure_min_, exposure_max_; -- int32_t again_min_, again_max_; -- int32_t again_, exposure_; -+ int32_t exposure_; -+ double again_min_, again_max_, againMinStep_; -+ double again_; - unsigned int ignore_updates_; - }; - --int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings, -+int IPASoftSimple::init(const IPASettings &settings, - const SharedFD &fdStats, - const SharedFD &fdParams, - const ControlInfoMap &sensorInfoMap) - { -+ camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel); -+ if (camHelper_ == nullptr) { -+ LOG(IPASoft, Warning) -+ << "Failed to create camera sensor helper for " -+ << settings.sensorModel; -+ } -+ - fdStats_ = fdStats; - if (!fdStats_.isValid()) { - LOG(IPASoft, Error) << "Invalid Statistics handle"; -@@ -132,25 +143,35 @@ int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap) - exposure_min_ = 1; - } - -- again_min_ = gain_info.min().get<int32_t>(); -- again_max_ = gain_info.max().get<int32_t>(); -- /* -- * The camera sensor gain (g) is usually not equal to the value written -- * into the gain register (x). But the way how the AGC algorithm changes -- * the gain value to make the total exposure closer to the optimum assumes -- * that g(x) is not too far from linear function. If the minimal gain is 0, -- * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c). -- * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near -- * one edge, and very small near the other) we limit the range of the gain -- * values used. -- */ -- if (!again_min_) { -- LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear"; -- again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2); -+ int32_t again_min = gain_info.min().get<int32_t>(); -+ int32_t again_max = gain_info.max().get<int32_t>(); -+ -+ if (camHelper_) { -+ again_min_ = camHelper_->gain(again_min); -+ again_max_ = camHelper_->gain(again_max); -+ againMinStep_ = (again_max_ - again_min_) / 100.0; -+ } else { -+ /* -+ * The camera sensor gain (g) is usually not equal to the value written -+ * into the gain register (x). But the way how the AGC algorithm changes -+ * the gain value to make the total exposure closer to the optimum assumes -+ * that g(x) is not too far from linear function. If the minimal gain is 0, -+ * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c). -+ * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near -+ * one edge, and very small near the other) we limit the range of the gain -+ * values used. -+ */ -+ again_max_ = again_max; -+ if (!again_min) { -+ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear"; -+ again_min_ = std::min(100, again_min / 2 + again_max / 2); -+ } -+ againMinStep_ = 1.0; - } - - LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_ -- << ", gain " << again_min_ << "-" << again_max_; -+ << ", gain " << again_min_ << "-" << again_max_ -+ << " (" << againMinStep_ << ")"; - - return 0; - } -@@ -252,12 +273,14 @@ void IPASoftSimple::processStats(const ControlList &sensorControls) - ControlList ctrls(sensorControls); - - exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>(); -- again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>(); -+ int32_t again = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>(); -+ again_ = camHelper_ ? camHelper_->gain(again) : again; - - updateExposure(exposureMSV); - - ctrls.set(V4L2_CID_EXPOSURE, exposure_); -- ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_); -+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, -+ static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(again_) : again_)); - - ignore_updates_ = 2; - -@@ -276,7 +299,7 @@ void IPASoftSimple::updateExposure(double exposureMSV) - static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1; - static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1; - -- int next; -+ double next; - - if (exposureMSV < kExposureOptimal - kExposureSatisfactory) { - next = exposure_ * kExpNumeratorUp / kExpDenominator; -@@ -286,18 +309,18 @@ void IPASoftSimple::updateExposure(double exposureMSV) - exposure_ = next; - if (exposure_ >= exposure_max_) { - next = again_ * kExpNumeratorUp / kExpDenominator; -- if (next - again_ < 1) -- again_ += 1; -+ if (next - again_ < againMinStep_) -+ again_ += againMinStep_; - else - again_ = next; - } - } - - if (exposureMSV > kExposureOptimal + kExposureSatisfactory) { -- if (exposure_ == exposure_max_ && again_ != again_min_) { -+ if (exposure_ == exposure_max_ && again_ > again_min_) { - next = again_ * kExpNumeratorDown / kExpDenominator; -- if (again_ - next < 1) -- again_ -= 1; -+ if (again_ - next < againMinStep_) -+ again_ -= againMinStep_; - else - again_ = next; - } else { -diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp -index c3ebb7b7..7e932a14 100644 ---- a/src/libcamera/pipeline/simple/simple.cpp -+++ b/src/libcamera/pipeline/simple/simple.cpp -@@ -525,7 +525,7 @@ int SimpleCameraData::init() - * Instantiate Soft ISP if this is enabled for the given driver and no converter is used. - */ - if (!converter_ && pipe->swIspEnabled()) { -- swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls()); -+ swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get()); - if (!swIsp_->isValid()) { - LOG(SimplePipeline, Warning) - << "Failed to create software ISP, disabling software debayering"; -diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp -index 9b49be41..ea4d96e4 100644 ---- a/src/libcamera/software_isp/software_isp.cpp -+++ b/src/libcamera/software_isp/software_isp.cpp -@@ -60,9 +60,9 @@ LOG_DEFINE_CATEGORY(SoftwareIsp) - /** - * \brief Constructs SoftwareIsp object - * \param[in] pipe The pipeline handler in use -- * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor -+ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline handler - */ --SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls) -+SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor) - : debayer_(nullptr), - debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 }, - dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System) -@@ -97,10 +97,10 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorCont - return; - } - -- int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" }, -+ int ret = ipa_->init(IPASettings{ "No cfg file", sensor->model() }, - debayer_->getStatsFD(), - sharedParams_.fd(), -- sensorControls); -+ sensor->controls()); - if (ret) { - LOG(SoftwareIsp, Error) << "IPA init failed"; - debayer_.reset(); --- -2.43.2 - |