From 0fdfffb8e6f5e3050c0338b0dcc7307320a8e8a9 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 28 Aug 2025 12:19:02 +0200 Subject: [PATCH 01/14] watchdog: apple: Add "apple,t8103-wdt" compatible After discussion with the devicetree maintainers we agreed to not extend lists with the generic compatible "apple,wdt" anymore [1]. Use "apple,t8103-wdt" as base compatible as it is the SoC the driver and bindings were written for. [1]: https://lore.kernel.org/asahi/12ab93b7-1fc2-4ce0-926e-c8141cfe81bf@kernel.org/ Reviewed-by: Neal Gompa Signed-off-by: Janne Grunau --- drivers/watchdog/apple_wdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index 66a158f67a712b..6b9b0f9b05cedf 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -218,6 +218,7 @@ static int apple_wdt_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(apple_wdt_pm_ops, apple_wdt_suspend, apple_wdt_resume); static const struct of_device_id apple_wdt_of_match[] = { + { .compatible = "apple,t8103-wdt" }, { .compatible = "apple,wdt" }, {}, }; From 2df7342af0d6a361032fb434702d691034bad059 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 20 Mar 2026 11:34:45 +0100 Subject: [PATCH 02/14] kbuild: modules-cpio-pkg: Respect INSTALL_MOD_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The modules-cpio-pkg target added in commit 2a9c8c0b59d3 ("kbuild: add target to build a cpio containing modules") is incompatible with initramfs with merged /lib and /usr/lib directories [1]. "/lib" cannot be a link and directory at the same time. Respect a non-empty INSTALL_MOD_PATH in the modules-cpio-pkg target so that `make INSTALL_MOD_PATH=/usr modules-cpio-pkg` results in the same module install location as `make INSTALL_MOD_PATH=/usr modules_install`. Tested with Fedora distribution initramfs produced by dracut. Link: https://systemd.io/THE_CASE_FOR_THE_USR_MERGE/ [1] Fixes: 2a9c8c0b59d3 ("kbuild: add target to build a cpio containing modules") Cc: stable@vger.kernel.org Reviewed-by: Simon Glass Reviewed-by: Nathan Chancellor Reviewed-by: Thomas Weißschuh Signed-off-by: Janne Grunau --- scripts/Makefile.package | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/Makefile.package b/scripts/Makefile.package index 83bfcf7cb09fd2..1b56b1fa0d3f3e 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -195,7 +195,7 @@ tar%-pkg: linux-$(KERNELRELEASE)-$(ARCH).tar.% FORCE .tmp_modules_cpio: FORCE $(Q)$(MAKE) -f $(srctree)/Makefile $(Q)rm -rf $@ - $(Q)$(MAKE) -f $(srctree)/Makefile INSTALL_MOD_PATH=$@ modules_install + $(Q)$(MAKE) -f $(srctree)/Makefile INSTALL_MOD_PATH=$@/$(INSTALL_MOD_PATH) modules_install quiet_cmd_cpio = CPIO $@ cmd_cpio = $(CONFIG_SHELL) $(srctree)/usr/gen_initramfs.sh -o $@ $< @@ -265,6 +265,7 @@ help: @echo ' tarxz-pkg - Build the kernel as a xz compressed tarball' @echo ' tarzst-pkg - Build the kernel as a zstd compressed tarball' @echo ' modules-cpio-pkg - Build the kernel modules as cpio archive' + @echo ' (uses INSTALL_MOD_PATH inside the archive)' @echo ' perf-tar-src-pkg - Build the perf source tarball with no compression' @echo ' perf-targz-src-pkg - Build the perf source tarball with gzip compression' @echo ' perf-tarbz2-src-pkg - Build the perf source tarball with bz2 compression' From bc163362d6f8ffa321ab1a2978a102ec5b851f33 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 26 Mar 2026 09:22:18 +0100 Subject: [PATCH 03/14] Revert "drm: Fix use-after-free on framebuffers and property blobs when calling drm_dev_unplug" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6bee098b91417654703e17eb5c1822c6dfd0c01d. Den 2026-03-25 kl. 22:11, skrev Simona Vetter: > On Wed, Mar 25, 2026 at 10:26:40AM -0700, Guenter Roeck wrote: >> Hi, >> >> On Fri, Mar 13, 2026 at 04:17:27PM +0100, Maarten Lankhorst wrote: >>> When trying to do a rather aggressive test of igt's "xe_module_load >>> --r reload" with a full desktop environment and game running I noticed >>> a few OOPSes when dereferencing freed pointers, related to >>> framebuffers and property blobs after the compositor exits. >>> >>> Solve this by guarding the freeing in drm_file with drm_dev_enter/exit, >>> and immediately put the references from struct drm_file objects during >>> drm_dev_unplug(). >>> >> >> With this patch in v6.18.20, I get the warning backtraces below. >> The backtraces are gone with the patch reverted. > > Yeah, this needs to be reverted, reasoning below. Maarten, can you please > take care of that and feed the revert through the usual channels? I don't > think it's critical enough that we need to fast-track this into drm.git > directly. > > Quoting the patch here again: > >> drivers/gpu/drm/drm_file.c | 5 ++++- >> drivers/gpu/drm/drm_mode_config.c | 9 ++++++--- >> 2 files changed, 10 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c >> index ec820686b3021..f52141f842a1f 100644 >> --- a/drivers/gpu/drm/drm_file.c >> +++ b/drivers/gpu/drm/drm_file.c >> @@ -233,6 +233,7 @@ static void drm_events_release(struct drm_file *file_priv) >> void drm_file_free(struct drm_file *file) >> { >> struct drm_device *dev; >> + int idx; >> >> if (!file) >> return; >> @@ -249,9 +250,11 @@ void drm_file_free(struct drm_file *file) >> >> drm_events_release(file); >> >> - if (drm_core_check_feature(dev, DRIVER_MODESET)) { >> + if (drm_core_check_feature(dev, DRIVER_MODESET) && >> + drm_dev_enter(dev, &idx)) { > > This is misplaced for two reasons: > > - Even if we'd want to guarantee that we hold a drm_dev_enter/exit > reference during framebuffer teardown, we'd need to do this > _consistently over all callsites. Not ad-hoc in just one place that a > testcase hits. This also means kerneldoc updates of the relevant hooks > and at least a bunch of acks from other driver people to document the > consensus. > > - More importantly, this is driver responsibilities in general unless we > have extremely good reasons to the contrary. Which means this must be > placed in xe. > >> drm_fb_release(file); >> drm_property_destroy_user_blobs(dev, file); >> + drm_dev_exit(idx); >> } >> >> if (drm_core_check_feature(dev, DRIVER_SYNCOBJ)) >> diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c >> index 84ae8a23a3678..e349418978f79 100644 >> --- a/drivers/gpu/drm/drm_mode_config.c >> +++ b/drivers/gpu/drm/drm_mode_config.c >> @@ -583,10 +583,13 @@ void drm_mode_config_cleanup(struct drm_device *dev) >> */ >> WARN_ON(!list_empty(&dev->mode_config.fb_list)); >> list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { >> - struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); >> + if (list_empty(&fb->filp_head) || drm_framebuffer_read_refcount(fb) > 1) { >> + struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); > > This is also wrong: > > - Firstly, it's a completely independent bug, we do not smash two bugfixes > into one patch. > > - Secondly, it's again a driver bug: drm_mode_cleanup must be called when > the last drm_device reference disappears (hence the existence of > drmm_mode_config_init), not when the driver gets unbound. The fact that > this shows up in a callchain from a devres cleanup means the intel > driver gets this wrong (like almost everyone else because historically > we didn't know better). > > If we don't follow this rule, then we get races with this code here > running concurrently with drm_file fb cleanups, which just does not > work. Review pointed that out, but then shrugged it off with a confused > explanation: > > https://lore.kernel.org/all/e61e64c796ccfb17ae673331a3df4b877bf42d82.camel@linux.intel.com/ > > Yes this also means a lot of the other drm_device teardown that drivers > do happens way too early. There is a massive can of worms here of a > magnitude that most likely is much, much bigger than what you can > backport to stable kernels. Hotunplug is _hard_. Back to the drawing board, and fixing it in the intel display driver instead. Cc: Thomas Hellström Reported-by: Guenter Roeck Acked-by: Simona Vetter Signed-off-by: Maarten Lankhorst Fixes: 6bee098b9141 ("drm: Fix use-after-free on framebuffers and property blobs when calling drm_dev_unplug") --- drivers/gpu/drm/drm_file.c | 5 +---- drivers/gpu/drm/drm_mode_config.c | 9 +++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 6ee9b3df873514..be5e617ceb9f13 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -233,7 +233,6 @@ static void drm_events_release(struct drm_file *file_priv) void drm_file_free(struct drm_file *file) { struct drm_device *dev; - int idx; if (!file) return; @@ -250,11 +249,9 @@ void drm_file_free(struct drm_file *file) drm_events_release(file); - if (drm_core_check_feature(dev, DRIVER_MODESET) && - drm_dev_enter(dev, &idx)) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) { drm_fb_release(file); drm_property_destroy_user_blobs(dev, file); - drm_dev_exit(idx); } if (drm_core_check_feature(dev, DRIVER_SYNCOBJ)) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 802bc4608abf54..d12db9b0bab810 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -577,13 +577,10 @@ void drm_mode_config_cleanup(struct drm_device *dev) */ WARN_ON(!list_empty(&dev->mode_config.fb_list)); list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { - if (list_empty(&fb->filp_head) || drm_framebuffer_read_refcount(fb) > 1) { - struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); + struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); - drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); - drm_framebuffer_print_info(&p, 1, fb); - } - list_del_init(&fb->filp_head); + drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); + drm_framebuffer_print_info(&p, 1, fb); drm_framebuffer_free(&fb->base.refcount); } From 4511a4b18668afa97f64f1f715661d7bee543768 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 3 Apr 2026 12:36:06 +0200 Subject: [PATCH 04/14] arm64: dts: apple: t8122: Add PCI power enable GPIOs - WLAN/BT (SMC PMU GPIO #13) (all devices) - ASM3142 (SMC PMU GPIO #14) (j434, iMac with 4 USB-C ports) - SD card reader (SMC PMU GPIO #23) (j504, 14-inch MacBook Pro) Signed-off-by: Janne Grunau --- arch/arm64/boot/dts/apple/t8122-j434.dts | 1 + arch/arm64/boot/dts/apple/t8122-j504.dts | 1 + arch/arm64/boot/dts/apple/t8122-jxxx.dtsi | 1 + 3 files changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8122-j434.dts b/arch/arm64/boot/dts/apple/t8122-j434.dts index f9635b6eb7ffe3..fd79ec61091391 100644 --- a/arch/arm64/boot/dts/apple/t8122-j434.dts +++ b/arch/arm64/boot/dts/apple/t8122-j434.dts @@ -38,6 +38,7 @@ &port02 { bus-range = <3 3>; + pwren-gpios = <&smc_gpio 14 GPIO_ACTIVE_HIGH>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/apple/t8122-j504.dts b/arch/arm64/boot/dts/apple/t8122-j504.dts index 5f19711a489bad..53859f64e76c8f 100644 --- a/arch/arm64/boot/dts/apple/t8122-j504.dts +++ b/arch/arm64/boot/dts/apple/t8122-j504.dts @@ -42,6 +42,7 @@ &port01 { /* SD card reader */ bus-range = <2 2>; + pwren-gpios = <&smc_gpio 23 GPIO_ACTIVE_HIGH>; status = "okay"; sdhci0: mmc@0,0 { diff --git a/arch/arm64/boot/dts/apple/t8122-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8122-jxxx.dtsi index 2d36782c920d4f..3eac7384882040 100644 --- a/arch/arm64/boot/dts/apple/t8122-jxxx.dtsi +++ b/arch/arm64/boot/dts/apple/t8122-jxxx.dtsi @@ -56,6 +56,7 @@ */ &port00 { bus-range = <1 1>; + pwren-gpios = <&smc_gpio 13 GPIO_ACTIVE_HIGH>; wifi0: wifi@0,0 { compatible = "pci14e4,4434"; From e6916c0787e072e412c5d447a7f742f46de00bc9 Mon Sep 17 00:00:00 2001 From: Michael Reeves Date: Fri, 30 Jan 2026 21:43:14 +1100 Subject: [PATCH 05/14] arm64: dts: apple: Add MTP DockChannel to M3 device tree The internal keyboard and trackpad HID on MacBook variants of the Apple M3 (t8122) SoC are connected through a Apple -developed protocol called DockChannel and mediated by a coprocessor known as the Multi-Touch Processor (MTP). This commit adds the nessecary device tree nodes to the M3's device tree for internal HID to work. It is disabled by default, to be enabled only in MacBook board files where it is tested and confirmed to work. Co-developed-by: Alyssa Milburn Signed-off-by: Alyssa Milburn Signed-off-by: Michael Reeves --- arch/arm64/boot/dts/apple/t8122.dtsi | 77 ++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8122.dtsi b/arch/arm64/boot/dts/apple/t8122.dtsi index 7de60a0b3b7c64..9d1a4d1ea06ce0 100644 --- a/arch/arm64/boot/dts/apple/t8122.dtsi +++ b/arch/arm64/boot/dts/apple/t8122.dtsi @@ -544,6 +544,83 @@ ; }; + mtp: mtp@2fa400000 { + compatible = "apple,t8122-mtp", "apple,t8122-rtk-helper-asc4", "apple,mtp", "apple,rtk-helper-asc4"; + reg = <0x2 0xfa400000 0x0 0x4000>, + <0x2 0xfac00000 0x0 0x100000>; + reg-names = "asc", "sram"; + + mboxes = <&mtp_mbox>; + iommus = <&mtp_dart 1>; + #helper-cells = <0>; + + status = "disabled"; + }; + + mtp_mbox: mbox@2fa408000 { + compatible = "apple,t8122-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x2 0xfa408000 0x0 0x4000>; + + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + status = "disabled"; + }; + + mtp_dart: iommu@2fa808000 { + compatible = "apple,t8122-dart", "apple,t8110-dart"; + reg = <0x2 0xfa808000 0x0 0x4000>; + + interrupt-parent = <&aic>; + interrupts = ; + + #iommu-cells = <1>; + + status = "disabled"; + }; + + mtp_dockchannel: fifo@2fab30000 { + compatible = "apple,t8122-dockchannel", "apple,dockchannel"; + reg = <0x2 0xfab14000 0x0 0x4000>; + reg-names = "irq"; + interrupt-parent = <&aic>; + interrupts = ; + + ranges = <0 0x2 0xfab30000 0x20000>; + nonposted-mmio; + #address-cells = <1>; + #size-cells = <1>; + + interrupt-controller; + #interrupt-cells = <2>; + + status = "disabled"; + + mtp_hid: input@8000 { + compatible = "apple,dockchannel-hid"; + reg = <0x8000 0x4000>, + <0xc000 0x4000>, + <0x0000 0x4000>, + <0x4000 0x4000>; + reg-names = "rmt-config", "rmt-data", "config", "data"; + + iommus = <&mtp_dart 1>; + + interrupt-parent = <&mtp_dockchannel>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>, + <3 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "tx", "rx"; + + apple,fifo-size = <0x800>; + apple,helper-cpu = <&mtp>; + }; + }; + ans_mbox: mbox@309408000 { compatible = "apple,t8122-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x3 0x09408000 0x0 0x4000>; From d183f642f306322e1ad6c0695aa8f880abb77c10 Mon Sep 17 00:00:00 2001 From: Michael Reeves Date: Fri, 30 Jan 2026 22:06:07 +1100 Subject: [PATCH 06/14] arm64: dts: apple: t8122: Add MTP device nodes to Macbook board files Add mtp device nodes for t8122 (M3) based MacBooks. Signed-off-by: Michael Reeves --- arch/arm64/boot/dts/apple/t8122-j504.dts | 39 ++++++++++++++++++++++++ arch/arm64/boot/dts/apple/t8122-j613.dts | 39 ++++++++++++++++++++++++ arch/arm64/boot/dts/apple/t8122-j615.dts | 39 ++++++++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t8122-j504.dts b/arch/arm64/boot/dts/apple/t8122-j504.dts index 53859f64e76c8f..7cc0ffc4a8a925 100644 --- a/arch/arm64/boot/dts/apple/t8122-j504.dts +++ b/arch/arm64/boot/dts/apple/t8122-j504.dts @@ -62,3 +62,42 @@ status = "okay"; }; +&mtp { + status = "okay"; +}; + +&mtp_mbox { + status = "okay"; +}; + +&mtp_dart { + status = "okay"; +}; + +&mtp_dockchannel { + status = "okay"; +}; + +&mtp_hid { + apple,afe-reset-gpios = <&smc_gpio 8 GPIO_ACTIVE_LOW>; + apple,stm-reset-gpios = <&smc_gpio 24 GPIO_ACTIVE_LOW>; + + multi-touch { + firmware-name = "apple/tpmtfw-j504.bin"; + }; + + keyboard: keyboard { + hid-country-code = <0>; + apple,keyboard-layout-id = <0>; + }; + + stm { + }; + + actuator { + }; + + tp_accel { + }; +}; + diff --git a/arch/arm64/boot/dts/apple/t8122-j613.dts b/arch/arm64/boot/dts/apple/t8122-j613.dts index 3e4e87cab2bf84..0e0ff85f7e793a 100644 --- a/arch/arm64/boot/dts/apple/t8122-j613.dts +++ b/arch/arm64/boot/dts/apple/t8122-j613.dts @@ -41,3 +41,42 @@ &fpwm1 { status = "okay"; }; + +&mtp { + status = "okay"; +}; + +&mtp_mbox { + status = "okay"; +}; + +&mtp_dart { + status = "okay"; +}; + +&mtp_dockchannel { + status = "okay"; +}; + +&mtp_hid { + apple,afe-reset-gpios = <&smc_gpio 8 GPIO_ACTIVE_LOW>; + apple,stm-reset-gpios = <&smc_gpio 24 GPIO_ACTIVE_LOW>; + + multi-touch { + firmware-name = "apple/tpmtfw-j613.bin"; + }; + + keyboard: keyboard { + hid-country-code = <0>; + apple,keyboard-layout-id = <0>; + }; + + stm { + }; + + actuator { + }; + + tp_accel { + }; +}; diff --git a/arch/arm64/boot/dts/apple/t8122-j615.dts b/arch/arm64/boot/dts/apple/t8122-j615.dts index 56ad290655dcb6..77b249dda6fcfc 100644 --- a/arch/arm64/boot/dts/apple/t8122-j615.dts +++ b/arch/arm64/boot/dts/apple/t8122-j615.dts @@ -41,3 +41,42 @@ &fpwm1 { status = "okay"; }; + +&mtp { + status = "okay"; +}; + +&mtp_mbox { + status = "okay"; +}; + +&mtp_dart { + status = "okay"; +}; + +&mtp_dockchannel { + status = "okay"; +}; + +&mtp_hid { + apple,afe-reset-gpios = <&smc_gpio 8 GPIO_ACTIVE_LOW>; + apple,stm-reset-gpios = <&smc_gpio 24 GPIO_ACTIVE_LOW>; + + multi-touch { + firmware-name = "apple/tpmtfw-j615.bin"; + }; + + keyboard: keyboard { + hid-country-code = <0>; + apple,keyboard-layout-id = <0>; + }; + + stm { + }; + + actuator { + }; + + tp_accel { + }; +}; From 39c90798c4226e55449843e9ff0e340e24c89828 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Mon, 6 Apr 2026 10:25:39 +1000 Subject: [PATCH 07/14] drm: apple: Define IOMFB parameter for Adaptive Sync IOMFB exposes a method that allows firmware consumers to change display behaviour parameters at runtime. One such parameter is IOMFBParameter_adaptive_sync, which allows DCP to be informed of the desired minimum refresh rate, media target rate, and fractional rate. Add an enum to define the supported parameters, and add IOMFBPARAM_ADAPTIVE_SYNC to it as a starting point. Signed-off-by: James Calligeros --- drivers/gpu/drm/apple/iomfb.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 5799586106713e..8a871db0b94a70 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -46,6 +46,15 @@ enum dcpep_type { IOMFB_MESSAGE_TYPE_MSG = 2, }; +/* + * IOMFB supports the setting of a number of parameters + * that alter various aspects of the connected sink's + * behaviour at runtime. + */ +enum iomfb_parameter { + IOMFBPARAM_ADAPTIVE_SYNC = 14, +}; + #define IOMFB_MESSAGE_TYPE GENMASK_ULL( 3, 0) /* Message */ From adf1383c2076a32ac45ed26222acec86aea977ea Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Mon, 6 Apr 2026 10:21:44 +1000 Subject: [PATCH 08/14] drm: apple: Do not set IOMFBParameter_adaptive_sync on poweron This was actually unnecessary, and having dcp_on_set_parameter as a dcp_callback_t will introduce some complicated duplication when enabling VRR. Remove this callback and just set the display handle on poweron instead. Signed-off-by: James Calligeros --- drivers/gpu/drm/apple/iomfb_template.c | 30 ++++++-------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 1b7ecbcba925d1..6aaa3888c83198 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -784,26 +784,11 @@ static void dcp_on_set_power_state(struct apple_dcp *dcp, void *out, void *cooki dcp_set_power_state(dcp, false, &req, dcp_on_final, cookie); } -static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_set_parameter_dcp param = { - .param = 14, - .value = { 0 }, -#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) - .count = 3, -#else - .count = 1, -#endif - }; - - dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_set_power_state, cookie); -} - void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) { struct dcp_wait_cookie *cookie; int ret; - u32 handle; + u32 handle = 0; dev_info(dcp->dev, "dcp_poweron() starting\n"); cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); @@ -815,15 +800,12 @@ void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) /* increase refcount to ensure the receiver has a reference */ kref_get(&cookie->refcount); - if (dcp->main_display) { - handle = 0; - dcp_set_display_device(dcp, false, &handle, dcp_on_set_power_state, - cookie); - } else { + if (!dcp->main_display) handle = 2; - dcp_set_display_device(dcp, false, &handle, - dcp_on_set_parameter, cookie); - } + + dcp_set_display_device(dcp, false, &handle, dcp_on_set_power_state, + cookie); + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(10000)); if (ret == 0) { From 7c7b7087475d164eca3e63d48f6d9b86bf2c14ef Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Fri, 3 Apr 2026 20:31:17 +1000 Subject: [PATCH 09/14] drm: apple: Add preliminary VRR support DCP supports VRR/Adaptive Sync, with its enormous firmware blob handling the low-level details for us. Display refresh rate is determined by the swap timing values provided to DCP on each swap request. VRR is activated by setting IOMFBadaptive_sync_parameter::minRR and then requesting a modeset. Wire up all of the required KMS properties to expose VRR to userspace, and tell DCP to enable it when supported. This enables VRR *unconditionally* for supported sinks, which will be fixed in a future commit. Signed-off-by: James Calligeros --- drivers/gpu/drm/apple/apple_drv.c | 4 +++ drivers/gpu/drm/apple/iomfb.c | 1 + drivers/gpu/drm/apple/iomfb_template.c | 46 ++++++++++++++++++++++++-- drivers/gpu/drm/apple/parser.c | 12 +++++-- drivers/gpu/drm/apple/parser.h | 2 ++ 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 0f36dad6f96351..31b86e909014ad 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -335,6 +335,10 @@ static int apple_probe_per_dcp(struct device *dev, if (ret) return ret; + ret = drm_connector_attach_vrr_capable_property(&connector->base); + if (ret) + return ret; + connector->base.polled = DRM_CONNECTOR_POLL_HPD; connector->connected = false; connector->dcp = dcp; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 1d9448f0f4dc47..1d90e4a2597303 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -244,6 +244,7 @@ void dcp_hotplug(struct work_struct *work) if (!connector->connected) { drm_edid_free(connector->drm_edid); + drm_connector_set_vrr_capable_property(&connector->base, false); connector->drm_edid = NULL; } diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 6aaa3888c83198..323cbf1b8b483d 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -546,8 +546,9 @@ static u8 dcpep_cb_prop_chunk(struct apple_dcp *dcp, static bool dcpep_process_chunks(struct apple_dcp *dcp, struct dcp_set_dcpav_prop_end_req *req) { + struct apple_connector *connector = dcp->connector; struct dcp_parse_ctx ctx; - int ret; + int ret, i; if (!dcp->chunks.data) { dev_warn(dcp->dev, "ignoring spurious end\n"); @@ -589,6 +590,15 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, dcp_set_dimensions(dcp); } + if (connector) { + for (i = 0; i < dcp->nr_modes; i++) { + if (dcp->modes[i].vrr) { + drm_connector_set_vrr_capable_property(&connector->base, true); + break; + } + } + } + return true; } @@ -1172,6 +1182,33 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, } } +/* Changes to Adaptive Sync require a trip through set_digital_out_mode */ +static void dcp_on_set_adaptive_sync(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_set_digital_out_mode(dcp, false, &dcp->mode, + complete_set_digital_out_mode, cookie); +} + +static void dcp_set_adaptive_sync(struct apple_dcp *dcp, u64 rate, void *cookie) +{ + struct dcp_set_parameter_dcp param = { + .param = IOMFBPARAM_ADAPTIVE_SYNC, + .value = { + rate & 0xffffffff, /* minRR */ + 0, /* mediaTargetRate */ + 0, /* Fractional Rate (?) */ + 0, /* unused */ + }, +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + .count = 3, +#else + .count = 1, +#endif + }; + + dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_set_adaptive_sync, cookie); +} + int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, struct drm_crtc_state *crtc_state) { @@ -1226,8 +1263,11 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, dcp->during_modeset = true; - dcp_set_digital_out_mode(dcp, false, &dcp->mode, - complete_set_digital_out_mode, cookie); + if (mode->vrr) + dcp_set_adaptive_sync(dcp, mode->min_vrr, cookie); + else + dcp_set_digital_out_mode(dcp, false, &dcp->mode, + complete_set_digital_out_mode, cookie); /* * The DCP firmware has an internal timeout of ~8 seconds for diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 7c7af18a7c7e15..010cb10d5c1e28 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -454,6 +454,10 @@ static int parse_mode(struct dcp_parse_ctx *handle, ret = parse_dimension(it.handle, &horiz); else if (!strcmp(key, "VerticalAttributes")) ret = parse_dimension(it.handle, &vert); + else if (!strcmp(key, "MinimumVariableRefreshRate")) + ret = parse_int(it.handle, &out->min_vrr); + else if (!strcmp(key, "MaximumVariableRefreshRate")) + ret = parse_int(it.handle, &out->max_vrr); else if (!strcmp(key, "ColorModes")) ret = parse_color_modes(it.handle, out); else if (!strcmp(key, "ID")) @@ -502,15 +506,17 @@ static int parse_mode(struct dcp_parse_ctx *handle, /* * HACK: * Mark the 120 Hz mode on j314/j316 (identified by resolution) as vrr. - * We still do not know how to drive VRR but at least seetinng timestamps - * in the the swap_surface message to non-zero values drives the display - * at 120 fps. + * Setting timestamps in the the swap_surface message to non-zero + * values drives the display at 120 fps. */ if (vert.precise_sync_rate >> 16 == 120 && ((horiz.active == 3024 && vert.active == 1964) || (horiz.active == 3456 && vert.active == 2234))) out->vrr = true; + if (out->min_vrr && out->max_vrr) + out->vrr = true; + vert.active -= notch_height; vert.sync_width += notch_height; diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index e03ee06ae98a75..18b85a06e8f9d7 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -92,6 +92,8 @@ struct dcp_display_mode { struct dcp_color_mode sdr; struct dcp_color_mode best; bool vrr; + s64 min_vrr; + s64 max_vrr; }; struct dimension { From f7f7e78c84a4429f17a842719b753d1db734cf5c Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sat, 4 Apr 2026 17:43:49 +1000 Subject: [PATCH 10/14] drm: apple: Force modeset when VRR is toggled DCP requires a "modeset" to trigger the upload of the SDP to the display. On some monitors, this is instant. On others, this seems to take as long as a real modeset. Given that in either case we still blank the display, let's just force a full modeset when VRR is toggled on or off. Signed-off-by: James Calligeros --- drivers/gpu/drm/apple/dcp-internal.h | 1 + drivers/gpu/drm/apple/dcp.c | 5 +++++ drivers/gpu/drm/apple/iomfb_template.c | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index f2eb2483c9a880..0a6859448e19eb 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -186,6 +186,7 @@ struct apple_dcp { bool during_modeset; bool valid_mode; bool use_timestamps; + bool vrr_enabled; struct dcp_set_digital_out_mode_req mode; /* completion for active turning true */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 9dfc3fd002f530..c309b8aa5e61ea 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -361,6 +361,11 @@ int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) return -EINVAL; } + if (dcp->vrr_enabled != crtc_state->vrr_enabled) { + dcp->vrr_enabled = crtc_state->vrr_enabled; + crtc_state->mode_changed = true; + } + return 0; } diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 323cbf1b8b483d..ad6af433555115 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1264,7 +1264,7 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, dcp->during_modeset = true; if (mode->vrr) - dcp_set_adaptive_sync(dcp, mode->min_vrr, cookie); + dcp_set_adaptive_sync(dcp, dcp->vrr_enabled ? mode->min_vrr : 0, cookie); else dcp_set_digital_out_mode(dcp, false, &dcp->mode, complete_set_digital_out_mode, cookie); From 126db21a2ef63cc869ebe91ff28979da3482f925 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Fri, 3 Apr 2026 21:59:47 +1000 Subject: [PATCH 11/14] drm: apple: Set swap timestamps to sane values for Adaptive Sync Setting these timestamps to a dummy value worked fine for enabling a fixed 120 Hz mode on the MacBook Pros, however doing so causes Adaptive Sync displays to simply switch between full and minimum refresh rates. Setting these timestamps based on the swap pacing seems to fix this, and makes the display's refresh rate match the incoming swap rate. Note that the names and values are best-guess only. These seem to work fine for driving VRR displays, but may still be incorrect. Signed-off-by: James Calligeros --- drivers/gpu/drm/apple/iomfb_template.c | 20 +++++++++++++++----- drivers/gpu/drm/apple/iomfb_template.h | 6 +++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index ad6af433555115..12418b5552abc1 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1299,6 +1299,15 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, return 0; } +/* + * DCP timestamps are expressed in system timer ticks. Approximate + * this by converting from ktime nanoseconds to 24 MHz ticks. + */ +static u64 ns_to_mach(u64 ns) +{ + return ns * 3 / 125; +} + void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_plane *plane; @@ -1415,12 +1424,13 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru if (has_surface && dcp->use_timestamps) { /* - * Fake timstamps to get 120hz refresh rate. It looks - * like the actual value does not matter, as long as it is non zero. + * TODO: ascertain with certainty what these timestamps + * are. These names are guesses based on what macOS populates + * them with. These values seem to work well with VRR. */ - req->swap.ts1 = 120; - req->swap.ts2 = 120; - req->swap.ts3 = 120; + req->swap.presentation_time = ns_to_mach(ktime_get_ns()); + req->swap.last_pres_time = ns_to_mach(ktime_to_ns(dcp->swap_start)); + req->swap.submit_time = req->swap.presentation_time; } /* These fields should be set together */ diff --git a/drivers/gpu/drm/apple/iomfb_template.h b/drivers/gpu/drm/apple/iomfb_template.h index 8efab49cc53d08..1fea73ff0b95c7 100644 --- a/drivers/gpu/drm/apple/iomfb_template.h +++ b/drivers/gpu/drm/apple/iomfb_template.h @@ -18,14 +18,14 @@ #include "version_utils.h" struct DCP_FW_NAME(dcp_swap) { - u64 ts1; - u64 ts2; + u64 presentation_time; + u64 last_pres_time; u64 unk_10; u64 unk_18; u64 ts64_unk; u64 unk_28; - u64 ts3; + u64 submit_time; u64 unk_38; u64 flags1; From bdb680f156a07be1def4a34edb17dbd002943149 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sat, 4 Apr 2026 23:32:06 +1000 Subject: [PATCH 12/14] drm: apple: Set min and max VRRs for MacBook Pros Since these machines do not have proper EDID/DisplayID data, we need to help the driver along a little bit. We know that "ProMotion" displays can do 24-120 Hz VRR, so let's populate the mode with those values. Signed-off-by: James Calligeros --- drivers/gpu/drm/apple/parser.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 010cb10d5c1e28..40f318c8dfd86e 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -511,8 +511,11 @@ static int parse_mode(struct dcp_parse_ctx *handle, */ if (vert.precise_sync_rate >> 16 == 120 && ((horiz.active == 3024 && vert.active == 1964) || - (horiz.active == 3456 && vert.active == 2234))) + (horiz.active == 3456 && vert.active == 2234))) { + out->min_vrr = 24 << 16; + out->max_vrr = 120 << 16; out->vrr = true; + } if (out->min_vrr && out->max_vrr) out->vrr = true; From 157e8a1d07e3e3493fe75f2c1ce87ea64145f09f Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sun, 5 Apr 2026 13:08:29 +1000 Subject: [PATCH 13/14] drm: apple: Only use swap timestamps if VRR is actually active macOS is inconsistent with how it uses DCP timestamps. Some swaps don't use them at all. We know they are required for VRR display modes to work properly, so let's just turn them on when we are connected to a VRR display. This includes the 120 Hz mode on the 14" and 16" MacBook Pros. Signed-off-by: James Calligeros --- drivers/gpu/drm/apple/iomfb_template.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 12418b5552abc1..dcb740127b2536 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1248,8 +1248,8 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, .timing_mode_id = mode->timing_mode_id }; - /* Keep track of suspected vrr modes */ - dcp->use_timestamps = mode->vrr; + /* Use DCP swap timestamps on MacBook Pros with VRR */ + dcp->use_timestamps = mode->vrr && dcp->main_display; cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (!cookie) { @@ -1422,7 +1422,7 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru req->clear = 1; } - if (has_surface && dcp->use_timestamps) { + if (has_surface && (dcp->use_timestamps || crtc_state->vrr_enabled)) { /* * TODO: ascertain with certainty what these timestamps * are. These names are guesses based on what macOS populates From 31bf70daae3525a4d1d4bbfd7fe07355c0281e3f Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Tue, 7 Apr 2026 21:43:32 +1000 Subject: [PATCH 14/14] NOUPSTREAM: drm: apple: Hide VRR behind a module parameter Given that DCP requires a modeset to activate VRR, and given that this is explicitly banned by KMS API contract and VESA DisplayPort specification, hide this experimental support behind a module param. Interestingly, the HDMI spec does not require a modeset-free VRR transition. For this reason, it is expected that the KMS API contract may change in the future, as both Intel and AMD hardware require a modeset to enable VRR in some circumstances. Either VRR will be expected to be enabled whenever it is supported, *or* modesetting to toggle it on or off will be allowed. When that happens, this commit *must* be reverted. Signed-off-by: James Calligeros --- drivers/gpu/drm/apple/dcp.c | 4 ++++ drivers/gpu/drm/apple/iomfb_template.c | 28 ++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index c309b8aa5e61ea..d830ff72ce8388 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -58,6 +58,10 @@ static bool unstable_edid = true; module_param(unstable_edid, bool, 0644); MODULE_PARM_DESC(unstable_edid, "Enable unstable EDID retrival support"); +bool vrr; +module_param(vrr, bool, 0644); +MODULE_PARM_DESC(vrr, "Enable Adaptive Sync/ProMotion on supported displays"); + /* copied and simplified from drm_vblank.c */ static void send_vblank_event(struct drm_device *dev, struct drm_pending_vblank_event *e, diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index dcb740127b2536..351ceb605f9749 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -34,6 +34,8 @@ /* Register defines used in bandwidth setup structure */ #define REG_DOORBELL_BIT(idx) (2 + (idx)) +extern bool vrr; + struct dcp_wait_cookie { struct kref refcount; struct completion done; @@ -546,9 +548,9 @@ static u8 dcpep_cb_prop_chunk(struct apple_dcp *dcp, static bool dcpep_process_chunks(struct apple_dcp *dcp, struct dcp_set_dcpav_prop_end_req *req) { - struct apple_connector *connector = dcp->connector; + // struct apple_connector *connector = dcp->connector; struct dcp_parse_ctx ctx; - int ret, i; + int ret; //, i; if (!dcp->chunks.data) { dev_warn(dcp->dev, "ignoring spurious end\n"); @@ -590,14 +592,14 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, dcp_set_dimensions(dcp); } - if (connector) { - for (i = 0; i < dcp->nr_modes; i++) { - if (dcp->modes[i].vrr) { - drm_connector_set_vrr_capable_property(&connector->base, true); - break; - } - } - } + // if (connector) { + // for (i = 0; i < dcp->nr_modes; i++) { + // if (dcp->modes[i].vrr) { + // drm_connector_set_vrr_capable_property(&connector->base, true); + // break; + // } + // } + // } return true; } @@ -1263,8 +1265,8 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, dcp->during_modeset = true; - if (mode->vrr) - dcp_set_adaptive_sync(dcp, dcp->vrr_enabled ? mode->min_vrr : 0, cookie); + if (mode->vrr && vrr) + dcp_set_adaptive_sync(dcp, mode->min_vrr, cookie); else dcp_set_digital_out_mode(dcp, false, &dcp->mode, complete_set_digital_out_mode, cookie); @@ -1422,7 +1424,7 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru req->clear = 1; } - if (has_surface && (dcp->use_timestamps || crtc_state->vrr_enabled)) { + if (has_surface && (dcp->use_timestamps || crtc_state->vrr_enabled || vrr)) { /* * TODO: ascertain with certainty what these timestamps * are. These names are guesses based on what macOS populates