public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [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
* [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 = ¶m->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
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 16:06 [rpms/kernel] f44: kernel-7.1.6-200 Justin M. Forbes
-- strict thread matches above, loose matches on Subject: below --
2026-08-03 15:42 Augusto Caringi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox