about summary refs log tree commit diff
path: root/users/flokli/ipu6-softisp/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'users/flokli/ipu6-softisp/kernel')
-rw-r--r--users/flokli/ipu6-softisp/kernel/softisp.patch16732
1 files changed, 59 insertions, 16673 deletions
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, &ltrdid);
-+
-+		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&#45;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&#45;&gt;n00000001 -->
-+<g id="edge1" class="edge"><title>n0000007d:port1&#45;&gt;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&#45;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&#45;&gt;n00000002 -->
-+<g id="edge2" class="edge"><title>n00000087:port1&#45;&gt;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&#45;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&#45;&gt;n00000003 -->
-+<g id="edge3" class="edge"><title>n00000091:port1&#45;&gt;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&#45;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&#45;&gt;n00000004 -->
-+<g id="edge4" class="edge"><title>n0000009b:port1&#45;&gt;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&#45;0036</text>
-+<text text-anchor="middle" x="75" y="-487.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;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&#45;&gt;n00000091 -->
-+<g id="edge5" class="edge"><title>n00000865:port0&#45;&gt;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&#45;&gt;n0000007d -->
-+<!-- n00000867 -->
-+<!-- n00000867&#45;&gt;n00000087 -->
-+<!-- n00000868 -->
-+<!-- n00000868&#45;&gt;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