diff --git a/Patchlist.changelog b/Patchlist.changelog index 335feab45..ff08db903 100644 --- a/Patchlist.changelog +++ b/Patchlist.changelog @@ -1,3 +1,162 @@ +https://gitlab.com/cki-project/kernel-ark/-/commit/dc3a145ac459508e4074946ab926a4d9803aa2a0 + dc3a145ac459508e4074946ab926a4d9803aa2a0 drm/v3d: Reject empty multisync extension to prevent infinite loop + +https://gitlab.com/cki-project/kernel-ark/-/commit/eb657386c5855d2a96e82426ff968a331056381b + eb657386c5855d2a96e82426ff968a331056381b net: macb: Use napi_schedule_irqoff() in IRQ handler + +https://gitlab.com/cki-project/kernel-ark/-/commit/f3cfe71f8a352c101188130ffdf50b14bede9dab + f3cfe71f8a352c101188130ffdf50b14bede9dab net: macb: Use netif_napi_add_tx() instead of netif_napi_add() for TX NAPI + +https://gitlab.com/cki-project/kernel-ark/-/commit/3cec419c07008a5344bb87c49d66c5fcf3a00142 + 3cec419c07008a5344bb87c49d66c5fcf3a00142 net: macb: Remove dedicated IRQ handler for WoL + +https://gitlab.com/cki-project/kernel-ark/-/commit/1ee63176faf30585031fb5ee02bc44d13155f2a3 + 1ee63176faf30585031fb5ee02bc44d13155f2a3 net: macb: Factor out the handling of non-hot IRQ events into a separate function + +https://gitlab.com/cki-project/kernel-ark/-/commit/095d65e8231e9bd4117f84e66ca690c3535dccdd + 095d65e8231e9bd4117f84e66ca690c3535dccdd net: macb: Introduce macb_queue_isr_clear() helper function + +https://gitlab.com/cki-project/kernel-ark/-/commit/cddb5ec47ff05cdb9e80dcadf78f59a29599d477 + cddb5ec47ff05cdb9e80dcadf78f59a29599d477 net: macb: Replace open-coded implementation with napi_schedule() + +https://gitlab.com/cki-project/kernel-ark/-/commit/def446b0ffaa2a41f119bb308b1a4bf510714089 + def446b0ffaa2a41f119bb308b1a4bf510714089 net: macb: fix use of at91_default_usrio without CONFIG_OF + +https://gitlab.com/cki-project/kernel-ark/-/commit/4bff799b00d5f84db3477fd5f95a23cb41a439e2 + 4bff799b00d5f84db3477fd5f95a23cb41a439e2 net: macb: drop usrio pointer on EyeQ5 config + +https://gitlab.com/cki-project/kernel-ark/-/commit/5fae490c804854e3ef23157ddd0c4339ad77c050 + 5fae490c804854e3ef23157ddd0c4339ad77c050 net: macb: set MACB_CAPS_USRIO_DISABLED if no usrio config is provided + +https://gitlab.com/cki-project/kernel-ark/-/commit/df0b024e10f3482302da7a6f557f5e17319463fa + df0b024e10f3482302da7a6f557f5e17319463fa net: macb: runtime detect MACB_CAPS_USRIO_DISABLED + +https://gitlab.com/cki-project/kernel-ark/-/commit/ecbc6c1a57b211699944065401cc8ed6400b258e + ecbc6c1a57b211699944065401cc8ed6400b258e net: macb: timer adjust mode is not supported + +https://gitlab.com/cki-project/kernel-ark/-/commit/c8eed9677ebc8a65d9d0f6663a101dfcefe11ead + c8eed9677ebc8a65d9d0f6663a101dfcefe11ead net: macb: clean up tsu clk rate acquisition + +https://gitlab.com/cki-project/kernel-ark/-/commit/6cbc69d90df8616e222cf1f7e4e102630637d7e2 + 6cbc69d90df8616e222cf1f7e4e102630637d7e2 net: macb: warn on pclk use as a tsu_clk fallback + +https://gitlab.com/cki-project/kernel-ark/-/commit/705ec686ecd84ee404832f77c2517038a5eb3024 + 705ec686ecd84ee404832f77c2517038a5eb3024 net: macb: add mpfs specific usrio configuration + +https://gitlab.com/cki-project/kernel-ark/-/commit/f6872aad2e0c77731ca433025be182744a4948f0 + f6872aad2e0c77731ca433025be182744a4948f0 net: macb: np4 doesn't need a usrio pointer + +https://gitlab.com/cki-project/kernel-ark/-/commit/29fe9fe82b8df6c552b301d278137e6df10c86db + 29fe9fe82b8df6c552b301d278137e6df10c86db net: macb: rework usrio refclk selection code + +https://gitlab.com/cki-project/kernel-ark/-/commit/7bfd324aeb36caeacec3b69bac1e805ad5e6b3f3 + 7bfd324aeb36caeacec3b69bac1e805ad5e6b3f3 net: macb: split USRIO_HAS_CLKEN capability in two + +https://gitlab.com/cki-project/kernel-ark/-/commit/8b2f394823a60e5a28f9653a137852061e69316b + 8b2f394823a60e5a28f9653a137852061e69316b net: macb: rename macb_default_usrio to at91_default_usrio as not all platforms have mii mode control in usrio + +https://gitlab.com/cki-project/kernel-ark/-/commit/d7bb406858ead33555064201870693990c9f7413 + d7bb406858ead33555064201870693990c9f7413 Revert "net: macb: Clean up the .usrio settings in macb_config instances" + +https://gitlab.com/cki-project/kernel-ark/-/commit/e630fb9b856c412625d09c24d18d8afeefaf82f1 + e630fb9b856c412625d09c24d18d8afeefaf82f1 net: macb: add support for Microchip pic64hpsc ethernet endpoint + +https://gitlab.com/cki-project/kernel-ark/-/commit/10a0b7af9e3b28ad971b883270c9af5afd0c9369 + 10a0b7af9e3b28ad971b883270c9af5afd0c9369 net: macb: add safeguards for jumbo frame larger than 10240 + +https://gitlab.com/cki-project/kernel-ark/-/commit/4b63219dfed63ebfaa78e55314c3382d53f39202 + 4b63219dfed63ebfaa78e55314c3382d53f39202 net: macb: set default_an_inband to true for SGMII + +https://gitlab.com/cki-project/kernel-ark/-/commit/f55d3cff1b85dab0298539695cff3005cfda2262 + f55d3cff1b85dab0298539695cff3005cfda2262 net: macb: Clean up the .usrio settings in macb_config instances + +https://gitlab.com/cki-project/kernel-ark/-/commit/f02732b2f4c4cc6e827ce73cd9e836ef590da006 + f02732b2f4c4cc6e827ce73cd9e836ef590da006 net: macb: Clean up the .init settings in macb_config instances + +https://gitlab.com/cki-project/kernel-ark/-/commit/c18f79c99588a7a1748c1551f3778a5920be5a46 + c18f79c99588a7a1748c1551f3778a5920be5a46 net: macb: Clean up the .clk_init setting in the macb_config instances + +https://gitlab.com/cki-project/kernel-ark/-/commit/3bd0daeaef5def29918567f3706552ef27795b71 + 3bd0daeaef5def29918567f3706552ef27795b71 net: cadence: macb: enable EEE for Mobileye EyeQ5 + +https://gitlab.com/cki-project/kernel-ark/-/commit/cdbb1a704ad445d22166a2a6f3892fb1112fecb1 + cdbb1a704ad445d22166a2a6f3892fb1112fecb1 net: cadence: macb: enable EEE for Raspberry Pi RP1 + +https://gitlab.com/cki-project/kernel-ark/-/commit/dc6150b07156d28f27728394ef2f8ac8e8576205 + dc6150b07156d28f27728394ef2f8ac8e8576205 net: cadence: macb: add ethtool EEE support + +https://gitlab.com/cki-project/kernel-ark/-/commit/426db79853e3f4661e47c2bb3898ab604f436f58 + 426db79853e3f4661e47c2bb3898ab604f436f58 net: cadence: macb: implement EEE TX LPI support + +https://gitlab.com/cki-project/kernel-ark/-/commit/c7b81454b4c8e83a9e4703d6a89a7d9f2182f4bd + c7b81454b4c8e83a9e4703d6a89a7d9f2182f4bd net: cadence: macb: add EEE LPI statistics counters + +https://gitlab.com/cki-project/kernel-ark/-/commit/324c3711f99352a519ced4f57a0b979d8a51c8ba + 324c3711f99352a519ced4f57a0b979d8a51c8ba net: macb: use ethtool_sprintf to fill ethtool stats strings + +https://gitlab.com/cki-project/kernel-ark/-/commit/ab0143c22156e2ceabee74f095cfaada38dd4806 + ab0143c22156e2ceabee74f095cfaada38dd4806 net: macb: add the .pcs_inband_caps() callback for SGMII + +https://gitlab.com/cki-project/kernel-ark/-/commit/d146cb96f4ed4270dc04a4f5cdf3d9a15cf80cbb + d146cb96f4ed4270dc04a4f5cdf3d9a15cf80cbb net: macb: add support for reporting SGMII inband link status + +https://gitlab.com/cki-project/kernel-ark/-/commit/ce128e34d6808c70c2bb0f12a37db37578ac7d46 + ce128e34d6808c70c2bb0f12a37db37578ac7d46 net: macb: fix SGMII with inband aneg disabled + +https://gitlab.com/cki-project/kernel-ark/-/commit/e4c8bf72a5de7abf94f30895c023abf25a259ace + e4c8bf72a5de7abf94f30895c023abf25a259ace net: cadence: macb: add ethtool nway_reset support + +https://gitlab.com/cki-project/kernel-ark/-/commit/78bc02e06af6a4dfaf7c101b44e90754c42d0ff5 + 78bc02e06af6a4dfaf7c101b44e90754c42d0ff5 ARM: dts: broadcom: bcm2835-rpi: Move non simple-bus nodes to root level + +https://gitlab.com/cki-project/kernel-ark/-/commit/aed7a1e46ab5a8f9cdf9d8e6f92205b8c9b94e83 + aed7a1e46ab5a8f9cdf9d8e6f92205b8c9b94e83 arm64: dts: broadcom: bcm2712: Move non simple-bus nodes to root level + +https://gitlab.com/cki-project/kernel-ark/-/commit/912c4e57060e87a71be98074c4c43658e402191d + 912c4e57060e87a71be98074c4c43658e402191d arm64: dts: broadcom: bcm2712-d-rpi-5-b: update uart10 interrupt + +https://gitlab.com/cki-project/kernel-ark/-/commit/b736d4af02c0dc45b62b1b57aa2c573cfa8bb70c + b736d4af02c0dc45b62b1b57aa2c573cfa8bb70c arm64: dts: broadcom: bcm2712-d-rpi-5-b: add fixes for pinctrl/pinctrl_aon + +https://gitlab.com/cki-project/kernel-ark/-/commit/9579b472973b51d5f24ed9682c704d59a4ebe23a + 9579b472973b51d5f24ed9682c704d59a4ebe23a arm64: dts: broadcom: bcm2712-rpi-5-b: add pinctrl properties for csi i2cs + +https://gitlab.com/cki-project/kernel-ark/-/commit/58d9a4f680a76144148b89439449596d034f59a3 + 58d9a4f680a76144148b89439449596d034f59a3 arm64: dts: broadcom: bcm2712: add camera backend node pispbe + +https://gitlab.com/cki-project/kernel-ark/-/commit/dd1830ef448bf366f67e6721a26cdba4cb7ff9fc + dd1830ef448bf366f67e6721a26cdba4cb7ff9fc arm64: dts: broadcom: rp1: add csi nodes + +https://gitlab.com/cki-project/kernel-ark/-/commit/410af8469a923dbf5b86a6803a11baf9b4389fd4 + 410af8469a923dbf5b86a6803a11baf9b4389fd4 arm64: dts: broadcom: rp1: add i2c controller + +https://gitlab.com/cki-project/kernel-ark/-/commit/bccab7a88dea23431407839dc6d2a64b4f1215e2 + bccab7a88dea23431407839dc6d2a64b4f1215e2 arm64: dts: broadcom: bcm2712: Add V3D device node + +https://gitlab.com/cki-project/kernel-ark/-/commit/53c9aad19855ba5a1cb45cbb6c44a0b046815959 + 53c9aad19855ba5a1cb45cbb6c44a0b046815959 arm64: dts: freescale: imx93: Add Ethos-U65 NPU and SRAM nodes + +https://gitlab.com/cki-project/kernel-ark/-/commit/4c2e732ad5d15a3d8080d8ebf7b2e86f51a153f5 + 4c2e732ad5d15a3d8080d8ebf7b2e86f51a153f5 Documentation: add documentation of AMD isp 4 driver + +https://gitlab.com/cki-project/kernel-ark/-/commit/711109dff36b615e872fe739dca0c8c5e219bee8 + 711109dff36b615e872fe739dca0c8c5e219bee8 media: platform: amd: isp4 debug fs logging and more descriptive errors + +https://gitlab.com/cki-project/kernel-ark/-/commit/d250c9ec6a1185759539136fc5c00b909ecb391a + d250c9ec6a1185759539136fc5c00b909ecb391a media: platform: amd: isp4 video node and buffers handling added + +https://gitlab.com/cki-project/kernel-ark/-/commit/d54ed8f39392e1c7e82f0a2dd2b94135b30e8a80 + d54ed8f39392e1c7e82f0a2dd2b94135b30e8a80 media: platform: amd: isp4 subdev and firmware loading handling added + +https://gitlab.com/cki-project/kernel-ark/-/commit/9a7de974831ae5560192b0e6e3051b379d55da69 + 9a7de974831ae5560192b0e6e3051b379d55da69 media: platform: amd: Add isp4 fw and hw interface + +https://gitlab.com/cki-project/kernel-ark/-/commit/931a94124353ed16aeb83077908d120f52fc5ed1 + 931a94124353ed16aeb83077908d120f52fc5ed1 media: platform: amd: low level support for isp4 firmware + +https://gitlab.com/cki-project/kernel-ark/-/commit/ec1a008cf8f5f4a854253363213af9b7f10b668a + ec1a008cf8f5f4a854253363213af9b7f10b668a media: platform: amd: Introduce amd isp4 capture driver + https://gitlab.com/cki-project/kernel-ark/-/commit/b34b1a79f731a2d213db5cd4e194d3f1471b00c8 b34b1a79f731a2d213db5cd4e194d3f1471b00c8 Fix up rebase typo in drivers/pci/quirks.c diff --git a/kernel-aarch64-16k-debug-fedora.config b/kernel-aarch64-16k-debug-fedora.config index 08cf1602d..5f514d163 100644 --- a/kernel-aarch64-16k-debug-fedora.config +++ b/kernel-aarch64-16k-debug-fedora.config @@ -10230,6 +10230,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 f36220ec6..923878117 100644 --- a/kernel-aarch64-16k-fedora.config +++ b/kernel-aarch64-16k-fedora.config @@ -10198,6 +10198,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 02121d8b4..5b2b22f34 100644 --- a/kernel-aarch64-debug-fedora.config +++ b/kernel-aarch64-debug-fedora.config @@ -10229,6 +10229,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 5e96bc199..c1479a1d3 100644 --- a/kernel-aarch64-fedora.config +++ b/kernel-aarch64-fedora.config @@ -10197,6 +10197,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 07f38ee13..7fbe9aeff 100644 --- a/kernel-aarch64-rt-64k-debug-fedora.config +++ b/kernel-aarch64-rt-64k-debug-fedora.config @@ -10245,6 +10245,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 12648b96e..3f26c500c 100644 --- a/kernel-aarch64-rt-64k-fedora.config +++ b/kernel-aarch64-rt-64k-fedora.config @@ -10213,6 +10213,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 7365ea527..e41f0d989 100644 --- a/kernel-aarch64-rt-debug-fedora.config +++ b/kernel-aarch64-rt-debug-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-fedora.config b/kernel-aarch64-rt-fedora.config index 29de4d9c1..13ca7ab93 100644 --- a/kernel-aarch64-rt-fedora.config +++ b/kernel-aarch64-rt-fedora.config @@ -10209,6 +10209,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 cdef8b346..39d2c1dab 100644 --- a/kernel-ppc64le-debug-fedora.config +++ b/kernel-ppc64le-debug-fedora.config @@ -8278,6 +8278,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 4ba47b54f..7c44e0e2b 100644 --- a/kernel-ppc64le-fedora.config +++ b/kernel-ppc64le-fedora.config @@ -8245,6 +8245,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 e43e19ccb..665785b4c 100644 --- a/kernel-riscv64-debug-fedora.config +++ b/kernel-riscv64-debug-fedora.config @@ -8448,6 +8448,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 8e3432e9a..5fdb6adad 100644 --- a/kernel-riscv64-fedora.config +++ b/kernel-riscv64-fedora.config @@ -8415,6 +8415,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 0845cf6bb..6a070395b 100644 --- a/kernel-riscv64-rt-debug-fedora.config +++ b/kernel-riscv64-rt-debug-fedora.config @@ -8460,6 +8460,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 13bd789e8..dbd98a811 100644 --- a/kernel-riscv64-rt-fedora.config +++ b/kernel-riscv64-rt-fedora.config @@ -8427,6 +8427,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 0e677bd93..a08197ee3 100644 --- a/kernel-s390x-debug-fedora.config +++ b/kernel-s390x-debug-fedora.config @@ -8234,6 +8234,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 af0bafcaa..45b01b243 100644 --- a/kernel-s390x-fedora.config +++ b/kernel-s390x-fedora.config @@ -8201,6 +8201,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 2fe6f4c6b..b9ab92f70 100644 --- a/kernel-x86_64-debug-fedora.config +++ b/kernel-x86_64-debug-fedora.config @@ -8877,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-fedora.config b/kernel-x86_64-fedora.config index 978b121e9..3068305b4 100644 --- a/kernel-x86_64-fedora.config +++ b/kernel-x86_64-fedora.config @@ -8845,6 +8845,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-fedora.config b/kernel-x86_64-rt-debug-fedora.config index 85743e39e..4e93a3dec 100644 --- a/kernel-x86_64-rt-debug-fedora.config +++ b/kernel-x86_64-rt-debug-fedora.config @@ -8889,6 +8889,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-fedora.config b/kernel-x86_64-rt-fedora.config index f8b7a6724..1acd41d52 100644 --- a/kernel-x86_64-rt-fedora.config +++ b/kernel-x86_64-rt-fedora.config @@ -8857,6 +8857,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.changelog b/kernel.changelog index f8d9a7786..186713167 100644 --- a/kernel.changelog +++ b/kernel.changelog @@ -1,3 +1,61 @@ +* Mon Apr 27 2026 Justin M. Forbes [7.0.2-0] +- drm/v3d: Reject empty multisync extension to prevent infinite loop (Ashutosh Desai) +- net: macb: Use napi_schedule_irqoff() in IRQ handler (Kevin Hao) +- net: macb: Use netif_napi_add_tx() instead of netif_napi_add() for TX NAPI (Kevin Hao) +- net: macb: Remove dedicated IRQ handler for WoL (Kevin Hao) +- net: macb: Factor out the handling of non-hot IRQ events into a separate function (Kevin Hao) +- net: macb: Introduce macb_queue_isr_clear() helper function (Kevin Hao) +- net: macb: Replace open-coded implementation with napi_schedule() (Kevin Hao) +- net: macb: fix use of at91_default_usrio without CONFIG_OF (Conor Dooley) +- net: macb: drop usrio pointer on EyeQ5 config (Théo Lebrun) +- net: macb: set MACB_CAPS_USRIO_DISABLED if no usrio config is provided (Théo Lebrun) +- net: macb: runtime detect MACB_CAPS_USRIO_DISABLED (Théo Lebrun) +- net: macb: timer adjust mode is not supported (Conor Dooley) +- net: macb: clean up tsu clk rate acquisition (Conor Dooley) +- net: macb: warn on pclk use as a tsu_clk fallback (Conor Dooley) +- net: macb: add mpfs specific usrio configuration (Conor Dooley) +- net: macb: np4 doesn't need a usrio pointer (Conor Dooley) +- net: macb: rework usrio refclk selection code (Conor Dooley) +- net: macb: split USRIO_HAS_CLKEN capability in two (Conor Dooley) +- net: macb: rename macb_default_usrio to at91_default_usrio as not all platforms have mii mode control in usrio (Conor Dooley) +- Revert "net: macb: Clean up the .usrio settings in macb_config instances" (Conor Dooley) +- net: macb: add support for Microchip pic64hpsc ethernet endpoint (Charles Perry) +- net: macb: add safeguards for jumbo frame larger than 10240 (Charles Perry) +- net: macb: set default_an_inband to true for SGMII (Charles Perry) +- net: macb: Clean up the .usrio settings in macb_config instances (Kevin Hao) +- net: macb: Clean up the .init settings in macb_config instances (Kevin Hao) +- net: macb: Clean up the .clk_init setting in the macb_config instances (Kevin Hao) +- net: cadence: macb: enable EEE for Mobileye EyeQ5 (Nicolai Buchwitz) +- net: cadence: macb: enable EEE for Raspberry Pi RP1 (Nicolai Buchwitz) +- net: cadence: macb: add ethtool EEE support (Nicolai Buchwitz) +- net: cadence: macb: implement EEE TX LPI support (Nicolai Buchwitz) +- net: cadence: macb: add EEE LPI statistics counters (Nicolai Buchwitz) +- net: macb: use ethtool_sprintf to fill ethtool stats strings (Sean Chang) +- net: macb: add the .pcs_inband_caps() callback for SGMII (Charles Perry) +- net: macb: add support for reporting SGMII inband link status (Charles Perry) +- net: macb: fix SGMII with inband aneg disabled (Charles Perry) +- net: cadence: macb: add ethtool nway_reset support (Nicolai Buchwitz) +- ARM: dts: broadcom: bcm2835-rpi: Move non simple-bus nodes to root level (Rob Herring (Arm)) +- arm64: dts: broadcom: bcm2712: Move non simple-bus nodes to root level (Rob Herring (Arm)) +- arm64: dts: broadcom: bcm2712-d-rpi-5-b: update uart10 interrupt (Gregor Herburger) +- arm64: dts: broadcom: bcm2712-d-rpi-5-b: add fixes for pinctrl/pinctrl_aon (Gregor Herburger) +- arm64: dts: broadcom: bcm2712-rpi-5-b: add pinctrl properties for csi i2cs (Gregor Herburger) +- arm64: dts: broadcom: bcm2712: add camera backend node pispbe (Gregor Herburger) +- arm64: dts: broadcom: rp1: add csi nodes (Gregor Herburger) +- arm64: dts: broadcom: rp1: add i2c controller (Gregor Herburger) +- arm64: dts: broadcom: bcm2712: Add V3D device node (Maíra Canal) +- arm64: dts: freescale: imx93: Add Ethos-U65 NPU and SRAM nodes (Rob Herring (Arm)) +- redhat: configs: fedora: Enable AMD ISP4 MIPI camera solution (Kate Hsuan) +- Documentation: add documentation of AMD isp 4 driver (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) +- Linux v7.0.2 +Resolves: + * Thu Apr 23 2026 Justin M. Forbes [7.0.1-0] - Initial setup for stable Fedora releases (Justin M. Forbes) - Fix up rebase typo in drivers/pci/quirks.c (Justin M. Forbes) diff --git a/kernel.spec b/kernel.spec index 74ccf7087..b87955c7e 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.0.1 -%define specversion 7.0.1 +%define specrpmversion 7.0.2 +%define specversion 7.0.2 %define patchversion 7.0 %define pkgrelease 200 %define kversion 7 -%define tarfile_release 7.0.1 +%define tarfile_release 7.0.2 # This is needed to do merge window version magic %define patchlevel 0 # This allows pkg_release to have configurable %%{?dist} tag %define specrelease 200%{?buildid}%{?dist} # This defines the kabi tarball version -%define kabiversion 7.0.1 +%define kabiversion 7.0.2 # If this variable is set to 1, a bpf selftests build failure will cause a # fatal kernel package build error @@ -4825,6 +4825,63 @@ fi\ # # %changelog +* Mon Apr 27 2026 Justin M. Forbes [7.0.2-0] +- drm/v3d: Reject empty multisync extension to prevent infinite loop (Ashutosh Desai) +- net: macb: Use napi_schedule_irqoff() in IRQ handler (Kevin Hao) +- net: macb: Use netif_napi_add_tx() instead of netif_napi_add() for TX NAPI (Kevin Hao) +- net: macb: Remove dedicated IRQ handler for WoL (Kevin Hao) +- net: macb: Factor out the handling of non-hot IRQ events into a separate function (Kevin Hao) +- net: macb: Introduce macb_queue_isr_clear() helper function (Kevin Hao) +- net: macb: Replace open-coded implementation with napi_schedule() (Kevin Hao) +- net: macb: fix use of at91_default_usrio without CONFIG_OF (Conor Dooley) +- net: macb: drop usrio pointer on EyeQ5 config (Théo Lebrun) +- net: macb: set MACB_CAPS_USRIO_DISABLED if no usrio config is provided (Théo Lebrun) +- net: macb: runtime detect MACB_CAPS_USRIO_DISABLED (Théo Lebrun) +- net: macb: timer adjust mode is not supported (Conor Dooley) +- net: macb: clean up tsu clk rate acquisition (Conor Dooley) +- net: macb: warn on pclk use as a tsu_clk fallback (Conor Dooley) +- net: macb: add mpfs specific usrio configuration (Conor Dooley) +- net: macb: np4 doesn't need a usrio pointer (Conor Dooley) +- net: macb: rework usrio refclk selection code (Conor Dooley) +- net: macb: split USRIO_HAS_CLKEN capability in two (Conor Dooley) +- net: macb: rename macb_default_usrio to at91_default_usrio as not all platforms have mii mode control in usrio (Conor Dooley) +- Revert "net: macb: Clean up the .usrio settings in macb_config instances" (Conor Dooley) +- net: macb: add support for Microchip pic64hpsc ethernet endpoint (Charles Perry) +- net: macb: add safeguards for jumbo frame larger than 10240 (Charles Perry) +- net: macb: set default_an_inband to true for SGMII (Charles Perry) +- net: macb: Clean up the .usrio settings in macb_config instances (Kevin Hao) +- net: macb: Clean up the .init settings in macb_config instances (Kevin Hao) +- net: macb: Clean up the .clk_init setting in the macb_config instances (Kevin Hao) +- net: cadence: macb: enable EEE for Mobileye EyeQ5 (Nicolai Buchwitz) +- net: cadence: macb: enable EEE for Raspberry Pi RP1 (Nicolai Buchwitz) +- net: cadence: macb: add ethtool EEE support (Nicolai Buchwitz) +- net: cadence: macb: implement EEE TX LPI support (Nicolai Buchwitz) +- net: cadence: macb: add EEE LPI statistics counters (Nicolai Buchwitz) +- net: macb: use ethtool_sprintf to fill ethtool stats strings (Sean Chang) +- net: macb: add the .pcs_inband_caps() callback for SGMII (Charles Perry) +- net: macb: add support for reporting SGMII inband link status (Charles Perry) +- net: macb: fix SGMII with inband aneg disabled (Charles Perry) +- net: cadence: macb: add ethtool nway_reset support (Nicolai Buchwitz) +- ARM: dts: broadcom: bcm2835-rpi: Move non simple-bus nodes to root level (Rob Herring (Arm)) +- arm64: dts: broadcom: bcm2712: Move non simple-bus nodes to root level (Rob Herring (Arm)) +- arm64: dts: broadcom: bcm2712-d-rpi-5-b: update uart10 interrupt (Gregor Herburger) +- arm64: dts: broadcom: bcm2712-d-rpi-5-b: add fixes for pinctrl/pinctrl_aon (Gregor Herburger) +- arm64: dts: broadcom: bcm2712-rpi-5-b: add pinctrl properties for csi i2cs (Gregor Herburger) +- arm64: dts: broadcom: bcm2712: add camera backend node pispbe (Gregor Herburger) +- arm64: dts: broadcom: rp1: add csi nodes (Gregor Herburger) +- arm64: dts: broadcom: rp1: add i2c controller (Gregor Herburger) +- arm64: dts: broadcom: bcm2712: Add V3D device node (Maíra Canal) +- arm64: dts: freescale: imx93: Add Ethos-U65 NPU and SRAM nodes (Rob Herring (Arm)) +- redhat: configs: fedora: Enable AMD ISP4 MIPI camera solution (Kate Hsuan) +- Documentation: add documentation of AMD isp 4 driver (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) +- Linux v7.0.2 + * Thu Apr 23 2026 Justin M. Forbes [7.0.1-0] - Initial setup for stable Fedora releases (Justin M. Forbes) - Fix up rebase typo in drivers/pci/quirks.c (Justin M. Forbes) diff --git a/patch-7.0-redhat.patch b/patch-7.0-redhat.patch index 489f1115c..5acb7639a 100644 --- a/patch-7.0-redhat.patch +++ b/patch-7.0-redhat.patch @@ -1,46 +1,208 @@ - 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 ++-- - crypto/sig.c | 3 +- - crypto/testmgr.c | 2 +- - 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/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/hid/hid-rmi.c | 66 ----------- - drivers/hwtracing/coresight/coresight-etm4x-core.c | 19 ++++ - drivers/input/rmi4/rmi_driver.c | 124 ++++++++++++--------- - drivers/iommu/iommu.c | 22 ++++ - drivers/pci/quirks.c | 24 ++++ - drivers/usb/core/hub.c | 7 ++ - include/linux/efi.h | 22 ++-- - include/linux/lsm_hook_defs.h | 1 + - include/linux/rmi.h | 1 + - 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 ++ - tools/testing/selftests/bpf/Makefile | 2 +- - tools/testing/selftests/bpf/prog_tests/ksyms_btf.c | 31 ------ - 39 files changed, 522 insertions(+), 215 deletions(-) + Documentation/admin-guide/media/amdisp4-1.rst | 63 ++ + Documentation/admin-guide/media/amdisp4.dot | 6 + + Documentation/admin-guide/media/v4l-drivers.rst | 1 + + MAINTAINERS | 25 + + Makefile | 30 + + arch/arm/Kconfig | 4 +- + arch/arm/boot/dts/broadcom/bcm2835-common.dtsi | 7 +- + arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi | 16 +- + arch/arm64/Kconfig | 2 +- + arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts | 14 + + .../boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi | 55 +- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 24 + + arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 29 +- + arch/arm64/boot/dts/broadcom/rp1-common.dtsi | 105 ++ + arch/arm64/boot/dts/freescale/imx93.dtsi | 24 + + 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 +- + crypto/sig.c | 3 +- + crypto/testmgr.c | 2 +- + 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/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/v3d/v3d_submit.c | 5 + + drivers/hid/hid-rmi.c | 66 -- + drivers/hwtracing/coresight/coresight-etm4x-core.c | 19 + + drivers/input/rmi4/rmi_driver.c | 124 ++- + drivers/iommu/iommu.c | 22 + + 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 | 15 + + 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/net/ethernet/cadence/macb.h | 35 + + drivers/net/ethernet/cadence/macb_main.c | 736 ++++++++------ + drivers/pci/quirks.c | 24 + + drivers/usb/core/hub.c | 7 + + include/linux/efi.h | 22 +- + include/linux/lsm_hook_defs.h | 1 + + include/linux/rmi.h | 1 + + 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 + + tools/testing/selftests/bpf/Makefile | 2 +- + tools/testing/selftests/bpf/prog_tests/ksyms_btf.c | 31 - + 72 files changed, 5392 insertions(+), 541 deletions(-) +diff --git a/Documentation/admin-guide/media/amdisp4-1.rst b/Documentation/admin-guide/media/amdisp4-1.rst +new file mode 100644 +index 0000000000000..878141154f962 +--- /dev/null ++++ b/Documentation/admin-guide/media/amdisp4-1.rst +@@ -0,0 +1,63 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++.. include:: ++ ++==================================== ++AMD Image Signal Processor (amdisp4) ++==================================== ++ ++Introduction ++============ ++ ++This file documents the driver for the AMD ISP4 that is part of ++AMD Ryzen AI Max 300 Series. ++ ++The driver is located under drivers/media/platform/amd/isp4 and uses ++the Media-Controller API. ++ ++The driver exposes one video capture device to userspace and provide ++web camera like interface. Internally the video device is connected ++to the isp4 sub-device responsible for communication with the CCPU FW. ++ ++Topology ++======== ++ ++.. _amdisp4_topology_graph: ++ ++.. kernel-figure:: amdisp4.dot ++ :alt: Diagram of the media pipeline topology ++ :align: center ++ ++ ++ ++The driver has 1 sub-device: Representing isp4 image signal processor. ++The driver has 1 video device: Capture device for retrieving images. ++ ++- ISP4 Image Signal Processing Subdevice Node ++ ++--------------------------------------------- ++ ++The isp4 is represented as a single V4L2 subdev, the sub-device does not ++provide interface to the user space. The sub-device is connected to one video node ++(isp4_capture) with immutable active link. The sub-device represents ISP with ++connected sensor similar to smart cameras (sensors with integrated ISP). ++sub-device has only one link to the video device for capturing the frames. ++The sub-device communicates with CCPU FW for streaming configuration and ++buffer management. ++ ++ ++- isp4_capture - Frames Capture Video Node ++ ++------------------------------------------ ++ ++Isp4_capture is a capture device to capture frames to memory. ++The entity is connected to isp4 sub-device. The video device ++provides web camera like interface to userspace. It supports ++mmap and dma buf types of memory. ++ ++Capturing Video Frames Example ++============================== ++ ++.. code-block:: bash ++ ++ v4l2-ctl "-d" "/dev/video0" "--set-fmt-video=width=1920,height=1080,pixelformat=NV12" "--stream-mmap" "--stream-count=10" +diff --git a/Documentation/admin-guide/media/amdisp4.dot b/Documentation/admin-guide/media/amdisp4.dot +new file mode 100644 +index 0000000000000..978f30c1a31a4 +--- /dev/null ++++ b/Documentation/admin-guide/media/amdisp4.dot +@@ -0,0 +1,6 @@ ++digraph board { ++ rankdir=TB ++ n00000001 [label="{{} | amd isp4\n | { 0}}", shape=Mrecord, style=filled, fillcolor=green] ++ n00000001:port0 -> n00000003 [style=bold] ++ n00000003 [label="Preview\n/dev/video0", shape=box, style=filled, fillcolor=yellow] ++} +diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst +index 393f83e8dc4dd..0fb88449fffd0 100644 +--- a/Documentation/admin-guide/media/v4l-drivers.rst ++++ b/Documentation/admin-guide/media/v4l-drivers.rst +@@ -9,6 +9,7 @@ Video4Linux (V4L) driver-specific documentation + .. toctree:: + :maxdepth: 2 + ++ amdisp4-1 + bttv + c3-isp + cafe_ccic +diff --git a/MAINTAINERS b/MAINTAINERS +index d1cc0e12fe1f0..985c66951475d 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1160,6 +1160,31 @@ 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 ++M: Nirujogi Pratap ++L: linux-media@vger.kernel.org ++S: Maintained ++T: git git://linuxtv.org/media.git ++F: Documentation/admin-guide/media/amdisp4-1.rst ++F: Documentation/admin-guide/media/amdisp4.dot ++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 + L: amd-gfx@lists.freedesktop.org diff --git a/Makefile b/Makefile -index edd04bdf3a39..b2cbb19d6723 100644 +index b17ca865bcee7..37d287b75ed25 100644 --- a/Makefile +++ b/Makefile @@ -356,6 +356,17 @@ ifneq ($(filter install,$(MAKECMDGOALS)),) @@ -95,7 +257,7 @@ index edd04bdf3a39..b2cbb19d6723 100644 @: diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index ec33376f8e2b..255679ba5351 100644 +index ec33376f8e2ba..255679ba53510 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1213,9 +1213,9 @@ config HIGHMEM @@ -110,8 +272,63 @@ index ec33376f8e2b..255679ba5351 100644 help The VM uses one page of physical memory for each page table. For systems with a lot of processes, this can use a lot of +diff --git a/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi b/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi +index 9261b67dbee1d..1e76b290510d3 100644 +--- a/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi ++++ b/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi +@@ -141,9 +141,10 @@ v3d: v3d@7ec00000 { + interrupts = <1 10>; + }; + +- vc4: gpu { +- compatible = "brcm,bcm2835-vc4"; +- }; ++ }; ++ ++ vc4: gpu { ++ compatible = "brcm,bcm2835-vc4"; + }; + }; + +diff --git a/arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi b/arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi +index e9bf41b9f5c18..46c91468f4c54 100644 +--- a/arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi ++++ b/arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi +@@ -1,8 +1,8 @@ + #include + + / { +- soc { +- firmware: firmware { ++ firmware { ++ firmware: rpi-firmware { + compatible = "raspberrypi,bcm2835-firmware", "simple-mfd"; + mboxes = <&mailbox>; + +@@ -10,14 +10,16 @@ firmware_clocks: clocks { + compatible = "raspberrypi,firmware-clocks"; + #clock-cells = <1>; + }; +- }; + +- power: power { +- compatible = "raspberrypi,bcm2835-power"; +- firmware = <&firmware>; +- #power-domain-cells = <1>; ++ power: power { ++ compatible = "raspberrypi,bcm2835-power"; ++ firmware = <&firmware>; ++ #power-domain-cells = <1>; ++ }; + }; ++ }; + ++ soc { + vchiq: mailbox@7e00b840 { + compatible = "brcm,bcm2835-vchiq"; + reg = <0x7e00b840 0x3c>; diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig -index 9ea19b74b6c3..6d2800a2a061 100644 +index 9ea19b74b6c3c..6d2800a2a0617 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1454,7 +1454,7 @@ endchoice @@ -123,8 +340,353 @@ index 9ea19b74b6c3..6d2800a2a061 100644 help For systems with 52-bit userspace VAs enabled, the kernel will attempt to maintain compatibility with older software by providing 48-bit VAs +diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts +index 7de24d60bcd1a..127be0fc27c22 100644 +--- a/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts +@@ -35,3 +35,17 @@ &gio_aon { + "PMIC_SCL", // AON_SGPIO_04 + "PMIC_SDA"; // AON_SGPIO_05 + }; ++ ++&pinctrl { ++ compatible = "brcm,bcm2712d0-pinctrl"; ++ reg = <0x7d504100 0x20>; ++}; ++ ++&pinctrl_aon { ++ compatible = "brcm,bcm2712d0-aon-pinctrl"; ++ reg = <0x7d510700 0x1c>; ++}; ++ ++&uart10 { ++ interrupts = ; ++}; +diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi +index 04738bf281ebd..b7a6bc34ae1ab 100644 +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi +@@ -44,6 +44,30 @@ power_button: power-button { + }; + }; + ++ firmware { ++ firmware: rpi-firmware { ++ compatible = "raspberrypi,bcm2835-firmware", "simple-mfd"; ++ ++ mboxes = <&mailbox>; ++ ++ firmware_clocks: clocks { ++ compatible = "raspberrypi,firmware-clocks"; ++ #clock-cells = <1>; ++ }; ++ ++ reset: reset { ++ compatible = "raspberrypi,firmware-reset"; ++ #reset-cells = <1>; ++ }; ++ ++ power: power { ++ compatible = "raspberrypi,bcm2835-power"; ++ firmware = <&firmware>; ++ #power-domain-cells = <1>; ++ }; ++ }; ++ }; ++ + sd_io_1v8_reg: sd-io-1v8-reg { + compatible = "regulator-gpio"; + regulator-name = "vdd-sd-io"; +@@ -189,33 +213,6 @@ wifi: wifi@1 { + }; + }; + +-&soc { +- firmware: firmware { +- compatible = "raspberrypi,bcm2835-firmware", "simple-mfd"; +- #address-cells = <1>; +- #size-cells = <1>; +- +- mboxes = <&mailbox>; +- dma-ranges; +- +- firmware_clocks: clocks { +- compatible = "raspberrypi,firmware-clocks"; +- #clock-cells = <1>; +- }; +- +- reset: reset { +- compatible = "raspberrypi,firmware-reset"; +- #reset-cells = <1>; +- }; +- }; +- +- power: power { +- compatible = "raspberrypi,bcm2835-power"; +- firmware = <&firmware>; +- #power-domain-cells = <1>; +- }; +-}; +- + /* uarta communicates with the BT module */ + &uarta { + uart-has-rtscts; +@@ -252,3 +249,7 @@ &pcie1 { + &pcie2 { + status = "okay"; + }; ++ ++&v3d { ++ clocks = <&firmware_clocks 5>; ++}; +diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +index 2856082814462..0fc57e72632ed 100644 +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +@@ -68,6 +68,30 @@ usb_vbus_default_state: usb-vbus-default-state { + function = "vbus1"; + groups = "vbus1"; + }; ++ ++ rp1_i2c4_default_state: rp1-i2c4-default-state { ++ function = "i2c4"; ++ groups = "i2c4_2"; ++ drive-strength = <12>; ++ bias-pull-up; ++ }; ++ ++ rp1_i2c6_default_state: rp1-i2c6-default-state { ++ function = "i2c6"; ++ groups = "i2c6_0"; ++ drive-strength = <12>; ++ bias-pull-up; ++ }; ++}; ++ ++&rp1_i2c4 { ++ pinctrl-0 = <&rp1_i2c4_default_state>; ++ pinctrl-names = "default"; ++}; ++ ++&rp1_i2c6 { ++ pinctrl-0 = <&rp1_i2c6_default_state>; ++ pinctrl-names = "default"; + }; + + &rp1_usb0 { +diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +index d57a9b1bff70c..761c59d90ffc9 100644 +--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +@@ -1,5 +1,6 @@ + // SPDX-License-Identifier: (GPL-2.0 OR MIT) + #include ++#include + + / { + compatible = "brcm,bcm2712"; +@@ -508,10 +509,6 @@ axi: axi { + <0x18 0x00000000 0x18 0x00000000 0x04 0x00000000>, + <0x1c 0x00000000 0x1c 0x00000000 0x04 0x00000000>; + +- vc4: gpu { +- compatible = "brcm,bcm2712-vc6"; +- }; +- + pcie0: pcie@1000100000 { + compatible = "brcm,bcm2712-pcie"; + reg = <0x10 0x00100000 0x00 0x9310>; +@@ -646,6 +643,30 @@ mip1: msi-controller@1000131000 { + msi-ranges = <&gicv2 GIC_SPI 247 IRQ_TYPE_EDGE_RISING 8>; + brcm,msi-offset = <8>; + }; ++ ++ isp: isp@1000880000 { ++ compatible = "brcm,bcm2712-pispbe", "raspberrypi,pispbe"; ++ reg = <0x10 0x00880000 0x0 0x4000>; ++ interrupts = ; ++ clocks = <&firmware_clocks 7>; ++ }; ++ ++ v3d: gpu@1002000000 { ++ compatible = "brcm,2712-v3d"; ++ reg = <0x10 0x02000000 0x00 0x4000>, ++ <0x10 0x02008000 0x00 0x6000>, ++ <0x10 0x02030800 0x00 0x0700>; ++ reg-names = "hub", "core0", "sms"; ++ ++ power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>; ++ resets = <&pm BCM2835_RESET_V3D>; ++ interrupts = , ++ ; ++ }; ++ }; ++ ++ vc4: gpu { ++ compatible = "brcm,bcm2712-vc6"; + }; + + timer { +diff --git a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi +index 5a815c3797945..16f5359395835 100644 +--- a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi +@@ -26,6 +26,83 @@ rp1_clocks: clocks@40018000 { + <200000000>; // RP1_CLK_SYS + }; + ++ rp1_i2c0: i2c@40070000 { ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x40070000 0x0 0x1000>; ++ interrupts = <7 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ i2c-scl-rising-time-ns = <65>; ++ i2c-scl-falling-time-ns = <100>; ++ ++ status = "disabled"; ++ }; ++ ++ rp1_i2c1: i2c@40074000 { ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x40074000 0x0 0x1000>; ++ interrupts = <8 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ i2c-scl-rising-time-ns = <65>; ++ i2c-scl-falling-time-ns = <100>; ++ ++ status = "disabled"; ++ }; ++ ++ rp1_i2c2: i2c@40078000 { ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x40078000 0x0 0x1000>; ++ interrupts = <9 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ i2c-scl-rising-time-ns = <65>; ++ i2c-scl-falling-time-ns = <100>; ++ ++ status = "disabled"; ++ }; ++ ++ rp1_i2c3: i2c@4007c000 { ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x4007c000 0x0 0x1000>; ++ interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ i2c-scl-rising-time-ns = <65>; ++ i2c-scl-falling-time-ns = <100>; ++ ++ status = "disabled"; ++ }; ++ ++ rp1_i2c4: i2c@40080000 { ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x40080000 0x0 0x1000>; ++ interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ i2c-scl-rising-time-ns = <65>; ++ i2c-scl-falling-time-ns = <100>; ++ ++ status = "disabled"; ++ }; ++ ++ rp1_i2c5: i2c@40084000 { ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x40084000 0x0 0x1000>; ++ interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ i2c-scl-rising-time-ns = <65>; ++ i2c-scl-falling-time-ns = <100>; ++ ++ status = "disabled"; ++ }; ++ ++ rp1_i2c6: i2c@40088000 { ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x40088000 0x0 0x1000>; ++ interrupts = <13 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ i2c-scl-rising-time-ns = <65>; ++ i2c-scl-falling-time-ns = <100>; ++ ++ status = "disabled"; ++ }; ++ + rp1_gpio: pinctrl@400d0000 { + compatible = "raspberrypi,rp1-gpio"; + reg = <0x00 0x400d0000 0x0 0xc000>, +@@ -56,6 +133,34 @@ rp1_eth: ethernet@40100000 { + #size-cells = <0>; + }; + ++ rp1_csi0: csi@40110000 { ++ compatible = "raspberrypi,rp1-cfe"; ++ reg = <0x0 0x40110000 0x0 0x100>, // CSI2 DMA address ++ <0x0 0x40114000 0x0 0x100>, // PHY/CSI Host address ++ <0x0 0x40120000 0x0 0x100>, // MIPI CFG address ++ <0x0 0x40124000 0x0 0x1000>; // PiSP FE address ++ interrupts = <47 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>; ++ assigned-clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>; ++ assigned-clock-rates = <25000000>; ++ ++ status = "disabled"; ++ }; ++ ++ rp1_csi1: csi@40128000 { ++ compatible = "raspberrypi,rp1-cfe"; ++ reg = <0x0 0x40128000 0x0 0x100>, // CSI2 DMA address ++ <0x0 0x4012c000 0x0 0x100>, // PHY/CSI Host address ++ <0x0 0x40138000 0x0 0x100>, // MIPI CFG address ++ <0x0 0x4013c000 0x0 0x1000>; // PiSP FE address ++ interrupts = <48 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>; ++ assigned-clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>; ++ assigned-clock-rates = <25000000>; ++ ++ status = "disabled"; ++ }; ++ + rp1_usb0: usb@40200000 { + compatible = "snps,dwc3"; + reg = <0x00 0x40200000 0x0 0x100000>; +diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi +index 7b27012dfcb56..27c9d88eaa965 100644 +--- a/arch/arm64/boot/dts/freescale/imx93.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx93.dtsi +@@ -43,6 +43,30 @@ map0 { + }; + }; + }; ++ ++ soc@0 { ++ npu@4a900000 { ++ compatible = "fsl,imx93-npu", "arm,ethos-u65"; ++ reg = <0x4a900000 0x1000>; ++ interrupts = ; ++ power-domains = <&mlmix>; ++ clocks = <&clk IMX93_CLK_ML>, <&clk IMX93_CLK_ML_APB>; ++ clock-names = "core", "apb"; ++ sram = <&sram>; ++ assigned-clocks = <&clk IMX93_CLK_ML>, <&clk IMX93_CLK_ML_APB>; ++ assigned-clock-parents = <&clk IMX93_CLK_SYS_PLL_PFD1>, ++ <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>; ++ assigned-clock-rates = <800000000>, <133000000>; ++ }; ++ }; ++ ++ sram: sram@20480000 { ++ compatible = "mmio-sram"; ++ reg = <0x0 0x20480000 0x0 0x18000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x0 0x20480000 0x18000>; ++ }; + }; + + &aips1 { diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c -index 23c05dc7a8f2..d7b7b2f39e16 100644 +index 23c05dc7a8f2a..d7b7b2f39e169 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -32,6 +32,8 @@ @@ -176,7 +738,7 @@ index 23c05dc7a8f2..d7b7b2f39e16 100644 arm64_memblock_init(); diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h -index b0d00032479d..afb9544fb007 100644 +index b0d00032479d6..afb9544fb0074 100644 --- a/arch/s390/include/asm/ipl.h +++ b/arch/s390/include/asm/ipl.h @@ -139,6 +139,7 @@ int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf, @@ -188,7 +750,7 @@ index b0d00032479d..afb9544fb007 100644 /* * DIAG 308 support diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c -index 049c557c452f..4c9fffbe6261 100644 +index 049c557c452ff..4c9fffbe62613 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -2521,3 +2521,8 @@ int ipl_report_free(struct ipl_report *report) @@ -201,7 +763,7 @@ index 049c557c452f..4c9fffbe6261 100644 + return !!ipl_secure_flag; +} diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c -index b60284328fe3..e4d37b08ca29 100644 +index b60284328fe3d..e4d37b08ca295 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -47,6 +47,7 @@ @@ -223,7 +785,7 @@ index b60284328fe3..e4d37b08ca29 100644 /* boot_command_line has been already set up in early.c */ *cmdline_p = boot_command_line; diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c -index eebcc9db1a1b..779aca6969df 100644 +index eebcc9db1a1bb..779aca6969dfd 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -21,6 +21,7 @@ @@ -270,7 +832,7 @@ index eebcc9db1a1b..779aca6969df 100644 reserve_initrd(); diff --git a/crypto/sig.c b/crypto/sig.c -index beba745b6405..fd41f6d3abf9 100644 +index beba745b64057..fd41f6d3abf9a 100644 --- a/crypto/sig.c +++ b/crypto/sig.c @@ -112,8 +112,7 @@ static int sig_prepare_alg(struct sig_alg *alg) @@ -284,7 +846,7 @@ index beba745b6405..fd41f6d3abf9 100644 alg->verify = sig_default_verify; if (!alg->set_priv_key) diff --git a/crypto/testmgr.c b/crypto/testmgr.c -index 4985411dedae..71065f31375d 100644 +index 4985411dedaec..71065f31375d3 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -3979,7 +3979,7 @@ static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs) @@ -297,7 +859,7 @@ index 4985411dedae..71065f31375d 100644 sig_size = crypto_sig_maxsize(tfm); diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c -index 20d757687e3d..90a13f20f052 100644 +index 20d757687e3d9..90a13f20f052b 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -142,6 +142,14 @@ static int apei_hest_parse(apei_hest_func_t func, void *data) @@ -316,7 +878,7 @@ index 20d757687e3d..90a13f20f052 100644 for (i = 0; i < hest_tab->error_source_count; i++) { len = hest_esrc_len(hest_hdr); diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c -index d1595156c86a..9485212f1a6d 100644 +index d1595156c86a4..9485212f1a6d6 100644 --- a/drivers/acpi/irq.c +++ b/drivers/acpi/irq.c @@ -143,6 +143,7 @@ struct acpi_irq_parse_one_ctx { @@ -359,7 +921,7 @@ index d1595156c86a..9485212f1a6d 100644 return ctx.rc; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c -index e8cdbdb46fdb..71366d15a7db 100644 +index e8cdbdb46fdb4..71366d15a7db3 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1792,6 +1792,15 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) @@ -379,7 +941,7 @@ index e8cdbdb46fdb..71366d15a7db 100644 acpi_dev_get_resources(device, &resource_list, acpi_check_serial_bus_slave, diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c -index c79abdfcd7a9..e23bfb7f94c7 100644 +index c79abdfcd7a9b..e23bfb7f94c72 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -731,6 +731,24 @@ int ahci_stop_engine(struct ata_port *ap) @@ -408,7 +970,7 @@ index c79abdfcd7a9..e23bfb7f94c7 100644 tmp = ata_wait_register(ap, port_mmio + PORT_CMD, PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c -index 505e32911c34..26f7ee0ccf40 100644 +index 505e32911c34c..26f7ee0ccf406 100644 --- a/drivers/char/ipmi/ipmi_dmi.c +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -215,6 +215,21 @@ static int __init scan_for_dmi_ipmi(void) @@ -434,7 +996,7 @@ index 505e32911c34..26f7ee0ccf40 100644 dmi_decode_ipmi((const struct dmi_header *) dev->device_data); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c -index c41f51c82edd..b36f35b54bd6 100644 +index c41f51c82eddc..b36f35b54bd60 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -34,6 +34,7 @@ @@ -469,7 +1031,7 @@ index c41f51c82edd..b36f35b54bd6 100644 rv = ipmi_register_driver(); mutex_unlock(&ipmi_interfaces_mutex); diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile -index 8efbcf699e4f..96d5a1ca981d 100644 +index 8efbcf699e4ff..96d5a1ca981df 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -25,6 +25,7 @@ subdir-$(CONFIG_EFI_STUB) += libstub @@ -481,7 +1043,7 @@ index 8efbcf699e4f..96d5a1ca981d 100644 obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c -index b2fb92a4bbd1..533e132e1bd8 100644 +index b2fb92a4bbd11..533e132e1bd8a 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -33,6 +33,7 @@ @@ -626,7 +1188,7 @@ index b2fb92a4bbd1..533e132e1bd8 100644 EXPORT_SYMBOL_GPL(efi_status_to_err); diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c -index 6a337f1f8787..89244e0d9fa8 100644 +index 6a337f1f8787b..89244e0d9fa86 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -132,6 +132,11 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, @@ -642,7 +1204,7 @@ index 6a337f1f8787..89244e0d9fa8 100644 fdt_pack(fdt); diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c -index 516f4f0069bd..380354755108 100644 +index 516f4f0069bd2..380354755108b 100644 --- a/drivers/firmware/efi/libstub/secureboot.c +++ b/drivers/firmware/efi/libstub/secureboot.c @@ -29,10 +29,13 @@ enum efi_secureboot_mode efi_get_secureboot(void) @@ -679,7 +1241,7 @@ index 516f4f0069bd..380354755108 100644 } diff --git a/drivers/firmware/efi/secureboot.c b/drivers/firmware/efi/secureboot.c new file mode 100644 -index 000000000000..de0a3714a5d4 +index 0000000000000..de0a3714a5d44 --- /dev/null +++ b/drivers/firmware/efi/secureboot.c @@ -0,0 +1,38 @@ @@ -721,8 +1283,24 @@ index 000000000000..de0a3714a5d4 + } + } +} +diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c +index 18f2bf1fe89fa..fc74351efad5c 100644 +--- a/drivers/gpu/drm/v3d/v3d_submit.c ++++ b/drivers/gpu/drm/v3d/v3d_submit.c +@@ -393,6 +393,11 @@ v3d_get_multisync_submit_deps(struct drm_file *file_priv, + if (multisync.pad) + return -EINVAL; + ++ if (!multisync.in_sync_count && !multisync.out_sync_count) { ++ drm_dbg(&v3d->drm, "Empty multisync extension\n"); ++ return -EINVAL; ++ } ++ + ret = v3d_get_multisync_post_deps(file_priv, se, multisync.out_sync_count, + multisync.out_syncs); + if (ret) diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c -index d4af17fdba46..154f0403cbf4 100644 +index d4af17fdba467..154f0403cbf4c 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -321,21 +321,12 @@ static int rmi_input_event(struct hid_device *hdev, u8 *data, int size) @@ -824,7 +1402,7 @@ index d4af17fdba46..154f0403cbf4 100644 data->xport.ops = &hid_rmi_ops; diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c -index d565a73f0042..0e64fc7c1004 100644 +index d565a73f0042e..0e64fc7c10041 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -12,6 +12,7 @@ @@ -875,7 +1453,7 @@ index d565a73f0042..0e64fc7c1004 100644 platform_driver_unregister(&etm4_platform_driver); etm4_pm_clear(); diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c -index ccd9338a44db..5c54e522e8a4 100644 +index ccd9338a44dbe..5c54e522e8a47 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -183,34 +183,47 @@ void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status, @@ -1067,7 +1645,7 @@ index ccd9338a44db..5c54e522e8a4 100644 if (data->f01_container->dev.driver) { /* Driver already bound, so enable ATTN now. */ diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c -index ee83850c7060..53ad11f18182 100644 +index ee83850c70605..53ad11f18182e 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -8,6 +8,7 @@ @@ -1106,8 +1684,5550 @@ index ee83850c7060..53ad11f18182 100644 /** * iommu_setup_default_domain - Set the default_domain for the group * @group: Group to change +diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig +index 3f0b7bb68cc94..0b33e927bd59c 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 6d5f79ddfcc37..16c185752474e 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 0000000000000..25af49f246b26 +--- /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 0000000000000..8bfc1955f22e6 +--- /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 0000000000000..55dd2dc453a20 +--- /dev/null ++++ b/drivers/media/platform/amd/isp4/Kconfig +@@ -0,0 +1,15 @@ ++# SPDX-License-Identifier: GPL-2.0+ ++ ++config VIDEO_AMD_ISP4_CAPTURE ++ tristate "AMD ISP4 and camera driver" ++ depends on DRM_AMD_ISP && VIDEO_DEV && HAS_DMA ++ 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 0000000000000..3849062e17f3d +--- /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 0000000000000..bf6b8e26c2c0f +--- /dev/null ++++ b/drivers/media/platform/amd/isp4/isp4.c +@@ -0,0 +1,240 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2025 Advanced Micro Devices, Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#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 */ ++ }, ++}; ++ ++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); ++} ++ ++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]; ++ ++ thread_ctx->resp_ready = true; ++ wake_up_interruptible(&thread_ctx->waitq); ++} ++ ++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); ++ } ++ } ++ ++ /* ++ * 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(). ++ */ ++ 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); ++ ++ /* ++ * 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. ++ */ ++ isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack); ++ ++ /* ++ * 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); ++ ++ return IRQ_HANDLED; ++} ++ ++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; ++ ++ 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 "); ++MODULE_AUTHOR("Pratap Nirujogi "); ++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 0000000000000..2db6683d6d8b5 +--- /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 ++#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 0000000000000..2fc00fc9a1940 +--- /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 0000000000000..d1262e03ae640 +--- /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 ++#include ++ ++#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 0000000000000..88bacb00355c4 +--- /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 0000000000000..09c76f75c5ee9 +--- /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 ++ ++#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 0000000000000..4324d4adbcffa +--- /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 ++ ++#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(sizeof(*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(sizeof(*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(sizeof(*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 0000000000000..ce3ac9b9e5cda +--- /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 ++#include ++#include ++#include ++ ++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 0000000000000..48deea79ce6c2 +--- /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 ++#include ++ ++#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 0000000000000..20ea08a830afd +--- /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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 0000000000000..0cebb39f98e1d +--- /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 ++#include ++#include ++ ++#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 0000000000000..c66451e261664 +--- /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 ++#include ++ ++#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/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h +index 87414a2ddf6e3..2de56017ee0d6 100644 +--- a/drivers/net/ethernet/cadence/macb.h ++++ b/drivers/net/ethernet/cadence/macb.h +@@ -170,6 +170,10 @@ + #define GEM_PCSANNPTX 0x021c /* PCS AN Next Page TX */ + #define GEM_PCSANNPLP 0x0220 /* PCS AN Next Page LP */ + #define GEM_PCSANEXTSTS 0x023c /* PCS AN Extended Status */ ++#define GEM_RXLPI 0x0270 /* RX LPI Transitions */ ++#define GEM_RXLPITIME 0x0274 /* RX LPI Time */ ++#define GEM_TXLPI 0x0278 /* TX LPI Transitions */ ++#define GEM_TXLPITIME 0x027c /* TX LPI Time */ + #define GEM_DCFG1 0x0280 /* Design Config 1 */ + #define GEM_DCFG2 0x0284 /* Design Config 2 */ + #define GEM_DCFG3 0x0288 /* Design Config 3 */ +@@ -305,6 +309,8 @@ + #define MACB_IRXFCS_SIZE 1 + + /* GEM specific NCR bitfields. */ ++#define GEM_TXLPIEN_OFFSET 19 ++#define GEM_TXLPIEN_SIZE 1 + #define GEM_ENABLE_HS_MAC_OFFSET 31 + #define GEM_ENABLE_HS_MAC_SIZE 1 + +@@ -518,6 +524,8 @@ + #define GEM_IRQCOR_SIZE 1 + #define GEM_DBWDEF_OFFSET 25 + #define GEM_DBWDEF_SIZE 3 ++#define GEM_USERIO_OFFSET 9 ++#define GEM_USERIO_SIZE 1 + #define GEM_NO_PCS_OFFSET 0 + #define GEM_NO_PCS_SIZE 1 + +@@ -779,6 +787,10 @@ + #define MACB_CAPS_DMA_PTP BIT(22) + #define MACB_CAPS_RSC BIT(23) + #define MACB_CAPS_NO_LSO BIT(24) ++#define MACB_CAPS_EEE BIT(25) ++#define MACB_CAPS_USRIO_HAS_MII BIT(26) ++#define MACB_CAPS_USRIO_HAS_REFCLK_SOURCE BIT(27) ++#define MACB_CAPS_USRIO_HAS_TSUCLK_SOURCE BIT(28) + + /* LSO settings */ + #define MACB_LSO_UFO_ENABLE 0x01 +@@ -1043,6 +1055,10 @@ struct gem_stats { + u64 rx_ip_header_checksum_errors; + u64 rx_tcp_checksum_errors; + u64 rx_udp_checksum_errors; ++ u64 rx_lpi_transitions; ++ u64 rx_lpi_time; ++ u64 tx_lpi_transitions; ++ u64 tx_lpi_time; + }; + + /* Describes the name and offset of an individual statistic register, as +@@ -1142,6 +1158,10 @@ static const struct gem_statistic gem_statistics[] = { + GEM_BIT(NDS_RXERR)), + GEM_STAT_TITLE_BITS(RXUDPCCNT, "rx_udp_checksum_errors", + GEM_BIT(NDS_RXERR)), ++ GEM_STAT_TITLE(RXLPI, "rx_lpi_transitions"), ++ GEM_STAT_TITLE(RXLPITIME, "rx_lpi_time"), ++ GEM_STAT_TITLE(TXLPI, "tx_lpi_transitions"), ++ GEM_STAT_TITLE(TXLPITIME, "tx_lpi_time"), + }; + + #define GEM_STATS_LEN ARRAY_SIZE(gem_statistics) +@@ -1210,7 +1230,10 @@ struct macb_usrio_config { + u32 rmii; + u32 rgmii; + u32 refclk; ++ u32 clken; + u32 hdfctlen; ++ u32 tsu_source; ++ bool refclk_default_external; + }; + + struct macb_config { +@@ -1357,6 +1380,11 @@ struct macb { + + struct work_struct hresp_err_bh_work; + ++ /* EEE / LPI state */ ++ bool eee_active; ++ struct delayed_work tx_lpi_work; ++ u32 tx_lpi_timer; ++ + int rx_bd_rd_prefetch; + int tx_bd_rd_prefetch; + +@@ -1446,6 +1474,13 @@ static inline bool macb_dma_ptp(struct macb *bp) + bp->caps & MACB_CAPS_DMA_PTP; + } + ++static inline void macb_queue_isr_clear(struct macb *bp, ++ struct macb_queue *queue, u32 value) ++{ ++ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) ++ queue_writel(queue, ISR, value); ++} ++ + /** + * struct macb_platform_data - platform data for MACB Ethernet used for PCI registration + * @pclk: platform clock +diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c +index 99e7d5cf3786f..a12aa21244e83 100644 +--- a/drivers/net/ethernet/cadence/macb_main.c ++++ b/drivers/net/ethernet/cadence/macb_main.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -49,6 +50,7 @@ struct sifive_fu540_macb_mgmt { + + #define MACB_RX_BUFFER_SIZE 128 + #define RX_BUFFER_MULTIPLE 64 /* bytes */ ++#define RX_BUFFER_MAX (0xFF * RX_BUFFER_MULTIPLE) /* 16320 bytes */ + + #define DEFAULT_RX_RING_SIZE 512 /* must be power of 2 */ + #define MIN_RX_RING_SIZE 64 +@@ -68,6 +70,10 @@ struct sifive_fu540_macb_mgmt { + #define MACB_TX_INT_FLAGS (MACB_TX_ERR_FLAGS | MACB_BIT(TCOMP) \ + | MACB_BIT(TXUBR)) + ++#define MACB_INT_MISC_FLAGS (MACB_TX_ERR_FLAGS | MACB_BIT(RXUBR) | \ ++ MACB_BIT(ISR_ROVR) | MACB_BIT(HRESP) | \ ++ GEM_BIT(WOL) | MACB_BIT(WOL)) ++ + /* Max length of transmit frame must be a multiple of 8 bytes */ + #define MACB_TX_LEN_ALIGN 8 + #define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1))) +@@ -558,10 +564,21 @@ static int macb_usx_pcs_config(struct phylink_pcs *pcs, + return 0; + } + ++static unsigned int macb_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++} ++ + static void macb_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, + struct phylink_link_state *state) + { +- state->link = 0; ++ struct macb *bp = container_of(pcs, struct macb, phylink_sgmii_pcs); ++ u16 bmsr, lpa; ++ ++ bmsr = gem_readl(bp, PCSSTS); ++ lpa = gem_readl(bp, PCSANLPBASE); ++ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa); + } + + static void macb_pcs_an_restart(struct phylink_pcs *pcs) +@@ -575,6 +592,26 @@ static int macb_pcs_config(struct phylink_pcs *pcs, + const unsigned long *advertising, + bool permit_pause_to_mac) + { ++ struct macb *bp = container_of(pcs, struct macb, phylink_sgmii_pcs); ++ u32 old, new; ++ ++ old = gem_readl(bp, PCSANADV); ++ new = phylink_mii_c22_pcs_encode_advertisement(interface, advertising); ++ if (new != -EINVAL && old != new) ++ gem_writel(bp, PCSANADV, new); ++ ++ /* Disable AN if it's not to be used, enable otherwise. ++ * Must be written after PCSSEL is set in NCFGR which is done in ++ * macb_mac_config(), otherwise writes will not take effect. ++ */ ++ old = gem_readl(bp, PCSCNTRL); ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) ++ new = old | BMCR_ANENABLE; ++ else ++ new = old & ~BMCR_ANENABLE; ++ if (old != new) ++ gem_writel(bp, PCSCNTRL, new); ++ + return 0; + } + +@@ -585,11 +622,113 @@ static const struct phylink_pcs_ops macb_phylink_usx_pcs_ops = { + }; + + static const struct phylink_pcs_ops macb_phylink_pcs_ops = { ++ .pcs_inband_caps = macb_pcs_inband_caps, + .pcs_get_state = macb_pcs_get_state, + .pcs_an_restart = macb_pcs_an_restart, + .pcs_config = macb_pcs_config, + }; + ++static bool macb_tx_lpi_set(struct macb *bp, bool enable) ++{ ++ u32 old, ncr; ++ ++ lockdep_assert_held(&bp->lock); ++ ++ ncr = macb_readl(bp, NCR); ++ old = ncr; ++ if (enable) ++ ncr |= GEM_BIT(TXLPIEN); ++ else ++ ncr &= ~GEM_BIT(TXLPIEN); ++ if (old != ncr) ++ macb_writel(bp, NCR, ncr); ++ ++ return old != ncr; ++} ++ ++static bool macb_tx_all_queues_idle(struct macb *bp) ++{ ++ struct macb_queue *queue; ++ unsigned int q; ++ ++ for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { ++ if (READ_ONCE(queue->tx_head) != READ_ONCE(queue->tx_tail)) ++ return false; ++ } ++ return true; ++} ++ ++static void macb_tx_lpi_work_fn(struct work_struct *work) ++{ ++ struct macb *bp = container_of(work, struct macb, tx_lpi_work.work); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&bp->lock, flags); ++ if (bp->eee_active && macb_tx_all_queues_idle(bp)) ++ macb_tx_lpi_set(bp, true); ++ spin_unlock_irqrestore(&bp->lock, flags); ++} ++ ++static void macb_tx_lpi_schedule(struct macb *bp) ++{ ++ if (bp->eee_active) ++ mod_delayed_work(system_wq, &bp->tx_lpi_work, ++ usecs_to_jiffies(bp->tx_lpi_timer)); ++} ++ ++/* Wake from LPI before transmitting. The MAC must deassert TXLPIEN ++ * and wait for the PHY to exit LPI before any frame can be sent. ++ * IEEE 802.3az Tw_sys is ~17us for 1000BASE-T, ~30us for 100BASE-TX; ++ * we use a conservative 50us. ++ */ ++static void macb_tx_lpi_wake(struct macb *bp) ++{ ++ lockdep_assert_held(&bp->lock); ++ ++ if (!bp->eee_active) ++ return; ++ ++ if (!macb_tx_lpi_set(bp, false)) ++ return; ++ ++ cancel_delayed_work(&bp->tx_lpi_work); ++ udelay(50); ++} ++ ++static void macb_mac_disable_tx_lpi(struct phylink_config *config) ++{ ++ struct net_device *ndev = to_net_dev(config->dev); ++ struct macb *bp = netdev_priv(ndev); ++ unsigned long flags; ++ ++ cancel_delayed_work_sync(&bp->tx_lpi_work); ++ ++ spin_lock_irqsave(&bp->lock, flags); ++ bp->eee_active = false; ++ macb_tx_lpi_set(bp, false); ++ spin_unlock_irqrestore(&bp->lock, flags); ++} ++ ++static int macb_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, ++ bool tx_clk_stop) ++{ ++ struct net_device *ndev = to_net_dev(config->dev); ++ struct macb *bp = netdev_priv(ndev); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&bp->lock, flags); ++ bp->tx_lpi_timer = timer; ++ bp->eee_active = true; ++ spin_unlock_irqrestore(&bp->lock, flags); ++ ++ /* Defer initial LPI entry by 1 second after link-up per ++ * IEEE 802.3az section 22.7a. ++ */ ++ mod_delayed_work(system_wq, &bp->tx_lpi_work, msecs_to_jiffies(1000)); ++ ++ return 0; ++} ++ + static void macb_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) + { +@@ -629,22 +768,6 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode, + if (old_ncr ^ ncr) + macb_or_gem_writel(bp, NCR, ncr); + +- /* Disable AN for SGMII fixed link configuration, enable otherwise. +- * Must be written after PCSSEL is set in NCFGR, +- * otherwise writes will not take effect. +- */ +- if (macb_is_gem(bp) && state->interface == PHY_INTERFACE_MODE_SGMII) { +- u32 pcsctrl, old_pcsctrl; +- +- old_pcsctrl = gem_readl(bp, PCSCNTRL); +- if (mode == MLO_AN_FIXED) +- pcsctrl = old_pcsctrl & ~GEM_BIT(PCSAUTONEG); +- else +- pcsctrl = old_pcsctrl | GEM_BIT(PCSAUTONEG); +- if (old_pcsctrl != pcsctrl) +- gem_writel(bp, PCSCNTRL, pcsctrl); +- } +- + spin_unlock_irqrestore(&bp->lock, flags); + } + +@@ -845,6 +968,8 @@ static const struct phylink_mac_ops macb_phylink_ops = { + .mac_config = macb_mac_config, + .mac_link_down = macb_mac_link_down, + .mac_link_up = macb_mac_link_up, ++ .mac_disable_tx_lpi = macb_mac_disable_tx_lpi, ++ .mac_enable_tx_lpi = macb_mac_enable_tx_lpi, + }; + + static bool macb_phy_handle_exists(struct device_node *dn) +@@ -909,6 +1034,12 @@ static int macb_mii_probe(struct net_device *dev) + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + bp->phylink_config.poll_fixed_state = true; + bp->phylink_config.get_fixed_state = macb_get_pcs_fixed_state; ++ /* The PCSAUTONEG bit in PCSCNTRL is on out of reset. Setting ++ * default_an_inband to true tells phylink to turn it off only ++ * if necessary (e.g. a fixed link or a PHY that doesn't support ++ * inband). ++ */ ++ bp->phylink_config.default_an_inband = true; + } + + bp->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | +@@ -940,6 +1071,18 @@ static int macb_mii_probe(struct net_device *dev) + } + } + ++ /* Configure EEE LPI if supported */ ++ if (bp->caps & MACB_CAPS_EEE) { ++ __set_bit(PHY_INTERFACE_MODE_MII, ++ bp->phylink_config.lpi_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_GMII, ++ bp->phylink_config.lpi_interfaces); ++ phy_interface_set_rgmii(bp->phylink_config.lpi_interfaces); ++ bp->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD; ++ bp->phylink_config.lpi_timer_default = 250000; ++ bp->phylink_config.eee_enabled_default = true; ++ } ++ + bp->phylink = phylink_create(&bp->phylink_config, bp->pdev->dev.fwnode, + bp->phy_interface, &macb_phylink_ops); + if (IS_ERR(bp->phylink)) { +@@ -1336,6 +1479,9 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) + netif_wake_subqueue(bp->dev, queue_index); + spin_unlock_irqrestore(&queue->tx_ptr_lock, flags); + ++ if (packets) ++ macb_tx_lpi_schedule(bp); ++ + return packets; + } + +@@ -1745,8 +1891,7 @@ static int macb_rx_poll(struct napi_struct *napi, int budget) + */ + if (macb_rx_pending(queue)) { + queue_writel(queue, IDR, bp->rx_intr_mask); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_BIT(RCOMP)); ++ macb_queue_isr_clear(bp, queue, MACB_BIT(RCOMP)); + netdev_vdbg(bp->dev, "poll: packets pending, reschedule\n"); + napi_schedule(napi); + } +@@ -1833,8 +1978,7 @@ static int macb_tx_poll(struct napi_struct *napi, int budget) + */ + if (macb_tx_complete_pending(queue)) { + queue_writel(queue, IDR, MACB_BIT(TCOMP)); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_BIT(TCOMP)); ++ macb_queue_isr_clear(bp, queue, MACB_BIT(TCOMP)); + netdev_vdbg(bp->dev, "TX poll: packets pending, reschedule\n"); + napi_schedule(napi); + } +@@ -1882,62 +2026,92 @@ static void macb_hresp_error_task(struct work_struct *work) + netif_tx_start_all_queues(dev); + } + +-static irqreturn_t macb_wol_interrupt(int irq, void *dev_id) ++static void macb_wol_interrupt(struct macb_queue *queue, u32 status) + { +- struct macb_queue *queue = dev_id; + struct macb *bp = queue->bp; +- u32 status; +- +- status = queue_readl(queue, ISR); +- +- if (unlikely(!status)) +- return IRQ_NONE; + +- spin_lock(&bp->lock); +- +- if (status & MACB_BIT(WOL)) { +- queue_writel(queue, IDR, MACB_BIT(WOL)); +- macb_writel(bp, WOL, 0); +- netdev_vdbg(bp->dev, "MACB WoL: queue = %u, isr = 0x%08lx\n", +- (unsigned int)(queue - bp->queues), +- (unsigned long)status); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_BIT(WOL)); +- pm_wakeup_event(&bp->pdev->dev, 0); +- } ++ queue_writel(queue, IDR, MACB_BIT(WOL)); ++ macb_writel(bp, WOL, 0); ++ netdev_vdbg(bp->dev, "MACB WoL: queue = %u, isr = 0x%08lx\n", ++ (unsigned int)(queue - bp->queues), ++ (unsigned long)status); ++ macb_queue_isr_clear(bp, queue, MACB_BIT(WOL)); ++ pm_wakeup_event(&bp->pdev->dev, 0); ++} + +- spin_unlock(&bp->lock); ++static void gem_wol_interrupt(struct macb_queue *queue, u32 status) ++{ ++ struct macb *bp = queue->bp; + +- return IRQ_HANDLED; ++ queue_writel(queue, IDR, GEM_BIT(WOL)); ++ gem_writel(bp, WOL, 0); ++ netdev_vdbg(bp->dev, "GEM WoL: queue = %u, isr = 0x%08lx\n", ++ (unsigned int)(queue - bp->queues), ++ (unsigned long)status); ++ macb_queue_isr_clear(bp, queue, GEM_BIT(WOL)); ++ pm_wakeup_event(&bp->pdev->dev, 0); + } + +-static irqreturn_t gem_wol_interrupt(int irq, void *dev_id) ++static int macb_interrupt_misc(struct macb_queue *queue, u32 status) + { +- struct macb_queue *queue = dev_id; + struct macb *bp = queue->bp; +- u32 status; ++ struct net_device *dev; ++ u32 ctrl; + +- status = queue_readl(queue, ISR); ++ dev = bp->dev; + +- if (unlikely(!status)) +- return IRQ_NONE; ++ if (unlikely(status & (MACB_TX_ERR_FLAGS))) { ++ queue_writel(queue, IDR, MACB_TX_INT_FLAGS); ++ schedule_work(&queue->tx_error_task); ++ macb_queue_isr_clear(bp, queue, MACB_TX_ERR_FLAGS); ++ return -1; ++ } + +- spin_lock(&bp->lock); ++ /* Link change detection isn't possible with RMII, so we'll ++ * add that if/when we get our hands on a full-blown MII PHY. ++ */ + +- if (status & GEM_BIT(WOL)) { +- queue_writel(queue, IDR, GEM_BIT(WOL)); +- gem_writel(bp, WOL, 0); +- netdev_vdbg(bp->dev, "GEM WoL: queue = %u, isr = 0x%08lx\n", +- (unsigned int)(queue - bp->queues), +- (unsigned long)status); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, GEM_BIT(WOL)); +- pm_wakeup_event(&bp->pdev->dev, 0); ++ /* There is a hardware issue under heavy load where DMA can ++ * stop, this causes endless "used buffer descriptor read" ++ * interrupts but it can be cleared by re-enabling RX. See ++ * the at91rm9200 manual, section 41.3.1 or the Zynq manual ++ * section 16.7.4 for details. RXUBR is only enabled for ++ * these two versions. ++ */ ++ if (status & MACB_BIT(RXUBR)) { ++ ctrl = macb_readl(bp, NCR); ++ macb_writel(bp, NCR, ctrl & ~MACB_BIT(RE)); ++ wmb(); ++ macb_writel(bp, NCR, ctrl | MACB_BIT(RE)); ++ macb_queue_isr_clear(bp, queue, MACB_BIT(RXUBR)); + } + +- spin_unlock(&bp->lock); ++ if (status & MACB_BIT(ISR_ROVR)) { ++ /* We missed at least one packet */ ++ spin_lock(&bp->stats_lock); ++ if (macb_is_gem(bp)) ++ bp->hw_stats.gem.rx_overruns++; ++ else ++ bp->hw_stats.macb.rx_overruns++; ++ spin_unlock(&bp->stats_lock); ++ macb_queue_isr_clear(bp, queue, MACB_BIT(ISR_ROVR)); ++ } + +- return IRQ_HANDLED; ++ if (status & MACB_BIT(HRESP)) { ++ queue_work(system_bh_wq, &bp->hresp_err_bh_work); ++ netdev_err(dev, "DMA bus error: HRESP not OK\n"); ++ macb_queue_isr_clear(bp, queue, MACB_BIT(HRESP)); ++ } ++ ++ if (macb_is_gem(bp)) { ++ if (status & GEM_BIT(WOL)) ++ gem_wol_interrupt(queue, status); ++ } else { ++ if (status & MACB_BIT(WOL)) ++ macb_wol_interrupt(queue, status); ++ } ++ ++ return 0; + } + + static irqreturn_t macb_interrupt(int irq, void *dev_id) +@@ -1945,7 +2119,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) + struct macb_queue *queue = dev_id; + struct macb *bp = queue->bp; + struct net_device *dev = bp->dev; +- u32 status, ctrl; ++ u32 status; + + status = queue_readl(queue, ISR); + +@@ -1958,8 +2132,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) + /* close possible race with dev_close */ + if (unlikely(!netif_running(dev))) { + queue_writel(queue, IDR, -1); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, -1); ++ macb_queue_isr_clear(bp, queue, -1); + break; + } + +@@ -1975,84 +2148,27 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) + * now. + */ + queue_writel(queue, IDR, bp->rx_intr_mask); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_BIT(RCOMP)); +- +- if (napi_schedule_prep(&queue->napi_rx)) { +- netdev_vdbg(bp->dev, "scheduling RX softirq\n"); +- __napi_schedule(&queue->napi_rx); +- } ++ macb_queue_isr_clear(bp, queue, MACB_BIT(RCOMP)); ++ napi_schedule_irqoff(&queue->napi_rx); + } + + if (status & (MACB_BIT(TCOMP) | + MACB_BIT(TXUBR))) { + queue_writel(queue, IDR, MACB_BIT(TCOMP)); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_BIT(TCOMP) | +- MACB_BIT(TXUBR)); +- ++ macb_queue_isr_clear(bp, queue, MACB_BIT(TCOMP) | ++ MACB_BIT(TXUBR)); + if (status & MACB_BIT(TXUBR)) { + queue->txubr_pending = true; + wmb(); // ensure softirq can see update + } + +- if (napi_schedule_prep(&queue->napi_tx)) { +- netdev_vdbg(bp->dev, "scheduling TX softirq\n"); +- __napi_schedule(&queue->napi_tx); +- } +- } +- +- if (unlikely(status & (MACB_TX_ERR_FLAGS))) { +- queue_writel(queue, IDR, MACB_TX_INT_FLAGS); +- schedule_work(&queue->tx_error_task); +- +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_TX_ERR_FLAGS); +- +- break; +- } +- +- /* Link change detection isn't possible with RMII, so we'll +- * add that if/when we get our hands on a full-blown MII PHY. +- */ +- +- /* There is a hardware issue under heavy load where DMA can +- * stop, this causes endless "used buffer descriptor read" +- * interrupts but it can be cleared by re-enabling RX. See +- * the at91rm9200 manual, section 41.3.1 or the Zynq manual +- * section 16.7.4 for details. RXUBR is only enabled for +- * these two versions. +- */ +- if (status & MACB_BIT(RXUBR)) { +- ctrl = macb_readl(bp, NCR); +- macb_writel(bp, NCR, ctrl & ~MACB_BIT(RE)); +- wmb(); +- macb_writel(bp, NCR, ctrl | MACB_BIT(RE)); +- +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_BIT(RXUBR)); +- } +- +- if (status & MACB_BIT(ISR_ROVR)) { +- /* We missed at least one packet */ +- spin_lock(&bp->stats_lock); +- if (macb_is_gem(bp)) +- bp->hw_stats.gem.rx_overruns++; +- else +- bp->hw_stats.macb.rx_overruns++; +- spin_unlock(&bp->stats_lock); +- +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_BIT(ISR_ROVR)); ++ napi_schedule_irqoff(&queue->napi_tx); + } + +- if (status & MACB_BIT(HRESP)) { +- queue_work(system_bh_wq, &bp->hresp_err_bh_work); +- netdev_err(dev, "DMA bus error: HRESP not OK\n"); ++ if (unlikely(status & MACB_INT_MISC_FLAGS)) ++ if (macb_interrupt_misc(queue, status)) ++ break; + +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, MACB_BIT(HRESP)); +- } + status = queue_readl(queue, ISR); + } + +@@ -2442,6 +2558,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev) + skb->len); + + spin_lock(&bp->lock); ++ macb_tx_lpi_wake(bp); + macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); + spin_unlock(&bp->lock); + +@@ -2459,7 +2576,7 @@ static void macb_init_rx_buffer_size(struct macb *bp, size_t size) + if (!macb_is_gem(bp)) { + bp->rx_buffer_size = MACB_RX_BUFFER_SIZE; + } else { +- bp->rx_buffer_size = size; ++ bp->rx_buffer_size = MIN(size, RX_BUFFER_MAX); + + if (bp->rx_buffer_size % RX_BUFFER_MULTIPLE) { + netdev_dbg(bp->dev, +@@ -2746,8 +2863,7 @@ static void macb_reset_hw(struct macb *bp) + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + queue_writel(queue, IDR, -1); + queue_readl(queue, ISR); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, -1); ++ macb_queue_isr_clear(bp, queue, -1); + } + } + +@@ -3107,6 +3223,8 @@ static int macb_close(struct net_device *dev) + netdev_tx_reset_queue(netdev_get_tx_queue(dev, q)); + } + ++ cancel_delayed_work_sync(&bp->tx_lpi_work); ++ + phylink_stop(bp->phylink); + phylink_disconnect_phy(bp->phylink); + +@@ -3242,7 +3360,6 @@ static int gem_get_sset_count(struct net_device *dev, int sset) + + static void gem_get_ethtool_strings(struct net_device *dev, u32 sset, u8 *p) + { +- char stat_string[ETH_GSTRING_LEN]; + struct macb *bp = netdev_priv(dev); + struct macb_queue *queue; + unsigned int i; +@@ -3255,11 +3372,8 @@ static void gem_get_ethtool_strings(struct net_device *dev, u32 sset, u8 *p) + ETH_GSTRING_LEN); + + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { +- for (i = 0; i < QUEUE_STATS_LEN; i++, p += ETH_GSTRING_LEN) { +- snprintf(stat_string, ETH_GSTRING_LEN, "q%d_%s", +- q, queue_statistics[i].stat_string); +- memcpy(p, stat_string, ETH_GSTRING_LEN); +- } ++ for (i = 0; i < QUEUE_STATS_LEN; i++) ++ ethtool_sprintf(&p, "q%u_%s", q, queue_statistics[i].stat_string); + } + break; + } +@@ -3624,15 +3738,14 @@ static unsigned int gem_get_tsu_rate(struct macb *bp) + struct clk *tsu_clk; + unsigned int tsu_rate; + +- tsu_clk = devm_clk_get(&bp->pdev->dev, "tsu_clk"); +- if (!IS_ERR(tsu_clk)) +- tsu_rate = clk_get_rate(tsu_clk); +- /* try pclk instead */ +- else if (!IS_ERR(bp->pclk)) { ++ if (!IS_ERR_OR_NULL(bp->tsu_clk)) { ++ tsu_rate = clk_get_rate(bp->tsu_clk); ++ } else { + tsu_clk = bp->pclk; + tsu_rate = clk_get_rate(tsu_clk); +- } else +- return -ENOTSUPP; ++ dev_warn(&bp->pdev->dev, "devicetree missing tsu_clk, using pclk as fallback\n"); ++ } ++ + return tsu_rate; + } + +@@ -4024,6 +4137,20 @@ static const struct ethtool_ops macb_ethtool_ops = { + .set_ringparam = macb_set_ringparam, + }; + ++static int macb_get_eee(struct net_device *dev, struct ethtool_keee *eee) ++{ ++ struct macb *bp = netdev_priv(dev); ++ ++ return phylink_ethtool_get_eee(bp->phylink, eee); ++} ++ ++static int macb_set_eee(struct net_device *dev, struct ethtool_keee *eee) ++{ ++ struct macb *bp = netdev_priv(dev); ++ ++ return phylink_ethtool_set_eee(bp->phylink, eee); ++} ++ + static const struct ethtool_ops gem_ethtool_ops = { + .get_regs_len = macb_get_regs_len, + .get_regs = macb_get_regs, +@@ -4045,6 +4172,9 @@ static const struct ethtool_ops gem_ethtool_ops = { + .get_rxnfc = gem_get_rxnfc, + .set_rxnfc = gem_set_rxnfc, + .get_rx_ring_count = gem_get_rx_ring_count, ++ .nway_reset = phy_ethtool_nway_reset, ++ .get_eee = macb_get_eee, ++ .set_eee = macb_set_eee, + }; + + static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +@@ -4418,14 +4548,12 @@ static const struct net_device_ops macb_netdev_ops = { + static void macb_configure_caps(struct macb *bp, + const struct macb_config *dt_conf) + { +- struct device_node *np = bp->pdev->dev.of_node; +- bool refclk_ext; + u32 dcfg; + +- refclk_ext = of_property_read_bool(np, "cdns,refclk-ext"); ++ bp->caps = dt_conf->caps; + +- if (dt_conf) +- bp->caps = dt_conf->caps; ++ if (!dt_conf->usrio) ++ bp->caps |= MACB_CAPS_USRIO_DISABLED; + + if (hw_is_gem(bp->regs, bp->native_io)) { + bp->caps |= MACB_CAPS_MACB_IS_GEM; +@@ -4435,6 +4563,8 @@ static void macb_configure_caps(struct macb *bp, + bp->caps |= MACB_CAPS_ISR_CLEAR_ON_WRITE; + if (GEM_BFEXT(NO_PCS, dcfg) == 0) + bp->caps |= MACB_CAPS_PCS; ++ if (!(dcfg & GEM_BIT(USERIO))) ++ bp->caps |= MACB_CAPS_USRIO_DISABLED; + dcfg = gem_readl(bp, DCFG12); + if (GEM_BFEXT(HIGH_SPEED, dcfg) == 1) + bp->caps |= MACB_CAPS_HIGH_SPEED; +@@ -4456,9 +4586,6 @@ static void macb_configure_caps(struct macb *bp, + } + } + +- if (refclk_ext) +- bp->caps |= MACB_CAPS_USRIO_HAS_CLKEN; +- + dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); + } + +@@ -4497,9 +4624,9 @@ static void macb_clks_disable(struct clk *pclk, struct clk *hclk, struct clk *tx + clk_bulk_disable_unprepare(ARRAY_SIZE(clks), clks); + } + +-static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, +- struct clk **hclk, struct clk **tx_clk, +- struct clk **rx_clk, struct clk **tsu_clk) ++static int macb_clk_init_dflt(struct platform_device *pdev, struct clk **pclk, ++ struct clk **hclk, struct clk **tx_clk, ++ struct clk **rx_clk, struct clk **tsu_clk) + { + struct macb_platform_data *pdata; + int err; +@@ -4582,7 +4709,20 @@ static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, + return err; + } + +-static int macb_init(struct platform_device *pdev) ++static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, ++ struct clk **hclk, struct clk **tx_clk, ++ struct clk **rx_clk, struct clk **tsu_clk, ++ const struct macb_config *config) ++{ ++ if (config->clk_init) ++ return config->clk_init(pdev, pclk, hclk, tx_clk, rx_clk, ++ tsu_clk); ++ else ++ return macb_clk_init_dflt(pdev, pclk, hclk, tx_clk, rx_clk, ++ tsu_clk); ++} ++ ++static int macb_init_dflt(struct platform_device *pdev) + { + struct net_device *dev = platform_get_drvdata(pdev); + unsigned int hw_q, q; +@@ -4603,7 +4743,7 @@ static int macb_init(struct platform_device *pdev) + queue->bp = bp; + spin_lock_init(&queue->tx_ptr_lock); + netif_napi_add(dev, &queue->napi_rx, macb_rx_poll); +- netif_napi_add(dev, &queue->napi_tx, macb_tx_poll); ++ netif_napi_add_tx(dev, &queue->napi_tx, macb_tx_poll); + if (hw_q) { + queue->ISR = GEM_ISR(hw_q - 1); + queue->IER = GEM_IER(hw_q - 1); +@@ -4713,16 +4853,52 @@ static int macb_init(struct platform_device *pdev) + + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) { + val = 0; +- if (phy_interface_mode_is_rgmii(bp->phy_interface)) +- val = bp->usrio->rgmii; +- else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII && +- (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII)) +- val = bp->usrio->rmii; +- else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII)) +- val = bp->usrio->mii; ++ if (bp->caps & MACB_CAPS_USRIO_HAS_MII) { ++ if (phy_interface_mode_is_rgmii(bp->phy_interface)) ++ val = bp->usrio->rgmii; ++ else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII && ++ (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII)) ++ val = bp->usrio->rmii; ++ else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII)) ++ val = bp->usrio->mii; ++ } + + if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN) +- val |= bp->usrio->refclk; ++ val |= bp->usrio->clken; ++ ++ if (bp->caps & MACB_CAPS_USRIO_HAS_REFCLK_SOURCE) { ++ const char *prop; ++ bool refclk_ext; ++ int ret; ++ ++ /* Default to whatever was set in the match data for ++ * this device. There's two properties for refclk ++ * control, but the boolean one is deprecated so is ++ * a lower priority to check, no device should have ++ * both. ++ */ ++ refclk_ext = bp->usrio->refclk_default_external; ++ ++ ret = of_property_read_string(pdev->dev.of_node, ++ "cdns,refclk-source", &prop); ++ if (!ret) { ++ if (!strcmp(prop, "external")) ++ refclk_ext = true; ++ else ++ refclk_ext = false; ++ } else { ++ ret = of_property_read_bool(pdev->dev.of_node, ++ "cdns,refclk-ext"); ++ if (ret) ++ refclk_ext = true; ++ } ++ ++ if (refclk_ext) ++ val |= bp->usrio->refclk; ++ } ++ ++ if (bp->caps & MACB_CAPS_USRIO_HAS_TSUCLK_SOURCE) ++ val |= bp->usrio->tsu_source; + + macb_or_gem_writel(bp, USRIO, val); + } +@@ -4737,11 +4913,20 @@ static int macb_init(struct platform_device *pdev) + return 0; + } + +-static const struct macb_usrio_config macb_default_usrio = { ++static int macb_init(struct platform_device *pdev, ++ const struct macb_config *config) ++{ ++ if (config->init) ++ return config->init(pdev); ++ else ++ return macb_init_dflt(pdev); ++} ++ ++static const struct macb_usrio_config at91_default_usrio = { + .mii = MACB_BIT(MII), + .rmii = MACB_BIT(RMII), + .rgmii = GEM_BIT(RGMII), +- .refclk = MACB_BIT(CLKEN), ++ .clken = MACB_BIT(CLKEN), + }; + + #if defined(CONFIG_OF) +@@ -5190,7 +5375,7 @@ static int fu540_c000_clk_init(struct platform_device *pdev, struct clk **pclk, + struct clk_init_data init; + int err = 0; + +- err = macb_clk_init(pdev, pclk, hclk, tx_clk, rx_clk, tsu_clk); ++ err = macb_clk_init_dflt(pdev, pclk, hclk, tx_clk, rx_clk, tsu_clk); + if (err) + return err; + +@@ -5237,7 +5422,7 @@ static int fu540_c000_init(struct platform_device *pdev) + if (IS_ERR(mgmt->reg)) + return PTR_ERR(mgmt->reg); + +- return macb_init(pdev); ++ return macb_init_dflt(pdev); + } + + static int init_reset_optional(struct platform_device *pdev) +@@ -5287,7 +5472,7 @@ static int init_reset_optional(struct platform_device *pdev) + return dev_err_probe(&pdev->dev, ret, "failed to reset controller"); + } + +- ret = macb_init(pdev); ++ ret = macb_init_dflt(pdev); + + err_out_phy_exit: + if (ret) +@@ -5312,130 +5497,134 @@ static int eyeq5_init(struct platform_device *pdev) + if (ret) + return dev_err_probe(dev, ret, "failed to init PHY\n"); + +- ret = macb_init(pdev); ++ ret = macb_init_dflt(pdev); + if (ret) + phy_exit(bp->phy); + return ret; + } + +-static const struct macb_usrio_config sama7g5_usrio = { ++static const struct macb_usrio_config mpfs_usrio = { ++ .tsu_source = 0, ++}; ++ ++static const struct macb_usrio_config sama7g5_gem_usrio = { ++ .mii = 0, ++ .rmii = 1, ++ .rgmii = 2, ++ .refclk = BIT(2), ++ .refclk_default_external = false, ++ .hdfctlen = BIT(6), ++}; ++ ++static const struct macb_usrio_config sama7g5_emac_usrio = { + .mii = 0, + .rmii = 1, + .rgmii = 2, + .refclk = BIT(2), ++ .refclk_default_external = true, + .hdfctlen = BIT(6), + }; + + static const struct macb_config fu540_c000_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO | +- MACB_CAPS_GEM_HAS_PTP, ++ MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, + .clk_init = fu540_c000_clk_init, + .init = fu540_c000_init, + .jumbo_max_len = 10240, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config at91sam9260_config = { +- .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, ++ .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | ++ MACB_CAPS_USRIO_HAS_MII, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config sama5d3macb_config = { + .caps = MACB_CAPS_SG_DISABLED | +- MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, ++ MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | ++ MACB_CAPS_USRIO_HAS_MII, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config pc302gem_config = { +- .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE, ++ .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config sama5d2_config = { +- .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_JUMBO, ++ .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_JUMBO | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, + .jumbo_max_len = 10240, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config sama5d29_config = { +- .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_GEM_HAS_PTP, ++ .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_GEM_HAS_PTP | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config sama5d3_config = { + .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE | +- MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_JUMBO, ++ MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_JUMBO | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, + .jumbo_max_len = 10240, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config sama5d4_config = { +- .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII, ++ .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 4, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config emac_config = { +- .caps = MACB_CAPS_NEEDS_RSTONUBR | MACB_CAPS_MACB_IS_EMAC, ++ .caps = MACB_CAPS_NEEDS_RSTONUBR | MACB_CAPS_MACB_IS_EMAC | ++ MACB_CAPS_USRIO_HAS_MII, + .clk_init = at91ether_clk_init, + .init = at91ether_init, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config np4_config = { + .caps = MACB_CAPS_USRIO_DISABLED, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, + }; + + static const struct macb_config zynqmp_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | +- MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_BD_RD_PREFETCH, ++ MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_BD_RD_PREFETCH | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, + .init = init_reset_optional, + .jumbo_max_len = 10240, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config zynq_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_NO_GIGABIT_HALF | +- MACB_CAPS_NEEDS_RSTONUBR, ++ MACB_CAPS_NEEDS_RSTONUBR | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config mpfs_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | +- MACB_CAPS_GEM_HAS_PTP, ++ MACB_CAPS_GEM_HAS_PTP | ++ MACB_CAPS_USRIO_HAS_TSUCLK_SOURCE, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, + .init = init_reset_optional, +- .usrio = &macb_default_usrio, ++ .usrio = &mpfs_usrio, + .max_tx_length = 4040, /* Cadence Erratum 1686 */ + .jumbo_max_len = 4040, + }; +@@ -5443,57 +5632,63 @@ static const struct macb_config mpfs_config = { + static const struct macb_config sama7g5_gem_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | + MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | +- MACB_CAPS_MIIONRGMII | MACB_CAPS_GEM_HAS_PTP, ++ MACB_CAPS_USRIO_HAS_REFCLK_SOURCE | ++ MACB_CAPS_MIIONRGMII | MACB_CAPS_GEM_HAS_PTP | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &sama7g5_usrio, ++ .usrio = &sama7g5_gem_usrio, + }; + + static const struct macb_config sama7g5_emac_config = { + .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | +- MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_MIIONRGMII | +- MACB_CAPS_GEM_HAS_PTP, ++ MACB_CAPS_MIIONRGMII | ++ MACB_CAPS_USRIO_HAS_REFCLK_SOURCE | ++ MACB_CAPS_GEM_HAS_PTP | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &sama7g5_usrio, ++ .usrio = &sama7g5_emac_usrio, + }; + + static const struct macb_config versal_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_NEED_TSUCLK | MACB_CAPS_QUEUE_DISABLE | +- MACB_CAPS_QBV, ++ MACB_CAPS_QBV | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, + .init = init_reset_optional, + .jumbo_max_len = 10240, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + }; + + static const struct macb_config eyeq5_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_QUEUE_DISABLE | +- MACB_CAPS_NO_LSO, ++ MACB_CAPS_NO_LSO | MACB_CAPS_EEE, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, + .init = eyeq5_init, + .jumbo_max_len = 10240, +- .usrio = &macb_default_usrio, + }; + + static const struct macb_config raspberrypi_rp1_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | + MACB_CAPS_JUMBO | +- MACB_CAPS_GEM_HAS_PTP, ++ MACB_CAPS_GEM_HAS_PTP | ++ MACB_CAPS_EEE | ++ MACB_CAPS_USRIO_HAS_MII, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + .jumbo_max_len = 10240, + }; + ++static const struct macb_config pic64hpsc_config = { ++ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO | ++ MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_USRIO_DISABLED, ++ .dma_burst_length = 16, ++ .init = init_reset_optional, ++ .jumbo_max_len = 16383, ++}; ++ + static const struct of_device_id macb_dt_ids[] = { + { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config }, + { .compatible = "cdns,macb" }, +@@ -5512,6 +5707,7 @@ static const struct of_device_id macb_dt_ids[] = { + { .compatible = "cdns,zynq-gem", .data = &zynq_config }, /* deprecated */ + { .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config }, + { .compatible = "microchip,mpfs-macb", .data = &mpfs_config }, ++ { .compatible = "microchip,pic64hpsc-gem", .data = &pic64hpsc_config}, + { .compatible = "microchip,sama7g5-gem", .data = &sama7g5_gem_config }, + { .compatible = "microchip,sama7g5-emac", .data = &sama7g5_emac_config }, + { .compatible = "mobileye,eyeq5-gem", .data = &eyeq5_config }, +@@ -5529,9 +5725,7 @@ static const struct macb_config default_gem_config = { + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP, + .dma_burst_length = 16, +- .clk_init = macb_clk_init, +- .init = macb_init, +- .usrio = &macb_default_usrio, ++ .usrio = &at91_default_usrio, + .jumbo_max_len = 10240, + }; + +@@ -5559,7 +5753,8 @@ static int macb_probe(struct platform_device *pdev) + if (!macb_config) + macb_config = &default_gem_config; + +- err = macb_config->clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk, &tsu_clk); ++ err = macb_clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk, &tsu_clk, ++ macb_config); + if (err) + return err; + +@@ -5619,6 +5814,13 @@ static int macb_probe(struct platform_device *pdev) + + bp->usrio = macb_config->usrio; + ++ if (of_property_read_bool(bp->pdev->dev.of_node, "cdns,timer-adjust") && ++ IS_ENABLED(CONFIG_MACB_USE_HWSTAMP)) { ++ dev_err(&pdev->dev, "Timer adjust mode is not supported\n"); ++ err = -EINVAL; ++ goto err_out_free_netdev; ++ } ++ + /* By default we set to partial store and forward mode for zynqmp. + * Disable if not set in devicetree. + */ +@@ -5665,7 +5867,8 @@ static int macb_probe(struct platform_device *pdev) + /* MTU range: 68 - 1518 or 10240 */ + dev->min_mtu = GEM_MTU_MIN_SIZE; + if ((bp->caps & MACB_CAPS_JUMBO) && bp->jumbo_max_len) +- dev->max_mtu = bp->jumbo_max_len - ETH_HLEN - ETH_FCS_LEN; ++ dev->max_mtu = MIN(bp->jumbo_max_len, RX_BUFFER_MAX) - ++ ETH_HLEN - ETH_FCS_LEN; + else + dev->max_mtu = 1536 - ETH_HLEN - ETH_FCS_LEN; + +@@ -5699,7 +5902,7 @@ static int macb_probe(struct platform_device *pdev) + bp->phy_interface = interface; + + /* IP specific init */ +- err = macb_config->init(pdev); ++ err = macb_init(pdev, macb_config); + if (err) + goto err_out_free_netdev; + +@@ -5716,6 +5919,7 @@ static int macb_probe(struct platform_device *pdev) + } + + INIT_WORK(&bp->hresp_err_bh_work, macb_hresp_error_task); ++ INIT_DELAYED_WORK(&bp->tx_lpi_work, macb_tx_lpi_work_fn); + + netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n", + macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID), +@@ -5759,6 +5963,7 @@ static void macb_remove(struct platform_device *pdev) + mdiobus_free(bp->mii_bus); + + device_set_wakeup_enable(&bp->pdev->dev, 0); ++ cancel_delayed_work_sync(&bp->tx_lpi_work); + cancel_work_sync(&bp->hresp_err_bh_work); + pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); +@@ -5778,7 +5983,6 @@ static int __maybe_unused macb_suspend(struct device *dev) + unsigned long flags; + u32 tmp, ifa_local; + unsigned int q; +- int err; + + if (!device_may_wakeup(&bp->dev->dev)) + phy_exit(bp->phy); +@@ -5827,8 +6031,7 @@ static int __maybe_unused macb_suspend(struct device *dev) + /* Disable all interrupts */ + queue_writel(queue, IDR, -1); + queue_readl(queue, ISR); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(queue, ISR, -1); ++ macb_queue_isr_clear(bp, queue, -1); + } + /* Enable Receive engine */ + macb_writel(bp, NCR, tmp | MACB_BIT(RE)); +@@ -5842,39 +6045,15 @@ static int __maybe_unused macb_suspend(struct device *dev) + /* write IP address into register */ + tmp |= MACB_BFEXT(IP, ifa_local); + } +- spin_unlock_irqrestore(&bp->lock, flags); + +- /* Change interrupt handler and +- * Enable WoL IRQ on queue 0 +- */ +- devm_free_irq(dev, bp->queues[0].irq, bp->queues); + if (macb_is_gem(bp)) { +- err = devm_request_irq(dev, bp->queues[0].irq, gem_wol_interrupt, +- IRQF_SHARED, netdev->name, bp->queues); +- if (err) { +- dev_err(dev, +- "Unable to request IRQ %d (error %d)\n", +- bp->queues[0].irq, err); +- return err; +- } +- spin_lock_irqsave(&bp->lock, flags); + queue_writel(bp->queues, IER, GEM_BIT(WOL)); + gem_writel(bp, WOL, tmp); +- spin_unlock_irqrestore(&bp->lock, flags); + } else { +- err = devm_request_irq(dev, bp->queues[0].irq, macb_wol_interrupt, +- IRQF_SHARED, netdev->name, bp->queues); +- if (err) { +- dev_err(dev, +- "Unable to request IRQ %d (error %d)\n", +- bp->queues[0].irq, err); +- return err; +- } +- spin_lock_irqsave(&bp->lock, flags); + queue_writel(bp->queues, IER, MACB_BIT(WOL)); + macb_writel(bp, WOL, tmp); +- spin_unlock_irqrestore(&bp->lock, flags); + } ++ spin_unlock_irqrestore(&bp->lock, flags); + + enable_irq_wake(bp->queues[0].irq); + } +@@ -5916,7 +6095,6 @@ static int __maybe_unused macb_resume(struct device *dev) + struct macb_queue *queue; + unsigned long flags; + unsigned int q; +- int err; + + if (!device_may_wakeup(&bp->dev->dev)) + phy_init(bp->phy); +@@ -5939,21 +6117,9 @@ static int __maybe_unused macb_resume(struct device *dev) + } + /* Clear ISR on queue 0 */ + queue_readl(bp->queues, ISR); +- if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) +- queue_writel(bp->queues, ISR, -1); ++ macb_queue_isr_clear(bp, bp->queues, -1); + spin_unlock_irqrestore(&bp->lock, flags); + +- /* Replace interrupt handler on queue 0 */ +- devm_free_irq(dev, bp->queues[0].irq, bp->queues); +- err = devm_request_irq(dev, bp->queues[0].irq, macb_interrupt, +- IRQF_SHARED, netdev->name, bp->queues); +- if (err) { +- dev_err(dev, +- "Unable to request IRQ %d (error %d)\n", +- bp->queues[0].irq, err); +- return err; +- } +- + disable_irq_wake(bp->queues[0].irq); + + /* Now make sure we disable phy before moving diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index 48946cca4be7..a1f5fe8e583f 100644 +index 48946cca4be72..a1f5fe8e583fb 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4508,6 +4508,30 @@ static void quirk_aspeed_pci_bridge_no_alias(struct pci_dev *pdev) @@ -1142,7 +7262,7 @@ index 48946cca4be7..a1f5fe8e583f 100644 * Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero) * class code. Fix it. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index 24960ba9caa9..32597cdb72ef 100644 +index 24960ba9caa91..32597cdb72ef9 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) @@ -1160,7 +7280,7 @@ index 24960ba9caa9..32597cdb72ef 100644 * disconnected while waiting for the lock to succeed. */ usb_lock_device(hdev); diff --git a/include/linux/efi.h b/include/linux/efi.h -index 664898d09ff5..34f476d02181 100644 +index 664898d09ff57..34f476d021815 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -45,6 +45,8 @@ struct screen_info; @@ -1228,7 +7348,7 @@ index 664898d09ff5..34f476d02181 100644 enum efi_secureboot_mode efi_get_secureboot_mode(efi_get_variable_t *get_var) { diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h -index 8c42b4bde09c..694ce5c5bcd4 100644 +index 8c42b4bde09c0..694ce5c5bcd43 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -446,6 +446,7 @@ LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap) @@ -1240,7 +7360,7 @@ index 8c42b4bde09c..694ce5c5bcd4 100644 LSM_HOOK(int, 0, perf_event_open, int type) LSM_HOOK(int, 0, perf_event_alloc, struct perf_event *event) diff --git a/include/linux/rmi.h b/include/linux/rmi.h -index ab7eea01ab42..fff7c5f737fc 100644 +index ab7eea01ab427..fff7c5f737fc8 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -364,6 +364,7 @@ struct rmi_driver_data { @@ -1252,7 +7372,7 @@ index ab7eea01ab42..fff7c5f737fc 100644 int rmi_register_transport_device(struct rmi_transport_dev *xport); diff --git a/include/linux/security.h b/include/linux/security.h -index ee88dd2d2d1f..5c816f0b8b67 100644 +index ee88dd2d2d1f7..5c816f0b8b671 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2405,4 +2405,13 @@ static inline void security_initramfs_populated(void) @@ -1270,7 +7390,7 @@ index ee88dd2d2d1f..5c816f0b8b67 100644 + #endif /* ! __LINUX_SECURITY_H */ diff --git a/kernel/module/signing.c b/kernel/module/signing.c -index a2ff4242e623..f0d2be1ee4f1 100644 +index a2ff4242e623d..f0d2be1ee4f1c 100644 --- a/kernel/module/signing.c +++ b/kernel/module/signing.c @@ -61,10 +61,17 @@ int mod_verify_sig(const void *mod, struct load_info *info) @@ -1293,7 +7413,7 @@ index a2ff4242e623..f0d2be1ee4f1 100644 int module_sig_check(struct load_info *info, int flags) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib -index 0718e39cedda..10e94a1b8b6e 100644 +index 0718e39cedda2..10e94a1b8b6e3 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -201,7 +201,10 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call @@ -1308,7 +7428,7 @@ index 0718e39cedda..10e94a1b8b6e 100644 objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \ diff --git a/scripts/tags.sh b/scripts/tags.sh -index 243373683f98..2affd5e58ee8 100755 +index 243373683f98a..2affd5e58ee82 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -16,6 +16,8 @@ fi @@ -1321,7 +7441,7 @@ index 243373683f98..2affd5e58ee8 100755 # ignore arbitrary directories if [ -n "${IGNORE_DIRS}" ]; then diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c -index d1fdd113450a..182e8090cfe8 100644 +index d1fdd113450a6..182e8090cfe85 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -74,7 +74,8 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, @@ -1345,7 +7465,7 @@ index d1fdd113450a..182e8090cfe8 100644 } diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig -index e84ddf484010..d0501353a4b9 100644 +index e84ddf4840101..d0501353a4b95 100644 --- a/security/lockdown/Kconfig +++ b/security/lockdown/Kconfig @@ -16,6 +16,19 @@ config SECURITY_LOCKDOWN_LSM_EARLY @@ -1369,7 +7489,7 @@ index e84ddf484010..d0501353a4b9 100644 prompt "Kernel default lockdown mode" default LOCK_DOWN_KERNEL_FORCE_NONE diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c -index 8d46886d2cca..14a9cdff942b 100644 +index 8d46886d2cca0..14a9cdff942b9 100644 --- a/security/lockdown/lockdown.c +++ b/security/lockdown/lockdown.c @@ -72,6 +72,17 @@ static int lockdown_is_locked_down(enum lockdown_reason what) @@ -1391,7 +7511,7 @@ index 8d46886d2cca..14a9cdff942b 100644 LSM_HOOK_INIT(locked_down, lockdown_is_locked_down), }; diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile -index 65485967c968..e44f47e01027 100644 +index 65485967c9681..e44f47e01027c 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -497,7 +497,7 @@ LSKELS := fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c \ @@ -1404,7 +7524,7 @@ index 65485967c968..e44f47e01027 100644 SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED) diff --git a/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c b/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c -index 1d7a2f1e0731..b22f3a9cb8b8 100644 +index 1d7a2f1e07317..b22f3a9cb8b80 100644 --- a/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c +++ b/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c @@ -7,7 +7,6 @@ diff --git a/sources b/sources index 2dcc53283..3e74a45b4 100644 --- a/sources +++ b/sources @@ -1,3 +1,3 @@ -SHA512 (linux-7.0.1.tar.xz) = 3ad0e54a5f7c5dd401fb357477cdfcc15ea36c16232ed0663d337b20ce473886154e5ba13998787a6264e2c92c964ff19d5846bd1e268abf6ec3d1f553f566ad -SHA512 (kernel-abi-stablelists-7.0.1.tar.xz) = 2e8e1c3635b2aa137f61d8d6108067f34b996a4fb04abd0ca6640cca225c59eacb5ec426b2419ddf1881d79e66b1e2e9bac25a4dc7b0139902da014e2acf826a -SHA512 (kernel-kabi-dw-7.0.1.tar.xz) = 48aec2e526b261cdee9f59eac131be13dabd87b7e959b3b45849969bf8136f7100c12b4d92ef45d9e18867bb021dba2d9e0d7754c15596cd972b99e439af86d4 +SHA512 (linux-7.0.2.tar.xz) = 4d0678ca27dda6b74e585fc706b119b95384112c1e759fe51d1876eb853dc6ae3b539302e2bbd8154387219f5d9f55957c364f69e7b10aeac34f7845051b3b9c +SHA512 (kernel-abi-stablelists-7.0.2.tar.xz) = d65b251f3902eb9720582b9eaec71dc1541c3c66a05b302f335cd0089daae9af2f11b9bfc2f7c798e3d76aff98a7d07f1227fe7cb6de166ba224b98137c9de49 +SHA512 (kernel-kabi-dw-7.0.2.tar.xz) = 2369d95fc4fa061198b64a2b7cba37ebcae1de9848d8a120077024f946aa2ac104fbe98d279c2a6e30b465ad76baaeeca40e414f113ef91070e3f6790b92af1f