* [PATCH v2 00/12] Fixes for apple-gmux and runtime support @ 2026-11-14 16:03 Atharva Tiwari 2026-11-14 16:03 ` [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems Atharva Tiwari 2026-11-14 16:03 ` [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices Atharva Tiwari 0 siblings, 2 replies; 5+ messages in thread From: Atharva Tiwari @ 2026-11-14 16:03 UTC (permalink / raw) Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT), H. Peter Anvin, Alex Deucher, Christian König, David Airlie, Simona Vetter, Harry Wentland, Leo Li, Rodrigo Siqueira, Kenneth Feng, Hans de Goede, Ilpo Järvinen, Andre Eikmeyer, open list:PCI SUBSYSTEM, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT), open list:RADEON and AMDGPU DRM DRIVERS, open list:DRM DRIVERS, open list:X86 PLATFORM DRIVERS This series fixes apple-gmux on T2 Macs (2018-2020): - Fix interrupt handling - Automatically turn on dGPU when a external monitor connects. - Fix base register read/write - Fix power handling - Improve documentation - New module parameter to toggle boot GPU. It also adds vga_switcheroo runtime power management for T2 Macs, built on gmux power control, so the dGPU is powered off automatically when idle and users no longer need to toggle it manually. Changes in v2: - Check the discrete pdev config before GFX0 restore (01/13) - Don't save bridges state when state is VGA_SWITCHEROO_OFF (01/13) - Fixes discrete_pdev initialization (01/13) - Used PWRD on 15,1 and 15,3 aswell - Add quirk to 0x7360 PCI id aswell (02/13) - Add 15,1 and 15,3 and its gpu's aswell - Removed ignore_access code (02/13) - Initialized val in read functions (03/13) - Fixed permanent mutex block on failure (03/13) - Only resumed when pm_runtime is available (skipping on iMacs) (04/13) - Corrected ordering of interrupt_work (04/13) - Notified drm about hotplug change (04/13) - Checked if EFI_RT_SUPPORTED_SET_VARIABLE is available before writing gpu-active (05/13) - Wrote gpu-active on shutdown aswell (05/13) - Dropped boot-gpu parameter (06/13) - Checked if vga_switcheroo has been initialized (09/13) - Added additional pci_is_thunderbolt_attached check to amdgpu_device_supports_gmux (09/13) - Removed duplicate check (10/13) - Renamed gmux_off to runpm_active - Checked if adev->dev exists in dm_dp_aux_transfer to fix null ptr deref (11/13) - Removed GMUX specific code (12/13) - Initialized edp_sink_present in testing code (13/13) Link to v1: https://lore.kernel.org/all/20261025040046.1602-1-atharvatiwarilinuxdev@gmail.com/ Andre Eikmeyer (6): platform/apple-gmux: Write switch state to EFI on MMIO devices drm/amdgpu: Add Apple GMUX runtime PM support drm/amdgpu: Catch register access while GMUX keeps the GPU off drm/amd/display: Refuse DP AUX while GMUX keeps the GPU off drm/amdgpu: Skip AC/DC notification while the GPU is runtime suspended drm/amd/display: Skip panel power savings on eDP links without a panel Atharva Tiwari (6): platform/apple-gmux: Fix power for MMIO systems PCI: Disable pcie port on specific amd devices platform/apple-gmux: Fix MMIO interface platform/apple-gmux: Fix Interrupts for MMIO systems platform/apple-gmux: Add check to detect FSS platform/apple-gmux: Add documentation about iMacs arch/x86/pci/fixup.c | 43 +++ drivers/gpu/drm/amd/amdgpu/amdgpu.h | 3 + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 61 ++- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 48 ++- .../gpu/drm/amd/amdgpu/amdgpu_reg_access.c | 24 ++ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 14 + .../display/amdgpu_dm/amdgpu_dm_backlight.c | 3 + .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 19 +- .../tests/amdgpu_dm_backlight_test.c | 1 + drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 19 +- drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 1 + drivers/pci/pcie/portdrv.c | 2 +- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/apple-gmux.c | 365 +++++++++++++++--- include/linux/apple-gmux.h | 1 + include/linux/pci.h | 1 + 16 files changed, 534 insertions(+), 72 deletions(-) -- 2.55.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems 2026-11-14 16:03 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari @ 2026-11-14 16:03 ` Atharva Tiwari 2026-11-14 16:03 ` [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices Atharva Tiwari 1 sibling, 0 replies; 5+ messages in thread From: Atharva Tiwari @ 2026-11-14 16:03 UTC (permalink / raw) Cc: Atharva Tiwari, Sebastian Zygadlo, Yossef Osman, Andre Eikmeyer, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT), H. Peter Anvin, Alex Deucher, Christian König, David Airlie, Simona Vetter, Harry Wentland, Leo Li, Rodrigo Siqueira, Kenneth Feng, Hans de Goede, Ilpo Järvinen, open list:PCI SUBSYSTEM, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT), open list:RADEON and AMDGPU DRM DRIVERS, open list:DRM DRIVERS, open list:X86 PLATFORM DRIVERS Currently The dGPU on MMIO Systems, dies when we write the gmux values to turn off the gpu. On MMIO macs Using only gmux dosent work, instead we have to use PWRD method + GMUX, which turns off the entire bridge, and completely messes the pci config, so we can't use pci to traverse through it. we save the entire bridge using ACPI addresses then we run PWRD(1) to turn off the bridge, then we write gmux states, and resume we do the vice versa, but here we dont need to retrain the link and write the MMIO variable. Tested-by: Sebastian Zygadlo <szgit@icloud.com> Tested-by: Yossef Osman <yossef.osman@proton.me> Co-developed-by: Andre Eikmeyer <dev@deq.rocks> Signed-off-by: Andre Eikmeyer <dev@deq.rocks> Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com> --- drivers/platform/x86/apple-gmux.c | 159 ++++++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 9c728ac4e..34e94f2b2 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -65,6 +65,10 @@ struct apple_gmux_data { struct backlight_device *bdev; + /* power handling data */ + struct pci_dev *discrete_pdev; + enum apple_gmux_type type; + /* switcheroo data */ acpi_handle dhandle; int gpe; @@ -502,23 +506,158 @@ static int gmux_switch_ddc(enum vga_switcheroo_client_id id) /** * DOC: Power control * - * gmux is able to cut power to the discrete GPU. It automatically takes care + * gmux is able to cut power to the discrete GPU. On most + * Systems the gmux automatically takes care * of the correct sequence to tear down and bring up the power rails for * core voltage, VRAM and PCIe. + * + * But on MMIO systems, the standard gmux writes does not work instead we + * have to use a combination of ACPI and GMUX, + * and Using PWRD (the method that used in macOS to turn on/off the dGPU and its bridges), + * which turns off the entire bridge and messes up the PCI config, + * so we can't use pci_upstream_bridge to traverse + * through the bridge, so we use ACPI addresses to save/restore bridges. + * + * NOTE: Communication to these bridges (not the dGPU/HDA) in any form + * when the bridge is off, will result in CPU CATERR. */ +static int gmux_save_restore_pci(acpi_handle parent_handle, const char *name, + bool save, acpi_handle *out_handle) +{ + acpi_status status; + acpi_handle handle; + struct acpi_device *adev; + struct device *dev; + struct pci_dev *pdev; + + if (name) { + status = acpi_get_handle(parent_handle, name, &handle); + if (ACPI_FAILURE(status)) + return -EIO; + + if (out_handle) + *out_handle = handle; + } else + handle = parent_handle; + + adev = acpi_fetch_acpi_dev(handle); + if (!adev) + return -EIO; + + dev = acpi_get_first_physical_node(adev); + if (!dev || !dev_is_pci(dev)) + return -EIO; + + pdev = to_pci_dev(dev); + + if (save) { + pci_save_state(pdev); + pdev->current_state = PCI_D3cold; + } else { + pdev->current_state = PCI_D0; + pci_restore_state(pdev); + } + return 0; +} + static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, enum vga_switcheroo_state state) { reinit_completion(&gmux_data->powerchange_done); + acpi_handle discrete_handle; + acpi_status status; + unsigned long long result; + + if (!gmux_data->discrete_pdev) + return -ENODEV; + + discrete_handle = ACPI_HANDLE(&gmux_data->discrete_pdev->dev); + if (!discrete_handle) + return -ENODEV; if (state == VGA_SWITCHEROO_ON) { - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO) { + acpi_handle handle; + u16 vendor; + int i; + + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 2); + msleep(100); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + + status = acpi_execute_simple_method(discrete_handle, "PWRD", 0); + if (ACPI_FAILURE(status)) + return -EIO; + msleep(20); + + if (gmux_save_restore_pci(NULL, "\\_SB_.PCI0.PEG0", 0, &handle)) + return -EIO; + + if (gmux_save_restore_pci(handle, "EGP0", 0, &handle)) + return -EIO; + + if (gmux_save_restore_pci(handle, "EGP1", 0, &handle)) + return -EIO; + + for (i = 0; i < 1000; i++) { + pci_read_config_word(gmux_data->discrete_pdev, + PCI_VENDOR_ID, &vendor); + if (vendor != 0xffff) + break; + msleep(1); + } + if (vendor == 0xffff) + return -ETIMEDOUT; + + if (gmux_save_restore_pci(handle, "GFX0", 0, NULL)) + return -EIO; + + if (gmux_save_restore_pci(handle, "HDAU", 0, NULL)) + return -EIO; + } else { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + } pr_debug("Discrete card powered up\n"); } else { - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO) { + acpi_handle parent_handle = discrete_handle; + bool saved_hda = false; + + /* If the power state was OFF, then we only do + * PWRD and we do not save registers, as the + * registers are already saved + */ + if (gmux_data->power_state == VGA_SWITCHEROO_OFF) + goto do_pwrd; + + while (ACPI_SUCCESS(acpi_get_parent(parent_handle, + &parent_handle))) { + if (!saved_hda) { + if (!gmux_save_restore_pci(parent_handle, + "HDAU", true, NULL)) { + saved_hda = true; + pci_save_state(gmux_data->discrete_pdev); + gmux_data->discrete_pdev->current_state = + PCI_D3cold; + } + } + if (gmux_save_restore_pci(parent_handle, NULL, true, NULL)) + break; + } +do_pwrd: + status = acpi_execute_simple_method(discrete_handle, "PWRD", 1); + if (ACPI_FAILURE(status)) + return -EIO; + msleep(20); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + mdelay(10); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + } else { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + } pr_debug("Discrete card powered down\n"); } @@ -552,10 +691,14 @@ static enum vga_switcheroo_client_id gmux_get_client_id(struct pci_dev *pdev) else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && pdev->device == 0x0863) return VGA_SWITCHEROO_IGD; - else + else { + if (!apple_gmux_data->discrete_pdev) + apple_gmux_data->discrete_pdev = pci_dev_get(pdev); + return VGA_SWITCHEROO_DIS; -} + } +} static const struct vga_switcheroo_handler gmux_handler_no_ddc = { .switchto = gmux_switchto, .power_state = gmux_set_power_state, @@ -803,6 +946,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) if (!gmux_data) return -ENOMEM; pnp_set_drvdata(pnp, gmux_data); + gmux_data->type = type; switch (type) { case APPLE_GMUX_TYPE_MMIO: @@ -993,6 +1137,7 @@ static void gmux_remove(struct pnp_dev *pnp) gmux_fini_debugfs(gmux_data); vga_switcheroo_unregister_handler(); + pci_dev_put(gmux_data->discrete_pdev); gmux_disable_interrupts(gmux_data); if (gmux_data->gpe >= 0) { acpi_disable_gpe(NULL, gmux_data->gpe); -- 2.55.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices 2026-11-14 16:03 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari 2026-11-14 16:03 ` [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems Atharva Tiwari @ 2026-11-14 16:03 ` Atharva Tiwari 1 sibling, 0 replies; 5+ messages in thread From: Atharva Tiwari @ 2026-11-14 16:03 UTC (permalink / raw) Cc: Atharva Tiwari, Sebastian Zygadlo, Yossef Osman, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT), H. Peter Anvin, Alex Deucher, Christian König, David Airlie, Simona Vetter, Harry Wentland, Leo Li, Rodrigo Siqueira, Kenneth Feng, Hans de Goede, Ilpo Järvinen, Andre Eikmeyer, open list:PCI SUBSYSTEM, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT), open list:RADEON and AMDGPU DRM DRIVERS, open list:DRM DRIVERS, open list:X86 PLATFORM DRIVERS As mentioned in the previous commit, PWRD turns off the entire bridge, and if we read/write anything to the config space or the PCIE space when the bridge is dead, the CPU CATERR's, so to avoid interaction when the bridge is dead, we remove pcieport support for the bridges.. Tested-by: Sebastian Zygadlo <szgit@icloud.com> Tested-by: Yossef Osman <yossef.osman@proton.me> Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com> --- arch/x86/pci/fixup.c | 43 ++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/portdrv.c | 2 +- include/linux/pci.h | 1 + 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 795d81c7a..b1697cc4a 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -1094,6 +1094,48 @@ static void asus_disable_nvme_d3cold(struct pci_dev *pdev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x9a09, asus_disable_nvme_d3cold); +static const struct dmi_system_id quirk_disable_pcie_port_dmi_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro15,1"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro15,3"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,1"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,4"), + }, + }, + {} +}; + +static void quirk_disable_pcie_port(struct pci_dev *pdev) +{ + struct pci_dev *upstream = pdev; + + if (dmi_check_system(quirk_disable_pcie_port_dmi_table)) { + while ((upstream = pci_upstream_bridge(upstream))) + upstream->no_pcie_port = 1; + } +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x67ef, quirk_disable_pcie_port); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x69af, quirk_disable_pcie_port); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x7340, quirk_disable_pcie_port); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x7360, quirk_disable_pcie_port); + #ifdef CONFIG_SUSPEND /* * Root Ports on some AMD SoCs advertise PME_Support for D3hot and D3cold, but @@ -1179,4 +1221,5 @@ static void quirk_tuxeo_rp_d3(struct pci_dev *pdev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x1502, quirk_tuxeo_rp_d3); + #endif /* CONFIG_SUSPEND */ diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index a9cbfc1d2..d2f19e566 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -688,7 +688,7 @@ static int pcie_portdrv_probe(struct pci_dev *dev, int type = pci_pcie_type(dev); int status; - if (!pci_is_pcie(dev) || + if (!pci_is_pcie(dev) || dev->no_pcie_port || ((type != PCI_EXP_TYPE_ROOT_PORT) && (type != PCI_EXP_TYPE_UPSTREAM) && (type != PCI_EXP_TYPE_DOWNSTREAM) && diff --git a/include/linux/pci.h b/include/linux/pci.h index d31a8d107..fcb4f5893 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -420,6 +420,7 @@ struct pci_dev { unsigned int no_d1d2:1; /* D1 and D2 are forbidden */ unsigned int no_d3cold:1; /* D3cold is forbidden */ unsigned int bridge_d3:1; /* Allow D3 for bridge */ + unsigned int no_pcie_port:1; /* PCIE Port driver is forbidden */ unsigned int d3cold_allowed:1; /* D3cold is allowed by user */ unsigned int mmio_always_on:1; /* Disallow turning off io/mem decoding during BAR sizing */ -- 2.55.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 00/12] Fixes for apple-gmux and runtime support @ 2026-11-14 16:06 Atharva Tiwari 2026-11-14 16:06 ` [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems Atharva Tiwari 0 siblings, 1 reply; 5+ messages in thread From: Atharva Tiwari @ 2026-11-14 16:06 UTC (permalink / raw) Cc: Atharva Tiwari, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT), H. Peter Anvin, Alex Deucher, Christian König, David Airlie, Simona Vetter, Harry Wentland, Leo Li, Rodrigo Siqueira, Kenneth Feng, Hans de Goede, Ilpo Järvinen, Andre Eikmeyer, open list:PCI SUBSYSTEM, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT), open list:RADEON and AMDGPU DRM DRIVERS, open list:DRM DRIVERS, open list:X86 PLATFORM DRIVERS This series fixes apple-gmux on T2 Macs (2018-2020): - Fix interrupt handling - Automatically turn on dGPU when a external monitor connects. - Fix base register read/write - Fix power handling - Improve documentation - New module parameter to toggle boot GPU. It also adds vga_switcheroo runtime power management for T2 Macs, built on gmux power control, so the dGPU is powered off automatically when idle and users no longer need to toggle it manually. Changes in v2: - Check the discrete pdev config before GFX0 restore (01/13) - Don't save bridges state when state is VGA_SWITCHEROO_OFF (01/13) - Fixes discrete_pdev initialization (01/13) - Used PWRD on 15,1 and 15,3 aswell - Add quirk to 0x7360 PCI id aswell (02/13) - Add 15,1 and 15,3 and its gpu's aswell - Removed ignore_access code (02/13) - Initialized val in read functions (03/13) - Fixed permanent mutex block on failure (03/13) - Only resumed when pm_runtime is available (skipping on iMacs) (04/13) - Corrected ordering of interrupt_work (04/13) - Notified drm about hotplug change (04/13) - Checked if EFI_RT_SUPPORTED_SET_VARIABLE is available before writing gpu-active (05/13) - Wrote gpu-active on shutdown aswell (05/13) - Dropped boot-gpu parameter (06/13) - Checked if vga_switcheroo has been initialized (09/13) - Added additional pci_is_thunderbolt_attached check to amdgpu_device_supports_gmux (09/13) - Removed duplicate check (10/13) - Renamed gmux_off to runpm_active - Checked if adev->dev exists in dm_dp_aux_transfer to fix null ptr deref (11/13) - Removed GMUX specific code (12/13) - Initialized edp_sink_present in testing code (13/13) Link to v1: https://lore.kernel.org/all/20261025040046.1602-1-atharvatiwarilinuxdev@gmail.com/ Andre Eikmeyer (6): platform/apple-gmux: Write switch state to EFI on MMIO devices drm/amdgpu: Add Apple GMUX runtime PM support drm/amdgpu: Catch register access while GMUX keeps the GPU off drm/amd/display: Refuse DP AUX while GMUX keeps the GPU off drm/amdgpu: Skip AC/DC notification while the GPU is runtime suspended drm/amd/display: Skip panel power savings on eDP links without a panel Atharva Tiwari (6): platform/apple-gmux: Fix power for MMIO systems PCI: Disable pcie port on specific amd devices platform/apple-gmux: Fix MMIO interface platform/apple-gmux: Fix Interrupts for MMIO systems platform/apple-gmux: Add check to detect FSS platform/apple-gmux: Add documentation about iMacs arch/x86/pci/fixup.c | 43 +++ drivers/gpu/drm/amd/amdgpu/amdgpu.h | 3 + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 61 ++- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 48 ++- .../gpu/drm/amd/amdgpu/amdgpu_reg_access.c | 24 ++ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 14 + .../display/amdgpu_dm/amdgpu_dm_backlight.c | 3 + .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 19 +- .../tests/amdgpu_dm_backlight_test.c | 1 + drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 19 +- drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 1 + drivers/pci/pcie/portdrv.c | 2 +- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/apple-gmux.c | 365 +++++++++++++++--- include/linux/apple-gmux.h | 1 + include/linux/pci.h | 1 + 16 files changed, 534 insertions(+), 72 deletions(-) -- 2.55.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems 2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari @ 2026-11-14 16:06 ` Atharva Tiwari 2026-11-14 16:42 ` sashiko-bot 0 siblings, 1 reply; 5+ messages in thread From: Atharva Tiwari @ 2026-11-14 16:06 UTC (permalink / raw) Cc: Atharva Tiwari, Sebastian Zygadlo, Yossef Osman, Andre Eikmeyer, Bjorn Helgaas, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT), H. Peter Anvin, Alex Deucher, Christian König, David Airlie, Simona Vetter, Harry Wentland, Leo Li, Rodrigo Siqueira, Kenneth Feng, Hans de Goede, Ilpo Järvinen, open list:PCI SUBSYSTEM, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT), open list:RADEON and AMDGPU DRM DRIVERS, open list:DRM DRIVERS, open list:X86 PLATFORM DRIVERS Currently The dGPU on MMIO Systems, dies when we write the gmux values to turn off the gpu. On MMIO macs Using only gmux dosent work, instead we have to use PWRD method + GMUX, which turns off the entire bridge, and completely messes the pci config, so we can't use pci to traverse through it. we save the entire bridge using ACPI addresses then we run PWRD(1) to turn off the bridge, then we write gmux states, and resume we do the vice versa, but here we dont need to retrain the link and write the MMIO variable. Tested-by: Sebastian Zygadlo <szgit@icloud.com> Tested-by: Yossef Osman <yossef.osman@proton.me> Co-developed-by: Andre Eikmeyer <dev@deq.rocks> Signed-off-by: Andre Eikmeyer <dev@deq.rocks> Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com> --- drivers/platform/x86/apple-gmux.c | 159 ++++++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 9c728ac4e..34e94f2b2 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -65,6 +65,10 @@ struct apple_gmux_data { struct backlight_device *bdev; + /* power handling data */ + struct pci_dev *discrete_pdev; + enum apple_gmux_type type; + /* switcheroo data */ acpi_handle dhandle; int gpe; @@ -502,23 +506,158 @@ static int gmux_switch_ddc(enum vga_switcheroo_client_id id) /** * DOC: Power control * - * gmux is able to cut power to the discrete GPU. It automatically takes care + * gmux is able to cut power to the discrete GPU. On most + * Systems the gmux automatically takes care * of the correct sequence to tear down and bring up the power rails for * core voltage, VRAM and PCIe. + * + * But on MMIO systems, the standard gmux writes does not work instead we + * have to use a combination of ACPI and GMUX, + * and Using PWRD (the method that used in macOS to turn on/off the dGPU and its bridges), + * which turns off the entire bridge and messes up the PCI config, + * so we can't use pci_upstream_bridge to traverse + * through the bridge, so we use ACPI addresses to save/restore bridges. + * + * NOTE: Communication to these bridges (not the dGPU/HDA) in any form + * when the bridge is off, will result in CPU CATERR. */ +static int gmux_save_restore_pci(acpi_handle parent_handle, const char *name, + bool save, acpi_handle *out_handle) +{ + acpi_status status; + acpi_handle handle; + struct acpi_device *adev; + struct device *dev; + struct pci_dev *pdev; + + if (name) { + status = acpi_get_handle(parent_handle, name, &handle); + if (ACPI_FAILURE(status)) + return -EIO; + + if (out_handle) + *out_handle = handle; + } else + handle = parent_handle; + + adev = acpi_fetch_acpi_dev(handle); + if (!adev) + return -EIO; + + dev = acpi_get_first_physical_node(adev); + if (!dev || !dev_is_pci(dev)) + return -EIO; + + pdev = to_pci_dev(dev); + + if (save) { + pci_save_state(pdev); + pdev->current_state = PCI_D3cold; + } else { + pdev->current_state = PCI_D0; + pci_restore_state(pdev); + } + return 0; +} + static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, enum vga_switcheroo_state state) { reinit_completion(&gmux_data->powerchange_done); + acpi_handle discrete_handle; + acpi_status status; + unsigned long long result; + + if (!gmux_data->discrete_pdev) + return -ENODEV; + + discrete_handle = ACPI_HANDLE(&gmux_data->discrete_pdev->dev); + if (!discrete_handle) + return -ENODEV; if (state == VGA_SWITCHEROO_ON) { - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO) { + acpi_handle handle; + u16 vendor; + int i; + + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 2); + msleep(100); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + + status = acpi_execute_simple_method(discrete_handle, "PWRD", 0); + if (ACPI_FAILURE(status)) + return -EIO; + msleep(20); + + if (gmux_save_restore_pci(NULL, "\\_SB_.PCI0.PEG0", 0, &handle)) + return -EIO; + + if (gmux_save_restore_pci(handle, "EGP0", 0, &handle)) + return -EIO; + + if (gmux_save_restore_pci(handle, "EGP1", 0, &handle)) + return -EIO; + + for (i = 0; i < 1000; i++) { + pci_read_config_word(gmux_data->discrete_pdev, + PCI_VENDOR_ID, &vendor); + if (vendor != 0xffff) + break; + msleep(1); + } + if (vendor == 0xffff) + return -ETIMEDOUT; + + if (gmux_save_restore_pci(handle, "GFX0", 0, NULL)) + return -EIO; + + if (gmux_save_restore_pci(handle, "HDAU", 0, NULL)) + return -EIO; + } else { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); + } pr_debug("Discrete card powered up\n"); } else { - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO) { + acpi_handle parent_handle = discrete_handle; + bool saved_hda = false; + + /* If the power state was OFF, then we only do + * PWRD and we do not save registers, as the + * registers are already saved + */ + if (gmux_data->power_state == VGA_SWITCHEROO_OFF) + goto do_pwrd; + + while (ACPI_SUCCESS(acpi_get_parent(parent_handle, + &parent_handle))) { + if (!saved_hda) { + if (!gmux_save_restore_pci(parent_handle, + "HDAU", true, NULL)) { + saved_hda = true; + pci_save_state(gmux_data->discrete_pdev); + gmux_data->discrete_pdev->current_state = + PCI_D3cold; + } + } + if (gmux_save_restore_pci(parent_handle, NULL, true, NULL)) + break; + } +do_pwrd: + status = acpi_execute_simple_method(discrete_handle, "PWRD", 1); + if (ACPI_FAILURE(status)) + return -EIO; + msleep(20); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + mdelay(10); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + } else { + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); + } pr_debug("Discrete card powered down\n"); } @@ -552,10 +691,14 @@ static enum vga_switcheroo_client_id gmux_get_client_id(struct pci_dev *pdev) else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && pdev->device == 0x0863) return VGA_SWITCHEROO_IGD; - else + else { + if (!apple_gmux_data->discrete_pdev) + apple_gmux_data->discrete_pdev = pci_dev_get(pdev); + return VGA_SWITCHEROO_DIS; -} + } +} static const struct vga_switcheroo_handler gmux_handler_no_ddc = { .switchto = gmux_switchto, .power_state = gmux_set_power_state, @@ -803,6 +946,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) if (!gmux_data) return -ENOMEM; pnp_set_drvdata(pnp, gmux_data); + gmux_data->type = type; switch (type) { case APPLE_GMUX_TYPE_MMIO: @@ -993,6 +1137,7 @@ static void gmux_remove(struct pnp_dev *pnp) gmux_fini_debugfs(gmux_data); vga_switcheroo_unregister_handler(); + pci_dev_put(gmux_data->discrete_pdev); gmux_disable_interrupts(gmux_data); if (gmux_data->gpe >= 0) { acpi_disable_gpe(NULL, gmux_data->gpe); -- 2.55.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems 2026-11-14 16:06 ` [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems Atharva Tiwari @ 2026-11-14 16:42 ` sashiko-bot 0 siblings, 0 replies; 5+ messages in thread From: sashiko-bot @ 2026-11-14 16:42 UTC (permalink / raw) To: Atharva Tiwari Cc: open list:PCI SUBSYSTEM, open list:RADEON and AMDGPU DRM DRIVERS, open list:DRM DRIVERS Thank you for your contribution! Sashiko AI review found 7 potential issues to consider: - [High] platform/x86: apple-gmux: hardcoded EGP0/EGP1 path in gmux_set_discrete_state() - [High] platform/x86: apple-gmux: inconsistent state in gmux_set_discrete_state() - [High] platform/x86: apple-gmux: bridge config access and state loss across suspend - [Medium] platform/x86: apple-gmux: unconditional discrete_pdev check on non-MMIO - [Medium] platform/x86: apple-gmux: dGPU PCI save gated on HDAU in power-off - [Low] platform/x86: apple-gmux: unused variable in gmux_set_discrete_state() - [Low] platform/x86: apple-gmux: race on discrete_pdev in gmux_get_client_id() -- commit fc14cf5232ba075c0f19cf13add68d34508463ca Author: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com> platform/apple-gmux: Fix power for MMIO systems This commit adds discrete GPU power control support for MMIO gmux systems by saving and restoring upstream PCIe bridge state via ACPI handles and evaluating the ACPI PWRD method alongside gmux port writes. > diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c > index 9c728ac4e045a..34e94f2b2dee1 100644 > --- a/drivers/platform/x86/apple-gmux.c > +++ b/drivers/platform/x86/apple-gmux.c [ ... ] > @@ -502,23 +506,158 @@ static int gmux_switch_ddc(enum vga_switcheroo_client_id id) [ ... ] > +static int gmux_save_restore_pci(acpi_handle parent_handle, const char *name, > + bool save, acpi_handle *out_handle) > +{ > + acpi_status status; > + acpi_handle handle; > + struct acpi_device *adev; > + struct device *dev; > + struct pci_dev *pdev; > + > + if (name) { > + status = acpi_get_handle(parent_handle, name, &handle); > + if (ACPI_FAILURE(status)) > + return -EIO; > + > + if (out_handle) > + *out_handle = handle; > + } else > + handle = parent_handle; > + > + adev = acpi_fetch_acpi_dev(handle); > + if (!adev) > + return -EIO; > + > + dev = acpi_get_first_physical_node(adev); > + if (!dev || !dev_is_pci(dev)) > + return -EIO; > + > + pdev = to_pci_dev(dev); > + > + if (save) { > + pci_save_state(pdev); > + pdev->current_state = PCI_D3cold; > + } else { > + pdev->current_state = PCI_D0; > + pci_restore_state(pdev); > + } > + return 0; > +} > + > static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, > enum vga_switcheroo_state state) > { > reinit_completion(&gmux_data->powerchange_done); > + acpi_handle discrete_handle; > + acpi_status status; > + unsigned long long result; [Severity: Low] The local variable result is unused and causes a -Wunused-variable warning (and a build failure with CONFIG_WERROR=y). Also, these variable declarations are placed after the reinit_completion() statement. > + > + if (!gmux_data->discrete_pdev) > + return -ENODEV; > + > + discrete_handle = ACPI_HANDLE(&gmux_data->discrete_pdev->dev); > + if (!discrete_handle) > + return -ENODEV; [Severity: Medium] Should the discrete_pdev and discrete_handle checks be restricted to APPLE_GMUX_TYPE_MMIO? Non-MMIO systems (PIO and indexed) only write to GMUX_PORT_DISCRETE_POWER and never use discrete_pdev or discrete_handle. Checking them unconditionally causes gmux_set_discrete_state() to fail with -ENODEV on non-MMIO systems if the discrete GPU lacks an ACPI companion node, if a GPU driver runtime-suspends before the second VGA client registers and triggers gmux_get_client_id(), or if apple-gmux is unbound and re-probed (since vga_switcheroo_enable() skips calling get_client_id() for clients whose ID is already assigned). > > if (state == VGA_SWITCHEROO_ON) { > - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); > - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); > + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO) { > + acpi_handle handle; > + u16 vendor; > + int i; > + > + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 2); > + msleep(100); > + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); > + > + status = acpi_execute_simple_method(discrete_handle, "PWRD", 0); > + if (ACPI_FAILURE(status)) > + return -EIO; > + msleep(20); > + > + if (gmux_save_restore_pci(NULL, "\\_SB_.PCI0.PEG0", 0, &handle)) > + return -EIO; > + > + if (gmux_save_restore_pci(handle, "EGP0", 0, &handle)) > + return -EIO; > + > + if (gmux_save_restore_pci(handle, "EGP1", 0, &handle)) > + return -EIO; [Severity: High] Does hardcoding EGP0 and EGP1 here break power-up on MMIO systems where the discrete GPU sits directly under PEG0 without a PCIe switch (such as Polaris-based T2 MacBookPro15,1 and MacBookPro15,3)? On power-down, gmux_set_discrete_state() dynamically walks up the ACPI parent chain from discrete_handle regardless of bridge depth and cuts power via PWRD(1). On power-up, however, gmux_save_restore_pci() returns -EIO if EGP0 or EGP1 has no physical struct pci_dev, aborting after PWRD(0) and leaving the discrete GPU un-restored in PCI_D3cold. > + > + for (i = 0; i < 1000; i++) { > + pci_read_config_word(gmux_data->discrete_pdev, > + PCI_VENDOR_ID, &vendor); > + if (vendor != 0xffff) > + break; > + msleep(1); > + } > + if (vendor == 0xffff) > + return -ETIMEDOUT; > + > + if (gmux_save_restore_pci(handle, "GFX0", 0, NULL)) > + return -EIO; > + > + if (gmux_save_restore_pci(handle, "HDAU", 0, NULL)) > + return -EIO; > + } else { > + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); > + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); > + } > pr_debug("Discrete card powered up\n"); > } else { > - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); > - gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); > + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO) { > + acpi_handle parent_handle = discrete_handle; > + bool saved_hda = false; > + > + /* If the power state was OFF, then we only do > + * PWRD and we do not save registers, as the > + * registers are already saved > + */ > + if (gmux_data->power_state == VGA_SWITCHEROO_OFF) > + goto do_pwrd; [Severity: High] What happens across system suspend and resume when the discrete GPU is powered off (gmux_data->power_state == VGA_SWITCHEROO_OFF)? gmux_save_restore_pci() sets pdev->current_state = PCI_D3cold on the upstream bridges (PEG0, EGP0, EGP1) and cuts their power via PWRD(1), but the bridge devices are not runtime-suspended. During system suspend, pci_pm_suspend() clears pci_dev->state_saved = false and pci_pm_suspend_noirq() calls pci_save_state() on the powered-off bridges; as noted in the comment above gmux_save_restore_pci(), accessing these bridges while off triggers a CPU CATERR or overwrites saved_config_space with 0xffffffff. Even if suspend completes, pci_pm_resume_noirq() runs before gmux_resume() and calls pci_pm_default_resume_early(), resetting the bridges to PCI_D0 and clearing state_saved = false, after which gmux_resume() -> gmux_set_discrete_state() takes this goto do_pwrd branch and cuts bridge power without saving their post-resume PCI state or marking them PCI_D3cold. > + > + while (ACPI_SUCCESS(acpi_get_parent(parent_handle, > + &parent_handle))) { > + if (!saved_hda) { > + if (!gmux_save_restore_pci(parent_handle, > + "HDAU", true, NULL)) { > + saved_hda = true; > + pci_save_state(gmux_data->discrete_pdev); > + gmux_data->discrete_pdev->current_state = > + PCI_D3cold; > + } > + } [Severity: Medium] Why is saving gmux_data->discrete_pdev gated on gmux_save_restore_pci() succeeding for HDAU? If the HDAU audio device is absent or has been removed via sysfs, gmux_save_restore_pci(parent_handle, "HDAU", ...) returns -EIO. On power-off, discrete_pdev config state is never saved and its current_state is not set to PCI_D3cold before PWRD(1) cuts power; on power-on, the HDAU restore at the end of the VGA_SWITCHEROO_ON branch also returns -EIO after the hardware and GFX0 have already been powered up. > + if (gmux_save_restore_pci(parent_handle, NULL, true, NULL)) > + break; > + } > +do_pwrd: > + status = acpi_execute_simple_method(discrete_handle, "PWRD", 1); > + if (ACPI_FAILURE(status)) > + return -EIO; > + msleep(20); > + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); > + mdelay(10); > + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); > + } else { > + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); > + gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); > + } > pr_debug("Discrete card powered down\n"); > } > > gmux_data->power_state = state; [Severity: High] Can early error returns in gmux_set_discrete_state() leave gmux_data->power_state out of sync with the hardware and PCI device states? In the VGA_SWITCHEROO_ON branch, if polling PCI_VENDOR_ID times out or restoring one of the devices (such as HDAU) fails after PWRD(0) and the upstream bridge restores have already run, the function returns an error before updating gmux_data->power_state = VGA_SWITCHEROO_ON. Callers in vga_switcheroo ignore the return value of handler->power_state(), leaving the hardware and bridges in PCI_D0 while gmux_data->power_state remains VGA_SWITCHEROO_OFF. On the next power-off or system resume, the if (gmux_data->power_state == VGA_SWITCHEROO_OFF) check jumps straight to do_pwrd and cuts bridge power via PWRD(1) without saving PCI state or setting current_state to PCI_D3cold, exposing the powered-off bridges to config accesses and a CPU CATERR. Conversely, if PWRD(1) fails at do_pwrd, the devices are left marked PCI_D3cold while power remains on. [ ... ] > @@ -552,10 +691,14 @@ static enum vga_switcheroo_client_id gmux_get_client_id(struct pci_dev *pdev) > else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && > pdev->device == 0x0863) > return VGA_SWITCHEROO_IGD; > - else > + else { > + if (!apple_gmux_data->discrete_pdev) > + apple_gmux_data->discrete_pdev = pci_dev_get(pdev); > + > return VGA_SWITCHEROO_DIS; > -} > > + } > +} [Severity: Low] Can concurrent calls to gmux_get_client_id() race on apple_gmux_data->discrete_pdev and leak a pci_dev reference? vga_switcheroo_enable() calls get_client_id() under vgasr_mutex, whereas vga_switcheroo_lock_ddc() and vga_switcheroo_unlock_ddc() call it under vgasr_priv.mux_hw_lock without holding vgasr_mutex. If the discrete GPU driver locks DDC while the second GPU client registers, both threads can see apple_gmux_data->discrete_pdev as NULL and call pci_dev_get(pdev), while gmux_remove() only calls pci_dev_put() once. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=1 ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-14 16:43 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-14 16:03 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari 2026-11-14 16:03 ` [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems Atharva Tiwari 2026-11-14 16:03 ` [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices Atharva Tiwari -- strict thread matches above, loose matches on Subject: below -- 2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari 2026-11-14 16:06 ` [PATCH v2 01/12] platform/apple-gmux: Fix power for MMIO systems Atharva Tiwari 2026-11-14 16:42 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox