public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/kernel] f44: kernel-7.1.6-200
@ 2026-08-03 15:42 Augusto Caringi
  0 siblings, 0 replies; 2+ messages in thread
From: Augusto Caringi @ 2026-08-03 15:42 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/kernel
            Branch : f44
            Commit : d0080d2a9325f400f31c1d18bb1130c032ded31a
            Author : Augusto Caringi <acaringi@redhat.com>
            Date   : 2026-08-03T12:42:34-03:00
            Stats  : +4549/-663 in 30 file(s)
            URL    : https://src.fedoraproject.org/rpms/kernel/c/d0080d2a9325f400f31c1d18bb1130c032ded31a?branch=f44

            Log:
            kernel-7.1.6-200

* Mon Aug 03 2026 Augusto Caringi <acaringi@redhat.com> [7.1.6-0]
- New config for stable (Justin M. Forbes)
- acpi: battery: Sanitise model_number by dropping unprintable characters (Kate Hsuan)
- redhat: configs: Enable AMD ISP4 MIPI camera solution (Kate Hsuan)
- media: platform: amd: add DRM_AMDGPU dependency (Arnd Bergmann)
- media: platform: amd: isp4: drop stale list reinit before free (Bin Du)
- media: platform: amd: isp4 debug fs logging and more descriptive errors (Bin Du)
- media: platform: amd: isp4 video node and buffers handling added (Bin Du)
- media: platform: amd: isp4 subdev and firmware loading handling added (Bin Du)
- media: platform: amd: Add isp4 fw and hw interface (Bin Du)
- media: platform: amd: low level support for isp4 firmware (Bin Du)
- media: platform: amd: Introduce amd isp4 capture driver (Bin Du)
- serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms (Jiangshan Yi)
- Linux v7.1.6
Resolves:

Signed-off-by: Augusto Caringi <acaringi@redhat.com>

---
diff --git a/Makefile.rhelver b/Makefile.rhelver
index e810b89..95b9fd7 100644
--- a/Makefile.rhelver
+++ b/Makefile.rhelver
@@ -12,7 +12,7 @@ RHEL_MINOR = 99
 #
 # Use this spot to avoid future merge conflicts.
 # Do not trim this comment.
-RHEL_RELEASE = 1
+RHEL_RELEASE = 0
 
 #
 # RHEL_REBASE_NUM

diff --git a/Patchlist.changelog b/Patchlist.changelog
index e09ffbc..7e26223 100644
--- a/Patchlist.changelog
+++ b/Patchlist.changelog
@@ -1,3 +1,30 @@
+https://gitlab.com/cki-project/kernel-ark/-/commit/73346d0cf08db8358dd8ce372c6b5605a77944c6
+ 73346d0cf08db8358dd8ce372c6b5605a77944c6 media: platform: amd: add DRM_AMDGPU dependency
+
+https://gitlab.com/cki-project/kernel-ark/-/commit/4e39a03df0faeadcec9d99ede2cc53e60fc97fc7
+ 4e39a03df0faeadcec9d99ede2cc53e60fc97fc7 media: platform: amd: isp4: drop stale list reinit before free
+
+https://gitlab.com/cki-project/kernel-ark/-/commit/a25aadb4e9fa50d660191d3e671fdc90731f1b90
+ a25aadb4e9fa50d660191d3e671fdc90731f1b90 media: platform: amd: isp4 debug fs logging and more descriptive errors
+
+https://gitlab.com/cki-project/kernel-ark/-/commit/b9d72b2a305489b0a084c800109a16a68f41b250
+ b9d72b2a305489b0a084c800109a16a68f41b250 media: platform: amd: isp4 video node and buffers handling added
+
+https://gitlab.com/cki-project/kernel-ark/-/commit/59ed38328c8378185de44dba741b4138ca6500aa
+ 59ed38328c8378185de44dba741b4138ca6500aa media: platform: amd: isp4 subdev and firmware loading handling added
+
+https://gitlab.com/cki-project/kernel-ark/-/commit/11af2852465177f2bdeda7e90ed5de0d557bf86d
+ 11af2852465177f2bdeda7e90ed5de0d557bf86d media: platform: amd: Add isp4 fw and hw interface
+
+https://gitlab.com/cki-project/kernel-ark/-/commit/ebbb4c12a58f5fa99d996fe0742b9b9c03fec7c8
+ ebbb4c12a58f5fa99d996fe0742b9b9c03fec7c8 media: platform: amd: low level support for isp4 firmware
+
+https://gitlab.com/cki-project/kernel-ark/-/commit/08fa27ef3838b4d294691e5f9f7bb88110d84fad
+ 08fa27ef3838b4d294691e5f9f7bb88110d84fad media: platform: amd: Introduce amd isp4 capture driver
+
+https://gitlab.com/cki-project/kernel-ark/-/commit/339af46d6f568742b005e846b113433fac929130
+ 339af46d6f568742b005e846b113433fac929130 serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms
+
 https://gitlab.com/cki-project/kernel-ark/-/commit/9fa1cceb384aadc5ca345e7f42522bcaf65b1a4f
  9fa1cceb384aadc5ca345e7f42522bcaf65b1a4f ASoC: cs42l43: Correct report for forced microphone jack
 
@@ -31,6 +58,9 @@ https://gitlab.com/cki-project/kernel-ark/-/commit/4735f2eec63a6a192b69a79921f85
 https://gitlab.com/cki-project/kernel-ark/-/commit/ce4210e437cef5c58d496117632473f6ad20f1d1
  ce4210e437cef5c58d496117632473f6ad20f1d1 net/sched: cls_api: Handle TC_ACT_CONSUMED in tcf_qevent_handle
 
+https://gitlab.com/cki-project/kernel-ark/-/commit/1077be9b9866a8a450b9b2620910e33fd8b3be76
+ 1077be9b9866a8a450b9b2620910e33fd8b3be76 acpi: battery: Sanitise model_number by dropping unprintable characters
+
 https://gitlab.com/cki-project/kernel-ark/-/commit/038190229a7c4ce9fd78a0ece5998a483e917b10
  038190229a7c4ce9fd78a0ece5998a483e917b10 Revert "PCI/MSI: Unmap MSI-X region on error"
 

diff --git a/kernel-aarch64-16k-debug-fedora.config b/kernel-aarch64-16k-debug-fedora.config
index ca19bed..3a131e3 100644
--- a/kernel-aarch64-16k-debug-fedora.config
+++ b/kernel-aarch64-16k-debug-fedora.config
@@ -10276,6 +10276,7 @@ CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 CONFIG_VIDEO_ALLEGRO_DVT=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AMPHION_VPU=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m

diff --git a/kernel-aarch64-16k-fedora.config b/kernel-aarch64-16k-fedora.config
index 067159b..542846d 100644
--- a/kernel-aarch64-16k-fedora.config
+++ b/kernel-aarch64-16k-fedora.config
@@ -10242,6 +10242,7 @@ CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 CONFIG_VIDEO_ALLEGRO_DVT=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AMPHION_VPU=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m

diff --git a/kernel-aarch64-debug-fedora.config b/kernel-aarch64-debug-fedora.config
index 2cf6577..3246ee6 100644
--- a/kernel-aarch64-debug-fedora.config
+++ b/kernel-aarch64-debug-fedora.config
@@ -10275,6 +10275,7 @@ CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 CONFIG_VIDEO_ALLEGRO_DVT=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AMPHION_VPU=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m

diff --git a/kernel-aarch64-fedora.config b/kernel-aarch64-fedora.config
index 20c22f6..a510338 100644
--- a/kernel-aarch64-fedora.config
+++ b/kernel-aarch64-fedora.config
@@ -10241,6 +10241,7 @@ CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 CONFIG_VIDEO_ALLEGRO_DVT=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AMPHION_VPU=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m

diff --git a/kernel-aarch64-rt-64k-debug-fedora.config b/kernel-aarch64-rt-64k-debug-fedora.config
index bf85180..51547eb 100644
--- a/kernel-aarch64-rt-64k-debug-fedora.config
+++ b/kernel-aarch64-rt-64k-debug-fedora.config
@@ -10291,6 +10291,7 @@ CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 CONFIG_VIDEO_ALLEGRO_DVT=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AMPHION_VPU=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m

diff --git a/kernel-aarch64-rt-64k-fedora.config b/kernel-aarch64-rt-64k-fedora.config
index 6bb7e22..4e8084d 100644
--- a/kernel-aarch64-rt-64k-fedora.config
+++ b/kernel-aarch64-rt-64k-fedora.config
@@ -10257,6 +10257,7 @@ CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 CONFIG_VIDEO_ALLEGRO_DVT=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AMPHION_VPU=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m

diff --git a/kernel-aarch64-rt-debug-fedora.config b/kernel-aarch64-rt-debug-fedora.config
index a419e51..cf1e54c 100644
--- a/kernel-aarch64-rt-debug-fedora.config
+++ b/kernel-aarch64-rt-debug-fedora.config
@@ -10287,6 +10287,7 @@ CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 CONFIG_VIDEO_ALLEGRO_DVT=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AMPHION_VPU=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m

diff --git a/kernel-aarch64-rt-fedora.config b/kernel-aarch64-rt-fedora.config
index 8fd8551..5fcef7e 100644
--- a/kernel-aarch64-rt-fedora.config
+++ b/kernel-aarch64-rt-fedora.config
@@ -10253,6 +10253,7 @@ CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 CONFIG_VIDEO_ALLEGRO_DVT=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AMPHION_VPU=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m

diff --git a/kernel-ppc64le-debug-fedora.config b/kernel-ppc64le-debug-fedora.config
index 7328d84..a1d7e2e 100644
--- a/kernel-ppc64le-debug-fedora.config
+++ b/kernel-ppc64le-debug-fedora.config
@@ -8301,6 +8301,7 @@ CONFIG_VIDEO_ADV7842=m
 CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-ppc64le-fedora.config b/kernel-ppc64le-fedora.config
index 788e27b..e1c4104 100644
--- a/kernel-ppc64le-fedora.config
+++ b/kernel-ppc64le-fedora.config
@@ -8266,6 +8266,7 @@ CONFIG_VIDEO_ADV7842=m
 CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-riscv64-debug-fedora.config b/kernel-riscv64-debug-fedora.config
index fe863c2..bb9ac73 100644
--- a/kernel-riscv64-debug-fedora.config
+++ b/kernel-riscv64-debug-fedora.config
@@ -8478,6 +8478,7 @@ CONFIG_VIDEO_ADP1653=m
 CONFIG_VIDEO_AK7375=m
 # CONFIG_VIDEO_AK881X is not set
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-riscv64-fedora.config b/kernel-riscv64-fedora.config
index a35f537..94a2f61 100644
--- a/kernel-riscv64-fedora.config
+++ b/kernel-riscv64-fedora.config
@@ -8443,6 +8443,7 @@ CONFIG_VIDEO_ADP1653=m
 CONFIG_VIDEO_AK7375=m
 # CONFIG_VIDEO_AK881X is not set
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-riscv64-rt-debug-fedora.config b/kernel-riscv64-rt-debug-fedora.config
index 8274716..ee9407e 100644
--- a/kernel-riscv64-rt-debug-fedora.config
+++ b/kernel-riscv64-rt-debug-fedora.config
@@ -8490,6 +8490,7 @@ CONFIG_VIDEO_ADP1653=m
 CONFIG_VIDEO_AK7375=m
 # CONFIG_VIDEO_AK881X is not set
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-riscv64-rt-fedora.config b/kernel-riscv64-rt-fedora.config
index 593c37b..4d39912 100644
--- a/kernel-riscv64-rt-fedora.config
+++ b/kernel-riscv64-rt-fedora.config
@@ -8455,6 +8455,7 @@ CONFIG_VIDEO_ADP1653=m
 CONFIG_VIDEO_AK7375=m
 # CONFIG_VIDEO_AK881X is not set
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-s390x-debug-fedora.config b/kernel-s390x-debug-fedora.config
index 37b905d..8d94733 100644
--- a/kernel-s390x-debug-fedora.config
+++ b/kernel-s390x-debug-fedora.config
@@ -8257,6 +8257,7 @@ CONFIG_VIDEO_ADV7842=m
 CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-s390x-fedora.config b/kernel-s390x-fedora.config
index e0cfa6b..93bb211 100644
--- a/kernel-s390x-fedora.config
+++ b/kernel-s390x-fedora.config
@@ -8222,6 +8222,7 @@ CONFIG_VIDEO_ADV7842=m
 CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AMD_ISP4_CAPTURE is not set
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-x86_64-debug-fedora.config b/kernel-x86_64-debug-fedora.config
index 71856e1..5efa1aa 100644
--- a/kernel-x86_64-debug-fedora.config
+++ b/kernel-x86_64-debug-fedora.config
@@ -7225,6 +7225,7 @@ CONFIG_SND_SIMPLE_CARD_UTILS=m
 # CONFIG_SND_SIS7019 is not set
 CONFIG_SND_SOC_AC97_BUS=y
 CONFIG_SND_SOC_AC97_CODEC=m
+CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=y
 # CONFIG_SND_SOC_ADAU1372_I2C is not set
 # CONFIG_SND_SOC_ADAU1372_SPI is not set
 CONFIG_SND_SOC_ADAU1373=m
@@ -8898,6 +8899,7 @@ CONFIG_VIDEO_ADV7842=m
 CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+CONFIG_VIDEO_AMD_ISP4_CAPTURE=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-x86_64-debug-rhel.config b/kernel-x86_64-debug-rhel.config
index ef85b0d..6fc17b5 100644
--- a/kernel-x86_64-debug-rhel.config
+++ b/kernel-x86_64-debug-rhel.config
@@ -6651,6 +6651,7 @@ CONFIG_SND_SEQ_UMP=y
 # CONFIG_SND_SIS7019 is not set
 # CONFIG_SND_SOC_AC97_BUS is not set
 # CONFIG_SND_SOC_AC97_CODEC is not set
+CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=y
 # CONFIG_SND_SOC_ADAU1372_I2C is not set
 # CONFIG_SND_SOC_ADAU1372_SPI is not set
 # CONFIG_SND_SOC_ADAU1373 is not set

diff --git a/kernel-x86_64-fedora.config b/kernel-x86_64-fedora.config
index fe52473..98632c6 100644
--- a/kernel-x86_64-fedora.config
+++ b/kernel-x86_64-fedora.config
@@ -7192,6 +7192,7 @@ CONFIG_SND_SIMPLE_CARD_UTILS=m
 # CONFIG_SND_SIS7019 is not set
 CONFIG_SND_SOC_AC97_BUS=y
 CONFIG_SND_SOC_AC97_CODEC=m
+CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=y
 # CONFIG_SND_SOC_ADAU1372_I2C is not set
 # CONFIG_SND_SOC_ADAU1372_SPI is not set
 CONFIG_SND_SOC_ADAU1373=m
@@ -8864,6 +8865,7 @@ CONFIG_VIDEO_ADV7842=m
 CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+CONFIG_VIDEO_AMD_ISP4_CAPTURE=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-x86_64-rhel.config b/kernel-x86_64-rhel.config
index c2ab198..4db9a46 100644
--- a/kernel-x86_64-rhel.config
+++ b/kernel-x86_64-rhel.config
@@ -6624,6 +6624,7 @@ CONFIG_SND_SEQ_UMP=y
 # CONFIG_SND_SIS7019 is not set
 # CONFIG_SND_SOC_AC97_BUS is not set
 # CONFIG_SND_SOC_AC97_CODEC is not set
+CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=y
 # CONFIG_SND_SOC_ADAU1372_I2C is not set
 # CONFIG_SND_SOC_ADAU1372_SPI is not set
 # CONFIG_SND_SOC_ADAU1373 is not set

diff --git a/kernel-x86_64-rt-debug-fedora.config b/kernel-x86_64-rt-debug-fedora.config
index 16018dd..98f88d9 100644
--- a/kernel-x86_64-rt-debug-fedora.config
+++ b/kernel-x86_64-rt-debug-fedora.config
@@ -7236,6 +7236,7 @@ CONFIG_SND_SIMPLE_CARD_UTILS=m
 # CONFIG_SND_SIS7019 is not set
 CONFIG_SND_SOC_AC97_BUS=y
 CONFIG_SND_SOC_AC97_CODEC=m
+CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=y
 # CONFIG_SND_SOC_ADAU1372_I2C is not set
 # CONFIG_SND_SOC_ADAU1372_SPI is not set
 CONFIG_SND_SOC_ADAU1373=m
@@ -8910,6 +8911,7 @@ CONFIG_VIDEO_ADV7842=m
 CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+CONFIG_VIDEO_AMD_ISP4_CAPTURE=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-x86_64-rt-debug-rhel.config b/kernel-x86_64-rt-debug-rhel.config
index aeefcdf..7fb4486 100644
--- a/kernel-x86_64-rt-debug-rhel.config
+++ b/kernel-x86_64-rt-debug-rhel.config
@@ -6696,6 +6696,7 @@ CONFIG_SND_SEQ_UMP=y
 # CONFIG_SND_SIS7019 is not set
 # CONFIG_SND_SOC_AC97_BUS is not set
 # CONFIG_SND_SOC_AC97_CODEC is not set
+CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=y
 # CONFIG_SND_SOC_ADAU1372_I2C is not set
 # CONFIG_SND_SOC_ADAU1372_SPI is not set
 # CONFIG_SND_SOC_ADAU1373 is not set

diff --git a/kernel-x86_64-rt-fedora.config b/kernel-x86_64-rt-fedora.config
index afead2b..074b56e 100644
--- a/kernel-x86_64-rt-fedora.config
+++ b/kernel-x86_64-rt-fedora.config
@@ -7203,6 +7203,7 @@ CONFIG_SND_SIMPLE_CARD_UTILS=m
 # CONFIG_SND_SIS7019 is not set
 CONFIG_SND_SOC_AC97_BUS=y
 CONFIG_SND_SOC_AC97_CODEC=m
+CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=y
 # CONFIG_SND_SOC_ADAU1372_I2C is not set
 # CONFIG_SND_SOC_ADAU1372_SPI is not set
 CONFIG_SND_SOC_ADAU1373=m
@@ -8876,6 +8877,7 @@ CONFIG_VIDEO_ADV7842=m
 CONFIG_VIDEO_AK7375=m
 CONFIG_VIDEO_AK881X=m
 # CONFIG_VIDEO_ALVIUM_CSI2 is not set
+CONFIG_VIDEO_AMD_ISP4_CAPTURE=m
 CONFIG_VIDEO_AR0521=m
 CONFIG_VIDEO_AU0828=m
 # CONFIG_VIDEO_AU0828_RC is not set

diff --git a/kernel-x86_64-rt-rhel.config b/kernel-x86_64-rt-rhel.config
index 086c33e..fb2c819 100644
--- a/kernel-x86_64-rt-rhel.config
+++ b/kernel-x86_64-rt-rhel.config
@@ -6669,6 +6669,7 @@ CONFIG_SND_SEQ_UMP=y
 # CONFIG_SND_SIS7019 is not set
 # CONFIG_SND_SOC_AC97_BUS is not set
 # CONFIG_SND_SOC_AC97_CODEC is not set
+CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS=y
 # CONFIG_SND_SOC_ADAU1372_I2C is not set
 # CONFIG_SND_SOC_ADAU1372_SPI is not set
 # CONFIG_SND_SOC_ADAU1373 is not set

diff --git a/kernel.changelog b/kernel.changelog
index 05a57d1..2690c18 100644
--- a/kernel.changelog
+++ b/kernel.changelog
@@ -1,3 +1,19 @@
+* Mon Aug 03 2026 Augusto Caringi <acaringi@redhat.com> [7.1.6-0]
+- New config for stable (Justin M. Forbes)
+- acpi: battery: Sanitise model_number by dropping unprintable characters (Kate Hsuan)
+- redhat: configs: Enable AMD ISP4 MIPI camera solution (Kate Hsuan)
+- media: platform: amd: add DRM_AMDGPU dependency (Arnd Bergmann)
+- media: platform: amd: isp4: drop stale list reinit before free (Bin Du)
+- media: platform: amd: isp4 debug fs logging and more descriptive errors (Bin Du)
+- media: platform: amd: isp4 video node and buffers handling added (Bin Du)
+- media: platform: amd: isp4 subdev and firmware loading handling added (Bin Du)
+- media: platform: amd: Add isp4 fw and hw interface (Bin Du)
+- media: platform: amd: low level support for isp4 firmware (Bin Du)
+- media: platform: amd: Introduce amd isp4 capture driver (Bin Du)
+- serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms (Jiangshan Yi)
+- Linux v7.1.6
+Resolves: 
+
 * Tue Jul 28 2026 Justin M. Forbes <jforbes@fedoraproject.org> [7.1.5-1]
 - ASoC: cs42l43: Correct report for forced microphone jack (Charles Keepax)
 - platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU hotplug (Guixiong Wei)

diff --git a/kernel.spec b/kernel.spec
index 9910061..8d46cfc 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -187,18 +187,18 @@ Summary: The Linux kernel
 #  the --with-release option overrides this setting.)
 %define debugbuildsenabled 1
 # define buildid .local
-%define specrpmversion 7.1.5
-%define specversion 7.1.5
+%define specrpmversion 7.1.6
+%define specversion 7.1.6
 %define patchversion 7.1
-%define pkgrelease 201
+%define pkgrelease 200
 %define kversion 7
-%define tarfile_release 7.1.5
+%define tarfile_release 7.1.6
 # This is needed to do merge window version magic
 %define patchlevel 1
 # This allows pkg_release to have configurable %%{?dist} tag
-%define specrelease 201%{?buildid}%{?dist}
+%define specrelease 200%{?buildid}%{?dist}
 # This defines the kabi tarball version
-%define kabiversion 7.1.5
+%define kabiversion 7.1.6
 
 # If this variable is set to 1, a bpf selftests build failure will cause a
 # fatal kernel package build error
@@ -1593,7 +1593,7 @@ AutoReqProv: no\
 %description %{?1:%{1}-}debuginfo\
 This package provides debug information for package %{name}%{?1:-%{1}}.\
 This is required to use SystemTap with %{name}%{?1:-%{1}}-%{KVERREL}.\
-%{expand:%%global _find_debuginfo_opts %{?_find_debuginfo_opts} --keep-section '.BTF' -p '.*\/usr\/src\/kernels/.*|XXX' -o ignored-debuginfo.list -p '/.*/%%{KVERREL_RE}%{?1:[+]%{1}}/.*|/.*%%{KVERREL_RE}%{?1:\+%{1}}(\.debug)?' -o debuginfo%{?1}.list}\
+%{expand:%%global _find_debuginfo_opts %{?_find_debuginfo_opts} --keep-section '.BTF' --keep-section '.rustc' -p '.*\/usr\/src\/kernels/.*|XXX' -o ignored-debuginfo.list -p '/.*/%%{KVERREL_RE}%{?1:[+]%{1}}/.*|/.*%%{KVERREL_RE}%{?1:\+%{1}}(\.debug)?' -o debuginfo%{?1}.list}\
 %{nil}
 
 #
@@ -2827,6 +2827,14 @@ BuildKernel() {
     if [ -f tools/objtool/fixdep ]; then
       cp -a tools/objtool/fixdep $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/tools/objtool/ || :
     fi
+    if ls rust/*.rmeta >/dev/null 2>&1; then
+      mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/rust
+      cp -a rust/*.rmeta $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/rust/ || :
+    fi
+    if ls rust/*.so >/dev/null 2>&1; then
+      mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/rust
+      cp -a rust/*.so $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/rust/ || :
+    fi
     if [ -d arch/$Arch/scripts ]; then
       cp -a arch/$Arch/scripts $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/arch/%{_arch} || :
     fi
@@ -4857,6 +4865,21 @@ fi\
 #
 #
 %changelog
+* Mon Aug 03 2026 Augusto Caringi <acaringi@redhat.com> [7.1.6-0]
+- New config for stable (Justin M. Forbes)
+- acpi: battery: Sanitise model_number by dropping unprintable characters (Kate Hsuan)
+- redhat: configs: Enable AMD ISP4 MIPI camera solution (Kate Hsuan)
+- media: platform: amd: add DRM_AMDGPU dependency (Arnd Bergmann)
+- media: platform: amd: isp4: drop stale list reinit before free (Bin Du)
+- media: platform: amd: isp4 debug fs logging and more descriptive errors (Bin Du)
+- media: platform: amd: isp4 video node and buffers handling added (Bin Du)
+- media: platform: amd: isp4 subdev and firmware loading handling added (Bin Du)
+- media: platform: amd: Add isp4 fw and hw interface (Bin Du)
+- media: platform: amd: low level support for isp4 firmware (Bin Du)
+- media: platform: amd: Introduce amd isp4 capture driver (Bin Du)
+- serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms (Jiangshan Yi)
+- Linux v7.1.6
+
 * Tue Jul 28 2026 Justin M. Forbes <jforbes@fedoraproject.org> [7.1.5-1]
 - ASoC: cs42l43: Correct report for forced microphone jack (Charles Keepax)
 - platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU hotplug (Guixiong Wei)

diff --git a/patch-7.1-redhat.patch b/patch-7.1-redhat.patch
index f2abc3a..1abcf6d 100644
--- a/patch-7.1-redhat.patch
+++ b/patch-7.1-redhat.patch
@@ -1,74 +1,112 @@
- MAINTAINERS                                        |   6 +
- Makefile                                           |  30 +
- arch/arm/Kconfig                                   |   4 +-
- arch/arm64/Kconfig                                 |   2 +-
- arch/arm64/kernel/setup.c                          |  27 +
- arch/s390/include/asm/ipl.h                        |   1 +
- arch/s390/kernel/ipl.c                             |   5 +
- arch/s390/kernel/setup.c                           |   4 +
- arch/x86/kernel/setup.c                            |  22 +-
- arch/x86/kvm/lapic.c                               |   2 +-
- arch/x86/kvm/mmu/mmu.c                             |  12 +-
- arch/x86/kvm/mmu/paging_tmpl.h                     |  10 +-
- arch/x86/kvm/vmx/nested.c                          |  11 +-
- crypto/akcipher.c                                  |   3 +-
- crypto/dh.c                                        |  25 +
- crypto/seqiv.c                                     |  15 +-
- crypto/sig.c                                       |   3 +-
- crypto/testmgr.c                                   |   6 +-
- drivers/acpi/apei/hest.c                           |   8 +
- drivers/acpi/irq.c                                 |  17 +-
- drivers/acpi/scan.c                                |   9 +
- drivers/ata/libahci.c                              |  18 +
- drivers/char/ipmi/ipmi_dmi.c                       |  15 +
- drivers/char/ipmi/ipmi_msghandler.c                |  16 +-
- drivers/char/random.c                              | 126 ++-
- drivers/firmware/efi/Makefile                      |   1 +
- drivers/firmware/efi/efi.c                         | 124 ++-
- drivers/firmware/efi/libstub/fdt.c                 |   5 +
- drivers/firmware/efi/libstub/secureboot.c          |  14 +-
- drivers/firmware/efi/secureboot.c                  |  38 +
- drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c          |  12 +
- drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h      |  11 +
- .../gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c   |  45 +
- drivers/gpu/drm/i915/display/intel_ddi.c           |  11 +-
- drivers/hwtracing/coresight/coresight-etm4x-core.c |  19 +
- drivers/iommu/iommu.c                              |  22 +
- drivers/media/i2c/Kconfig                          |  10 +
- drivers/media/i2c/Makefile                         |   1 +
- drivers/media/i2c/imx471.c                         | 957 +++++++++++++++++++++
- drivers/media/pci/intel/ipu-bridge.c               |  41 +
- drivers/pci/quirks.c                               |  24 +
- drivers/platform/x86/intel/int3472/discrete.c      |  18 +
- .../uncore-frequency/uncore-frequency-common.c     |   7 +-
- drivers/usb/core/hub.c                             |   7 +
- fs/xfs/libxfs/xfs_exchmaps.c                       |  10 +
- include/linux/crypto.h                             |   2 +
- include/linux/efi.h                                |  22 +-
- include/linux/lsm_hook_defs.h                      |   1 +
- include/linux/random.h                             |  10 +
- include/linux/security.h                           |   9 +
- kernel/module/signing.c                            |   9 +-
- kernel/sched/ext.c                                 |  75 +-
- net/packet/af_packet.c                             |   6 +-
- scripts/Makefile.lib                               |   3 +
- scripts/tags.sh                                    |   2 +
- security/integrity/platform_certs/load_uefi.c      |   6 +-
- security/lockdown/Kconfig                          |  13 +
- security/lockdown/lockdown.c                       |  11 +
- sound/soc/codecs/cs42l43-jack.c                    |   3 +-
- sound/soc/codecs/rt722-sdca.c                      |  77 +-
- sound/soc/codecs/rt722-sdca.h                      |   4 +
- tools/testing/selftests/bpf/DENYLIST.rhel          |  76 ++
- tools/testing/selftests/bpf/Makefile               |   2 +-
- tools/testing/selftests/bpf/prog_tests/ksyms_btf.c |  31 -
- 64 files changed, 1984 insertions(+), 152 deletions(-)
+ MAINTAINERS                                        |   29 +
+ Makefile                                           |   30 +
+ arch/arm/Kconfig                                   |    4 +-
+ arch/arm64/Kconfig                                 |    2 +-
+ arch/arm64/kernel/setup.c                          |   27 +
+ arch/s390/include/asm/ipl.h                        |    1 +
+ arch/s390/kernel/ipl.c                             |    5 +
+ arch/s390/kernel/setup.c                           |    4 +
+ arch/x86/kernel/setup.c                            |   22 +-
+ arch/x86/kvm/mmu/mmu.c                             |    1 +
+ crypto/akcipher.c                                  |    3 +-
+ crypto/dh.c                                        |   25 +
+ crypto/seqiv.c                                     |   15 +-
+ crypto/sig.c                                       |    3 +-
+ crypto/testmgr.c                                   |    6 +-
+ drivers/acpi/apei/hest.c                           |    8 +
+ drivers/acpi/battery.c                             |   14 +
+ drivers/acpi/irq.c                                 |   17 +-
+ drivers/acpi/scan.c                                |    9 +
+ drivers/ata/libahci.c                              |   18 +
+ drivers/char/ipmi/ipmi_dmi.c                       |   15 +
+ drivers/char/ipmi/ipmi_msghandler.c                |   16 +-
+ drivers/char/random.c                              |  126 ++-
+ drivers/firmware/efi/Makefile                      |    1 +
+ drivers/firmware/efi/efi.c                         |  124 ++-
+ drivers/firmware/efi/libstub/fdt.c                 |    5 +
+ drivers/firmware/efi/libstub/secureboot.c          |   14 +-
+ drivers/firmware/efi/secureboot.c                  |   38 +
+ drivers/hwtracing/coresight/coresight-etm4x-core.c |   19 +
+ drivers/iommu/iommu.c                              |   22 +
+ drivers/media/i2c/Kconfig                          |   10 +
+ drivers/media/i2c/Makefile                         |    1 +
+ drivers/media/i2c/imx471.c                         |  957 ++++++++++++++++++
+ drivers/media/pci/intel/ipu-bridge.c               |   41 +
+ drivers/media/platform/Kconfig                     |    1 +
+ drivers/media/platform/Makefile                    |    1 +
+ drivers/media/platform/amd/Kconfig                 |    3 +
+ drivers/media/platform/amd/Makefile                |    3 +
+ drivers/media/platform/amd/isp4/Kconfig            |   17 +
+ drivers/media/platform/amd/isp4/Makefile           |   10 +
+ drivers/media/platform/amd/isp4/isp4.c             |  240 +++++
+ drivers/media/platform/amd/isp4/isp4.h             |   20 +
+ drivers/media/platform/amd/isp4/isp4_debug.c       |  271 +++++
+ drivers/media/platform/amd/isp4/isp4_debug.h       |   41 +
+ drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h |  318 ++++++
+ drivers/media/platform/amd/isp4/isp4_hw_reg.h      |  124 +++
+ drivers/media/platform/amd/isp4/isp4_interface.c   |  832 ++++++++++++++++
+ drivers/media/platform/amd/isp4/isp4_interface.h   |  144 +++
+ drivers/media/platform/amd/isp4/isp4_subdev.c      | 1047 ++++++++++++++++++++
+ drivers/media/platform/amd/isp4/isp4_subdev.h      |  127 +++
+ drivers/media/platform/amd/isp4/isp4_video.c       |  797 +++++++++++++++
+ drivers/media/platform/amd/isp4/isp4_video.h       |   57 ++
+ drivers/pci/quirks.c                               |   24 +
+ drivers/platform/x86/intel/int3472/discrete.c      |   18 +
+ drivers/usb/core/hub.c                             |    7 +
+ fs/xfs/libxfs/xfs_exchmaps.c                       |   10 +
+ include/linux/crypto.h                             |    2 +
+ include/linux/efi.h                                |   22 +-
+ include/linux/lsm_hook_defs.h                      |    1 +
+ include/linux/random.h                             |   10 +
+ include/linux/security.h                           |    9 +
+ kernel/module/signing.c                            |    9 +-
+ scripts/Makefile.lib                               |    3 +
+ scripts/tags.sh                                    |    2 +
+ security/integrity/platform_certs/load_uefi.c      |    6 +-
+ security/lockdown/Kconfig                          |   13 +
+ security/lockdown/lockdown.c                       |   11 +
+ sound/soc/codecs/rt722-sdca.c                      |   77 +-
+ sound/soc/codecs/rt722-sdca.h                      |    4 +
+ tools/testing/selftests/bpf/DENYLIST.rhel          |   76 ++
+ tools/testing/selftests/bpf/Makefile               |    2 +-
+ tools/testing/selftests/bpf/prog_tests/ksyms_btf.c |   31 -
+ 72 files changed, 5902 insertions(+), 120 deletions(-)
 
 diff --git a/MAINTAINERS b/MAINTAINERS
-index f74da642290a..06c8bb2075e6 100644
+index f74da642290a..3a0e977fc9cf 100644
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -24964,6 +24964,12 @@ T:	git git://linuxtv.org/media.git
+@@ -1159,6 +1159,29 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git
+ F:	drivers/iommu/amd/
+ F:	include/linux/amd-iommu.h
+ 
++AMD ISP4 DRIVER
++M:	Bin Du <bin.du@amd.com>
++M:	Nirujogi Pratap <pratap.nirujogi@amd.com>
++L:	linux-media@vger.kernel.org
++S:	Maintained
++T:	git git://linuxtv.org/media.git
++F:	drivers/media/platform/amd/Kconfig
++F:	drivers/media/platform/amd/Makefile
++F:	drivers/media/platform/amd/isp4/Kconfig
++F:	drivers/media/platform/amd/isp4/Makefile
++F:	drivers/media/platform/amd/isp4/isp4.c
++F:	drivers/media/platform/amd/isp4/isp4.h
++F:	drivers/media/platform/amd/isp4/isp4_debug.c
++F:	drivers/media/platform/amd/isp4/isp4_debug.h
++F:	drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
++F:	drivers/media/platform/amd/isp4/isp4_hw_reg.h
++F:	drivers/media/platform/amd/isp4/isp4_interface.c
++F:	drivers/media/platform/amd/isp4/isp4_interface.h
++F:	drivers/media/platform/amd/isp4/isp4_subdev.c
++F:	drivers/media/platform/amd/isp4/isp4_subdev.h
++F:	drivers/media/platform/amd/isp4/isp4_video.c
++F:	drivers/media/platform/amd/isp4/isp4_video.h
++
+ AMD KFD
+ M:	Felix Kuehling <Felix.Kuehling@amd.com>
+ L:	amd-gfx@lists.freedesktop.org
+@@ -24964,6 +24987,12 @@ T:	git git://linuxtv.org/media.git
  F:	Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
  F:	drivers/media/i2c/imx415.c
  
@@ -82,7 +120,7 @@ index f74da642290a..06c8bb2075e6 100644
  M:	Maxim Levitsky <maximlevitsky@gmail.com>
  M:	Alex Dubov <oakad@yahoo.com>
 diff --git a/Makefile b/Makefile
-index 58f3b3e2dc1c..c57ee1ced308 100644
+index 75e5c9573863..734d4e2da969 100644
 --- a/Makefile
 +++ b/Makefile
 @@ -356,6 +356,17 @@ ifneq ($(filter install,$(MAKECMDGOALS)),)
@@ -103,7 +141,7 @@ index 58f3b3e2dc1c..c57ee1ced308 100644
  ifdef mixed-build
  # ===========================================================================
  # We're called with mixed targets (*config and build targets).
-@@ -1418,6 +1429,8 @@ uapi-asm-generic:
+@@ -1423,6 +1434,8 @@ uapi-asm-generic:
  # Generate some files
  # ---------------------------------------------------------------------------
  
@@ -112,7 +150,7 @@ index 58f3b3e2dc1c..c57ee1ced308 100644
  # KERNELRELEASE can change from a few different places, meaning version.h
  # needs to be updated, so this check is forced on all builds
  
-@@ -2083,6 +2096,23 @@ endif
+@@ -2088,6 +2101,23 @@ endif
  
  ifdef CONFIG_MODULES
  
@@ -311,21 +349,8 @@ index 46882ce79c3a..9c73814c31e0 100644
  
  	reserve_initrd();
  
-diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
-index d806abaeda3d..3a0eef95e905 100644
---- a/arch/x86/kvm/lapic.c
-+++ b/arch/x86/kvm/lapic.c
-@@ -2052,7 +2052,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)
- 	if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use)
- 		ktimer->expired_tscdeadline = ktimer->tscdeadline;
- 
--	if (!from_timer_fn && apic->apicv_active) {
-+	if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {
- 		WARN_ON(kvm_get_running_vcpu() != vcpu);
- 		kvm_apic_inject_pending_timer_irqs(apic);
- 		return;
 diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
-index 0c2e35e3ffbd..dc4fda3b9769 100644
+index 34a312705253..dc4fda3b9769 100644
 --- a/arch/x86/kvm/mmu/mmu.c
 +++ b/arch/x86/kvm/mmu/mmu.c
 @@ -2635,6 +2635,7 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp,
@@ -336,94 +361,6 @@ index 0c2e35e3ffbd..dc4fda3b9769 100644
  			    !atomic_long_read(&child->parent_ptes.val))
  				return kvm_mmu_prepare_zap_page(kvm, child,
  								invalid_list);
-@@ -4837,16 +4838,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
- 	if (r != RET_PF_CONTINUE)
- 		return r;
- 
--	r = RET_PF_RETRY;
- 	write_lock(&vcpu->kvm->mmu_lock);
- 
--	if (is_page_fault_stale(vcpu, fault))
--		goto out_unlock;
--
- 	r = make_mmu_pages_available(vcpu);
- 	if (r)
- 		goto out_unlock;
- 
-+	if (is_page_fault_stale(vcpu, fault)) {
-+		r = RET_PF_RETRY;
-+		goto out_unlock;
-+	}
-+
- 	r = direct_map(vcpu, fault);
- 
- out_unlock:
-@@ -7501,7 +7503,9 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen)
- static void mmu_destroy_caches(void)
- {
- 	kmem_cache_destroy(pte_list_desc_cache);
-+	pte_list_desc_cache = NULL;
- 	kmem_cache_destroy(mmu_page_header_cache);
-+	mmu_page_header_cache = NULL;
- }
- 
- static void kvm_wake_nx_recovery_thread(struct kvm *kvm)
-diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h
-index 901cd2bd40b8..6465de820e70 100644
---- a/arch/x86/kvm/mmu/paging_tmpl.h
-+++ b/arch/x86/kvm/mmu/paging_tmpl.h
-@@ -827,15 +827,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
- 	}
- #endif
- 
--	r = RET_PF_RETRY;
- 	write_lock(&vcpu->kvm->mmu_lock);
- 
--	if (is_page_fault_stale(vcpu, fault))
--		goto out_unlock;
--
- 	r = make_mmu_pages_available(vcpu);
- 	if (r)
- 		goto out_unlock;
-+
-+	if (is_page_fault_stale(vcpu, fault)) {
-+		r = RET_PF_RETRY;
-+		goto out_unlock;
-+	}
-+
- 	r = FNAME(fetch)(vcpu, fault, &walker);
- 
- out_unlock:
-diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
-index 52ab52b0e1cc..170b0c4d865f 100644
---- a/arch/x86/kvm/vmx/nested.c
-+++ b/arch/x86/kvm/vmx/nested.c
-@@ -336,6 +336,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu)
- static void free_nested(struct kvm_vcpu *vcpu)
- {
- 	struct vcpu_vmx *vmx = to_vmx(vcpu);
-+	struct vmcs *shadow_vmcs;
- 
- 	if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
- 		vmx_switch_vmcs(vcpu, &vmx->vmcs01);
-@@ -353,9 +354,15 @@ static void free_nested(struct kvm_vcpu *vcpu)
- 	vmx->nested.current_vmptr = INVALID_GPA;
- 	if (enable_shadow_vmcs) {
- 		vmx_disable_shadow_vmcs(vmx);
--		vmcs_clear(vmx->vmcs01.shadow_vmcs);
--		free_vmcs(vmx->vmcs01.shadow_vmcs);
-+
-+		/*
-+		 * Keep the pointer visible until after VMCLEAR, so migration
-+		 * can clear an active shadow VMCS on the old CPU.
-+		 */
-+		shadow_vmcs = vmx->vmcs01.shadow_vmcs;
-+		vmcs_clear(shadow_vmcs);
- 		vmx->vmcs01.shadow_vmcs = NULL;
-+		free_vmcs(shadow_vmcs);
- 	}
- 	kfree(vmx->nested.cached_vmcs12);
- 	vmx->nested.cached_vmcs12 = NULL;
 diff --git a/crypto/akcipher.c b/crypto/akcipher.c
 index dfe87b3ce183..cd1302ca3b36 100644
 --- a/crypto/akcipher.c
@@ -581,6 +518,45 @@ index 20d757687e3d..90a13f20f052 100644
  	hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
  	for (i = 0; i < hest_tab->error_source_count; i++) {
  		len = hest_esrc_len(hest_hdr);
+diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
+index b82dd67d98c9..85ac301e1834 100644
+--- a/drivers/acpi/battery.c
++++ b/drivers/acpi/battery.c
+@@ -10,6 +10,7 @@
+ 
+ #define pr_fmt(fmt) "ACPI: battery: " fmt
+ 
++#include <linux/ctype.h>
+ #include <linux/delay.h>
+ #include <linux/dmi.h>
+ #include <linux/jiffies.h>
+@@ -483,6 +484,15 @@ static int acpi_battery_get_status(struct acpi_battery *battery)
+ 	return 0;
+ }
+ 
++static void acpi_battery_clean_unprintable_chars(char *str, size_t length)
++{
++	for (unsigned int i = 0; i < length; i++) {
++		if (!isascii(str[i]) || !isprint(str[i])) {
++			str[i] = '\0';
++			break;
++		}
++	}
++}
+ 
+ static int extract_battery_info(const int use_bix,
+ 			 struct acpi_battery *battery,
+@@ -524,6 +534,10 @@ static int extract_battery_info(const int use_bix,
+ 	    battery->capacity_now > battery->full_charge_capacity)
+ 		battery->capacity_now = battery->full_charge_capacity;
+ 
++	if (!result)
++		acpi_battery_clean_unprintable_chars(battery->model_number,
++					ARRAY_SIZE(battery->model_number));
++
+ 	return result;
+ }
+ 
 diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
 index d1595156c86a..9485212f1a6d 100644
 --- a/drivers/acpi/irq.c
@@ -1198,164 +1174,6 @@ index 000000000000..de0a3714a5d4
 +		}
 +	}
 +}
-diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
-index 8faf7de7aaa9..f65bb1dc44ac 100644
---- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
-+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
-@@ -1365,6 +1365,14 @@ static void smu_feature_cap_init(struct smu_context *smu)
- 	bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT);
- }
- 
-+static int smu_set_power_dep(struct smu_context *smu, bool enable)
-+{
-+	if (!smu->ppt_funcs->set_power_dep)
-+		return 0;
-+
-+	return smu->ppt_funcs->set_power_dep(smu, enable);
-+}
-+
- static int smu_sw_init(struct amdgpu_ip_block *ip_block)
- {
- 	struct amdgpu_device *adev = ip_block->adev;
-@@ -1426,6 +1434,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block)
- 	if (!smu->ppt_funcs->get_fan_control_mode)
- 		smu->adev->pm.no_fan = true;
- 
-+	smu_set_power_dep(smu, true);
-+
- 	return 0;
- }
- 
-@@ -1448,6 +1458,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block)
- 
- 	smu_fini_microcode(smu);
- 
-+	smu_set_power_dep(smu, false);
-+
- 	return 0;
- }
- 
-diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
-index d76e0b005308..e14bcc3f937c 100644
---- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
-+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
-@@ -749,6 +749,9 @@ struct smu_context {
- 	bool pm_enabled;
- 	bool is_apu;
- 
-+	/* Power dependency link from an integrated xHCI controller to the GPU */
-+	struct device_link		*usb_power_link;
-+
- 	uint32_t smc_driver_if_version;
- 	uint32_t smc_fw_if_version;
- 	uint32_t smc_fw_version;
-@@ -1654,6 +1657,14 @@ struct pptable_funcs {
- 	 * Return: ras_smu_drv *
- 	 */
- 	int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv);
-+
-+	/**
-+	 * @set_power_dep: Create or destroy a power dependency link
-+	 * from an integrated xHCI controller to the GPU so that the GPU is
-+	 * resumed before the USB controller during PM resume. @enable is true
-+	 * to create the link and false to tear it down.
-+	 */
-+	int (*set_power_dep)(struct smu_context *smu, bool enable);
- };
- 
- typedef enum {
-diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
-index 75719c47a41e..3d73f2050bbe 100644
---- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
-+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
-@@ -1701,6 +1701,50 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu)
- 	return 0;
- }
- 
-+/*
-+ * Link any xHCI controller sharing the GPU's PCIe root port as a consumer
-+ * of the GPU so the GPU resumes first, avoiding an xHCI resume race.
-+ */
-+static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool enable)
-+{
-+	struct amdgpu_device *adev = smu->adev;
-+	struct pci_dev *gpu_pdev = adev->pdev;
-+	struct pci_dev *root_port, *usb_pdev = NULL;
-+	struct device_link *link;
-+
-+	if (!enable) {
-+		if (smu->usb_power_link) {
-+			device_link_del(smu->usb_power_link);
-+			smu->usb_power_link = NULL;
-+		}
-+		return 0;
-+	}
-+
-+	root_port = pcie_find_root_port(gpu_pdev);
-+	while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, usb_pdev))) {
-+		struct pci_dev *usb_root;
-+
-+		usb_root = pcie_find_root_port(usb_pdev);
-+		if (usb_root != root_port)
-+			continue;
-+
-+		/* Create device link: USB (consumer) depends on GPU (supplier) */
-+		link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev,
-+				       DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
-+		if (link) {
-+			smu->usb_power_link = link;
-+			drm_info(adev_to_drm(adev), "USB controller %s D0 power state depends on %s\n",
-+				 pci_name(usb_pdev), pci_name(gpu_pdev));
-+			/* Only create one link for the first USB controller found */
-+			break;
-+		}
-+	}
-+
-+	pci_dev_put(usb_pdev);
-+
-+	return 0;
-+}
-+
- static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
- 	.check_fw_status = smu_v14_0_check_fw_status,
- 	.check_fw_version = smu_cmn_check_fw_version,
-@@ -1734,6 +1778,7 @@ static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
- 	.dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable,
- 	.get_dpm_clock_table = smu_v14_0_common_get_dpm_table,
- 	.set_mall_enable = smu_v14_0_common_set_mall_enable,
-+	.set_power_dep = smu_v14_0_0_set_power_dep,
- };
- 
- static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu)
-diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
-index ebefa889bc8c..0987eee38bd1 100644
---- a/drivers/gpu/drm/i915/display/intel_ddi.c
-+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
-@@ -2652,9 +2652,6 @@ static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state,
- 	/* 3. Select Thunderbolt */
- 	mtl_port_buf_ctl_io_selection(encoder);
- 
--	/* 4. Enable Panel Power if PPS is required */
--	intel_pps_on(intel_dp);
--
- 	/* 5. Enable the port PLL */
- 	intel_ddi_enable_clock(encoder, crtc_state);
- 
-@@ -3710,6 +3707,14 @@ intel_ddi_pre_pll_enable(struct intel_atomic_state *state,
- 	else if (display->platform.geminilake || display->platform.broxton)
- 		bxt_dpio_phy_set_lane_optim_mask(encoder,
- 						 crtc_state->lane_lat_optim_mask);
-+
-+	/*
-+	 * There is no direct connection between the PLL and PPS, however
-+	 * enabling PPS before PLL is required to avoid PLL/DDI BUF timeouts
-+	 * during system resume. Do that matching the Bspec order as well.
-+	 */
-+	if (DISPLAY_VER(display) >= 14)
-+		intel_pps_on(&dig_port->dp);
- }
- 
- static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder)
 diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
 index a251375db24b..2b5337eaeb6d 100644
 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -2502,171 +2320,4316 @@ index fc6608e33de4..8cfa3282bf08 100644
  	{} /* Terminating entry */
  };
  
-diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
-index dd0025d3914e..e57d10dc843c 100644
---- a/drivers/pci/quirks.c
-+++ b/drivers/pci/quirks.c
-@@ -4491,6 +4491,30 @@ static void quirk_aspeed_pci_bridge_no_alias(struct pci_dev *pdev)
- }
- DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASPEED, 0x1150, quirk_aspeed_pci_bridge_no_alias);
- 
+diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
+index 3f0b7bb68cc9..0b33e927bd59 100644
+--- a/drivers/media/platform/Kconfig
++++ b/drivers/media/platform/Kconfig
+@@ -63,6 +63,7 @@ config VIDEO_MUX
+ 
+ # Platform drivers - Please keep it alphabetically sorted
+ source "drivers/media/platform/allegro-dvt/Kconfig"
++source "drivers/media/platform/amd/Kconfig"
+ source "drivers/media/platform/amlogic/Kconfig"
+ source "drivers/media/platform/amphion/Kconfig"
+ source "drivers/media/platform/arm/Kconfig"
+diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
+index 6d5f79ddfcc3..16c185752474 100644
+--- a/drivers/media/platform/Makefile
++++ b/drivers/media/platform/Makefile
+@@ -6,6 +6,7 @@
+ # Place here, alphabetically sorted by directory
+ # (e. g. LC_ALL=C sort Makefile)
+ obj-y += allegro-dvt/
++obj-y += amd/
+ obj-y += amlogic/
+ obj-y += amphion/
+ obj-y += arm/
+diff --git a/drivers/media/platform/amd/Kconfig b/drivers/media/platform/amd/Kconfig
+new file mode 100644
+index 000000000000..25af49f246b2
+--- /dev/null
++++ b/drivers/media/platform/amd/Kconfig
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: GPL-2.0+
++
++source "drivers/media/platform/amd/isp4/Kconfig"
+diff --git a/drivers/media/platform/amd/Makefile b/drivers/media/platform/amd/Makefile
+new file mode 100644
+index 000000000000..8bfc1955f22e
+--- /dev/null
++++ b/drivers/media/platform/amd/Makefile
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: GPL-2.0+
++
++obj-y += isp4/
+diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig
+new file mode 100644
+index 000000000000..9d1927af1cb8
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/Kconfig
+@@ -0,0 +1,17 @@
++# SPDX-License-Identifier: GPL-2.0+
++
++config VIDEO_AMD_ISP4_CAPTURE
++	tristate "AMD ISP4 and camera driver"
++	depends on DRM_AMDGPU && DRM_AMD_ISP
++	depends on HAS_DMA
++	depends on VIDEO_DEV
++	select VIDEOBUF2_CORE
++	select VIDEOBUF2_MEMOPS
++	select VIDEOBUF2_V4L2
++	select VIDEOBUF2_VMALLOC
++	select VIDEO_V4L2_SUBDEV_API
++	help
++	  This is support for AMD ISP4 and camera subsystem driver.
++	  Say Y here to enable the ISP4 and camera device for video capture.
++	  To compile this driver as a module, choose M here. The module will
++	  be called amd_isp4_capture.
+diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile
+new file mode 100644
+index 000000000000..3849062e17f3
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/Makefile
+@@ -0,0 +1,10 @@
++# SPDX-License-Identifier: GPL-2.0+
++#
++# Copyright (C) 2025 Advanced Micro Devices, Inc.
++
++obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o
++amd_isp4_capture-objs := isp4.o \
++                         isp4_debug.o \
++                         isp4_interface.o \
++                         isp4_subdev.o \
++                         isp4_video.o
+diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c
+new file mode 100644
+index 000000000000..bf6b8e26c2c0
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4.c
+@@ -0,0 +1,240 @@
++// SPDX-License-Identifier: GPL-2.0+
 +/*
-+ * PCI BAR 5 is not setup correctly for the on-board AHCI controller
-+ * on Broadcom's Vulcan processor. Added a quirk to fix BAR 5 by
-+ * using BAR 4's resources which are populated correctly and NOT
-+ * actually used by the AHCI controller.
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
 + */
-+static void quirk_fix_vulcan_ahci_bars(struct pci_dev *dev)
-+{
-+	struct resource *r =  &dev->resource[4];
 +
-+	if (!(r->flags & IORESOURCE_MEM) || (r->start == 0))
-+		return;
++#include <linux/irq.h>
++#include <linux/pm_runtime.h>
++#include <linux/vmalloc.h>
++#include <media/v4l2-ioctl.h>
++
++#include "isp4.h"
++#include "isp4_debug.h"
++#include "isp4_hw_reg.h"
++
++#define ISP4_DRV_NAME "amd_isp_capture"
++#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \
++	(ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK  | \
++	 ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK)
++
++static const struct {
++	const char *name;
++	u32 status_mask;
++	u32 en_mask;
++	u32 ack_mask;
++	u32 rb_int_num;
++} isp4_irq[ISP4SD_MAX_FW_RESP_STREAM_NUM] = {
++	/* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */
++	{
++		.name = "isp_irq_global",
++		.status_mask =
++		ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK,
++		.en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK,
++		.ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK,
++		.rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */
++	},
++	{
++		.name = "isp_irq_stream1",
++		.status_mask =
++		ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK,
++		.en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK,
++		.ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK,
++		.rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */
++	},
++};
 +
-+	/* Set BAR5 resource to BAR4 */
-+	dev->resource[5] = *r;
++void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable)
++{
++	u32 intr_en;
++
++	/* Synchronize ISP_SYS_INT0_EN writes with the IRQ handler's writes */
++	spin_lock_irq(&isp_subdev->irq_lock);
++	intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN);
++	if (enable)
++		intr_en |= isp4_irq[index].en_mask;
++	else
++		intr_en &= ~isp4_irq[index].en_mask;
++
++	isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en);
++	spin_unlock_irq(&isp_subdev->irq_lock);
++}
 +
-+	/* Update BAR5 in pci config space */
-+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, r->start);
++static void isp4_wake_up_resp_thread(struct isp4_subdev *isp_subdev, u32 index)
++{
++	struct isp4sd_thread_handler *thread_ctx =
++			&isp_subdev->fw_resp_thread[index];
 +
-+	/* Clear BAR4's resource */
-+	memset(r, 0, sizeof(*r));
++	thread_ctx->resp_ready = true;
++	wake_up_interruptible(&thread_ctx->waitq);
 +}
-+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9027, quirk_fix_vulcan_ahci_bars);
 +
- /*
-  * Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
-  * class code.  Fix it.
-diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
-index 115bb37577a1..adff564bf3fd 100644
---- a/drivers/platform/x86/intel/int3472/discrete.c
-+++ b/drivers/platform/x86/intel/int3472/discrete.c
-@@ -164,6 +164,24 @@ static const struct int3472_gpio_map int3472_gpio_map[] = {
- 		.con_id = "dvdd",
- 		.enable_time_us = 45 * USEC_PER_MSEC,
- 	},
-+	{	/* imx471 expects "vana" as con_id for power enable */
-+		.hid = "SONY471A",
-+		.type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
-+		.type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
-+		.con_id = "vana",
-+		.enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
-+	},
-+	{
-+		/*
-+		 * imx471 (on Lenovo ThinkPads X1 G14) expects "vana" as con_id
-+		 * for power enable
-+		 */
-+		.hid = "TBE20A0",
-+		.type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
-+		.type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
-+		.con_id = "vana",
-+		.enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
-+	},
- };
- 
- static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type,
-diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c
-index 7070c94324e0..f8137ee92e47 100644
---- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c
-+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c
-@@ -275,15 +275,20 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu)
- 			  data->package_id, data->die_id);
- 	}
- 
++static irqreturn_t isp4_irq_handler(int irq, void *arg)
++{
++	struct isp4_subdev *isp_subdev = arg;
++	u32 intr_ack = 0, intr_en = 0, intr_status;
++	int seen = 0;
++
++	/* Get the ISP_SYS interrupt status */
++	intr_status = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_STATUS);
++	intr_status &= ISP4_FW_RESP_RB_IRQ_STATUS_MASK;
++
++	/* Find which ISP_SYS interrupts fired */
++	for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) {
++		if (intr_status & isp4_irq[i].status_mask) {
++			intr_ack |= isp4_irq[i].ack_mask;
++			intr_en |= isp4_irq[i].en_mask;
++			seen |= BIT(i);
++		}
++	}
++
 +	/*
-+	 * Set the control CPU before any read path so entry recreation after CPU
-+	 * hotplug can populate read-only attributes from the new online CPU.
++	 * Disable the ISP_SYS interrupts that fired. Must be done before waking
++	 * the response threads, since they re-enable interrupts when finished.
++	 * The lock synchronizes RMW of INT0_EN with isp4_enable_interrupt().
 +	 */
-+	data->control_cpu = cpu;
- 	uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ);
- 	uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ);
- 
- 	ret = create_attr_group(data, data->name);
- 	if (ret) {
-+		data->control_cpu = -1;
- 		if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
- 			ida_free(&intel_uncore_ida, data->instance_id);
- 	} else {
--		data->control_cpu = cpu;
- 		data->valid = true;
- 	}
- 
-diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
-index 24960ba9caa9..32597cdb72ef 100644
---- a/drivers/usb/core/hub.c
-+++ b/drivers/usb/core/hub.c
-@@ -5894,6 +5894,13 @@ static void hub_event(struct work_struct *work)
- 			(u16) hub->change_bits[0],
- 			(u16) hub->event_bits[0]);
- 
-+	/* Don't disconnect USB-SATA on TrimSlice */
-+	if (strcmp(dev_name(hdev->bus->controller), "tegra-ehci.0") == 0) {
-+		if ((hdev->state == 7) && (hub->change_bits[0] == 0) &&
-+				(hub->event_bits[0] == 0x2))
-+			hub->event_bits[0] = 0;
-+	}
++	spin_lock(&isp_subdev->irq_lock);
++	intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN) & ~intr_en;
++	isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en);
++	spin_unlock(&isp_subdev->irq_lock);
 +
- 	/* Lock the device, then check to see if we were
- 	 * disconnected while waiting for the lock to succeed. */
- 	usb_lock_device(hdev);
-diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c
-index 541e33f33167..7fb28c8afef8 100644
---- a/fs/xfs/libxfs/xfs_exchmaps.c
-+++ b/fs/xfs/libxfs/xfs_exchmaps.c
-@@ -959,6 +959,16 @@ xmi_can_exchange_reflink_flags(
- {
- 	struct xfs_mount		*mp = req->ip1->i_mount;
- 
 +	/*
-+	 * The INO1_WRITTEN optimization can skip exchanging hole and
-+	 * unwritten mappings, which means we cannot guarantee that all
-+	 * shared extents actually moved to the other file.  Clearing the
-+	 * reflink flag of an inode that still holds shared extents breaks
-+	 * the CoW write path, so refuse to exchange the flags in that case.
++	 * Clear the ISP_SYS interrupts. This must be done after the interrupts
++	 * are disabled, so that ISP FW won't flag any new interrupts on these
++	 * streams, and thus we don't need to clear interrupts again before
++	 * re-enabling them in the response thread.
 +	 */
-+	if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN)
-+		return false;
++	isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack);
 +
- 	if (hweight32(reflink_state) != 1)
- 		return false;
- 	if (req->startoff1 != 0 || req->startoff2 != 0)
-diff --git a/include/linux/crypto.h b/include/linux/crypto.h
-index a2137e19be7d..79ccb6fabc10 100644
---- a/include/linux/crypto.h
-+++ b/include/linux/crypto.h
-@@ -152,6 +152,8 @@
- #define CRYPTO_TFM_REQ_MAY_BACKLOG	0x00000400
- #define CRYPTO_TFM_REQ_ON_STACK		0x00000800
- 
-+#define CRYPTO_TFM_FIPS_COMPLIANCE	0x80000000
++	/*
++	 * The operation `(seen >> i) << i` is logically equivalent to
++	 * `seen &= ~BIT(i)`, with fewer instructions after compilation.
++	 */
++	for (int i; (i = ffs(seen)); seen = (seen >> i) << i)
++		isp4_wake_up_resp_thread(isp_subdev, i - 1);
 +
- /*
-  * Miscellaneous stuff.
-  */
-diff --git a/include/linux/efi.h b/include/linux/efi.h
-index ccbc35479684..8eb9a1276ba7 100644
---- a/include/linux/efi.h
-+++ b/include/linux/efi.h
-@@ -45,6 +45,8 @@ struct screen_info;
- #define EFI_ABORTED		(21 | (1UL << (BITS_PER_LONG-1)))
- #define EFI_SECURITY_VIOLATION	(26 | (1UL << (BITS_PER_LONG-1)))
- 
-+#define EFI_IS_ERROR(x)		((x) & (1UL << (BITS_PER_LONG-1)))
++	return IRQ_HANDLED;
++}
 +
- typedef unsigned long efi_status_t;
- typedef u8 efi_bool_t;
- typedef u16 efi_char16_t;		/* UNICODE character */
-@@ -847,6 +849,14 @@ extern int __init parse_efi_signature_list(
- #define EFI_MEM_ATTR		9	/* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */
- #define EFI_MEM_NO_SOFT_RESERVE	10	/* Is the kernel configured to ignore soft reservations? */
- #define EFI_PRESERVE_BS_REGIONS	11	/* Are EFI boot-services memory segments available? */
-+#define EFI_SECURE_BOOT		12	/* Are we in Secure Boot mode? */
++static int isp4_capture_probe(struct platform_device *pdev)
++{
++	int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM];
++	struct device *dev = &pdev->dev;
++	struct isp4_subdev *isp_subdev;
++	struct isp4_device *isp_dev;
++	int ret;
 +
-+enum efi_secureboot_mode {
-+	efi_secureboot_mode_unset,
-+	efi_secureboot_mode_unknown,
-+	efi_secureboot_mode_disabled,
++	isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL);
++	if (!isp_dev)
++		return -ENOMEM;
++
++	dev->init_name = ISP4_DRV_NAME;
++
++	isp_subdev = &isp_dev->isp_subdev;
++	isp_subdev->mmio = devm_platform_ioremap_resource(pdev, 0);
++	if (IS_ERR(isp_subdev->mmio))
++		return dev_err_probe(dev, PTR_ERR(isp_subdev->mmio),
++				     "isp ioremap fail\n");
++
++	for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) {
++		irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num);
++		if (irq[i] < 0)
++			return dev_err_probe(dev, irq[i],
++					     "fail to get irq %d\n",
++					     isp4_irq[i].rb_int_num);
++
++		ret = devm_request_irq(dev, irq[i], isp4_irq_handler,
++				       IRQF_NO_AUTOEN, isp4_irq[i].name,
++				       isp_subdev);
++		if (ret)
++			return dev_err_probe(dev, ret, "fail to req irq %d\n",
++					     irq[i]);
++	}
++
++	isp_dev->v4l2_dev.mdev = &isp_dev->mdev;
++
++	strscpy(isp_dev->mdev.model, "amd_isp41_mdev",
++		sizeof(isp_dev->mdev.model));
++	isp_dev->mdev.dev = dev;
++	media_device_init(&isp_dev->mdev);
++
++	snprintf(isp_dev->v4l2_dev.name, sizeof(isp_dev->v4l2_dev.name),
++		 "AMD-V4L2-ROOT");
++	ret = v4l2_device_register(dev, &isp_dev->v4l2_dev);
++	if (ret) {
++		dev_err_probe(dev, ret, "fail register v4l2 device\n");
++		goto err_clean_media;
++	}
++
++	pm_runtime_set_suspended(dev);
++	pm_runtime_enable(dev);
++	spin_lock_init(&isp_subdev->irq_lock);
++	ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq);
++	if (ret) {
++		dev_err_probe(dev, ret, "fail init isp4 sub dev\n");
++		goto err_pm_disable;
++	}
++
++	ret = media_create_pad_link(&isp_dev->isp_subdev.sdev.entity,
++				    0,
++				    &isp_dev->isp_subdev.isp_vdev.vdev.entity,
++				    0,
++				    MEDIA_LNK_FL_ENABLED |
++				    MEDIA_LNK_FL_IMMUTABLE);
++	if (ret) {
++		dev_err_probe(dev, ret, "fail to create pad link\n");
++		goto err_isp4_deinit;
++	}
++
++	ret = media_device_register(&isp_dev->mdev);
++	if (ret) {
++		dev_err_probe(dev, ret, "fail to register media device\n");
++		goto err_isp4_deinit;
++	}
++
++	platform_set_drvdata(pdev, isp_dev);
++	isp_debugfs_create(isp_dev);
++
++	return 0;
++
++err_isp4_deinit:
++	isp4sd_deinit(&isp_dev->isp_subdev);
++err_pm_disable:
++	pm_runtime_disable(dev);
++	v4l2_device_unregister(&isp_dev->v4l2_dev);
++err_clean_media:
++	media_device_cleanup(&isp_dev->mdev);
++
++	return ret;
++}
++
++static void isp4_capture_remove(struct platform_device *pdev)
++{
++	struct isp4_device *isp_dev = platform_get_drvdata(pdev);
++	struct device *dev = &pdev->dev;
++
++	isp_debugfs_remove(isp_dev);
++
++	media_device_unregister(&isp_dev->mdev);
++	isp4sd_deinit(&isp_dev->isp_subdev);
++	pm_runtime_disable(dev);
++	v4l2_device_unregister(&isp_dev->v4l2_dev);
++	media_device_cleanup(&isp_dev->mdev);
++}
++
++static struct platform_driver isp4_capture_drv = {
++	.probe = isp4_capture_probe,
++	.remove = isp4_capture_remove,
++	.driver = {
++		.name = ISP4_DRV_NAME,
++	}
++};
++
++module_platform_driver(isp4_capture_drv);
++
++MODULE_ALIAS("platform:" ISP4_DRV_NAME);
++MODULE_IMPORT_NS("DMA_BUF");
++
++MODULE_DESCRIPTION("AMD ISP4 Driver");
++MODULE_AUTHOR("Bin Du <bin.du@amd.com>");
++MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h
+new file mode 100644
+index 000000000000..2db6683d6d8b
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4.h
+@@ -0,0 +1,20 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#ifndef _ISP4_H_
++#define _ISP4_H_
++
++#include <drm/amd/isp.h>
++#include "isp4_subdev.h"
++
++struct isp4_device {
++	struct v4l2_device v4l2_dev;
++	struct isp4_subdev isp_subdev;
++	struct media_device mdev;
++};
++
++void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable);
++
++#endif /* _ISP4_H_ */
+diff --git a/drivers/media/platform/amd/isp4/isp4_debug.c b/drivers/media/platform/amd/isp4/isp4_debug.c
+new file mode 100644
+index 000000000000..2fc00fc9a194
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_debug.c
+@@ -0,0 +1,271 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#include "isp4.h"
++#include "isp4_debug.h"
++#include "isp4_hw_reg.h"
++#include "isp4_interface.h"
++
++#define ISP4DBG_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024)
++#define ISP4DBG_MACRO_2_STR(X) #X
++#define ISP4DBG_ONE_TIME_LOG_LEN 510
++
++#ifdef CONFIG_DEBUG_FS
++
++void isp_debugfs_create(struct isp4_device *isp_dev)
++{
++	isp_dev->isp_subdev.debugfs_dir = debugfs_create_dir("amd_isp4", NULL);
++	debugfs_create_bool("fw_log_enable", 0644,
++			    isp_dev->isp_subdev.debugfs_dir,
++			    &isp_dev->isp_subdev.enable_fw_log);
++	isp_dev->isp_subdev.fw_log_output =
++		devm_kzalloc(isp_dev->isp_subdev.dev,
++			     ISP4DBG_FW_LOG_RINGBUF_SIZE + 32,
++			     GFP_KERNEL);
++}
++
++void isp_debugfs_remove(struct isp4_device *isp_dev)
++{
++	debugfs_remove_recursive(isp_dev->isp_subdev.debugfs_dir);
++	isp_dev->isp_subdev.debugfs_dir = NULL;
++}
++
++static u32 isp_fw_fill_rb_log(struct isp4_subdev *isp, void *sys, u32 rb_size)
++{
++	struct isp4_interface *ispif = &isp->ispif;
++	char *buf = isp->fw_log_output;
++	struct device *dev = isp->dev;
++	u32 rd_ptr, wr_ptr;
++	u32 total_cnt = 0;
++	u32 offset = 0;
++	u32 cnt;
++
++	if (!sys || !rb_size)
++		return 0;
++
++	guard(mutex)(&ispif->isp4if_mutex);
++
++	rd_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_RPTR0);
++	wr_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_WPTR0);
++
++	do {
++		if (wr_ptr > rd_ptr)
++			cnt = wr_ptr - rd_ptr;
++		else if (wr_ptr < rd_ptr)
++			cnt = rb_size - rd_ptr;
++		else
++			goto quit;
++
++		if (cnt > rb_size) {
++			dev_err(dev, "fail bad fw log size %u\n", cnt);
++			goto quit;
++		}
++
++		memcpy(buf + offset, sys + rd_ptr, cnt);
++
++		offset += cnt;
++		total_cnt += cnt;
++		rd_ptr = (rd_ptr + cnt) % rb_size;
++	} while (rd_ptr < wr_ptr);
++
++	isp4hw_wreg(isp->mmio, ISP_LOG_RB_RPTR0, rd_ptr);
++
++quit:
++	return total_cnt;
++}
++
++void isp_fw_log_print(struct isp4_subdev *isp)
++{
++	struct isp4_interface *ispif = &isp->ispif;
++	char *fw_log_buf = isp->fw_log_output;
++	u32 cnt;
++
++	if (!isp->enable_fw_log || !fw_log_buf)
++		return;
++
++	cnt = isp_fw_fill_rb_log(isp, ispif->fw_log_buf->sys_addr,
++				 ispif->fw_log_buf->mem_size);
++
++	if (cnt) {
++		char temp_ch;
++		char *str;
++		char *end;
++		/* line end */
++		char *le;
++
++		str = (char *)fw_log_buf;
++		end = ((char *)fw_log_buf + cnt);
++		fw_log_buf[cnt] = 0;
++
++		while (str < end) {
++			le = strchr(str, 0x0A);
++			if ((le && str + ISP4DBG_ONE_TIME_LOG_LEN >= le) ||
++			    (!le && str + ISP4DBG_ONE_TIME_LOG_LEN >= end)) {
++				if (le)
++					*le = 0;
++
++				if (*str != '\0')
++					dev_dbg(isp->dev, "%s", str);
++
++				if (le) {
++					*le = 0x0A;
++					str = le + 1;
++				} else {
++					break;
++				}
++			} else {
++				u32 tmp_len = ISP4DBG_ONE_TIME_LOG_LEN;
++
++				temp_ch = str[tmp_len];
++				str[tmp_len] = 0;
++				dev_dbg(isp->dev, "%s", str);
++				str[tmp_len] = temp_ch;
++				str = &str[tmp_len];
++			}
++		}
++	}
++}
++#endif
++
++char *isp4dbg_get_buf_src_str(u32 src)
++{
++	switch (src) {
++	case ISP4FW_BUFFER_SOURCE_STREAM:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_SOURCE_STREAM);
++	default:
++		return "Unknown buf source";
++	}
++}
++
++char *isp4dbg_get_buf_done_str(u32 status)
++{
++	switch (status) {
++	case ISP4FW_BUFFER_STATUS_INVALID:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_INVALID);
++	case ISP4FW_BUFFER_STATUS_SKIPPED:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_SKIPPED);
++	case ISP4FW_BUFFER_STATUS_EXIST:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_EXIST);
++	case ISP4FW_BUFFER_STATUS_DONE:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DONE);
++	case ISP4FW_BUFFER_STATUS_LACK:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_LACK);
++	case ISP4FW_BUFFER_STATUS_DIRTY:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DIRTY);
++	case ISP4FW_BUFFER_STATUS_MAX:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_MAX);
++	default:
++		return "Unknown Buf Done Status";
++	}
++}
++
++char *isp4dbg_get_img_fmt_str(int fmt /* enum isp4fw_image_format * */)
++{
++	switch (fmt) {
++	case ISP4FW_IMAGE_FORMAT_NV12:
++		return "NV12";
++	case ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED:
++		return "YUV422INTERLEAVED";
++	default:
++		return "unknown fmt";
++	}
++}
++
++void isp4dbg_show_bufmeta_info(struct device *dev, char *pre,
++			       void *in, void *orig_buf)
++{
++	struct isp4fw_buffer_meta_info *p;
++	struct isp4if_img_buf_info *orig;
++
++	if (!in)
++		return;
++
++	if (!pre)
++		pre = "";
++
++	p = in;
++	orig = orig_buf;
++
++	dev_dbg(dev, "%s(%s) en:%d,stat:%s(%u),src:%s\n", pre,
++		isp4dbg_get_img_fmt_str(p->image_prop.image_format),
++		p->enabled, isp4dbg_get_buf_done_str(p->status), p->status,
++		isp4dbg_get_buf_src_str(p->source));
++
++	dev_dbg(dev, "%p,0x%llx(%u) %p,0x%llx(%u) %p,0x%llx(%u)\n",
++		orig->planes[0].sys_addr, orig->planes[0].mc_addr,
++		orig->planes[0].len, orig->planes[1].sys_addr,
++		orig->planes[1].mc_addr, orig->planes[1].len,
++		orig->planes[2].sys_addr, orig->planes[2].mc_addr,
++		orig->planes[2].len);
++}
++
++char *isp4dbg_get_buf_type(u32 type)
++{
++	/* enum isp4fw_buffer_type */
++	switch (type) {
++	case ISP4FW_BUFFER_TYPE_PREVIEW:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_PREVIEW);
++	case ISP4FW_BUFFER_TYPE_META_INFO:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_META_INFO);
++	case ISP4FW_BUFFER_TYPE_MEM_POOL:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_MEM_POOL);
++	default:
++		return "unknown type";
++	}
++}
++
++char *isp4dbg_get_cmd_str(u32 cmd)
++{
++	switch (cmd) {
++	case ISP4FW_CMD_ID_START_STREAM:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_START_STREAM);
++	case ISP4FW_CMD_ID_STOP_STREAM:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_STOP_STREAM);
++	case ISP4FW_CMD_ID_SEND_BUFFER:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SEND_BUFFER);
++	case ISP4FW_CMD_ID_SET_STREAM_CONFIG:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_STREAM_CONFIG);
++	case ISP4FW_CMD_ID_SET_OUT_CHAN_PROP:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_OUT_CHAN_PROP);
++	case ISP4FW_CMD_ID_ENABLE_OUT_CHAN:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_ENABLE_OUT_CHAN);
++	default:
++		return "unknown cmd";
++	}
++}
++
++char *isp4dbg_get_resp_str(u32 cmd)
++{
++	switch (cmd) {
++	case ISP4FW_RESP_ID_CMD_DONE:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_CMD_DONE);
++	case ISP4FW_RESP_ID_NOTI_FRAME_DONE:
++		return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_NOTI_FRAME_DONE);
++	default:
++		return "unknown respid";
++	}
++}
++
++char *isp4dbg_get_if_stream_str(u32 stream /* enum fw_cmd_resp_stream_id */)
++{
++	switch (stream) {
++	case ISP4IF_STREAM_ID_GLOBAL:
++		return "STREAM_GLOBAL";
++	case ISP4IF_STREAM_ID_1:
++		return "STREAM1";
++	default:
++		return "unknown streamID";
++	}
++}
++
++char *isp4dbg_get_out_ch_str(int ch /* enum isp4fw_pipe_out_ch */)
++{
++	switch ((enum isp4fw_pipe_out_ch)ch) {
++	case ISP4FW_ISP_PIPE_OUT_CH_PREVIEW:
++		return "prev";
++	default:
++		return "unknown channel";
++	}
++}
+diff --git a/drivers/media/platform/amd/isp4/isp4_debug.h b/drivers/media/platform/amd/isp4/isp4_debug.h
+new file mode 100644
+index 000000000000..d1262e03ae64
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_debug.h
+@@ -0,0 +1,41 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#ifndef _ISP4_DEBUG_H_
++#define _ISP4_DEBUG_H_
++
++#include <linux/dev_printk.h>
++#include <linux/printk.h>
++
++#include "isp4_subdev.h"
++
++#ifdef CONFIG_DEBUG_FS
++struct isp4_device;
++
++void isp_debugfs_create(struct isp4_device *isp_dev);
++void isp_debugfs_remove(struct isp4_device *isp_dev);
++void isp_fw_log_print(struct isp4_subdev *isp);
++
++#else
++
++/* to avoid checkpatch warning */
++#define isp_debugfs_create(cam) ((void)(cam))
++#define isp_debugfs_remove(cam) ((void)(cam))
++#define isp_fw_log_print(isp) ((void)(isp))
++
++#endif /* CONFIG_DEBUG_FS */
++
++void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, void *p,
++			       void *orig_buf /* struct sys_img_buf_handle */);
++char *isp4dbg_get_img_fmt_str(int fmt /* enum _image_format_t */);
++char *isp4dbg_get_out_ch_str(int ch /* enum _isp_pipe_out_ch_t */);
++char *isp4dbg_get_cmd_str(u32 cmd);
++char *isp4dbg_get_buf_type(u32 type);/* enum _buffer_type_t */
++char *isp4dbg_get_resp_str(u32 resp);
++char *isp4dbg_get_buf_src_str(u32 src);
++char *isp4dbg_get_buf_done_str(u32 status);
++char *isp4dbg_get_if_stream_str(u32 stream);
++
++#endif /* _ISP4_DEBUG_H_ */
+diff --git a/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
+new file mode 100644
+index 000000000000..88bacb00355c
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
+@@ -0,0 +1,318 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#ifndef _ISP4_FW_CMD_RESP_H_
++#define _ISP4_FW_CMD_RESP_H_
++
++/*
++ *        Two types of command/response channel.
++ *          Type Global Command has one command/response channel.
++ *          Type Stream Command has one command/response channel.
++ *-----------                                        ------------
++ *|         |       ---------------------------      |          |
++ *|         |  ---->|  Global Command         |----> |          |
++ *|         |       ---------------------------      |          |
++ *|         |                                        |          |
++ *|         |                                        |          |
++ *|         |       ---------------------------      |          |
++ *|         |  ---->|   Stream Command        |----> |          |
++ *|         |       ---------------------------      |          |
++ *|         |                                        |          |
++ *|         |                                        |          |
++ *|         |                                        |          |
++ *|  HOST   |                                        | Firmware |
++ *|         |                                        |          |
++ *|         |                                        |          |
++ *|         |       --------------------------       |          |
++ *|         |  <----|  Global Response       |<----  |          |
++ *|         |       --------------------------       |          |
++ *|         |                                        |          |
++ *|         |                                        |          |
++ *|         |       --------------------------       |          |
++ *|         |  <----|  Stream Response       |<----  |          |
++ *|         |       --------------------------       |          |
++ *|         |                                        |          |
++ *|         |                                        |          |
++ *-----------                                        ------------
++ */
++
++/*
++ *        cmd_id is in the format of following type:
++ *        type: indicate command type, global/stream commands.
++ *        group: indicate the command group.
++ *        id: A unique command identification in one type and group.
++ *        |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->|
++ *        |      type       |      group      |       id       |
++ */
++
++#define ISP4FW_CMD_TYPE_SHIFT            24
++#define ISP4FW_CMD_GROUP_SHIFT           16
++#define ISP4FW_CMD_TYPE_STREAM_CTRL      (0x2U << ISP4FW_CMD_TYPE_SHIFT)
++
++#define ISP4FW_CMD_GROUP_STREAM_CTRL     (0x1U << ISP4FW_CMD_GROUP_SHIFT)
++#define ISP4FW_CMD_GROUP_STREAM_BUFFER   (0x4U << ISP4FW_CMD_GROUP_SHIFT)
++
++/* Stream  Command */
++#define ISP4FW_CMD_ID_SET_STREAM_CONFIG  (ISP4FW_CMD_TYPE_STREAM_CTRL\
++					 | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x1)
++#define ISP4FW_CMD_ID_SET_OUT_CHAN_PROP  (ISP4FW_CMD_TYPE_STREAM_CTRL\
++					 | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x3)
++#define ISP4FW_CMD_ID_ENABLE_OUT_CHAN    (ISP4FW_CMD_TYPE_STREAM_CTRL\
++					 | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x5)
++#define ISP4FW_CMD_ID_START_STREAM       (ISP4FW_CMD_TYPE_STREAM_CTRL\
++					 | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x7)
++#define ISP4FW_CMD_ID_STOP_STREAM        (ISP4FW_CMD_TYPE_STREAM_CTRL\
++					 | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x8)
++
++/* Stream Buffer Command */
++#define ISP4FW_CMD_ID_SEND_BUFFER        (ISP4FW_CMD_TYPE_STREAM_CTRL\
++					 | ISP4FW_CMD_GROUP_STREAM_BUFFER | 0x1)
++
++/*
++ *        resp_id is in the format of following type:
++ *        type: indicate command type, global/stream commands.
++ *        group: indicate the command group.
++ *        id: A unique command identification in one type and group.
++ *        |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->|
++ *        |      type       |      group      |       id       |
++ */
++
++#define ISP4FW_RESP_GROUP_SHIFT          16
++
++#define ISP4FW_RESP_GROUP_GENERAL        (0x1 << ISP4FW_RESP_GROUP_SHIFT)
++#define ISP4FW_RESP_GROUP_NOTIFICATION   (0x3 << ISP4FW_RESP_GROUP_SHIFT)
++
++/* General Response */
++#define ISP4FW_RESP_ID_CMD_DONE          (ISP4FW_RESP_GROUP_GENERAL | 0x1)
++
++/* Notification */
++#define ISP4FW_RESP_ID_NOTI_FRAME_DONE   (ISP4FW_RESP_GROUP_NOTIFICATION | 0x1)
++
++#define ISP4FW_CMD_STATUS_SUCCESS        0
++#define ISP4FW_CMD_STATUS_FAIL           1
++#define ISP4FW_CMD_STATUS_SKIPPED        2
++
++#define ISP4FW_ADDR_SPACE_TYPE_GPU_VA    4
++
++#define ISP4FW_MEMORY_POOL_SIZE          (100 * 1024 * 1024)
++
++/*
++ * standard ISP pipeline: mipicsi=>isp
++ */
++#define ISP4FW_MIPI0_ISP_PIPELINE_ID     0x5f91
++
++enum isp4fw_sensor_id {
++	/* Sensor id for ISP input from MIPI port 0 */
++	ISP4FW_SENSOR_ID_ON_MIPI0  = 0,
++};
++
++enum isp4fw_stream_id {
++	ISP4FW_STREAM_ID_INVALID = -1,
++	ISP4FW_STREAM_ID_1 = 0,
++	ISP4FW_STREAM_ID_2 = 1,
++	ISP4FW_STREAM_ID_3 = 2,
++	ISP4FW_STREAM_ID_MAXIMUM
++};
++
++enum isp4fw_image_format {
++	/* 4:2:0,semi-planar, 8-bit */
++	ISP4FW_IMAGE_FORMAT_NV12 = 1,
++	/* interleave, 4:2:2, 8-bit */
++	ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED = 7,
++};
++
++enum isp4fw_pipe_out_ch {
++	ISP4FW_ISP_PIPE_OUT_CH_PREVIEW = 0,
++};
++
++enum isp4fw_yuv_range {
++	ISP4FW_ISP_YUV_RANGE_FULL = 0,     /* YUV value range in 0~255 */
++	ISP4FW_ISP_YUV_RANGE_NARROW = 1,   /* YUV value range in 16~235 */
++	ISP4FW_ISP_YUV_RANGE_MAX
++};
++
++enum isp4fw_buffer_type {
++	ISP4FW_BUFFER_TYPE_PREVIEW = 8,
++	ISP4FW_BUFFER_TYPE_META_INFO = 10,
++	ISP4FW_BUFFER_TYPE_MEM_POOL = 15,
++};
++
++enum isp4fw_buffer_status {
++	/* The buffer is INVALID */
++	ISP4FW_BUFFER_STATUS_INVALID,
++	/* The buffer is not filled with image data */
++	ISP4FW_BUFFER_STATUS_SKIPPED,
++	/* The buffer is available and awaiting to be filled */
++	ISP4FW_BUFFER_STATUS_EXIST,
++	/* The buffer is filled with image data */
++	ISP4FW_BUFFER_STATUS_DONE,
++	/* The buffer is unavailable */
++	ISP4FW_BUFFER_STATUS_LACK,
++	/* The buffer is dirty, probably caused by LMI leakage */
++	ISP4FW_BUFFER_STATUS_DIRTY,
++	ISP4FW_BUFFER_STATUS_MAX
++};
++
++enum isp4fw_buffer_source {
++	/* The buffer is from the stream buffer queue */
++	ISP4FW_BUFFER_SOURCE_STREAM,
++};
++
++struct isp4fw_error_code {
++	u32 code1;
++	u32 code2;
++	u32 code3;
++	u32 code4;
++	u32 code5;
++};
++
++/* Command Structure for FW */
++
++struct isp4fw_cmd {
++	u32 cmd_seq_num;
++	u32 cmd_id;
++	u32 cmd_param[12];
++	u16 cmd_stream_id;
++	u8 cmd_silent_resp;
++	u8 reserved;
++	u32 cmd_check_sum;
++};
++
++struct isp4fw_resp_cmd_done {
++	/*
++	 * The host2fw command seqNum.
++	 * To indicate which command this response refers to.
++	 */
++	u32 cmd_seq_num;
++	/* The host2fw command id for host double check. */
++	u32 cmd_id;
++	/*
++	 * Indicate the command process status.
++	 * 0 means success. 1 means fail. 2 means skipped
++	 */
++	u16 cmd_status;
++	/*
++	 * If cmd_status is 1, the command failed. The host can check
++	 * isp4fw_error_code for details.
++	 */
++	u16 isp4fw_error_code;
++	/* The response payload type varies by cmd. */
++	u8 payload[36];
++};
++
++struct isp4fw_resp_param_package {
++	u32 package_addr_lo;	/* The low 32 bit of the pkg address. */
++	u32 package_addr_hi;	/* The high 32 bit of the pkg address. */
++	u32 package_size;	/* The total pkg size in bytes. */
++	u32 package_check_sum;	/* The byte sum of the pkg. */
++};
++
++struct isp4fw_resp {
++	u32 resp_seq_num;
++	u32 resp_id;
++	union {
++		struct isp4fw_resp_cmd_done cmd_done;
++		struct isp4fw_resp_param_package frame_done;
++		u32 resp_param[12];
++	} param;
++	u8  reserved[4];
++	u32 resp_check_sum;
++};
++
++struct isp4fw_mipi_pipe_path_cfg {
++	u32 b_enable;
++	enum isp4fw_sensor_id isp4fw_sensor_id;
++};
++
++struct isp4fw_isp_pipe_path_cfg {
++	u32  isp_pipe_id;	/* pipe ids for pipeline construction */
++};
++
++struct isp4fw_isp_stream_cfg {
++	/* Isp mipi path */
++	struct isp4fw_mipi_pipe_path_cfg mipi_pipe_path_cfg;
++	/* Isp pipe path */
++	struct isp4fw_isp_pipe_path_cfg  isp_pipe_path_cfg;
++	/* enable TNR */
++	u32 b_enable_tnr;
++	/*
++	 * Number of frames for RTA processing.
++	 * Set to 0 to use the firmware's default value.
++	 */
++	u32 rta_frames_per_proc;
++};
++
++struct isp4fw_image_prop {
++	enum isp4fw_image_format image_format;
++	u32 width;
++	u32 height;
++	u32 luma_pitch;
++	u32 chroma_pitch;
++	enum isp4fw_yuv_range yuv_range;
++};
++
++struct isp4fw_buffer {
++	/*
++	 * A check num for debug usage, host can set the buf_tags
++	 * to different numbers
++	 */
++	u32 buf_tags;
++	union {
++		u32 value;
++		struct {
++			u32 space : 16;
++			u32 vmid  : 16;
++		} bit;
++	} vmid_space;
++	u32 buf_base_a_lo;		/* Low address of buffer A */
++	u32 buf_base_a_hi;		/* High address of buffer A */
++	u32 buf_size_a;			/* Buffer size of buffer A */
++
++	u32 buf_base_b_lo;		/* Low address of buffer B */
++	u32 buf_base_b_hi;		/* High address of buffer B */
++	u32 buf_size_b;			/* Buffer size of buffer B */
++
++	u32 buf_base_c_lo;		/* Low address of buffer C */
++	u32 buf_base_c_hi;		/* High address of buffer C */
++	u32 buf_size_c;			/* Buffer size of buffer C */
++};
++
++struct isp4fw_buffer_meta_info {
++	u32 enabled;					/* enabled flag */
++	enum isp4fw_buffer_status status;		/* BufferStatus */
++	struct isp4fw_error_code err;			/* err code */
++	enum isp4fw_buffer_source source;		/* BufferSource */
++	struct isp4fw_image_prop image_prop;		/* image_prop */
++	struct isp4fw_buffer buffer;			/* buffer info */
++};
++
++struct isp4fw_meta_info {
++	u32 poc;				/* frame id */
++	u32 fc_id;				/* frame ctl id */
++	u32 time_stamp_lo;			/* timestamp low 32 bits */
++	u32 time_stamp_hi;			/* timestamp_high 32 bits */
++	struct isp4fw_buffer_meta_info preview;	/* preview BufferMetaInfo */
++};
++
++struct isp4fw_cmd_send_buffer {
++	enum isp4fw_buffer_type buffer_type;
++	struct isp4fw_buffer buffer;		/* buffer info */
++};
++
++struct isp4fw_cmd_set_out_ch_prop {
++	enum isp4fw_pipe_out_ch ch;	/* ISP output channel */
++	struct isp4fw_image_prop image_prop;	/* image property */
++};
++
++struct isp4fw_cmd_enable_out_ch {
++	enum isp4fw_pipe_out_ch ch;	/* ISP output channel */
++	u32 is_enable;			/* If channel is enabled or not */
++};
++
++struct isp4fw_cmd_set_stream_cfg {
++	struct isp4fw_isp_stream_cfg stream_cfg; /* stream path config */
++};
++
++#endif /* _ISP4_FW_CMD_RESP_H_ */
+diff --git a/drivers/media/platform/amd/isp4/isp4_hw_reg.h b/drivers/media/platform/amd/isp4/isp4_hw_reg.h
+new file mode 100644
+index 000000000000..09c76f75c5ee
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_hw_reg.h
+@@ -0,0 +1,124 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#ifndef _ISP4_HW_REG_H_
++#define _ISP4_HW_REG_H_
++
++#include <linux/io.h>
++
++#define ISP_SOFT_RESET			0x62000
++#define ISP_SYS_INT0_EN			0x62010
++#define ISP_SYS_INT0_STATUS		0x62014
++#define ISP_SYS_INT0_ACK		0x62018
++#define ISP_CCPU_CNTL			0x62054
++#define ISP_STATUS			0x62058
++#define ISP_LOG_RB_BASE_LO0		0x62148
++#define ISP_LOG_RB_BASE_HI0		0x6214c
++#define ISP_LOG_RB_SIZE0		0x62150
++#define ISP_LOG_RB_RPTR0		0x62154
++#define ISP_LOG_RB_WPTR0		0x62158
++#define ISP_RB_BASE_LO1			0x62170
++#define ISP_RB_BASE_HI1			0x62174
++#define ISP_RB_SIZE1			0x62178
++#define ISP_RB_RPTR1			0x6217c
++#define ISP_RB_WPTR1			0x62180
++#define ISP_RB_BASE_LO2			0x62184
++#define ISP_RB_BASE_HI2			0x62188
++#define ISP_RB_SIZE2			0x6218c
++#define ISP_RB_RPTR2			0x62190
++#define ISP_RB_WPTR2			0x62194
++#define ISP_RB_BASE_LO3			0x62198
++#define ISP_RB_BASE_HI3			0x6219c
++#define ISP_RB_SIZE3			0x621a0
++#define ISP_RB_RPTR3			0x621a4
++#define ISP_RB_WPTR3			0x621a8
++#define ISP_RB_BASE_LO4			0x621ac
++#define ISP_RB_BASE_HI4			0x621b0
++#define ISP_RB_SIZE4			0x621b4
++#define ISP_RB_RPTR4			0x621b8
++#define ISP_RB_WPTR4			0x621bc
++#define ISP_RB_BASE_LO5			0x621c0
++#define ISP_RB_BASE_HI5			0x621c4
++#define ISP_RB_SIZE5			0x621c8
++#define ISP_RB_RPTR5			0x621cc
++#define ISP_RB_WPTR5			0x621d0
++#define ISP_RB_BASE_LO6			0x621d4
++#define ISP_RB_BASE_HI6			0x621d8
++#define ISP_RB_SIZE6			0x621dc
++#define ISP_RB_RPTR6			0x621e0
++#define ISP_RB_WPTR6			0x621e4
++#define ISP_RB_BASE_LO7			0x621e8
++#define ISP_RB_BASE_HI7			0x621ec
++#define ISP_RB_SIZE7			0x621f0
++#define ISP_RB_RPTR7			0x621f4
++#define ISP_RB_WPTR7			0x621f8
++#define ISP_RB_BASE_LO8			0x621fc
++#define ISP_RB_BASE_HI8			0x62200
++#define ISP_RB_SIZE8			0x62204
++#define ISP_RB_RPTR8			0x62208
++#define ISP_RB_WPTR8			0x6220c
++#define ISP_RB_BASE_LO9			0x62210
++#define ISP_RB_BASE_HI9			0x62214
++#define ISP_RB_SIZE9			0x62218
++#define ISP_RB_RPTR9			0x6221c
++#define ISP_RB_WPTR9			0x62220
++#define ISP_RB_BASE_LO10		0x62224
++#define ISP_RB_BASE_HI10		0x62228
++#define ISP_RB_SIZE10			0x6222c
++#define ISP_RB_RPTR10			0x62230
++#define ISP_RB_WPTR10			0x62234
++#define ISP_RB_BASE_LO11		0x62238
++#define ISP_RB_BASE_HI11		0x6223c
++#define ISP_RB_SIZE11			0x62240
++#define ISP_RB_RPTR11			0x62244
++#define ISP_RB_WPTR11			0x62248
++#define ISP_RB_BASE_LO12		0x6224c
++#define ISP_RB_BASE_HI12		0x62250
++#define ISP_RB_SIZE12			0x62254
++#define ISP_RB_RPTR12			0x62258
++#define ISP_RB_WPTR12			0x6225c
++
++#define ISP_POWER_STATUS		0x60000
++
++/* ISP_SOFT_RESET */
++#define ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK			0x00000001UL
++
++/* ISP_CCPU_CNTL */
++#define ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK			0x00040000UL
++
++/* ISP_STATUS */
++#define ISP_STATUS__CCPU_REPORT_MASK				0x000000feUL
++
++/* ISP_SYS_INT0_STATUS */
++#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK	0x00010000UL
++#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK	0x00040000UL
++#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK	0x00100000UL
++#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK	0x00400000UL
++
++/* ISP_SYS_INT0_EN */
++#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK	0x00010000UL
++#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT10_EN_MASK	0x00040000UL
++#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT11_EN_MASK	0x00100000UL
++#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK	0x00400000UL
++
++/* ISP_SYS_INT0_ACK */
++#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK	0x00010000UL
++#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT10_ACK_MASK	0x00040000UL
++#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT11_ACK_MASK	0x00100000UL
++#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK	0x00400000UL
++
++/* Helper functions for reading isp registers */
++static inline u32 isp4hw_rreg(void __iomem *base, u32 reg)
++{
++	return readl(base + reg);
++}
++
++/* Helper functions for writing isp registers */
++static inline void isp4hw_wreg(void __iomem *base, u32 reg, u32 val)
++{
++	return writel(val, base + reg);
++}
++
++#endif /* _ISP4_HW_REG_H_ */
+diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c
+new file mode 100644
+index 000000000000..8d73f66bb42c
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_interface.c
+@@ -0,0 +1,832 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#include <linux/iopoll.h>
++
++#include "isp4_debug.h"
++#include "isp4_fw_cmd_resp.h"
++#include "isp4_hw_reg.h"
++#include "isp4_interface.h"
++
++#define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \
++	(ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK\
++	 | ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK)
++
++#define ISP4IF_FW_CMD_TIMEOUT (HZ / 2)
++
++struct isp4if_rb_config {
++	const char *name;
++	u32 index;
++	u32 reg_rptr;
++	u32 reg_wptr;
++	u32 reg_base_lo;
++	u32 reg_base_hi;
++	u32 reg_size;
++	u32 val_size;
++	u64 base_mc_addr;
++	void *base_sys_addr;
++};
++
++/* FW cmd ring buffer configuration */
++static struct isp4if_rb_config isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = {
++	{
++		.name = "CMD_RB_GBL0",
++		.index = 3,
++		.reg_rptr = ISP_RB_RPTR4,
++		.reg_wptr = ISP_RB_WPTR4,
++		.reg_base_lo = ISP_RB_BASE_LO4,
++		.reg_base_hi = ISP_RB_BASE_HI4,
++		.reg_size = ISP_RB_SIZE4,
++	},
++	{
++		.name = "CMD_RB_STR1",
++		.index = 0,
++		.reg_rptr = ISP_RB_RPTR1,
++		.reg_wptr = ISP_RB_WPTR1,
++		.reg_base_lo = ISP_RB_BASE_LO1,
++		.reg_base_hi = ISP_RB_BASE_HI1,
++		.reg_size = ISP_RB_SIZE1,
++	},
++	{
++		.name = "CMD_RB_STR2",
++		.index = 1,
++		.reg_rptr = ISP_RB_RPTR2,
++		.reg_wptr = ISP_RB_WPTR2,
++		.reg_base_lo = ISP_RB_BASE_LO2,
++		.reg_base_hi = ISP_RB_BASE_HI2,
++		.reg_size = ISP_RB_SIZE2,
++	},
++	{
++		.name = "CMD_RB_STR3",
++		.index = 2,
++		.reg_rptr = ISP_RB_RPTR3,
++		.reg_wptr = ISP_RB_WPTR3,
++		.reg_base_lo = ISP_RB_BASE_LO3,
++		.reg_base_hi = ISP_RB_BASE_HI3,
++		.reg_size = ISP_RB_SIZE3,
++	},
++};
++
++/* FW resp ring buffer configuration */
++static struct isp4if_rb_config isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = {
++	{
++		.name = "RES_RB_GBL0",
++		.index = 3,
++		.reg_rptr = ISP_RB_RPTR12,
++		.reg_wptr = ISP_RB_WPTR12,
++		.reg_base_lo = ISP_RB_BASE_LO12,
++		.reg_base_hi = ISP_RB_BASE_HI12,
++		.reg_size = ISP_RB_SIZE12,
++	},
++	{
++		.name = "RES_RB_STR1",
++		.index = 0,
++		.reg_rptr = ISP_RB_RPTR9,
++		.reg_wptr = ISP_RB_WPTR9,
++		.reg_base_lo = ISP_RB_BASE_LO9,
++		.reg_base_hi = ISP_RB_BASE_HI9,
++		.reg_size = ISP_RB_SIZE9,
++	},
++	{
++		.name = "RES_RB_STR2",
++		.index = 1,
++		.reg_rptr = ISP_RB_RPTR10,
++		.reg_wptr = ISP_RB_WPTR10,
++		.reg_base_lo = ISP_RB_BASE_LO10,
++		.reg_base_hi = ISP_RB_BASE_HI10,
++		.reg_size = ISP_RB_SIZE10,
++	},
++	{
++		.name = "RES_RB_STR3",
++		.index = 2,
++		.reg_rptr = ISP_RB_RPTR11,
++		.reg_wptr = ISP_RB_WPTR11,
++		.reg_base_lo = ISP_RB_BASE_LO11,
++		.reg_base_hi = ISP_RB_BASE_HI11,
++		.reg_size = ISP_RB_SIZE11,
++	},
++};
++
++/* FW log ring buffer configuration */
++static struct isp4if_rb_config isp4if_log_rb_config = {
++	.name = "LOG_RB",
++	.index = 0,
++	.reg_rptr = ISP_LOG_RB_RPTR0,
++	.reg_wptr = ISP_LOG_RB_WPTR0,
++	.reg_base_lo = ISP_LOG_RB_BASE_LO0,
++	.reg_base_hi = ISP_LOG_RB_BASE_HI0,
++	.reg_size = ISP_LOG_RB_SIZE0,
++};
++
++static struct isp4if_gpu_mem_info *
++isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size)
++{
++	struct isp4if_gpu_mem_info *mem_info;
++	struct device *dev = ispif->dev;
++	int ret;
++
++	mem_info = kmalloc_obj(*mem_info, GFP_KERNEL);
++	if (!mem_info)
++		return NULL;
++
++	mem_info->mem_size = mem_size;
++	ret = isp_kernel_buffer_alloc(dev, mem_info->mem_size,
++				      &mem_info->mem_handle,
++				      &mem_info->gpu_mc_addr,
++				      &mem_info->sys_addr);
++	if (ret) {
++		kfree(mem_info);
++		return NULL;
++	}
++
++	return mem_info;
++}
++
++static void isp4if_gpu_mem_free(struct isp4_interface *ispif,
++				struct isp4if_gpu_mem_info **mem_info_ptr)
++{
++	struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr;
++	struct device *dev = ispif->dev;
++
++	if (!mem_info) {
++		dev_err(dev, "invalid mem_info\n");
++		return;
++	}
++
++	*mem_info_ptr = NULL;
++	isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr,
++			       &mem_info->sys_addr);
++	kfree(mem_info);
++}
++
++static void isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif)
++{
++	isp4if_gpu_mem_free(ispif, &ispif->fw_mem_pool);
++	isp4if_gpu_mem_free(ispif, &ispif->fw_cmd_resp_buf);
++	isp4if_gpu_mem_free(ispif, &ispif->fw_log_buf);
++
++	for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++)
++		isp4if_gpu_mem_free(ispif, &ispif->meta_info_buf[i]);
++}
++
++static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif)
++{
++	struct device *dev = ispif->dev;
++
++	ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif,
++						  ISP4FW_MEMORY_POOL_SIZE);
++	if (!ispif->fw_mem_pool)
++		goto error_no_memory;
++
++	ispif->fw_cmd_resp_buf =
++		isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE);
++	if (!ispif->fw_cmd_resp_buf)
++		goto error_no_memory;
++
++	ispif->fw_log_buf =
++		isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE);
++	if (!ispif->fw_log_buf)
++		goto error_no_memory;
++
++	for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
++		ispif->meta_info_buf[i] =
++			isp4if_gpu_mem_alloc(ispif, ISP4IF_META_INFO_BUF_SIZE);
++		if (!ispif->meta_info_buf[i])
++			goto error_no_memory;
++	}
++
++	return 0;
++
++error_no_memory:
++	dev_err(dev, "failed to allocate gpu memory\n");
++	return -ENOMEM;
++}
++
++static u32 isp4if_compute_check_sum(const void *buf, size_t buf_size)
++{
++	const u8 *surplus_ptr;
++	const u32 *buffer;
++	u32 checksum = 0;
++	size_t i;
++
++	buffer = (const u32 *)buf;
++	for (i = 0; i < buf_size / sizeof(u32); i++)
++		checksum += buffer[i];
++
++	surplus_ptr = (const u8 *)&buffer[i];
++	/* add surplus data crc checksum */
++	for (i = 0; i < buf_size % sizeof(u32); i++)
++		checksum += surplus_ptr[i];
++
++	return checksum;
++}
++
++void isp4if_clear_cmdq(struct isp4_interface *ispif)
++{
++	struct isp4if_cmd_element *buf_node, *tmp_node;
++	LIST_HEAD(free_list);
++
++	scoped_guard(spinlock, &ispif->cmdq_lock)
++		list_splice_init(&ispif->cmdq, &free_list);
++
++	list_for_each_entry_safe(buf_node, tmp_node, &free_list, list)
++		kfree(buf_node);
++}
++
++static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif,
++				   enum isp4if_stream_id stream)
++{
++	struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream];
++	u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
++	u32 len = rb_config->val_size;
++	u32 rd_ptr, wr_ptr;
++	u32 bytes_free;
++
++	rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
++	wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
++
++	/*
++	 * Read and write pointers are equal, indicating the ring buffer
++	 * is empty
++	 */
++	if (wr_ptr == rd_ptr)
++		return false;
++
++	if (wr_ptr > rd_ptr)
++		bytes_free = len - (wr_ptr - rd_ptr);
++	else
++		bytes_free = rd_ptr - wr_ptr;
++
++	/*
++	 * Ignore one byte from the bytes free to prevent rd_ptr from equaling
++	 * wr_ptr when the ring buffer is full, because rd_ptr == wr_ptr is
++	 * supposed to indicate that the ring buffer is empty.
++	 */
++	return bytes_free <= sizeof(struct isp4fw_cmd);
++}
++
++struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif,
++						   u32 seq_num, u32 cmd_id)
++{
++	struct isp4if_cmd_element *ele;
++
++	guard(spinlock)(&ispif->cmdq_lock);
++
++	list_for_each_entry(ele, &ispif->cmdq, list) {
++		if (ele->seq_num == seq_num && ele->cmd_id == cmd_id) {
++			list_del(&ele->list);
++			return ele;
++		}
++	}
++
++	return NULL;
++}
++
++/* Must check that isp4if_is_cmdq_rb_full() == false before calling */
++static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif,
++				    enum isp4if_stream_id stream,
++				    const struct isp4fw_cmd *cmd)
++{
++	struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream];
++	u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
++	void *mem_sys = rb_config->base_sys_addr;
++	const u32 cmd_sz = sizeof(*cmd);
++	struct device *dev = ispif->dev;
++	u32 len = rb_config->val_size;
++	const void *src = cmd;
++	u32 rd_ptr, wr_ptr;
++	u32 bytes_to_end;
++
++	rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
++	wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
++	if (rd_ptr >= len || wr_ptr >= len) {
++		dev_err(dev,
++			"rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, cmd_sz=%u\n",
++			stream, isp4dbg_get_if_stream_str(stream), rd_ptr,
++			wr_ptr, len, cmd_sz);
++		return -EINVAL;
++	}
++
++	bytes_to_end = len - wr_ptr;
++	if (bytes_to_end >= cmd_sz) {
++		/* FW cmd is just a straight copy to the write pointer */
++		memcpy(mem_sys + wr_ptr, src, cmd_sz);
++		isp4hw_wreg(ispif->mmio, wreg, (wr_ptr + cmd_sz) % len);
++	} else {
++		/*
++		 * FW cmd is split because the ring buffer needs to wrap
++		 * around
++		 */
++		memcpy(mem_sys + wr_ptr, src, bytes_to_end);
++		memcpy(mem_sys, src + bytes_to_end, cmd_sz - bytes_to_end);
++		isp4hw_wreg(ispif->mmio, wreg, cmd_sz - bytes_to_end);
++	}
++
++	return 0;
++}
++
++static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id)
++{
++	return ISP4IF_STREAM_ID_1;
++}
++
++static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id,
++			      const void *package,
++			      u32 package_size, bool sync)
++{
++	enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id);
++	struct isp4if_cmd_element *ele = NULL;
++	struct device *dev = ispif->dev;
++	struct isp4fw_cmd cmd;
++	u32 seq_num;
++	int ret;
++
++	if (package_size > sizeof(cmd.cmd_param)) {
++		dev_err(dev, "fail pkgsize(%u) > %zu cmd:0x%x, stream %d\n",
++			package_size, sizeof(cmd.cmd_param), cmd_id, stream);
++		return -EINVAL;
++	}
++
++	/*
++	 * The struct will be shared with ISP FW, use memset() to guarantee
++	 * padding bits are zeroed, since this is not guaranteed on all
++	 * compilers.
++	 */
++	memset(&cmd, 0, sizeof(cmd));
++	cmd.cmd_id = cmd_id;
++	switch (stream) {
++	case ISP4IF_STREAM_ID_GLOBAL:
++		cmd.cmd_stream_id = ISP4FW_STREAM_ID_INVALID;
++		break;
++	case ISP4IF_STREAM_ID_1:
++		cmd.cmd_stream_id = ISP4FW_STREAM_ID_1;
++		break;
++	default:
++		dev_err(dev, "fail bad stream id %d\n", stream);
++		return -EINVAL;
++	}
++
++	/* Allocate the sync command object early and outside of the lock */
++	if (sync) {
++		ele = kmalloc_obj(*ele, GFP_KERNEL);
++		if (!ele)
++			return -ENOMEM;
++
++		/* Get two references: one for the resp thread, one for us */
++		atomic_set(&ele->refcnt, 2);
++		init_completion(&ele->cmd_done);
++	}
++
++	if (package && package_size)
++		memcpy(cmd.cmd_param, package, package_size);
++
++	scoped_guard(mutex, &ispif->isp4if_mutex) {
++		ret = read_poll_timeout(isp4if_is_cmdq_rb_full, ret, !ret,
++					ISP4IF_RB_FULL_SLEEP_US,
++					ISP4IF_RB_FULL_TIMEOUT_US, false, ispif,
++					stream);
++		if (ret) {
++			struct isp4if_rb_config *rb_config =
++					&isp4if_resp_rb_config[stream];
++			u32 rd_ptr = isp4hw_rreg(ispif->mmio,
++						 rb_config->reg_rptr);
++			u32 wr_ptr = isp4hw_rreg(ispif->mmio,
++						 rb_config->reg_wptr);
++
++			dev_err(dev,
++				"failed to get free cmdq slot, stream %s(%d),rd %u, wr %u\n",
++				isp4dbg_get_if_stream_str(stream), stream,
++				rd_ptr, wr_ptr);
++			ret = -ETIMEDOUT;
++			goto free_ele;
++		}
++
++		seq_num = ispif->host2fw_seq_num++;
++		cmd.cmd_seq_num = seq_num;
++		cmd.cmd_check_sum = isp4if_compute_check_sum(&cmd, sizeof(cmd)
++							     - sizeof(u32));
++
++		/*
++		 * only append the fw cmd to queue when its response needs to
++		 * be waited for, currently there are only two such commands,
++		 * disable channel and stop stream which are only sent after
++		 * close camera
++		 */
++		if (ele) {
++			ele->seq_num = seq_num;
++			ele->cmd_id = cmd_id;
++			scoped_guard(spinlock, &ispif->cmdq_lock)
++				list_add_tail(&ele->list, &ispif->cmdq);
++		}
++
++		ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd);
++		if (ret) {
++			dev_err(dev,
++				"fail for insert_isp_fw_cmd cmd_id %s(0x%08x)\n",
++				isp4dbg_get_cmd_str(cmd_id), cmd_id);
++			goto err_dequeue_ele;
++		}
++	}
++
++	if (ele) {
++		ret = wait_for_completion_timeout(&ele->cmd_done,
++						  ISP4IF_FW_CMD_TIMEOUT);
++		if (!ret) {
++			ret = -ETIMEDOUT;
++			goto err_dequeue_ele;
++		}
++
++		ret = 0;
++		goto put_ele_ref;
++	}
++
++	return 0;
++
++err_dequeue_ele:
++	/*
++	 * Try to remove the command from the queue. If that fails, then it
++	 * means the response thread is currently using the object, and we need
++	 * to use the refcount to avoid a use-after-free by either side.
++	 */
++	if (ele && isp4if_rm_cmd_from_cmdq(ispif, seq_num, cmd_id))
++		goto free_ele;
++
++put_ele_ref:
++	/* Don't free the command if we didn't put the last reference */
++	if (ele && atomic_dec_return(&ele->refcnt))
++		ele = NULL;
++
++free_ele:
++	kfree(ele);
++	return ret;
++}
++
++static int isp4if_send_buffer(struct isp4_interface *ispif,
++			      struct isp4if_img_buf_info *buf_info)
++{
++	struct isp4fw_cmd_send_buffer cmd;
++
++	/*
++	 * The struct will be shared with ISP FW, use memset() to guarantee
++	 * padding bits are zeroed, since this is not guaranteed on all
++	 * compilers.
++	 */
++	memset(&cmd, 0, sizeof(cmd));
++	cmd.buffer_type = ISP4FW_BUFFER_TYPE_PREVIEW;
++	cmd.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
++	isp4if_split_addr64(buf_info->planes[0].mc_addr,
++			    &cmd.buffer.buf_base_a_lo,
++			    &cmd.buffer.buf_base_a_hi);
++	cmd.buffer.buf_size_a = buf_info->planes[0].len;
++
++	isp4if_split_addr64(buf_info->planes[1].mc_addr,
++			    &cmd.buffer.buf_base_b_lo,
++			    &cmd.buffer.buf_base_b_hi);
++	cmd.buffer.buf_size_b = buf_info->planes[1].len;
++
++	isp4if_split_addr64(buf_info->planes[2].mc_addr,
++			    &cmd.buffer.buf_base_c_lo,
++			    &cmd.buffer.buf_base_c_hi);
++	cmd.buffer.buf_size_c = buf_info->planes[2].len;
++
++	return isp4if_send_fw_cmd(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &cmd,
++				  sizeof(cmd), false);
++}
++
++static void isp4if_init_rb_config(struct isp4_interface *ispif,
++				  struct isp4if_rb_config *rb_config)
++{
++	isp4hw_wreg(ispif->mmio, rb_config->reg_rptr, 0x0);
++	isp4hw_wreg(ispif->mmio, rb_config->reg_wptr, 0x0);
++	isp4hw_wreg(ispif->mmio, rb_config->reg_base_lo,
++		    rb_config->base_mc_addr);
++	isp4hw_wreg(ispif->mmio, rb_config->reg_base_hi,
++		    rb_config->base_mc_addr >> 32);
++	isp4hw_wreg(ispif->mmio, rb_config->reg_size, rb_config->val_size);
++}
++
++static int isp4if_fw_init(struct isp4_interface *ispif)
++{
++	u32 aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0;
++	struct isp4if_rb_config *rb_config;
++	u32 offset;
++	unsigned int i;
++
++	/* initialize CMD_RB streams */
++	for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) {
++		rb_config = (isp4if_cmd_rb_config + i);
++		offset = aligned_rb_chunk_size * rb_config->index;
++
++		rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE;
++		rb_config->base_sys_addr =
++			ispif->fw_cmd_resp_buf->sys_addr + offset;
++		rb_config->base_mc_addr =
++			ispif->fw_cmd_resp_buf->gpu_mc_addr + offset;
++
++		isp4if_init_rb_config(ispif, rb_config);
++	}
++
++	/* initialize RESP_RB streams */
++	for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) {
++		rb_config = (isp4if_resp_rb_config + i);
++		offset = aligned_rb_chunk_size *
++			 (rb_config->index + ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1);
++
++		rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE;
++		rb_config->base_sys_addr =
++			ispif->fw_cmd_resp_buf->sys_addr + offset;
++		rb_config->base_mc_addr =
++			ispif->fw_cmd_resp_buf->gpu_mc_addr + offset;
++
++		isp4if_init_rb_config(ispif, rb_config);
++	}
++
++	/* initialize LOG_RB stream */
++	rb_config = &isp4if_log_rb_config;
++	rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE;
++	rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr;
++	rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr;
++
++	isp4if_init_rb_config(ispif, rb_config);
++
++	return 0;
++}
++
++static int isp4if_wait_fw_ready(struct isp4_interface *ispif,
++				u32 isp_status_addr)
++{
++	struct device *dev = ispif->dev;
++	u32 timeout_ms = 100;
++	u32 interval_ms = 1;
++	u32 reg_val;
++
++	/* wait for FW initialize done! */
++	if (!read_poll_timeout(isp4hw_rreg, reg_val, reg_val
++			       & ISP_STATUS__CCPU_REPORT_MASK,
++			       interval_ms * 1000, timeout_ms * 1000, false,
++			       ispif->mmio, isp_status_addr))
++		return 0;
++
++	dev_err(dev, "ISP CCPU FW boot failed\n");
++
++	return -ETIME;
++}
++
++static void isp4if_enable_ccpu(struct isp4_interface *ispif)
++{
++	u32 reg_val;
++
++	reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET);
++	reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK);
++	isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val);
++
++	usleep_range(100, 150);
++
++	reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL);
++	reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK);
++	isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val);
++}
++
++static void isp4if_disable_ccpu(struct isp4_interface *ispif)
++{
++	u32 reg_val;
++
++	reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL);
++	reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK;
++	isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val);
++
++	usleep_range(100, 150);
++
++	reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET);
++	reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK;
++	isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val);
++}
++
++static int isp4if_fw_boot(struct isp4_interface *ispif)
++{
++	struct device *dev = ispif->dev;
++
++	if (ispif->status != ISP4IF_STATUS_PWR_ON) {
++		dev_err(dev, "invalid isp power status %d\n", ispif->status);
++		return -EINVAL;
++	}
++
++	isp4if_disable_ccpu(ispif);
++
++	isp4if_fw_init(ispif);
++
++	/* clear ccpu status */
++	isp4hw_wreg(ispif->mmio, ISP_STATUS, 0x0);
++
++	isp4if_enable_ccpu(ispif);
++
++	if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) {
++		isp4if_disable_ccpu(ispif);
++		return -EINVAL;
++	}
++
++	/* enable interrupts */
++	isp4hw_wreg(ispif->mmio, ISP_SYS_INT0_EN,
++		    ISP4IF_FW_RESP_RB_IRQ_EN_MASK);
++
++	ispif->status = ISP4IF_STATUS_FW_RUNNING;
++
++	dev_dbg(dev, "ISP CCPU FW boot success\n");
++
++	return 0;
++}
++
++int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream,
++		    struct isp4fw_resp *resp)
++{
++	struct isp4if_rb_config *rb_config = &isp4if_resp_rb_config[stream];
++	u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
++	void *mem_sys = rb_config->base_sys_addr;
++	const u32 resp_sz = sizeof(*resp);
++	struct device *dev = ispif->dev;
++	u32 len = rb_config->val_size;
++	u32 rd_ptr, wr_ptr;
++	u32 bytes_to_end;
++	void *dst = resp;
++	u32 checksum;
++
++	rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
++	wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
++	if (rd_ptr >= len || wr_ptr >= len)
++		goto err_rb_invalid;
++
++	/*
++	 * Read and write pointers are equal, indicating the ring buffer is
++	 * empty
++	 */
++	if (rd_ptr == wr_ptr)
++		return -ENODATA;
++
++	bytes_to_end = len - rd_ptr;
++	if (bytes_to_end >= resp_sz) {
++		/* FW response is just a straight copy from the read pointer */
++		if (wr_ptr > rd_ptr && wr_ptr - rd_ptr < resp_sz)
++			goto err_rb_invalid;
++
++		memcpy(dst, mem_sys + rd_ptr, resp_sz);
++		isp4hw_wreg(ispif->mmio, rreg, (rd_ptr + resp_sz) % len);
++	} else {
++		/*
++		 * FW response is split because the ring buffer wrapped
++		 * around
++		 */
++		if (wr_ptr > rd_ptr || wr_ptr < resp_sz - bytes_to_end)
++			goto err_rb_invalid;
++
++		memcpy(dst, mem_sys + rd_ptr, bytes_to_end);
++		memcpy(dst + bytes_to_end, mem_sys, resp_sz - bytes_to_end);
++		isp4hw_wreg(ispif->mmio, rreg, resp_sz - bytes_to_end);
++	}
++
++	checksum = isp4if_compute_check_sum(resp, resp_sz - sizeof(u32));
++	if (checksum != resp->resp_check_sum) {
++		dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n",
++			checksum, resp->resp_check_sum, rd_ptr, wr_ptr);
++		dev_err(dev, "%s(%u), seqNo %u, resp_id %s(0x%x)\n",
++			isp4dbg_get_if_stream_str(stream), stream,
++			resp->resp_seq_num, isp4dbg_get_resp_str(resp->resp_id),
++			resp->resp_id);
++		return -EINVAL;
++	}
++
++	return 0;
++
++err_rb_invalid:
++	dev_err(dev,
++		"rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, resp_sz=%u\n",
++		stream, isp4dbg_get_if_stream_str(stream), rd_ptr, wr_ptr, len,
++		resp_sz);
++	return -EINVAL;
++}
++
++int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id,
++			const void *package, u32 package_size)
++{
++	return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, false);
++}
++
++int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id,
++			     const void *package, u32 package_size)
++{
++	return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, true);
++}
++
++void isp4if_clear_bufq(struct isp4_interface *ispif)
++{
++	struct isp4if_img_buf_node *buf_node, *tmp_node;
++	LIST_HEAD(free_list);
++
++	scoped_guard(spinlock, &ispif->bufq_lock)
++		list_splice_init(&ispif->bufq, &free_list);
++
++	list_for_each_entry_safe(buf_node, tmp_node, &free_list, node)
++		kfree(buf_node);
++}
++
++void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node)
++{
++	kfree(buf_node);
++}
++
++struct isp4if_img_buf_node *
++isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info)
++{
++	struct isp4if_img_buf_node *node;
++
++	node = kmalloc_obj(*node, GFP_KERNEL);
++	if (node)
++		node->buf_info = *buf_info;
++
++	return node;
++}
++
++struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif)
++{
++	struct isp4if_img_buf_node *buf_node;
++
++	guard(spinlock)(&ispif->bufq_lock);
++
++	buf_node = list_first_entry_or_null(&ispif->bufq, typeof(*buf_node),
++					    node);
++	if (buf_node)
++		list_del(&buf_node->node);
++
++	return buf_node;
++}
++
++int isp4if_queue_buffer(struct isp4_interface *ispif,
++			struct isp4if_img_buf_node *buf_node)
++{
++	int ret;
++
++	ret = isp4if_send_buffer(ispif, &buf_node->buf_info);
++	if (ret)
++		return ret;
++
++	scoped_guard(spinlock, &ispif->bufq_lock)
++		list_add_tail(&buf_node->node, &ispif->bufq);
++
++	return 0;
++}
++
++int isp4if_stop(struct isp4_interface *ispif)
++{
++	isp4if_disable_ccpu(ispif);
++
++	isp4if_dealloc_fw_gpumem(ispif);
++
++	return 0;
++}
++
++int isp4if_start(struct isp4_interface *ispif)
++{
++	int ret;
++
++	ret = isp4if_alloc_fw_gpumem(ispif);
++	if (ret)
++		return ret;
++
++	ret = isp4if_fw_boot(ispif);
++	if (ret)
++		goto failed_fw_boot;
++
++	return 0;
++
++failed_fw_boot:
++	isp4if_dealloc_fw_gpumem(ispif);
++	return ret;
++}
++
++int isp4if_deinit(struct isp4_interface *ispif)
++{
++	isp4if_clear_cmdq(ispif);
++
++	isp4if_clear_bufq(ispif);
++
++	mutex_destroy(&ispif->isp4if_mutex);
++
++	return 0;
++}
++
++int isp4if_init(struct isp4_interface *ispif, struct device *dev,
++		void __iomem *isp_mmio)
++{
++	ispif->dev = dev;
++	ispif->mmio = isp_mmio;
++
++	spin_lock_init(&ispif->cmdq_lock); /* used for cmdq access */
++	spin_lock_init(&ispif->bufq_lock); /* used for bufq access */
++	mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */
++
++	INIT_LIST_HEAD(&ispif->cmdq);
++	INIT_LIST_HEAD(&ispif->bufq);
++
++	return 0;
++}
+diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h
+new file mode 100644
+index 000000000000..ce3ac9b9e5cd
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_interface.h
+@@ -0,0 +1,144 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#ifndef _ISP4_INTERFACE_H_
++#define _ISP4_INTERFACE_H_
++
++#include <drm/amd/isp.h>
++#include <linux/mutex.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++struct isp4fw_resp;
++
++#define ISP4IF_RB_MAX 25
++#define ISP4IF_RESP_CHAN_TO_RB_OFFSET 9
++#define ISP4IF_RB_PMBMAP_MEM_SIZE (SZ_16M - 1)
++#define ISP4IF_RB_PMBMAP_MEM_CHUNK \
++	(ISP4IF_RB_PMBMAP_MEM_SIZE / (ISP4IF_RB_MAX - 1))
++#define ISP4IF_HOST2FW_COMMAND_SIZE sizeof(struct isp4fw_cmd)
++#define ISP4IF_MAX_NUM_HOST2FW_COMMAND 40
++#define ISP4IF_FW_CMD_BUF_SIZE \
++	(ISP4IF_MAX_NUM_HOST2FW_COMMAND * ISP4IF_HOST2FW_COMMAND_SIZE)
++#define ISP4IF_RB_FULL_SLEEP_US (33 * USEC_PER_MSEC)
++#define ISP4IF_RB_FULL_TIMEOUT_US (10 * ISP4IF_RB_FULL_SLEEP_US)
++
++#define ISP4IF_META_INFO_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000)
++#define ISP4IF_MAX_STREAM_BUF_COUNT 8
++
++#define ISP4IF_FW_LOG_RINGBUF_SIZE SZ_2M
++
++enum isp4if_stream_id {
++	ISP4IF_STREAM_ID_GLOBAL = 0,
++	ISP4IF_STREAM_ID_1 = 1,
++	ISP4IF_STREAM_ID_MAX = 4
++};
++
++enum isp4if_status {
++	ISP4IF_STATUS_PWR_OFF,
++	ISP4IF_STATUS_PWR_ON,
++	ISP4IF_STATUS_FW_RUNNING,
++	ISP4IF_FSM_STATUS_MAX
++};
++
++struct isp4if_gpu_mem_info {
++	u64 mem_size;
++	u64 gpu_mc_addr;
++	void *sys_addr;
++	void *mem_handle;
++};
++
++struct isp4if_img_buf_info {
++	struct {
++		void *sys_addr;
++		u64 mc_addr;
++		u32 len;
++	} planes[3];
++};
++
++struct isp4if_img_buf_node {
++	struct list_head node;
++	struct isp4if_img_buf_info buf_info;
++};
++
++struct isp4if_cmd_element {
++	struct list_head list;
++	u32 seq_num;
++	u32 cmd_id;
++	struct completion cmd_done;
++	atomic_t refcnt;
++};
++
++struct isp4_interface {
++	struct device *dev;
++	void __iomem *mmio;
++
++	spinlock_t cmdq_lock; /* used for cmdq access */
++	spinlock_t bufq_lock; /* used for bufq access */
++	struct mutex isp4if_mutex; /* used to send fw cmd and read fw log */
++
++	struct list_head cmdq; /* commands sent to fw */
++	struct list_head bufq; /* buffers sent to fw */
++
++	enum isp4if_status status;
++	u32 host2fw_seq_num;
++
++	/* ISP fw buffers */
++	struct isp4if_gpu_mem_info *fw_log_buf;
++	struct isp4if_gpu_mem_info *fw_cmd_resp_buf;
++	struct isp4if_gpu_mem_info *fw_mem_pool;
++	struct isp4if_gpu_mem_info *meta_info_buf[ISP4IF_MAX_STREAM_BUF_COUNT];
++};
++
++static inline void isp4if_split_addr64(u64 addr, u32 *lo, u32 *hi)
++{
++	if (lo)
++		*lo = addr & 0xffffffff;
++
++	if (hi)
++		*hi = addr >> 32;
++}
++
++static inline u64 isp4if_join_addr64(u32 lo, u32 hi)
++{
++	return (((u64)hi) << 32) | (u64)lo;
++}
++
++int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream,
++		    struct isp4fw_resp *resp);
++
++int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id,
++			const void *package, u32 package_size);
++
++int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id,
++			     const void *package, u32 package_size);
++
++struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif,
++						   u32 seq_num, u32 cmd_id);
++
++void isp4if_clear_cmdq(struct isp4_interface *ispif);
++
++void isp4if_clear_bufq(struct isp4_interface *ispif);
++
++void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node);
++
++struct isp4if_img_buf_node *
++isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info);
++
++struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif);
++
++int isp4if_queue_buffer(struct isp4_interface *ispif,
++			struct isp4if_img_buf_node *buf_node);
++
++int isp4if_stop(struct isp4_interface *ispif);
++
++int isp4if_start(struct isp4_interface *ispif);
++
++int isp4if_deinit(struct isp4_interface *ispif);
++
++int isp4if_init(struct isp4_interface *ispif, struct device *dev,
++		void __iomem *isp_mmio);
++
++#endif /* _ISP4_INTERFACE_H_ */
+diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c
+new file mode 100644
+index 000000000000..48deea79ce6c
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_subdev.c
+@@ -0,0 +1,1047 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#include <linux/pm_domain.h>
++#include <linux/units.h>
++
++#include "isp4.h"
++#include "isp4_debug.h"
++#include "isp4_fw_cmd_resp.h"
++#include "isp4_interface.h"
++
++#define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4
++
++#define ISP4SD_PERFORMANCE_STATE_LOW 0
++#define ISP4SD_PERFORMANCE_STATE_HIGH 1
++
++/* align 32KB */
++#define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000)
++
++#define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev)
++
++static const char *isp4sd_entity_name = "amd isp4";
++
++static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = {
++	"amd_isp4_thread_global",
++	"amd_isp4_thread_stream1",
++};
++
++static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable)
++{
++	if (isp_subdev->enable_gpio) {
++		gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0);
++		dev_dbg(isp_subdev->dev, "%s isp_subdev module\n",
++			enable ? "enable" : "disable");
++	}
++}
++
++static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev)
++{
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct isp4fw_cmd_send_buffer buf_type;
++	struct device *dev = isp_subdev->dev;
++	int ret;
++
++	if (!ispif->fw_mem_pool) {
++		dev_err(dev, "fail to alloc mem pool\n");
++		return -ENOMEM;
++	}
++
++	/*
++	 * The struct will be shared with ISP FW, use memset() to guarantee
++	 * padding bits are zeroed, since this is not guaranteed on all
++	 * compilers.
++	 */
++	memset(&buf_type, 0, sizeof(buf_type));
++	buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL;
++	buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
++	isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr,
++			    &buf_type.buffer.buf_base_a_lo,
++			    &buf_type.buffer.buf_base_a_hi);
++	buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size;
++
++	ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
++				  &buf_type, sizeof(buf_type));
++	if (ret) {
++		dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n",
++			ispif->fw_mem_pool->gpu_mc_addr,
++			buf_type.buffer.buf_size_a, ret);
++		return ret;
++	}
++
++	dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n",
++		ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a);
++
++	return 0;
++}
++
++static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev)
++{
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct isp4fw_cmd_set_stream_cfg cmd;
++	struct device *dev = isp_subdev->dev;
++
++	/*
++	 * The struct will be shared with ISP FW, use memset() to guarantee
++	 * padding bits are zeroed, since this is not guaranteed on all
++	 * compilers.
++	 */
++	memset(&cmd, 0, sizeof(cmd));
++	cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id =
++		ISP4FW_SENSOR_ID_ON_MIPI0;
++	cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true;
++	cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id =
++		ISP4FW_MIPI0_ISP_PIPELINE_ID;
++
++	cmd.stream_cfg.b_enable_tnr = true;
++	dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n",
++		cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id,
++		cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id,
++		cmd.stream_cfg.b_enable_tnr);
++
++	return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG,
++				   &cmd, sizeof(cmd));
++}
++
++static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev)
++{
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct isp4fw_cmd_send_buffer buf_type;
++	struct device *dev = isp_subdev->dev;
++
++	/*
++	 * The struct will be shared with ISP FW, use memset() to guarantee
++	 * padding bits are zeroed, since this is not guaranteed on all
++	 * compilers.
++	 */
++	memset(&buf_type, 0, sizeof(buf_type));
++	for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
++		struct isp4if_gpu_mem_info *meta_info_buf =
++				isp_subdev->ispif.meta_info_buf[i];
++		int ret;
++
++		if (!meta_info_buf) {
++			dev_err(dev, "fail for no meta info buf(%u)\n", i);
++			return -ENOMEM;
++		}
++
++		buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO;
++		buf_type.buffer.vmid_space.bit.space =
++			ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
++		isp4if_split_addr64(meta_info_buf->gpu_mc_addr,
++				    &buf_type.buffer.buf_base_a_lo,
++				    &buf_type.buffer.buf_base_a_hi);
++		buf_type.buffer.buf_size_a = meta_info_buf->mem_size;
++		ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
++					  &buf_type, sizeof(buf_type));
++		if (ret) {
++			dev_err(dev, "send meta info(%u) fail\n", i);
++			return ret;
++		}
++	}
++
++	dev_dbg(dev, "send meta info suc\n");
++	return 0;
++}
++
++static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev,
++				    struct isp4fw_image_prop *out_prop,
++				    struct v4l2_subdev_state *state, u32 pad)
++{
++	struct device *dev = isp_subdev->dev;
++	struct v4l2_mbus_framefmt *format;
++
++	format = v4l2_subdev_state_get_format(state, pad, 0);
++	if (!format) {
++		dev_err(dev, "fail get subdev state format\n");
++		return false;
++	}
++
++	switch (format->code) {
++	case MEDIA_BUS_FMT_YUYV8_1_5X8:
++		out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12;
++		out_prop->width = format->width;
++		out_prop->height = format->height;
++		out_prop->luma_pitch = format->width;
++		out_prop->chroma_pitch = out_prop->width;
++		break;
++	case MEDIA_BUS_FMT_YUYV8_1X16:
++		out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED;
++		out_prop->width = format->width;
++		out_prop->height = format->height;
++		out_prop->luma_pitch = format->width * 2;
++		out_prop->chroma_pitch = 0;
++		break;
++	default:
++		dev_err(dev, "fail for bad image format:0x%x\n",
++			format->code);
++		return false;
++	}
++
++	if (!out_prop->width || !out_prop->height)
++		return false;
++
++	return true;
++}
++
++static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h)
++{
++	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct device *dev = isp_subdev->dev;
++
++	if (sensor_info->status == ISP4SD_START_STATUS_STARTED)
++		return 0;
++
++	if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) {
++		dev_err(dev, "fail for previous start fail\n");
++		return -EINVAL;
++	}
++
++	dev_dbg(dev, "w:%u,h:%u\n", w, h);
++
++	if (isp4sd_send_meta_buf(isp_subdev)) {
++		dev_err(dev, "fail to send meta buf\n");
++		sensor_info->status = ISP4SD_START_STATUS_START_FAIL;
++		return -EINVAL;
++	}
++
++	sensor_info->status = ISP4SD_START_STATUS_OFF;
++
++	if (!sensor_info->start_stream_cmd_sent &&
++	    sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) {
++		int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM,
++					      NULL, 0);
++		if (ret) {
++			dev_err(dev, "fail to start stream\n");
++			return ret;
++		}
++
++		sensor_info->start_stream_cmd_sent = true;
++	} else {
++		dev_dbg(dev,
++			"no send START_STREAM, start_sent %u, buf_sent %u\n",
++			sensor_info->start_stream_cmd_sent,
++			sensor_info->buf_sent_cnt);
++	}
++
++	return 0;
++}
++
++static int isp4sd_setup_output(struct isp4_subdev *isp_subdev,
++			       struct v4l2_subdev_state *state, u32 pad)
++{
++	struct isp4sd_output_info *output_info =
++			&isp_subdev->sensor_info.output_info;
++	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop;
++	struct isp4fw_cmd_enable_out_ch cmd_ch_en;
++	struct device *dev = isp_subdev->dev;
++	int ret;
++
++	if (output_info->start_status == ISP4SD_START_STATUS_STARTED)
++		return 0;
++
++	if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) {
++		dev_err(dev, "fail for previous start fail\n");
++		return -EINVAL;
++	}
++
++	/*
++	 * The struct will be shared with ISP FW, use memset() to guarantee
++	 * padding bits are zeroed, since this is not guaranteed on all
++	 * compilers.
++	 */
++	memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop));
++	cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
++
++	if (!isp4sd_get_str_out_prop(isp_subdev,
++				     &cmd_ch_prop.image_prop, state, pad)) {
++		dev_err(dev, "fail to get out prop\n");
++		return -EINVAL;
++	}
++
++	dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n",
++		isp4dbg_get_out_ch_str(cmd_ch_prop.ch),
++		isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format),
++		cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height,
++		cmd_ch_prop.image_prop.luma_pitch,
++		cmd_ch_prop.image_prop.chroma_pitch);
++
++	ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP,
++				  &cmd_ch_prop, sizeof(cmd_ch_prop));
++	if (ret) {
++		output_info->start_status = ISP4SD_START_STATUS_START_FAIL;
++		dev_err(dev, "fail to set out prop\n");
++		return ret;
++	}
++
++	/*
++	 * The struct will be shared with ISP FW, use memset() to guarantee
++	 * padding bits are zeroed, since this is not guaranteed on all
++	 * compilers.
++	 */
++	memset(&cmd_ch_en, 0, sizeof(cmd_ch_en));
++	cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
++	cmd_ch_en.is_enable = true;
++	ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN,
++				  &cmd_ch_en, sizeof(cmd_ch_en));
++	if (ret) {
++		output_info->start_status = ISP4SD_START_STATUS_START_FAIL;
++		dev_err(dev, "fail to enable channel\n");
++		return ret;
++	}
++
++	dev_dbg(dev, "enable channel %s\n",
++		isp4dbg_get_out_ch_str(cmd_ch_en.ch));
++
++	if (!sensor_info->start_stream_cmd_sent) {
++		ret = isp4sd_kickoff_stream(isp_subdev,
++					    cmd_ch_prop.image_prop.width,
++					    cmd_ch_prop.image_prop.height);
++		if (ret) {
++			dev_err(dev, "kickoff stream fail %d\n", ret);
++			return ret;
++		}
++		/*
++		 * sensor_info->start_stream_cmd_sent will be set to true
++		 * 1. in isp4sd_kickoff_stream, if app first send buffer then
++		 * start stream
++		 * 2. in isp_set_stream_buf, if app first start stream, then
++		 * send buffer because ISP FW has the requirement, host needs
++		 * to send buffer before send start stream cmd
++		 */
++		if (sensor_info->start_stream_cmd_sent) {
++			sensor_info->status = ISP4SD_START_STATUS_STARTED;
++			output_info->start_status = ISP4SD_START_STATUS_STARTED;
++			dev_dbg(dev, "kickoff stream suc,start cmd sent\n");
++		}
++	} else {
++		dev_dbg(dev, "stream running, no need kickoff\n");
++		output_info->start_status = ISP4SD_START_STATUS_STARTED;
++	}
++
++	dev_dbg(dev, "setup output suc\n");
++	return 0;
++}
++
++static int isp4sd_init_stream(struct isp4_subdev *isp_subdev)
++{
++	struct device *dev = isp_subdev->dev;
++	int ret;
++
++	ret = isp4sd_setup_fw_mem_pool(isp_subdev);
++	if (ret) {
++		dev_err(dev, "fail to setup fw mem pool\n");
++		return ret;
++	}
++
++	ret = isp4sd_set_stream_path(isp_subdev);
++	if (ret) {
++		dev_err(dev, "fail to setup stream path\n");
++		return ret;
++	}
++
++	return 0;
++}
++
++static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev,
++				 struct v4l2_subdev_state *state, u32 pad)
++{
++	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
++	struct isp4sd_output_info *output_info = &sensor_info->output_info;
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct v4l2_mbus_framefmt *format;
++
++	format = v4l2_subdev_state_get_format(state, pad, 0);
++	if (!format) {
++		dev_err(isp_subdev->dev, "fail to get v4l2 format\n");
++	} else {
++		memset(format, 0, sizeof(*format));
++		format->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
++	}
++
++	isp4if_clear_bufq(ispif);
++	isp4if_clear_cmdq(ispif);
++
++	sensor_info->start_stream_cmd_sent = false;
++	sensor_info->buf_sent_cnt = 0;
++
++	sensor_info->status = ISP4SD_START_STATUS_OFF;
++	output_info->start_status = ISP4SD_START_STATUS_OFF;
++}
++
++static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev,
++				    enum isp4if_stream_id stream_id,
++				    struct isp4fw_resp_cmd_done *para)
++{
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct isp4if_cmd_element *ele =
++		isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id);
++	struct device *dev = isp_subdev->dev;
++
++	dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n",
++		stream_id,
++		isp4dbg_get_cmd_str(para->cmd_id),
++		para->cmd_id, para->cmd_status, para->cmd_seq_num,
++		ele);
++
++	if (ele) {
++		complete(&ele->cmd_done);
++		if (atomic_dec_and_test(&ele->refcnt))
++			kfree(ele);
++	}
++}
++
++static struct isp4fw_meta_info *
++isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc)
++{
++	for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
++		struct isp4if_gpu_mem_info *meta_info_buf =
++				isp_subdev->ispif.meta_info_buf[i];
++
++		if (meta_info_buf->gpu_mc_addr == mc)
++			return meta_info_buf->sys_addr;
++	}
++
++	return NULL;
++}
++
++static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev,
++				  u64 meta_info_mc)
++{
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct isp4fw_cmd_send_buffer buf_type;
++	struct device *dev = isp_subdev->dev;
++
++	if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) {
++		dev_warn(dev, "not working status %i, meta_info 0x%llx\n",
++			 isp_subdev->sensor_info.status, meta_info_mc);
++		return;
++	}
++
++	/*
++	 * The struct will be shared with ISP FW, use memset() to guarantee
++	 * padding bits are zeroed, since this is not guaranteed on all
++	 * compilers.
++	 */
++	memset(&buf_type, 0, sizeof(buf_type));
++	buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO;
++	buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
++	isp4if_split_addr64(meta_info_mc,
++			    &buf_type.buffer.buf_base_a_lo,
++			    &buf_type.buffer.buf_base_a_hi);
++	buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE;
++
++	if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
++				&buf_type, sizeof(buf_type)))
++		dev_err(dev, "fail send meta_info 0x%llx\n",
++			meta_info_mc);
++	else
++		dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc);
++}
++
++static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev,
++				      enum isp4if_stream_id stream_id,
++				      struct isp4fw_resp_param_package *para)
++{
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct device *dev = isp_subdev->dev;
++	struct isp4if_img_buf_node *prev;
++	struct isp4fw_meta_info *meta;
++	u64 mc;
++
++	mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi);
++	meta = isp4sd_get_meta_by_mc(isp_subdev, mc);
++	if (!meta) {
++		dev_err(dev, "fail to get meta from mc %llx\n", mc);
++		return;
++	}
++
++	dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n",
++		ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled,
++		isp4dbg_get_buf_done_str(meta->preview.status),
++		meta->preview.status);
++
++	if (meta->preview.enabled &&
++	    (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED ||
++	     meta->preview.status == ISP4FW_BUFFER_STATUS_DONE ||
++	     meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) {
++		prev = isp4if_dequeue_buffer(ispif);
++		if (prev) {
++			isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview,
++						  &prev->buf_info);
++			isp4vid_handle_frame_done(&isp_subdev->isp_vdev,
++						  &prev->buf_info);
++			isp4if_dealloc_buffer_node(prev);
++		} else {
++			dev_err(dev, "fail null prev buf\n");
++		}
++	} else if (meta->preview.enabled) {
++		dev_err(dev, "fail bad preview status %u(%s)\n",
++			meta->preview.status,
++			isp4dbg_get_buf_done_str(meta->preview.status));
++	}
++
++	if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED)
++		isp4sd_send_meta_info(isp_subdev, mc);
++
++	dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id,
++		isp_subdev->sensor_info.status);
++}
++
++static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev,
++				enum isp4if_stream_id stream_id)
++{
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct device *dev = isp_subdev->dev;
++	struct isp4fw_resp resp;
++
++	if (stream_id == ISP4IF_STREAM_ID_1)
++		isp_fw_log_print(isp_subdev);
++
++	while (true) {
++		if (isp4if_f2h_resp(ispif, stream_id, &resp)) {
++			/* Re-enable the interrupt */
++			isp4_intr_enable(isp_subdev, stream_id, true);
++			/*
++			 * Recheck to see if there is a new response.
++			 * To ensure that an in-flight interrupt is not lost,
++			 * enabling the interrupt must occur _before_ checking
++			 * for a new response, hence a memory barrier is needed.
++			 * Disable the interrupt again if there was a new
++			 * response.
++			 */
++			mb();
++			if (likely(isp4if_f2h_resp(ispif, stream_id, &resp)))
++				break;
++
++			isp4_intr_enable(isp_subdev, stream_id, false);
++		}
++
++		switch (resp.resp_id) {
++		case ISP4FW_RESP_ID_CMD_DONE:
++			isp4sd_fw_resp_cmd_done(isp_subdev, stream_id,
++						&resp.param.cmd_done);
++			break;
++		case ISP4FW_RESP_ID_NOTI_FRAME_DONE:
++			isp4sd_fw_resp_frame_done(isp_subdev, stream_id,
++						  &resp.param.frame_done);
++			break;
++		default:
++			dev_err(dev, "-><- fail respid %s(0x%x)\n",
++				isp4dbg_get_resp_str(resp.resp_id),
++				resp.resp_id);
++			break;
++		}
++	}
++}
++
++static s32 isp4sd_fw_resp_thread(void *context)
++{
++	struct isp4_subdev_thread_param *para = context;
++	struct isp4_subdev *isp_subdev = para->isp_subdev;
++	struct isp4sd_thread_handler *thread_ctx =
++			&isp_subdev->fw_resp_thread[para->idx];
++	struct device *dev = isp_subdev->dev;
++
++	dev_dbg(dev, "[%u] fw resp thread started\n", para->idx);
++	while (true) {
++		wait_event_interruptible(thread_ctx->waitq,
++					 thread_ctx->resp_ready);
++		thread_ctx->resp_ready = false;
++
++		if (kthread_should_stop()) {
++			dev_dbg(dev, "[%u] fw resp thread quit\n", para->idx);
++			break;
++		}
++
++		isp4sd_fw_resp_func(isp_subdev, para->idx);
++	}
++
++	return 0;
++}
++
++static int isp4sd_stop_resp_proc_threads(struct isp4_subdev *isp_subdev)
++{
++	for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) {
++		struct isp4sd_thread_handler *thread_ctx =
++				&isp_subdev->fw_resp_thread[i];
++
++		if (thread_ctx->thread) {
++			kthread_stop(thread_ctx->thread);
++			thread_ctx->thread = NULL;
++		}
++	}
++
++	return 0;
++}
++
++static int isp4sd_start_resp_proc_threads(struct isp4_subdev *isp_subdev)
++{
++	struct device *dev = isp_subdev->dev;
++
++	for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) {
++		struct isp4sd_thread_handler *thread_ctx =
++				&isp_subdev->fw_resp_thread[i];
++
++		isp_subdev->isp_resp_para[i].idx = i;
++		isp_subdev->isp_resp_para[i].isp_subdev = isp_subdev;
++		init_waitqueue_head(&thread_ctx->waitq);
++		thread_ctx->resp_ready = false;
++
++		thread_ctx->thread = kthread_run(isp4sd_fw_resp_thread,
++						 &isp_subdev->isp_resp_para[i],
++						 isp4sd_thread_name[i]);
++		if (IS_ERR(thread_ctx->thread)) {
++			dev_err(dev, "create thread [%d] fail\n", i);
++			thread_ctx->thread = NULL;
++			isp4sd_stop_resp_proc_threads(isp_subdev);
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd)
++{
++	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
++	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
++	unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW;
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct device *dev = isp_subdev->dev;
++	int ret;
++
++	guard(mutex)(&isp_subdev->ops_mutex);
++	if (sensor_info->status == ISP4SD_START_STATUS_STARTED) {
++		dev_err(dev, "fail for stream still running\n");
++		return -EINVAL;
++	}
++
++	sensor_info->status = ISP4SD_START_STATUS_OFF;
++
++	if (isp_subdev->irq_enabled) {
++		for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
++			disable_irq(isp_subdev->irq[i]);
++		isp_subdev->irq_enabled = false;
++	}
++
++	isp4sd_stop_resp_proc_threads(isp_subdev);
++	dev_dbg(dev, "isp_subdev stop resp proc threads suc\n");
++
++	isp4if_stop(ispif);
++
++	ret = dev_pm_genpd_set_performance_state(dev, perf_state);
++	if (ret)
++		dev_err(dev,
++			"fail to set isp_subdev performance state %u,ret %d\n",
++			perf_state, ret);
++
++	/* hold ccpu reset */
++	isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0);
++	isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0);
++	ret = pm_runtime_put_sync(dev);
++	if (ret)
++		dev_err(dev, "power off isp_subdev fail %d\n", ret);
++	else
++		dev_dbg(dev, "power off isp_subdev suc\n");
++
++	ispif->status = ISP4IF_STATUS_PWR_OFF;
++	isp4if_clear_cmdq(ispif);
++	isp4sd_module_enable(isp_subdev, false);
++
++	/*
++	 * When opening the camera, isp4sd_module_enable(isp_subdev, true) is
++	 * called. Hardware requires at least a 20ms delay between disabling
++	 * and enabling the module, so a sleep is added to ensure ISP stability
++	 * during quick reopen scenarios.
++	 */
++	msleep(20);
++
++	return 0;
++}
++
++int isp4sd_pwron_and_init(struct v4l2_subdev *sd)
++{
++	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct device *dev = isp_subdev->dev;
++	int ret;
++
++	guard(mutex)(&isp_subdev->ops_mutex);
++	if (ispif->status == ISP4IF_STATUS_FW_RUNNING) {
++		dev_dbg(dev, "camera already opened, do nothing\n");
++		return 0;
++	}
++
++	isp4sd_module_enable(isp_subdev, true);
++
++	if (ispif->status < ISP4IF_STATUS_PWR_ON) {
++		unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_HIGH;
++
++		ret = pm_runtime_resume_and_get(dev);
++		if (ret) {
++			dev_err(dev, "fail to power on isp_subdev ret %d\n",
++				ret);
++			goto err_deinit;
++		}
++
++		/* ISPPG ISP Power Status */
++		isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0x7FF);
++		ret = dev_pm_genpd_set_performance_state(dev, perf_state);
++		if (ret) {
++			dev_err(dev,
++				"fail to set performance state %u, ret %d\n",
++				perf_state, ret);
++			goto err_deinit;
++		}
++
++		ispif->status = ISP4IF_STATUS_PWR_ON;
++	}
++
++	isp_subdev->sensor_info.start_stream_cmd_sent = false;
++	isp_subdev->sensor_info.buf_sent_cnt = 0;
++
++	ret = isp4if_start(ispif);
++	if (ret) {
++		dev_err(dev, "fail to start isp_subdev interface\n");
++		goto err_deinit;
++	}
++
++	if (isp4sd_start_resp_proc_threads(isp_subdev)) {
++		dev_err(dev, "isp_start_resp_proc_threads fail\n");
++		goto err_deinit;
++	}
++
++	dev_dbg(dev, "create resp threads ok\n");
++
++	for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
++		enable_irq(isp_subdev->irq[i]);
++	isp_subdev->irq_enabled = true;
++
++	return 0;
++err_deinit:
++	isp4sd_pwroff_and_deinit(sd);
++	return -EINVAL;
++}
++
++static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev,
++			      struct v4l2_subdev_state *state, u32 pad)
++{
++	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
++	struct isp4sd_output_info *output_info = &sensor_info->output_info;
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct device *dev = isp_subdev->dev;
++
++	guard(mutex)(&isp_subdev->ops_mutex);
++	dev_dbg(dev, "status %i\n", output_info->start_status);
++
++	if (output_info->start_status == ISP4SD_START_STATUS_STARTED) {
++		struct isp4fw_cmd_enable_out_ch cmd_ch_disable;
++		int ret;
++
++		/*
++		 * The struct will be shared with ISP FW, use memset() to
++		 * guarantee padding bits are zeroed, since this is not
++		 * guaranteed on all compilers.
++		 */
++		memset(&cmd_ch_disable, 0, sizeof(cmd_ch_disable));
++		cmd_ch_disable.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
++		/* `cmd_ch_disable.is_enable` is already false */
++		ret = isp4if_send_command_sync(ispif,
++					       ISP4FW_CMD_ID_ENABLE_OUT_CHAN,
++					       &cmd_ch_disable,
++					       sizeof(cmd_ch_disable));
++		if (ret)
++			dev_err(dev, "fail to disable stream\n");
++		else
++			dev_dbg(dev, "wait disable stream suc\n");
++
++		ret = isp4if_send_command_sync(ispif, ISP4FW_CMD_ID_STOP_STREAM,
++					       NULL, 0);
++		if (ret)
++			dev_err(dev, "fail to stop stream\n");
++		else
++			dev_dbg(dev, "wait stop stream suc\n");
++	}
++
++	isp4sd_uninit_stream(isp_subdev, state, pad);
++
++	/*
++	 * Return success to ensure the stop process proceeds,
++	 * and disregard any errors since they are not fatal.
++	 */
++	return 0;
++}
++
++static int isp4sd_start_stream(struct isp4_subdev *isp_subdev,
++			       struct v4l2_subdev_state *state, u32 pad)
++{
++	struct isp4sd_output_info *output_info =
++			&isp_subdev->sensor_info.output_info;
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct device *dev = isp_subdev->dev;
++	int ret;
++
++	guard(mutex)(&isp_subdev->ops_mutex);
++
++	if (ispif->status != ISP4IF_STATUS_FW_RUNNING) {
++		dev_err(dev, "fail, bad fsm %d\n", ispif->status);
++		return -EINVAL;
++	}
++
++	switch (output_info->start_status) {
++	case ISP4SD_START_STATUS_OFF:
++		break;
++	case ISP4SD_START_STATUS_STARTED:
++		dev_dbg(dev, "stream already started, do nothing\n");
++		return 0;
++	case ISP4SD_START_STATUS_START_FAIL:
++		dev_err(dev, "stream previously failed to start\n");
++		return -EINVAL;
++	}
++
++	ret = isp4sd_init_stream(isp_subdev);
++	if (ret) {
++		dev_err(dev, "fail to init isp_subdev stream\n");
++		goto err_stop_stream;
++	}
++
++	ret = isp4sd_setup_output(isp_subdev, state, pad);
++	if (ret) {
++		dev_err(dev, "fail to setup output\n");
++		goto err_stop_stream;
++	}
++
++	return 0;
++
++err_stop_stream:
++	isp4sd_stop_stream(isp_subdev, state, pad);
++	return ret;
++}
++
++int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd,
++			    struct isp4if_img_buf_info *buf_info)
++{
++	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct isp4if_img_buf_node *buf_node;
++	struct device *dev = isp_subdev->dev;
++	int ret;
++
++	guard(mutex)(&isp_subdev->ops_mutex);
++
++	if (ispif->status != ISP4IF_STATUS_FW_RUNNING) {
++		dev_err(dev, "fail send img buf for bad fsm %d\n",
++			ispif->status);
++		return -EINVAL;
++	}
++
++	buf_node = isp4if_alloc_buffer_node(buf_info);
++	if (!buf_node) {
++		dev_err(dev, "fail alloc sys img buf info node\n");
++		return -ENOMEM;
++	}
++
++	ret = isp4if_queue_buffer(ispif, buf_node);
++	if (ret) {
++		dev_err(dev, "fail to queue image buf, %d\n", ret);
++		goto error_release_buf_node;
++	}
++
++	if (!isp_subdev->sensor_info.start_stream_cmd_sent) {
++		isp_subdev->sensor_info.buf_sent_cnt++;
++
++		if (isp_subdev->sensor_info.buf_sent_cnt >=
++		    ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) {
++			ret = isp4if_send_command(ispif,
++						  ISP4FW_CMD_ID_START_STREAM,
++						  NULL, 0);
++			if (ret) {
++				dev_err(dev, "fail to START_STREAM");
++				goto error_release_buf_node;
++			}
++			isp_subdev->sensor_info.start_stream_cmd_sent = true;
++			isp_subdev->sensor_info.output_info.start_status =
++				ISP4SD_START_STATUS_STARTED;
++			isp_subdev->sensor_info.status =
++				ISP4SD_START_STATUS_STARTED;
++		} else {
++			dev_dbg(dev,
++				"no send start, required %u, buf sent %u\n",
++				ISP4SD_MIN_BUF_CNT_BEF_START_STREAM,
++				isp_subdev->sensor_info.buf_sent_cnt);
++		}
++	}
++
++	return 0;
++
++error_release_buf_node:
++	isp4if_dealloc_buffer_node(buf_node);
++	return ret;
++}
++
++static const struct v4l2_subdev_video_ops isp4sd_video_ops = {
++	.s_stream = v4l2_subdev_s_stream_helper,
++};
++
++static int isp4sd_set_pad_format(struct v4l2_subdev *sd,
++				 struct v4l2_subdev_state *sd_state,
++				 struct v4l2_subdev_format *format)
++{
++	struct isp4sd_output_info *stream_info =
++		&(to_isp4_subdev(sd)->sensor_info.output_info);
++	struct v4l2_mbus_framefmt *fmt;
++
++	fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
++
++	if (!fmt) {
++		dev_err(sd->dev, "fail to get state format\n");
++		return -EINVAL;
++	}
++
++	*fmt = format->format;
++	switch (fmt->code) {
++	case MEDIA_BUS_FMT_YUYV8_1X16:
++		stream_info->image_size = fmt->width * fmt->height * 2;
++		break;
++	case MEDIA_BUS_FMT_YUYV8_1_5X8:
++	default:
++		stream_info->image_size = fmt->width * fmt->height * 3 / 2;
++		break;
++	}
++
++	if (!stream_info->image_size) {
++		dev_err(sd->dev,
++			"fail set pad format,code 0x%x,width %u, height %u\n",
++			fmt->code, fmt->width, fmt->height);
++		return -EINVAL;
++	}
++
++	dev_dbg(sd->dev, "set pad format suc, code:%x w:%u h:%u size:%u\n",
++		fmt->code, fmt->width, fmt->height,
++		stream_info->image_size);
++
++	return 0;
++}
++
++static int isp4sd_enable_streams(struct v4l2_subdev *sd,
++				 struct v4l2_subdev_state *state, u32 pad,
++				 u64 streams_mask)
++{
++	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
++
++	return isp4sd_start_stream(isp_subdev, state, pad);
++}
++
++static int isp4sd_disable_streams(struct v4l2_subdev *sd,
++				  struct v4l2_subdev_state *state, u32 pad,
++				  u64 streams_mask)
++{
++	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
++
++	return isp4sd_stop_stream(isp_subdev, state, pad);
++}
++
++static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = {
++	.get_fmt = v4l2_subdev_get_fmt,
++	.set_fmt = isp4sd_set_pad_format,
++	.enable_streams = isp4sd_enable_streams,
++	.disable_streams = isp4sd_disable_streams,
++};
++
++static const struct v4l2_subdev_ops isp4sd_subdev_ops = {
++	.video = &isp4sd_video_ops,
++	.pad = &isp4sd_pad_ops,
++};
++
++int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev,
++		int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM])
++{
++	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++	struct device *dev = v4l2_dev->dev;
++	int ret;
++
++	isp_subdev->dev = dev;
++	v4l2_subdev_init(&isp_subdev->sdev, &isp4sd_subdev_ops);
++	isp_subdev->sdev.owner = THIS_MODULE;
++	isp_subdev->sdev.dev = dev;
++	snprintf(isp_subdev->sdev.name, sizeof(isp_subdev->sdev.name), "%s",
++		 dev_name(dev));
++
++	isp_subdev->sdev.entity.name = isp4sd_entity_name;
++	isp_subdev->sdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
++	isp_subdev->sdev_pad.flags = MEDIA_PAD_FL_SOURCE;
++	ret = media_entity_pads_init(&isp_subdev->sdev.entity, 1,
++				     &isp_subdev->sdev_pad);
++	if (ret) {
++		dev_err(dev, "fail to init isp4 subdev entity pad %d\n", ret);
++		return ret;
++	}
++
++	ret = v4l2_subdev_init_finalize(&isp_subdev->sdev);
++	if (ret < 0) {
++		dev_err(dev, "fail to init finalize isp4 subdev %d\n",
++			ret);
++		return ret;
++	}
++
++	ret = v4l2_device_register_subdev(v4l2_dev, &isp_subdev->sdev);
++	if (ret) {
++		dev_err(dev, "fail to register isp4 subdev to V4L2 device %d\n",
++			ret);
++		goto err_media_clean_up;
++	}
++
++	isp4if_init(ispif, dev, isp_subdev->mmio);
++
++	mutex_init(&isp_subdev->ops_mutex);
++	sensor_info->status = ISP4SD_START_STATUS_OFF;
++
++	/* create ISP enable gpio control */
++	isp_subdev->enable_gpio = devm_gpiod_get(isp_subdev->dev,
++						 "enable_isp",
++						 GPIOD_OUT_LOW);
++	if (IS_ERR(isp_subdev->enable_gpio)) {
++		ret = PTR_ERR(isp_subdev->enable_gpio);
++		dev_err(dev, "fail to get gpiod %d\n", ret);
++		goto err_subdev_unreg;
++	}
++
++	for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
++		isp_subdev->irq[i] = irq[i];
++
++	isp_subdev->host2fw_seq_num = 1;
++	ispif->status = ISP4IF_STATUS_PWR_OFF;
++
++	ret = isp4vid_dev_init(&isp_subdev->isp_vdev, &isp_subdev->sdev);
++	if (ret)
++		goto err_subdev_unreg;
++
++	return 0;
++
++err_subdev_unreg:
++	v4l2_device_unregister_subdev(&isp_subdev->sdev);
++err_media_clean_up:
++	v4l2_subdev_cleanup(&isp_subdev->sdev);
++	media_entity_cleanup(&isp_subdev->sdev.entity);
++	return ret;
++}
++
++void isp4sd_deinit(struct isp4_subdev *isp_subdev)
++{
++	struct isp4_interface *ispif = &isp_subdev->ispif;
++
++	isp4vid_dev_deinit(&isp_subdev->isp_vdev);
++	v4l2_device_unregister_subdev(&isp_subdev->sdev);
++	media_entity_cleanup(&isp_subdev->sdev.entity);
++	isp4if_deinit(ispif);
++	isp4sd_module_enable(isp_subdev, false);
++
++	ispif->status = ISP4IF_STATUS_PWR_OFF;
++}
+diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h
+new file mode 100644
+index 000000000000..20ea08a830af
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_subdev.h
+@@ -0,0 +1,127 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#ifndef _ISP4_SUBDEV_H_
++#define _ISP4_SUBDEV_H_
++
++#include <linux/debugfs.h>
++#include <linux/delay.h>
++#include <linux/firmware.h>
++#include <linux/pm_runtime.h>
++#include <linux/types.h>
++#include <linux/uaccess.h>
++#include <media/v4l2-device.h>
++
++#include "isp4_fw_cmd_resp.h"
++#include "isp4_hw_reg.h"
++#include "isp4_interface.h"
++#include "isp4_video.h"
++
++/*
++ * One is for none sensor specific response which is not used now.
++ * Another is for sensor specific response
++ */
++#define ISP4SD_MAX_FW_RESP_STREAM_NUM 2
++
++/* Indicates the ISP status */
++enum isp4sd_status {
++	ISP4SD_STATUS_PWR_OFF,
++	ISP4SD_STATUS_PWR_ON,
++	ISP4SD_STATUS_FW_RUNNING,
++	ISP4SD_STATUS_MAX
++};
++
++/* Indicates sensor and output stream status */
++enum isp4sd_start_status {
++	ISP4SD_START_STATUS_OFF,
++	ISP4SD_START_STATUS_STARTED,
++	ISP4SD_START_STATUS_START_FAIL,
++};
++
++struct isp4sd_img_buf_node {
++	struct list_head node;
++	struct isp4if_img_buf_info buf_info;
++};
++
++/* This is ISP output after processing Bayer raw sensor input */
++struct isp4sd_output_info {
++	enum isp4sd_start_status start_status;
++	u32 image_size;
++};
++
++/*
++ * Struct for sensor info used as ISP input or source.
++ * status: sensor status.
++ * output_info: ISP output after processing the sensor input.
++ * start_stream_cmd_sent: indicates if ISP4FW_CMD_ID_START_STREAM was sent
++ * to firmware.
++ * buf_sent_cnt: number of buffers sent to receive images.
++ */
++struct isp4sd_sensor_info {
++	struct isp4sd_output_info output_info;
++	enum isp4sd_start_status status;
++	bool start_stream_cmd_sent;
++	u32 buf_sent_cnt;
++};
++
++/*
++ * The thread is created by the driver to handle firmware responses which will
++ * be waken up when a firmware-to-driver response interrupt occurs.
++ */
++struct isp4sd_thread_handler {
++	struct task_struct *thread;
++	wait_queue_head_t waitq;
++	bool resp_ready;
++};
++
++struct isp4_subdev_thread_param {
++	u32 idx;
++	struct isp4_subdev *isp_subdev;
++};
++
++struct isp4_subdev {
++	struct v4l2_subdev sdev;
++	struct isp4_interface ispif;
++	struct isp4vid_dev isp_vdev;
++
++	struct media_pad sdev_pad;
++
++	enum isp4sd_status isp_status;
++	/* mutex used to synchronize the operation with firmware */
++	struct mutex ops_mutex;
++
++	struct isp4sd_thread_handler
++		fw_resp_thread[ISP4SD_MAX_FW_RESP_STREAM_NUM];
++
++	u32 host2fw_seq_num;
++
++	struct isp4sd_sensor_info sensor_info;
++
++	/* gpio descriptor */
++	struct gpio_desc *enable_gpio;
++	struct device *dev;
++	void __iomem *mmio;
++	struct isp4_subdev_thread_param
++		isp_resp_para[ISP4SD_MAX_FW_RESP_STREAM_NUM];
++	int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM];
++	bool irq_enabled;
++	/* spin lock to access ISP_SYS_INT0_EN exclusively */
++	spinlock_t irq_lock;
++#ifdef CONFIG_DEBUG_FS
++	bool enable_fw_log;
++	struct dentry *debugfs_dir;
++	char *fw_log_output;
++#endif
++};
++
++int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev,
++		int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]);
++void isp4sd_deinit(struct isp4_subdev *isp_subdev);
++int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd,
++			    struct isp4if_img_buf_info *buf_info);
++int isp4sd_pwron_and_init(struct v4l2_subdev *sd);
++int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd);
++
++#endif /* _ISP4_SUBDEV_H_ */
+diff --git a/drivers/media/platform/amd/isp4/isp4_video.c b/drivers/media/platform/amd/isp4/isp4_video.c
+new file mode 100644
+index 000000000000..0cebb39f98e1
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_video.c
+@@ -0,0 +1,797 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-mc.h>
++#include <media/videobuf2-vmalloc.h>
++
++#include "isp4_interface.h"
++#include "isp4_subdev.h"
++#include "isp4_video.h"
++
++#define ISP4VID_ISP_DRV_NAME "amd_isp_capture"
++#define ISP4VID_MAX_PREVIEW_FPS 30
++#define ISP4VID_DEFAULT_FMT V4L2_PIX_FMT_NV12
++
++#define ISP4VID_PAD_VIDEO_OUTPUT 0
++
++/* time perframe default */
++#define ISP4VID_ISP_TPF_DEFAULT isp4vid_tpfs[0]
++
++static const char *const isp4vid_video_dev_name = "Preview";
++
++/* Sizes must be in increasing order */
++static const struct v4l2_frmsize_discrete isp4vid_frmsize[] = {
++	{640, 360},
++	{640, 480},
++	{1280, 720},
++	{1280, 960},
++	{1920, 1080},
++	{1920, 1440},
++	{2560, 1440},
++	{2880, 1620},
++	{2880, 1624},
++	{2888, 1808},
++};
++
++static const u32 isp4vid_formats[] = {
++	V4L2_PIX_FMT_NV12,
++	V4L2_PIX_FMT_YUYV
++};
++
++/* time perframe list */
++static const struct v4l2_fract isp4vid_tpfs[] = {
++	{ 1, ISP4VID_MAX_PREVIEW_FPS }
++};
++
++void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev,
++			       const struct isp4if_img_buf_info *img_buf)
++{
++	struct isp4vid_capture_buffer *isp4vid_buf;
++	void *vbuf;
++
++	scoped_guard(mutex, &isp_vdev->buf_list_lock) {
++		isp4vid_buf = list_first_entry_or_null(&isp_vdev->buf_list,
++						       typeof(*isp4vid_buf),
++						       list);
++		if (!isp4vid_buf)
++			return;
++
++		vbuf = vb2_plane_vaddr(&isp4vid_buf->vb2.vb2_buf, 0);
++
++		if (vbuf != img_buf->planes[0].sys_addr) {
++			dev_err(isp_vdev->dev, "Invalid vbuf\n");
++			return;
++		}
++
++		list_del(&isp4vid_buf->list);
++	}
++
++	/* Fill the buffer */
++	isp4vid_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
++	isp4vid_buf->vb2.sequence = isp_vdev->sequence++;
++	isp4vid_buf->vb2.field = V4L2_FIELD_ANY;
++
++	vb2_set_plane_payload(&isp4vid_buf->vb2.vb2_buf,
++			      0, isp_vdev->format.sizeimage);
++
++	vb2_buffer_done(&isp4vid_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
++
++	dev_dbg(isp_vdev->dev, "call vb2_buffer_done(size=%u)\n",
++		isp_vdev->format.sizeimage);
++}
++
++static const struct v4l2_pix_format isp4vid_fmt_default = {
++	.width = 1920,
++	.height = 1080,
++	.pixelformat = ISP4VID_DEFAULT_FMT,
++	.field = V4L2_FIELD_NONE,
++	.colorspace = V4L2_COLORSPACE_SRGB,
++};
++
++static void isp4vid_capture_return_all_buffers(struct isp4vid_dev *isp_vdev,
++					       enum vb2_buffer_state state)
++{
++	struct isp4vid_capture_buffer *vbuf, *node;
++
++	scoped_guard(mutex, &isp_vdev->buf_list_lock) {
++		list_for_each_entry_safe(vbuf, node, &isp_vdev->buf_list, list)
++			vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
++		INIT_LIST_HEAD(&isp_vdev->buf_list);
++	}
++
++	dev_dbg(isp_vdev->dev, "call vb2_buffer_done(%d)\n", state);
++}
++
++static int isp4vid_vdev_link_validate(struct media_link *link)
++{
++	return 0;
++}
++
++static const struct media_entity_operations isp4vid_vdev_ent_ops = {
++	.link_validate = isp4vid_vdev_link_validate,
++};
++
++static const struct v4l2_file_operations isp4vid_vdev_fops = {
++	.owner = THIS_MODULE,
++	.open = v4l2_fh_open,
++	.release = vb2_fop_release,
++	.read = vb2_fop_read,
++	.poll = vb2_fop_poll,
++	.unlocked_ioctl = video_ioctl2,
++	.mmap = vb2_fop_mmap,
++};
++
++static int isp4vid_ioctl_querycap(struct file *file, void *fh,
++				  struct v4l2_capability *cap)
++{
++	struct isp4vid_dev *isp_vdev = video_drvdata(file);
++
++	strscpy(cap->driver, ISP4VID_ISP_DRV_NAME, sizeof(cap->driver));
++	snprintf(cap->card, sizeof(cap->card), "%s", ISP4VID_ISP_DRV_NAME);
++	cap->capabilities |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
++
++	dev_dbg(isp_vdev->dev, "%s|capabilities=0x%X\n", isp_vdev->vdev.name,
++		cap->capabilities);
++
++	return 0;
++}
++
++static int isp4vid_g_fmt_vid_cap(struct file *file, void *priv,
++				 struct v4l2_format *f)
++{
++	struct isp4vid_dev *isp_vdev = video_drvdata(file);
++
++	f->fmt.pix = isp_vdev->format;
++
++	return 0;
++}
++
++static int isp4vid_fill_buffer_size(struct v4l2_pix_format *fmt)
++{
++	int ret = 0;
++
++	switch (fmt->pixelformat) {
++	case V4L2_PIX_FMT_NV12:
++		fmt->bytesperline = fmt->width;
++		fmt->sizeimage = fmt->bytesperline * fmt->height * 3 / 2;
++		break;
++	case V4L2_PIX_FMT_YUYV:
++		fmt->bytesperline = fmt->width * 2;
++		fmt->sizeimage = fmt->bytesperline * fmt->height;
++		break;
++	default:
++		ret = -EINVAL;
++		break;
++	}
++
++	return ret;
++}
++
++static int isp4vid_try_fmt_vid_cap(struct file *file, void *priv,
++				   struct v4l2_format *f)
++{
++	struct isp4vid_dev *isp_vdev = video_drvdata(file);
++	struct v4l2_pix_format *format = &f->fmt.pix;
++	const struct v4l2_frmsize_discrete *fsz;
++	size_t i;
++
++	/*
++	 * Check if the hardware supports the requested format, use the default
++	 * format otherwise.
++	 */
++	for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++)
++		if (isp4vid_formats[i] == format->pixelformat)
++			break;
++
++	if (i == ARRAY_SIZE(isp4vid_formats))
++		format->pixelformat = ISP4VID_DEFAULT_FMT;
++
++	switch (format->pixelformat) {
++	case V4L2_PIX_FMT_NV12:
++	case V4L2_PIX_FMT_YUYV:
++		fsz = v4l2_find_nearest_size(isp4vid_frmsize,
++					     ARRAY_SIZE(isp4vid_frmsize),
++					     width, height, format->width,
++					     format->height);
++		format->width = fsz->width;
++		format->height = fsz->height;
++		break;
++	default:
++		dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n",
++			isp_vdev->vdev.name,
++			format->pixelformat);
++		return -EINVAL;
++	}
++
++	/*
++	 * There is no need to check the return value, as failure will never
++	 * happen here
++	 */
++	isp4vid_fill_buffer_size(format);
++
++	if (format->field == V4L2_FIELD_ANY)
++		format->field = isp4vid_fmt_default.field;
++
++	if (format->colorspace == V4L2_COLORSPACE_DEFAULT)
++		format->colorspace = isp4vid_fmt_default.colorspace;
++
++	return 0;
++}
++
++static int isp4vid_set_fmt_2_isp(struct v4l2_subdev *sdev,
++				 struct v4l2_pix_format *pix_fmt)
++{
++	struct v4l2_subdev_format fmt = {};
++
++	switch (pix_fmt->pixelformat) {
++	case V4L2_PIX_FMT_NV12:
++		fmt.format.code = MEDIA_BUS_FMT_YUYV8_1_5X8;
++		break;
++	case V4L2_PIX_FMT_YUYV:
++		fmt.format.code = MEDIA_BUS_FMT_YUYV8_1X16;
++		break;
++	default:
++		return -EINVAL;
++	}
++	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++	fmt.pad = ISP4VID_PAD_VIDEO_OUTPUT;
++	fmt.format.width = pix_fmt->width;
++	fmt.format.height = pix_fmt->height;
++	return v4l2_subdev_call(sdev, pad, set_fmt, NULL, &fmt);
++}
++
++static int isp4vid_s_fmt_vid_cap(struct file *file, void *priv,
++				 struct v4l2_format *f)
++{
++	struct isp4vid_dev *isp_vdev = video_drvdata(file);
++	int ret;
++
++	/* Do not change the format while stream is on */
++	if (vb2_is_busy(&isp_vdev->vbq))
++		return -EBUSY;
++
++	ret = isp4vid_try_fmt_vid_cap(file, priv, f);
++	if (ret)
++		return ret;
++
++	dev_dbg(isp_vdev->dev, "%s|width height:%ux%u->%ux%u\n",
++		isp_vdev->vdev.name,
++		isp_vdev->format.width, isp_vdev->format.height,
++		f->fmt.pix.width, f->fmt.pix.height);
++	dev_dbg(isp_vdev->dev, "%s|pixelformat:0x%x-0x%x\n",
++		isp_vdev->vdev.name, isp_vdev->format.pixelformat,
++		f->fmt.pix.pixelformat);
++	dev_dbg(isp_vdev->dev, "%s|bytesperline:%u->%u\n",
++		isp_vdev->vdev.name, isp_vdev->format.bytesperline,
++		f->fmt.pix.bytesperline);
++	dev_dbg(isp_vdev->dev, "%s|sizeimage:%u->%u\n",
++		isp_vdev->vdev.name, isp_vdev->format.sizeimage,
++		f->fmt.pix.sizeimage);
++
++	isp_vdev->format = f->fmt.pix;
++	ret = isp4vid_set_fmt_2_isp(isp_vdev->isp_sdev, &isp_vdev->format);
++
++	return ret;
++}
++
++static int isp4vid_enum_fmt_vid_cap(struct file *file, void *priv,
++				    struct v4l2_fmtdesc *f)
++{
++	struct isp4vid_dev *isp_vdev = video_drvdata(file);
++
++	switch (f->index) {
++	case 0:
++		f->pixelformat = V4L2_PIX_FMT_NV12;
++		break;
++	case 1:
++		f->pixelformat = V4L2_PIX_FMT_YUYV;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	dev_dbg(isp_vdev->dev, "%s|index=%d, pixelformat=0x%X\n",
++		isp_vdev->vdev.name, f->index, f->pixelformat);
++
++	return 0;
++}
++
++static int isp4vid_enum_framesizes(struct file *file, void *fh,
++				   struct v4l2_frmsizeenum *fsize)
++{
++	struct isp4vid_dev *isp_vdev = video_drvdata(file);
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) {
++		if (isp4vid_formats[i] == fsize->pixel_format)
++			break;
++	}
++
++	if (i == ARRAY_SIZE(isp4vid_formats))
++		return -EINVAL;
++
++	if (fsize->index < ARRAY_SIZE(isp4vid_frmsize)) {
++		fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
++		fsize->discrete = isp4vid_frmsize[fsize->index];
++		dev_dbg(isp_vdev->dev, "%s|size[%d]=%dx%d\n",
++			isp_vdev->vdev.name, fsize->index,
++			fsize->discrete.width, fsize->discrete.height);
++	} else {
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int isp4vid_ioctl_enum_frameintervals(struct file *file, void *priv,
++					     struct v4l2_frmivalenum *fival)
++{
++	struct isp4vid_dev *isp_vdev = video_drvdata(file);
++	size_t i;
++
++	if (fival->index >= ARRAY_SIZE(isp4vid_tpfs))
++		return -EINVAL;
++
++	for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++)
++		if (isp4vid_formats[i] == fival->pixel_format)
++			break;
++
++	if (i == ARRAY_SIZE(isp4vid_formats))
++		return -EINVAL;
++
++	for (i = 0; i < ARRAY_SIZE(isp4vid_frmsize); i++)
++		if (isp4vid_frmsize[i].width == fival->width &&
++		    isp4vid_frmsize[i].height == fival->height)
++			break;
++
++	if (i == ARRAY_SIZE(isp4vid_frmsize))
++		return -EINVAL;
++
++	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
++	fival->discrete = isp4vid_tpfs[fival->index];
++	v4l2_simplify_fraction(&fival->discrete.numerator,
++			       &fival->discrete.denominator, 8, 333);
++
++	dev_dbg(isp_vdev->dev, "%s|interval[%d]=%d/%d\n",
++		isp_vdev->vdev.name, fival->index,
++		fival->discrete.numerator,
++		fival->discrete.denominator);
++
++	return 0;
++}
++
++static int isp4vid_ioctl_g_param(struct file *file, void *priv,
++				 struct v4l2_streamparm *param)
++{
++	struct v4l2_captureparm *capture = &param->parm.capture;
++	struct isp4vid_dev *isp_vdev = video_drvdata(file);
++
++	if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++		return -EINVAL;
++
++	capture->capability   = V4L2_CAP_TIMEPERFRAME;
++	capture->timeperframe = isp_vdev->timeperframe;
++	capture->readbuffers  = 0;
++
++	dev_dbg(isp_vdev->dev, "%s|timeperframe=%d/%d\n", isp_vdev->vdev.name,
++		capture->timeperframe.numerator,
++		capture->timeperframe.denominator);
++
++	return 0;
++}
++
++static const struct v4l2_ioctl_ops isp4vid_vdev_ioctl_ops = {
++	.vidioc_querycap            = isp4vid_ioctl_querycap,
++	.vidioc_enum_fmt_vid_cap    = isp4vid_enum_fmt_vid_cap,
++	.vidioc_g_fmt_vid_cap       = isp4vid_g_fmt_vid_cap,
++	.vidioc_s_fmt_vid_cap       = isp4vid_s_fmt_vid_cap,
++	.vidioc_try_fmt_vid_cap     = isp4vid_try_fmt_vid_cap,
++	.vidioc_reqbufs             = vb2_ioctl_reqbufs,
++	.vidioc_querybuf            = vb2_ioctl_querybuf,
++	.vidioc_qbuf                = vb2_ioctl_qbuf,
++	.vidioc_expbuf              = vb2_ioctl_expbuf,
++	.vidioc_dqbuf               = vb2_ioctl_dqbuf,
++	.vidioc_create_bufs         = vb2_ioctl_create_bufs,
++	.vidioc_prepare_buf         = vb2_ioctl_prepare_buf,
++	.vidioc_streamon            = vb2_ioctl_streamon,
++	.vidioc_streamoff           = vb2_ioctl_streamoff,
++	.vidioc_g_parm              = isp4vid_ioctl_g_param,
++	.vidioc_s_parm              = isp4vid_ioctl_g_param,
++	.vidioc_enum_framesizes     = isp4vid_enum_framesizes,
++	.vidioc_enum_frameintervals = isp4vid_ioctl_enum_frameintervals,
++};
++
++static unsigned int isp4vid_get_image_size(struct v4l2_pix_format *fmt)
++{
++	switch (fmt->pixelformat) {
++	case V4L2_PIX_FMT_NV12:
++		return fmt->width * fmt->height * 3 / 2;
++	case V4L2_PIX_FMT_YUYV:
++		return fmt->width * fmt->height * 2;
++	default:
++		return 0;
++	}
++}
++
++static int isp4vid_qops_queue_setup(struct vb2_queue *vq,
++				    unsigned int *nbuffers,
++				    unsigned int *nplanes, unsigned int sizes[],
++				    struct device *alloc_devs[])
++{
++	struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq);
++	unsigned int q_num_bufs = vb2_get_num_buffers(vq);
++
++	if (*nplanes > 1) {
++		dev_err(isp_vdev->dev,
++			"fail to setup queue, no mplane supported %u\n",
++			*nplanes);
++		return -EINVAL;
++	}
++
++	if (*nplanes == 1) {
++		unsigned int size;
++
++		size = isp4vid_get_image_size(&isp_vdev->format);
++		if (sizes[0] < size) {
++			dev_err(isp_vdev->dev,
++				"fail for small plane size %u, %u expected\n",
++				sizes[0], size);
++			return -EINVAL;
++		}
++	}
++
++	if (q_num_bufs + *nbuffers < ISP4IF_MAX_STREAM_BUF_COUNT)
++		*nbuffers = ISP4IF_MAX_STREAM_BUF_COUNT - q_num_bufs;
++
++	switch (isp_vdev->format.pixelformat) {
++	case V4L2_PIX_FMT_NV12:
++	case V4L2_PIX_FMT_YUYV: {
++		*nplanes = 1;
++		sizes[0] = max(sizes[0], isp_vdev->format.sizeimage);
++		isp_vdev->format.sizeimage = sizes[0];
++	}
++	break;
++	default:
++		dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n",
++			isp_vdev->vdev.name, isp_vdev->format.pixelformat);
++		return -EINVAL;
++	}
++
++	dev_dbg(isp_vdev->dev, "%s|*nbuffers=%u *nplanes=%u sizes[0]=%u\n",
++		isp_vdev->vdev.name,
++		*nbuffers, *nplanes, sizes[0]);
++
++	return 0;
++}
++
++static void isp4vid_qops_buffer_queue(struct vb2_buffer *vb)
++{
++	struct isp4vid_capture_buffer *buf =
++		container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf);
++	struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue);
++	struct isp4if_img_buf_info *img_buf = &buf->img_buf;
++	void *vaddr = vb2_plane_vaddr(vb, 0);
++
++	dev_dbg(isp_vdev->dev, "queue buf, vaddr %p, gpuva 0x%llx, size %u\n",
++		vaddr, buf->gpu_addr, vb->planes[0].length);
++
++	switch (isp_vdev->format.pixelformat) {
++	case V4L2_PIX_FMT_NV12: {
++		u32 y_size = isp_vdev->format.sizeimage / 3 * 2;
++		u32 uv_size = isp_vdev->format.sizeimage / 3;
++
++		img_buf->planes[0].len = y_size;
++		img_buf->planes[0].sys_addr = vaddr;
++		img_buf->planes[0].mc_addr = buf->gpu_addr;
++
++		dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n",
++			img_buf->planes[0].mc_addr,
++			img_buf->planes[0].len);
++
++		img_buf->planes[1].len = uv_size;
++		img_buf->planes[1].sys_addr = vaddr + y_size;
++		img_buf->planes[1].mc_addr = buf->gpu_addr + y_size;
++
++		dev_dbg(isp_vdev->dev, "img_buf[1]: mc=0x%llx size=%u\n",
++			img_buf->planes[1].mc_addr,
++			img_buf->planes[1].len);
++
++		img_buf->planes[2].len = 0;
++	}
++	break;
++	case V4L2_PIX_FMT_YUYV: {
++		img_buf->planes[0].len = isp_vdev->format.sizeimage;
++		img_buf->planes[0].sys_addr = vaddr;
++		img_buf->planes[0].mc_addr = buf->gpu_addr;
++
++		dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n",
++			img_buf->planes[0].mc_addr,
++			img_buf->planes[0].len);
++
++		img_buf->planes[1].len = 0;
++		img_buf->planes[2].len = 0;
++	}
++	break;
++	default:
++		dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n",
++			isp_vdev->vdev.name, isp_vdev->format.pixelformat);
++		return;
++	}
++
++	if (isp_vdev->stream_started)
++		isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, img_buf);
++
++	scoped_guard(mutex, &isp_vdev->buf_list_lock)
++		list_add_tail(&buf->list, &isp_vdev->buf_list);
++}
++
++static int isp4vid_qops_start_streaming(struct vb2_queue *vq,
++					unsigned int count)
++{
++	struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq);
++	struct isp4vid_capture_buffer *isp4vid_buf;
++	struct media_entity *entity;
++	struct v4l2_subdev *subdev;
++	struct media_pad *pad;
++	int ret = 0;
++
++	isp_vdev->sequence = 0;
++
++	ret = isp4sd_pwron_and_init(isp_vdev->isp_sdev);
++	if (ret) {
++		dev_err(isp_vdev->dev, "power up isp fail %d\n", ret);
++		goto release_buffers;
++	}
++
++	entity = &isp_vdev->vdev.entity;
++	while (1) {
++		pad = &entity->pads[0];
++		if (!(pad->flags & MEDIA_PAD_FL_SINK))
++			break;
++
++		pad = media_pad_remote_pad_first(pad);
++		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++			break;
++
++		entity = pad->entity;
++		subdev = media_entity_to_v4l2_subdev(entity);
++
++		ret = v4l2_subdev_call(subdev, video, s_stream, 1);
++		if (ret < 0 && ret != -ENOIOCTLCMD) {
++			dev_dbg(isp_vdev->dev, "fail start streaming: %s %d\n",
++				subdev->name, ret);
++			goto release_buffers;
++		}
++	}
++
++	list_for_each_entry(isp4vid_buf, &isp_vdev->buf_list, list)
++		isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev,
++					&isp4vid_buf->img_buf);
++
++	isp_vdev->stream_started = true;
++
++	return 0;
++
++release_buffers:
++	isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_QUEUED);
++	return ret;
++}
++
++static void isp4vid_qops_stop_streaming(struct vb2_queue *vq)
++{
++	struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq);
++	struct media_entity *entity;
++	struct v4l2_subdev *subdev;
++	struct media_pad *pad;
++	int ret;
++
++	entity = &isp_vdev->vdev.entity;
++	while (1) {
++		pad = &entity->pads[0];
++		if (!(pad->flags & MEDIA_PAD_FL_SINK))
++			break;
++
++		pad = media_pad_remote_pad_first(pad);
++		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++			break;
++
++		entity = pad->entity;
++		subdev = media_entity_to_v4l2_subdev(entity);
++
++		ret = v4l2_subdev_call(subdev, video, s_stream, 0);
++
++		if (ret < 0 && ret != -ENOIOCTLCMD)
++			dev_dbg(isp_vdev->dev, "fail stop streaming: %s %d\n",
++				subdev->name, ret);
++	}
++
++	isp_vdev->stream_started = false;
++	isp4sd_pwroff_and_deinit(isp_vdev->isp_sdev);
++
++	/* Release all active buffers */
++	isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_ERROR);
++}
++
++static int isp4vid_qops_buf_init(struct vb2_buffer *vb)
++{
++	struct isp4vid_capture_buffer *buf =
++		container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf);
++	struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue);
++	void *mem_priv = vb->planes[0].mem_priv;
++	struct device *dev = isp_vdev->dev;
++	u64 gpu_addr;
++	void *bo;
++	int ret;
++
++	if (vb->planes[0].dbuf) {
++		buf->dbuf = vb->planes[0].dbuf;
++	} else {
++		/*
++		 * HAS_DMA is a Kconfig dependency so CONFIG_HAS_DMA is always
++		 * defined when this driver is compiled. The #else branch is
++		 * kept as a safeguard in case the dependency is ever removed.
++		 */
++#ifdef CONFIG_HAS_DMA
++		buf->dbuf = vb2_vmalloc_memops.get_dmabuf(vb, mem_priv, 0);
++		if (IS_ERR_OR_NULL(buf->dbuf)) {
++			dev_err(dev, "fail to get dma buf\n");
++			return -EINVAL;
++		}
++#else
++		dev_err(dev, "get dmabuf fail -- CONFIG_HAS_DMA not defined\n");
++		buf->dbuf = NULL;
++		return -EINVAL;
++#endif
++	}
++
++	/* create isp user BO and obtain gpu_addr */
++	ret = isp_user_buffer_alloc(dev, buf->dbuf, &bo, &gpu_addr);
++	if (ret) {
++		dev_err(dev, "fail to create isp user BO\n");
++		if (!vb->planes[0].dbuf) {
++			dma_buf_put(buf->dbuf);
++			buf->dbuf = NULL;
++		}
++
++		return ret;
++	}
++
++	buf->bo = bo;
++	buf->gpu_addr = gpu_addr;
++	return 0;
++}
++
++static void isp4vid_qops_buf_cleanup(struct vb2_buffer *vb)
++{
++	struct isp4vid_capture_buffer *buf =
++		container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf);
++
++	if (buf->bo) {
++		isp_user_buffer_free(buf->bo);
++		buf->bo = NULL;
++	}
++
++	/*
++	 * Only put dmabufs we obtained ourselves via get_dmabuf, not ones
++	 * provided by the framework for DMABUF import
++	 */
++	if (buf->dbuf && buf->dbuf != vb->planes[0].dbuf)
++		dma_buf_put(buf->dbuf);
++
++	buf->dbuf = NULL;
++}
++
++static const struct vb2_ops isp4vid_qops = {
++	.queue_setup = isp4vid_qops_queue_setup,
++	.buf_init = isp4vid_qops_buf_init,
++	.buf_cleanup = isp4vid_qops_buf_cleanup,
++	.start_streaming = isp4vid_qops_start_streaming,
++	.stop_streaming = isp4vid_qops_stop_streaming,
++	.buf_queue = isp4vid_qops_buffer_queue,
++};
++
++int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd)
++{
++	const char *vdev_name = isp4vid_video_dev_name;
++	struct v4l2_device *v4l2_dev;
++	struct video_device *vdev;
++	struct vb2_queue *q;
++	int ret;
++
++	if (!isp_vdev || !isp_sd || !isp_sd->v4l2_dev)
++		return -EINVAL;
++
++	v4l2_dev = isp_sd->v4l2_dev;
++	vdev = &isp_vdev->vdev;
++
++	isp_vdev->isp_sdev = isp_sd;
++	isp_vdev->dev = v4l2_dev->dev;
++
++	/* Initialize the vb2_queue struct */
++	mutex_init(&isp_vdev->vbq_lock);
++	q = &isp_vdev->vbq;
++	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++	q->io_modes = VB2_MMAP | VB2_DMABUF;
++	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++	q->buf_struct_size = sizeof(struct isp4vid_capture_buffer);
++	q->min_queued_buffers = 2;
++	q->ops = &isp4vid_qops;
++	q->drv_priv = isp_vdev;
++	q->mem_ops = &vb2_vmalloc_memops;
++	q->lock = &isp_vdev->vbq_lock;
++	q->dev = v4l2_dev->dev;
++	ret = vb2_queue_init(q);
++	if (ret) {
++		dev_err(v4l2_dev->dev, "vb2_queue_init error:%d\n", ret);
++		return ret;
++	}
++
++	/* Initialize buffer list and its lock */
++	mutex_init(&isp_vdev->buf_list_lock);
++	INIT_LIST_HEAD(&isp_vdev->buf_list);
++
++	/* Set default frame format */
++	isp_vdev->format = isp4vid_fmt_default;
++	isp_vdev->timeperframe = ISP4VID_ISP_TPF_DEFAULT;
++	v4l2_simplify_fraction(&isp_vdev->timeperframe.numerator,
++			       &isp_vdev->timeperframe.denominator, 8, 333);
++
++	ret = isp4vid_fill_buffer_size(&isp_vdev->format);
++	if (ret) {
++		dev_err(v4l2_dev->dev, "fail to fill buffer size: %d\n", ret);
++		goto err_release_vb2_queue;
++	}
++
++	ret = isp4vid_set_fmt_2_isp(isp_sd, &isp_vdev->format);
++	if (ret) {
++		dev_err(v4l2_dev->dev, "fail init format :%d\n", ret);
++		goto err_release_vb2_queue;
++	}
++
++	/* Initialize the video_device struct */
++	isp_vdev->vdev.entity.name = vdev_name;
++	isp_vdev->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
++	isp_vdev->vdev_pad.flags = MEDIA_PAD_FL_SINK;
++	ret = media_entity_pads_init(&isp_vdev->vdev.entity, 1,
++				     &isp_vdev->vdev_pad);
++
++	if (ret) {
++		dev_err(v4l2_dev->dev, "init media entity pad fail:%d\n", ret);
++		goto err_release_vb2_queue;
++	}
++
++	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE |
++			    V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
++	vdev->entity.ops = &isp4vid_vdev_ent_ops;
++	vdev->release = video_device_release_empty;
++	vdev->fops = &isp4vid_vdev_fops;
++	vdev->ioctl_ops = &isp4vid_vdev_ioctl_ops;
++	vdev->lock = NULL;
++	vdev->queue = q;
++	vdev->v4l2_dev = v4l2_dev;
++	vdev->vfl_dir = VFL_DIR_RX;
++	strscpy(vdev->name, vdev_name, sizeof(vdev->name));
++	video_set_drvdata(vdev, isp_vdev);
++
++	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
++	if (ret) {
++		dev_err(v4l2_dev->dev, "register video device fail:%d\n", ret);
++		goto err_entity_cleanup;
++	}
++
++	return 0;
++
++err_entity_cleanup:
++	media_entity_cleanup(&isp_vdev->vdev.entity);
++err_release_vb2_queue:
++	vb2_queue_release(q);
++	return ret;
++}
++
++void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev)
++{
++	vb2_video_unregister_device(&isp_vdev->vdev);
++}
+diff --git a/drivers/media/platform/amd/isp4/isp4_video.h b/drivers/media/platform/amd/isp4/isp4_video.h
+new file mode 100644
+index 000000000000..c66451e26166
+--- /dev/null
++++ b/drivers/media/platform/amd/isp4/isp4_video.h
+@@ -0,0 +1,57 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2025 Advanced Micro Devices, Inc.
++ */
++
++#ifndef _ISP4_VIDEO_H_
++#define _ISP4_VIDEO_H_
++
++#include <media/v4l2-dev.h>
++#include <media/videobuf2-memops.h>
++
++#include "isp4_interface.h"
++
++struct isp4vid_capture_buffer {
++	/*
++	 * struct vb2_v4l2_buffer must be the first element
++	 * the videobuf2 framework will allocate this struct based on
++	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
++	 * memory as a vb2_buffer
++	 */
++	struct vb2_v4l2_buffer vb2;
++	struct isp4if_img_buf_info img_buf;
++	struct list_head list;
++	struct dma_buf *dbuf;
++	void *bo;
++	u64 gpu_addr;
++};
++
++struct isp4vid_dev {
++	struct video_device vdev;
++	struct media_pad vdev_pad;
++	struct v4l2_pix_format format;
++
++	/* mutex that protects vbq */
++	struct mutex vbq_lock;
++	struct vb2_queue vbq;
++
++	/* mutex that protects buf_list */
++	struct mutex buf_list_lock;
++	struct list_head buf_list;
++
++	u32 sequence;
++	bool stream_started;
++
++	struct device *dev;
++	struct v4l2_subdev *isp_sdev;
++	struct v4l2_fract timeperframe;
++};
++
++int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd);
++
++void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev);
++
++void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev,
++			       const struct isp4if_img_buf_info *img_buf);
++
++#endif /* _ISP4_VIDEO_H_ */
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index dd0025d3914e..e57d10dc843c 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -4491,6 +4491,30 @@ static void quirk_aspeed_pci_bridge_no_alias(struct pci_dev *pdev)
+ }
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASPEED, 0x1150, quirk_aspeed_pci_bridge_no_alias);
+ 
++/*
++ * PCI BAR 5 is not setup correctly for the on-board AHCI controller
++ * on Broadcom's Vulcan processor. Added a quirk to fix BAR 5 by
++ * using BAR 4's resources which are populated correctly and NOT
++ * actually used by the AHCI controller.
++ */
++static void quirk_fix_vulcan_ahci_bars(struct pci_dev *dev)
++{
++	struct resource *r =  &dev->resource[4];
++
++	if (!(r->flags & IORESOURCE_MEM) || (r->start == 0))
++		return;
++
++	/* Set BAR5 resource to BAR4 */
++	dev->resource[5] = *r;
++
++	/* Update BAR5 in pci config space */
++	pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, r->start);
++
++	/* Clear BAR4's resource */
++	memset(r, 0, sizeof(*r));
++}
++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9027, quirk_fix_vulcan_ahci_bars);
++
+ /*
+  * Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
+  * class code.  Fix it.
+diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
+index 115bb37577a1..adff564bf3fd 100644
+--- a/drivers/platform/x86/intel/int3472/discrete.c
++++ b/drivers/platform/x86/intel/int3472/discrete.c
+@@ -164,6 +164,24 @@ static const struct int3472_gpio_map int3472_gpio_map[] = {
+ 		.con_id = "dvdd",
+ 		.enable_time_us = 45 * USEC_PER_MSEC,
+ 	},
++	{	/* imx471 expects "vana" as con_id for power enable */
++		.hid = "SONY471A",
++		.type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
++		.type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
++		.con_id = "vana",
++		.enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
++	},
++	{
++		/*
++		 * imx471 (on Lenovo ThinkPads X1 G14) expects "vana" as con_id
++		 * for power enable
++		 */
++		.hid = "TBE20A0",
++		.type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
++		.type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
++		.con_id = "vana",
++		.enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
++	},
+ };
+ 
+ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type,
+diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
+index 24960ba9caa9..32597cdb72ef 100644
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -5894,6 +5894,13 @@ static void hub_event(struct work_struct *work)
+ 			(u16) hub->change_bits[0],
+ 			(u16) hub->event_bits[0]);
+ 
++	/* Don't disconnect USB-SATA on TrimSlice */
++	if (strcmp(dev_name(hdev->bus->controller), "tegra-ehci.0") == 0) {
++		if ((hdev->state == 7) && (hub->change_bits[0] == 0) &&
++				(hub->event_bits[0] == 0x2))
++			hub->event_bits[0] = 0;
++	}
++
+ 	/* Lock the device, then check to see if we were
+ 	 * disconnected while waiting for the lock to succeed. */
+ 	usb_lock_device(hdev);
+diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c
+index 541e33f33167..7fb28c8afef8 100644
+--- a/fs/xfs/libxfs/xfs_exchmaps.c
++++ b/fs/xfs/libxfs/xfs_exchmaps.c
+@@ -959,6 +959,16 @@ xmi_can_exchange_reflink_flags(
+ {
+ 	struct xfs_mount		*mp = req->ip1->i_mount;
+ 
++	/*
++	 * The INO1_WRITTEN optimization can skip exchanging hole and
++	 * unwritten mappings, which means we cannot guarantee that all
++	 * shared extents actually moved to the other file.  Clearing the
++	 * reflink flag of an inode that still holds shared extents breaks
++	 * the CoW write path, so refuse to exchange the flags in that case.
++	 */
++	if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN)
++		return false;
++
+ 	if (hweight32(reflink_state) != 1)
+ 		return false;
+ 	if (req->startoff1 != 0 || req->startoff2 != 0)
+diff --git a/include/linux/crypto.h b/include/linux/crypto.h
+index a2137e19be7d..79ccb6fabc10 100644
+--- a/include/linux/crypto.h
++++ b/include/linux/crypto.h
+@@ -152,6 +152,8 @@
+ #define CRYPTO_TFM_REQ_MAY_BACKLOG	0x00000400
+ #define CRYPTO_TFM_REQ_ON_STACK		0x00000800
+ 
++#define CRYPTO_TFM_FIPS_COMPLIANCE	0x80000000
++
+ /*
+  * Miscellaneous stuff.
+  */
+diff --git a/include/linux/efi.h b/include/linux/efi.h
+index ccbc35479684..8eb9a1276ba7 100644
+--- a/include/linux/efi.h
++++ b/include/linux/efi.h
+@@ -45,6 +45,8 @@ struct screen_info;
+ #define EFI_ABORTED		(21 | (1UL << (BITS_PER_LONG-1)))
+ #define EFI_SECURITY_VIOLATION	(26 | (1UL << (BITS_PER_LONG-1)))
+ 
++#define EFI_IS_ERROR(x)		((x) & (1UL << (BITS_PER_LONG-1)))
++
+ typedef unsigned long efi_status_t;
+ typedef u8 efi_bool_t;
+ typedef u16 efi_char16_t;		/* UNICODE character */
+@@ -847,6 +849,14 @@ extern int __init parse_efi_signature_list(
+ #define EFI_MEM_ATTR		9	/* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */
+ #define EFI_MEM_NO_SOFT_RESERVE	10	/* Is the kernel configured to ignore soft reservations? */
+ #define EFI_PRESERVE_BS_REGIONS	11	/* Are EFI boot-services memory segments available? */
++#define EFI_SECURE_BOOT		12	/* Are we in Secure Boot mode? */
++
++enum efi_secureboot_mode {
++	efi_secureboot_mode_unset,
++	efi_secureboot_mode_unknown,
++	efi_secureboot_mode_disabled,
 +	efi_secureboot_mode_enabled,
 +};
  
@@ -2793,159 +6756,6 @@ index 590ba29c85ab..02153d857531 100644
  }
  
  int module_sig_check(struct load_info *info, int flags)
-diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
-index 5d2d19473a82..46f7b33b565b 100644
---- a/kernel/sched/ext.c
-+++ b/kernel/sched/ext.c
-@@ -2985,24 +2985,38 @@ static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first)
- 
- 	/*
- 	 * @p is getting newly scheduled or got kicked after someone updated its
--	 * slice. Refresh whether tick can be stopped. See scx_can_stop_tick().
-+	 * slice. Update SCX_RQ_CAN_STOP_TICK to reflect whether the tick can be
-+	 * stopped. See scx_can_stop_tick().
-+	 *
-+	 * Moreover, refresh the load_avgs just when transitioning in and out of
-+	 * nohz. In the future, we might want to add a mechanism to update
-+	 * load_avgs periodically on tick-stopped CPUs.
- 	 */
--	if ((p->scx.slice == SCX_SLICE_INF) !=
--	    (bool)(rq->scx.flags & SCX_RQ_CAN_STOP_TICK)) {
--		if (p->scx.slice == SCX_SLICE_INF)
-+	if (p->scx.slice == SCX_SLICE_INF) {
-+		if (!(rq->scx.flags & SCX_RQ_CAN_STOP_TICK)) {
-+			/*
-+			 * Bypass mode always assigns finite slices, so @p
-+			 * can't have an infinite slice while bypassing.
-+			 * Therefore, sched_update_tick_dependency() can safely
-+			 * evaluate the outgoing task.
-+			 */
- 			rq->scx.flags |= SCX_RQ_CAN_STOP_TICK;
--		else
--			rq->scx.flags &= ~SCX_RQ_CAN_STOP_TICK;
-+			sched_update_tick_dependency(rq);
- 
--		sched_update_tick_dependency(rq);
-+			update_other_load_avgs(rq);
-+		}
-+	} else {
-+		if (rq->scx.flags & SCX_RQ_CAN_STOP_TICK) {
-+			rq->scx.flags &= ~SCX_RQ_CAN_STOP_TICK;
-+			update_other_load_avgs(rq);
-+		}
- 
- 		/*
--		 * For now, let's refresh the load_avgs just when transitioning
--		 * in and out of nohz. In the future, we might want to add a
--		 * mechanism which calls the following periodically on
--		 * tick-stopped CPUs.
-+		 * @rq still references the outgoing scheduling context. A finite
-+		 * slice is sufficient by itself to require the tick.
- 		 */
--		update_other_load_avgs(rq);
-+		if (tick_nohz_full_cpu(cpu_of(rq)))
-+			tick_nohz_dep_set_cpu(cpu_of(rq), TICK_DEP_BIT_SCHED);
- 	}
- }
- 
-@@ -3097,9 +3111,14 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
- 		 * sched_class, %SCX_OPS_ENQ_LAST must be set. Tell
- 		 * ops.enqueue() that @p is the only one available for this cpu,
- 		 * which should trigger an explicit follow-up scheduling event.
-+		 *
-+		 * Core scheduling can force this CPU idle while @p stays
-+		 * runnable. @p's cookie then won't match the core's, so skip
-+		 * the warning in that case.
- 		 */
- 		if (next && sched_class_above(&ext_sched_class, next->sched_class)) {
--			WARN_ON_ONCE(!(sch->ops.flags & SCX_OPS_ENQ_LAST));
-+			WARN_ON_ONCE(sched_cpu_cookie_match(rq, p) &&
-+				     !(sch->ops.flags & SCX_OPS_ENQ_LAST));
- 			do_enqueue_task(rq, p, SCX_ENQ_LAST, -1);
- 		} else {
- 			do_enqueue_task(rq, p, 0, -1);
-@@ -3904,6 +3923,17 @@ static void reweight_task_scx(struct rq *rq, struct task_struct *p,
- 	if (task_dead_and_done(p))
- 		return;
- 
-+	/*
-+	 * When switching sched_class away from SCX, reweight_task_scx()
-+	 * is called _after_ scx_disable_task(). Skip calling ops.set_weight()
-+	 * since the BPF scheduler may have already forgotten the task in
-+	 * ops.disable().
-+	 * p->scx.weight will be recalculated in scx_enable_task() if the task
-+	 * ever returns to SCX class.
-+	 */
-+	if (scx_get_task_state(p) != SCX_TASK_ENABLED)
-+		return;
-+
- 	p->scx.weight = sched_weight_to_cgroup(scale_load_down(lw->weight));
- 	if (SCX_HAS_OP(sch, set_weight))
- 		SCX_CALL_OP_TASK(sch, set_weight, rq, p, p->scx.weight);
-@@ -4280,6 +4310,15 @@ bool scx_can_stop_tick(struct rq *rq)
- 	if (p->sched_class != &ext_sched_class)
- 		return true;
- 
-+	/*
-+	 * @rq->curr may still reference an outgoing EXT task after it has been
-+	 * dequeued. If no EXT tasks are accounted on @rq, ignore its stale
-+	 * slice state. If another task is dispatched from a DSQ,
-+	 * set_next_task_scx() will update the dependency for the incoming task.
-+	 */
-+	if (!rq->scx.nr_running)
-+		return true;
-+
- 	if (scx_bypassing(sch, cpu_of(rq)))
- 		return false;
- 
-@@ -5604,7 +5643,7 @@ static void free_kick_syncs(void)
- 	int cpu;
- 
- 	for_each_possible_cpu(cpu) {
--		struct scx_kick_syncs **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
-+		struct scx_kick_syncs __rcu **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
- 		struct scx_kick_syncs *to_free;
- 
- 		to_free = rcu_replace_pointer(*ksyncs, NULL, true);
-@@ -6527,7 +6566,7 @@ static int alloc_kick_syncs(void)
- 	 * can exceed percpu allocator limits on large machines.
- 	 */
- 	for_each_possible_cpu(cpu) {
--		struct scx_kick_syncs **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
-+		struct scx_kick_syncs __rcu **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
- 		struct scx_kick_syncs *new_ksyncs;
- 
- 		WARN_ON_ONCE(rcu_access_pointer(*ksyncs));
-@@ -7401,6 +7440,12 @@ static void scx_sub_enable_workfn(struct kthread_work *work)
- 	percpu_up_write(&scx_fork_rwsem);
- err_disable:
- 	mutex_unlock(&scx_enable_mutex);
-+	/*
-+	 * Some enable failures only return an errno (e.g. -ENOMEM from an
-+	 * allocation) without calling scx_error(). Record it so
-+	 * scx_flush_disable_work() runs the disable and ops.exit() fires.
-+	 */
-+	scx_error(sch, "scx_sub_enable() failed (%d)", ret);
- 	scx_flush_disable_work(sch);
- 	cmd->ret = 0;
- }
-diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
-index 8e6f3a734ba0..e75d2932475a 100644
---- a/net/packet/af_packet.c
-+++ b/net/packet/af_packet.c
-@@ -4561,7 +4561,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
- 
- 	spin_lock(&po->bind_lock);
- 	WRITE_ONCE(po->num, num);
--	if (was_running)
-+	/*
-+	 * NETDEV_UNREGISTER may have invalidated the binding while bind_lock
-+	 * was dropped above.  Do not re-add a fanout hook to a dead device.
-+	 */
-+	if (was_running && READ_ONCE(po->ifindex) != -1)
- 		register_prot_hook(sk);
- 
- 	spin_unlock(&po->bind_lock);
 diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
 index 0718e39cedda..10e94a1b8b6e 100644
 --- a/scripts/Makefile.lib
@@ -3044,27 +6854,6 @@ index 8d46886d2cca..14a9cdff942b 100644
  static struct security_hook_list lockdown_hooks[] __ro_after_init = {
  	LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
  };
-diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
-index 3e04e6897b14..e88109979c18 100644
---- a/sound/soc/codecs/cs42l43-jack.c
-+++ b/sound/soc/codecs/cs42l43-jack.c
-@@ -310,6 +310,7 @@ irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data)
- #define CS42L43_JACK_ABSENT 0x0
- 
- #define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT)
-+#define CS42L43_JACK_MICROPHONE (SND_JACK_MECHANICAL | SND_JACK_MICROPHONE)
- #define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE)
- #define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET)
- #define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
-@@ -871,7 +872,7 @@ static const struct cs42l43_jack_override_mode {
- 		.hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
- 		.mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) |
- 			    CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK,
--		.report = CS42L43_JACK_LINEIN,
-+		.report = CS42L43_JACK_MICROPHONE,
- 	},
- 	[CS42L43_JACK_RAW_OPTICAL] = {
- 		.hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
 diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
 index 79b8b7e70a33..23d2f63d68ef 100644
 --- a/sound/soc/codecs/rt722-sdca.c

diff --git a/sources b/sources
index 31b5923..2bb216d 100644
--- a/sources
+++ b/sources
@@ -1,3 +1,3 @@
-SHA512 (linux-7.1.5.tar.xz) = 76494c121d81bf6b631128234ad2a8037490521f77664ee74bf9c143dd4d3f282cec571e9f96a63004f88ebfcde376b483dac5ccac4f43f138ab5bbeb8441e03
-SHA512 (kernel-abi-stablelists-7.1.5.tar.xz) = 4b8b3703bf2a363714463dbd3e07c686e343db151611d95a7f7489e9b2e5b461614ad4bd9e4fae5a97da69da8eb9bd929cf36499ec72ca06d51990d182b9425c
-SHA512 (kernel-kabi-dw-7.1.5.tar.xz) = 4639079ff90478411ac29ac9736d9507584494f1adbc97914a6a86251baa6f97da0000bc4d06b0407c1b565373ccde285cecf6f71ad5632402a7282243cc7540
+SHA512 (linux-7.1.6.tar.xz) = abd9e127261acc7fde3acf98a6a4992c58427c8d93aa3798e4c35a316043f90bc5e2fbce547637ac97b0231cdaac81f25fcda990a674212d74f6d6d752f98c08
+SHA512 (kernel-abi-stablelists-7.1.6.tar.xz) = 7c2aba1f1b9d9fc72b591be908f361b36163d58cfbd0f55a6035a1fea3283aaffd182e6ea34ace5c929eada9739b1c6de36dafde76b05fc622e46d4fe008a64c
+SHA512 (kernel-kabi-dw-7.1.6.tar.xz) = 118395369f81561f8e87ba70f468324bb336197d3522e83e76857fe1a1c670a1a55871a76ddb1bd455c519db9d606234f64f4b33578e8ebdf9051f773433a7d1

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [rpms/kernel] f44: kernel-7.1.6-200
@ 2026-08-03 16:06 Justin M. Forbes
  0 siblings, 0 replies; 2+ messages in thread
From: Justin M. Forbes @ 2026-08-03 16:06 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/kernel
            Branch : f44
            Commit : 9f834313be548268120104825a296d60beaff1ba
            Author : Justin M. Forbes <jforbes@fedoraproject.org>
            Date   : 2026-08-03T10:06:16-06:00
            Stats  : +33/-3 in 5 file(s)
            URL    : https://src.fedoraproject.org/rpms/kernel/c/9f834313be548268120104825a296d60beaff1ba?branch=f44

            Log:
            kernel-7.1.6-200

* Mon Aug 03 2026 Justin M. Forbes <jforbes@fedoraproject.org> [7.1.6-0]
- vhost: reset the vring metadata cache on vring reconfiguration (Jun Yang)
Resolves:

Signed-off-by: Justin M. Forbes <jforbes@fedoraproject.org>

---
diff --git a/Patchlist.changelog b/Patchlist.changelog
index 7e26223..3be03b5 100644
--- a/Patchlist.changelog
+++ b/Patchlist.changelog
@@ -1,3 +1,6 @@
+https://gitlab.com/cki-project/kernel-ark/-/commit/9f166bfb17130303352cef32fca1860d76fe60b1
+ 9f166bfb17130303352cef32fca1860d76fe60b1 vhost: reset the vring metadata cache on vring reconfiguration
+
 https://gitlab.com/cki-project/kernel-ark/-/commit/73346d0cf08db8358dd8ce372c6b5605a77944c6
  73346d0cf08db8358dd8ce372c6b5605a77944c6 media: platform: amd: add DRM_AMDGPU dependency
 

diff --git a/kernel.changelog b/kernel.changelog
index 2690c18..b0f33a2 100644
--- a/kernel.changelog
+++ b/kernel.changelog
@@ -1,3 +1,7 @@
+* Mon Aug 03 2026 Justin M. Forbes <jforbes@fedoraproject.org> [7.1.6-0]
+- vhost: reset the vring metadata cache on vring reconfiguration (Jun Yang)
+Resolves: 
+
 * Mon Aug 03 2026 Augusto Caringi <acaringi@redhat.com> [7.1.6-0]
 - New config for stable (Justin M. Forbes)
 - acpi: battery: Sanitise model_number by dropping unprintable characters (Kate Hsuan)

diff --git a/kernel.spec b/kernel.spec
index 8d46cfc..aa3301a 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -4865,6 +4865,9 @@ fi\
 #
 #
 %changelog
+* Mon Aug 03 2026 Justin M. Forbes <jforbes@fedoraproject.org> [7.1.6-0]
+- vhost: reset the vring metadata cache on vring reconfiguration (Jun Yang)
+
 * Mon Aug 03 2026 Augusto Caringi <acaringi@redhat.com> [7.1.6-0]
 - New config for stable (Justin M. Forbes)
 - acpi: battery: Sanitise model_number by dropping unprintable characters (Kate Hsuan)

diff --git a/patch-7.1-redhat.patch b/patch-7.1-redhat.patch
index 1abcf6d..050d1c4 100644
--- a/patch-7.1-redhat.patch
+++ b/patch-7.1-redhat.patch
@@ -53,6 +53,7 @@
  drivers/pci/quirks.c                               |   24 +
  drivers/platform/x86/intel/int3472/discrete.c      |   18 +
  drivers/usb/core/hub.c                             |    7 +
+ drivers/vhost/vhost.c                              |    8 +
  fs/xfs/libxfs/xfs_exchmaps.c                       |   10 +
  include/linux/crypto.h                             |    2 +
  include/linux/efi.h                                |   22 +-
@@ -70,7 +71,7 @@
  tools/testing/selftests/bpf/DENYLIST.rhel          |   76 ++
  tools/testing/selftests/bpf/Makefile               |    2 +-
  tools/testing/selftests/bpf/prog_tests/ksyms_btf.c |   31 -
- 72 files changed, 5902 insertions(+), 120 deletions(-)
+ 73 files changed, 5910 insertions(+), 120 deletions(-)
 
 diff --git a/MAINTAINERS b/MAINTAINERS
 index f74da642290a..3a0e977fc9cf 100644
@@ -6573,6 +6574,25 @@ index 24960ba9caa9..32597cdb72ef 100644
  	/* Lock the device, then check to see if we were
  	 * disconnected while waiting for the lock to succeed. */
  	usb_lock_device(hdev);
+diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
+index db329a6f6145..89062f60e369 100644
+--- a/drivers/vhost/vhost.c
++++ b/drivers/vhost/vhost.c
+@@ -2126,6 +2126,14 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d,
+ 		BUG();
+ 	}
+ 
++	/*
++	 * The metadata cache holds the IOTLB mapping that backed the previous
++	 * desc/avail/used addresses and vring size, both of which are being
++	 * replaced here.  iotlb_access_ok() takes a cache hit as proof that the
++	 * region was validated, so the stale entries have to go.
++	 */
++	__vhost_vq_meta_reset(vq);
++
+ 	mutex_unlock(&vq->mutex);
+ 
+ 	return r;
 diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c
 index 541e33f33167..7fb28c8afef8 100644
 --- a/fs/xfs/libxfs/xfs_exchmaps.c

diff --git a/sources b/sources
index 2bb216d..fc5bc99 100644
--- a/sources
+++ b/sources
@@ -1,3 +1,3 @@
 SHA512 (linux-7.1.6.tar.xz) = abd9e127261acc7fde3acf98a6a4992c58427c8d93aa3798e4c35a316043f90bc5e2fbce547637ac97b0231cdaac81f25fcda990a674212d74f6d6d752f98c08
-SHA512 (kernel-abi-stablelists-7.1.6.tar.xz) = 7c2aba1f1b9d9fc72b591be908f361b36163d58cfbd0f55a6035a1fea3283aaffd182e6ea34ace5c929eada9739b1c6de36dafde76b05fc622e46d4fe008a64c
-SHA512 (kernel-kabi-dw-7.1.6.tar.xz) = 118395369f81561f8e87ba70f468324bb336197d3522e83e76857fe1a1c670a1a55871a76ddb1bd455c519db9d606234f64f4b33578e8ebdf9051f773433a7d1
+SHA512 (kernel-abi-stablelists-7.1.6.tar.xz) = d9e7d72d2557189b29265a5bdddc923ba533f1bdcf5f5a883cba117723728f11a61f7b849e6712d3ebacea3d601cbd01c7a060095541ed86dc09c6b9157e96a5
+SHA512 (kernel-kabi-dw-7.1.6.tar.xz) = ecf3652402b9b7ca02a18e19aee16bce19db1d3a446f900d04e27174d78c35e3aa4902372326f124b6d410d98768480b9f9dac38498176771b023757b7f41b0a

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-03 16:06 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-03 15:42 [rpms/kernel] f44: kernel-7.1.6-200 Augusto Caringi
2026-08-03 16:06 Justin M. Forbes

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox