* [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
` (11 more replies)
0 siblings, 12 replies; 24+ 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] 24+ 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
2026-11-14 16:06 ` [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices Atharva Tiwari
` (10 subsequent siblings)
11 siblings, 1 reply; 24+ 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] 24+ messages in thread
* [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices
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:06 ` Atharva Tiwari
2026-11-14 16:34 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 03/12] platform/apple-gmux: Fix MMIO interface Atharva Tiwari
` (9 subsequent siblings)
11 siblings, 1 reply; 24+ messages in thread
From: Atharva Tiwari @ 2026-11-14 16:06 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] 24+ messages in thread
* [PATCH v2 03/12] platform/apple-gmux: Fix MMIO interface
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:06 ` [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:17 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 04/12] platform/apple-gmux: Fix Interrupts for MMIO systems Atharva Tiwari
` (8 subsequent siblings)
11 siblings, 1 reply; 24+ 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
This patch brings a lot of fixes to MMIO interface such as:
1. Using 1ms waits instead of 100us.
2. Checking if Timed out before reading and writing
3. Adding missing wait before gmux_mmio_write32
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 | 60 +++++++++++++++++--------------
1 file changed, 34 insertions(+), 26 deletions(-)
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 34e94f2b2..9625551e5 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -235,8 +235,8 @@ static int gmux_mmio_wait(struct apple_gmux_data *gmux_data)
u8 gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
while (i && gwr) {
+ fsleep(1000);
gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
- udelay(100);
i--;
}
@@ -245,15 +245,16 @@ static int gmux_mmio_wait(struct apple_gmux_data *gmux_data)
static u8 gmux_mmio_read8(struct apple_gmux_data *gmux_data, int port)
{
- u8 val;
+ u8 val = 0;
mutex_lock(&gmux_data->index_lock);
- gmux_mmio_wait(gmux_data);
- iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
- iowrite8(GMUX_MMIO_READ | sizeof(val),
- gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
- gmux_mmio_wait(gmux_data);
- val = ioread8(gmux_data->iomem_base);
+ if (gmux_mmio_wait(gmux_data)) {
+ iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
+ iowrite8(GMUX_MMIO_READ | sizeof(val),
+ gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
+ if (gmux_mmio_wait(gmux_data))
+ val = ioread8(gmux_data->iomem_base);
+ }
mutex_unlock(&gmux_data->index_lock);
return val;
@@ -263,28 +264,33 @@ static void gmux_mmio_write8(struct apple_gmux_data *gmux_data, int port,
u8 val)
{
mutex_lock(&gmux_data->index_lock);
- gmux_mmio_wait(gmux_data);
- iowrite8(val, gmux_data->iomem_base);
- iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
- iowrite8(GMUX_MMIO_WRITE | sizeof(val),
- gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
+ if (gmux_mmio_wait(gmux_data)) {
+ iowrite8(val, gmux_data->iomem_base);
- gmux_mmio_wait(gmux_data);
+ iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
+ iowrite8(GMUX_MMIO_WRITE | sizeof(val),
+ gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
+
+ /* We already wrote the value there is no point to check if timeout */
+ gmux_mmio_wait(gmux_data);
+ }
mutex_unlock(&gmux_data->index_lock);
}
static u32 gmux_mmio_read32(struct apple_gmux_data *gmux_data, int port)
{
- u32 val;
+ u32 val = 0;
mutex_lock(&gmux_data->index_lock);
- gmux_mmio_wait(gmux_data);
- iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
- iowrite8(GMUX_MMIO_READ | sizeof(val),
- gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
- gmux_mmio_wait(gmux_data);
- val = ioread32be(gmux_data->iomem_base);
+
+ if (gmux_mmio_wait(gmux_data)) {
+ iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
+ iowrite8(GMUX_MMIO_READ | sizeof(val),
+ gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
+ if (gmux_mmio_wait(gmux_data))
+ val = ioread32be(gmux_data->iomem_base);
+ }
mutex_unlock(&gmux_data->index_lock);
return val;
@@ -294,11 +300,13 @@ static void gmux_mmio_write32(struct apple_gmux_data *gmux_data, int port,
u32 val)
{
mutex_lock(&gmux_data->index_lock);
- iowrite32be(val, gmux_data->iomem_base);
- iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
- iowrite8(GMUX_MMIO_WRITE | sizeof(val),
- gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
- gmux_mmio_wait(gmux_data);
+ if (gmux_mmio_wait(gmux_data)) {
+ iowrite32be(val, gmux_data->iomem_base);
+ iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
+ iowrite8(GMUX_MMIO_WRITE | sizeof(val),
+ gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
+ gmux_mmio_wait(gmux_data);
+ }
mutex_unlock(&gmux_data->index_lock);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 04/12] platform/apple-gmux: Fix Interrupts for MMIO systems
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (2 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 03/12] platform/apple-gmux: Fix MMIO interface Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:27 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 05/12] platform/apple-gmux: Write switch state to EFI on MMIO devices Atharva Tiwari
` (7 subsequent siblings)
11 siblings, 1 reply; 24+ 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
on MMIO systems, we need check to for failure in interrupts,
and we have implimented display hotplug to wake up the gpu.
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 | 101 ++++++++++++++++++++++++++----
1 file changed, 88 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 9625551e5..37ba20779 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -6,6 +6,8 @@
* Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
* Copyright (C) 2015 Lukas Wunner <lukas@wunner.de>
* Copyright (C) 2023 Orlando Chamberlain <orlandoch.dev@gmail.com>
+ * Copyright (C) 2026 Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
+ * Copyright (C) 2026 Andre Eikmeyer <dev@deq.rocks>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -22,6 +24,8 @@
#include <linux/pci.h>
#include <linux/vga_switcheroo.h>
#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
#include <acpi/video.h>
#include <asm/io.h>
@@ -65,6 +69,10 @@ struct apple_gmux_data {
struct backlight_device *bdev;
+ /* interrupt handling data */
+ u8 external_state;
+ struct work_struct interrupt_work;
+
/* power handling data */
struct pci_dev *discrete_pdev;
enum apple_gmux_type type;
@@ -103,6 +111,7 @@ struct apple_gmux_config {
#define GMUX_INTERRUPT_STATUS_ACTIVE 0
#define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0)
+#define GMUX_INTERRUPT_STATUS_FAILED (1 << 1)
#define GMUX_INTERRUPT_STATUS_POWER (1 << 2)
#define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3)
@@ -412,8 +421,8 @@ static const struct backlight_ops gmux_bl_ops = {
* the discrete GPU can then be woken to drive the newly connected display.
* The ability to switch AUX on these generations could be used to improve
* reliability of hotplug detection by having the integrated GPU poll the
- * ports while the discrete GPU is asleep, but currently we do not make use
- * of this feature.
+ * ports while the discrete GPU is asleep, but currently we only make use of
+ * this feature on MMIO/T2 Macs.
*
* Our switching policy for the external port is that on those generations
* which are able to switch it fully, the port is switched together with the
@@ -777,9 +786,10 @@ static const struct apple_gmux_config apple_gmux_mmio = {
* value is read and then inverted.
*
* Because Linux masquerades as Darwin, it ends up in the notification-only code
- * path. On MMIO gmux's, this seems to lead to us being unable to clear interrupts,
- * unless we call GMSP(0). Without this, there is a flood of status=0 interrupts
- * that can't be cleared. This issue seems to be unique to MMIO gmux's.
+ * path. On MMIO gmux's, The Polarity is initally left in a undefined state leading
+ * to lots of interrupts, that we can't clear, to fix this we set the polarity to 0
+ * everytime we enable interrupts, On MMIO gmux's, GMLV reads the same GPIO pin as
+ * GMSP, and setting the polarity to 1, stops the Interrupts completely.
*/
static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
@@ -788,8 +798,11 @@ static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
GMUX_INTERRUPT_DISABLE);
}
-static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
+static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data, bool do_gmsp)
{
+ if (gmux_data->type == APPLE_GMUX_TYPE_MMIO && do_gmsp)
+ acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
+
gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
GMUX_INTERRUPT_ENABLE);
}
@@ -806,23 +819,78 @@ static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
/* to clear interrupts write back current status */
status = gmux_interrupt_get_status(gmux_data);
gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
- /* Prevent flood of status=0 interrupts */
- if (gmux_data->config == &apple_gmux_mmio)
- acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
}
+static void gmux_notify_work(struct work_struct *work)
+{
+ struct apple_gmux_data *gmux_data =
+ container_of(work, struct apple_gmux_data, interrupt_work);
+ u8 status;
+ bool turn_on_gpu = false;
+
+ status = gmux_interrupt_get_status(gmux_data);
+ if (!status)
+ return;
+
+ gmux_disable_interrupts(gmux_data);
+ pr_debug("Notify handler called: status %d\n", status);
+ acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
+
+ status = gmux_interrupt_get_status(gmux_data);
+ while (status) {
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
+ if (status & GMUX_INTERRUPT_STATUS_FAILED) {
+ pr_warn_ratelimited("interrupt failed: status: 0x%x\n", status);
+ status = gmux_interrupt_get_status(gmux_data);
+ continue;
+ }
+ if (status & GMUX_INTERRUPT_STATUS_POWER)
+ complete(&gmux_data->powerchange_done);
+
+ if (status & GMUX_INTERRUPT_STATUS_HOTPLUG) {
+ /*
+ * We get an interrupt every time a display connects or disconnects, but
+ * the GET_DISPLAY port only tells us if a display is connected or not,
+ * so we resume every time a display is connected or disconnected.
+ */
+ u8 external_state = gmux_read8(gmux_data,
+ GMUX_PORT_SWITCH_GET_DISPLAY);
+ if (external_state != gmux_data->external_state) {
+ gmux_data->external_state = external_state;
+ if (external_state)
+ pr_debug("A display has been connected\n");
+ else
+ pr_debug("All displays has been disconnected\n");
+ }
+ turn_on_gpu = true;
+ }
+ status = gmux_interrupt_get_status(gmux_data);
+ }
+
+ gmux_enable_interrupts(gmux_data, 0);
+
+ if (turn_on_gpu && !gmux_data->external_switchable && gmux_data->discrete_pdev &&
+ pm_runtime_enabled(&gmux_data->discrete_pdev->dev))
+ pm_request_resume(&gmux_data->discrete_pdev->dev);
+
+}
static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
{
u8 status;
struct pnp_dev *pnp = (struct pnp_dev *)context;
struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+ if (gmux_data->type == APPLE_GMUX_TYPE_MMIO) {
+ schedule_work(&gmux_data->interrupt_work);
+ return;
+ }
+
status = gmux_interrupt_get_status(gmux_data);
gmux_disable_interrupts(gmux_data);
pr_debug("Notify handler called: status %d\n", status);
gmux_clear_interrupts(gmux_data);
- gmux_enable_interrupts(gmux_data);
+ gmux_enable_interrupts(gmux_data, 0);
if (status & GMUX_INTERRUPT_STATUS_POWER)
complete(&gmux_data->powerchange_done);
@@ -916,7 +984,7 @@ static int gmux_resume(struct device *dev)
struct pnp_dev *pnp = to_pnp_dev(dev);
struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
- gmux_enable_interrupts(gmux_data);
+ gmux_enable_interrupts(gmux_data, 1);
gmux_write_switch_state(gmux_data);
if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
gmux_set_discrete_state(gmux_data, gmux_data->power_state);
@@ -1072,6 +1140,8 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
goto err_notify;
}
+ if (type == APPLE_GMUX_TYPE_MMIO)
+ INIT_WORK(&gmux_data->interrupt_work, gmux_notify_work);
status = acpi_enable_gpe(NULL, gmux_data->gpe);
if (ACPI_FAILURE(status)) {
pr_err("Cannot enable gpe: %s\n",
@@ -1094,7 +1164,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
apple_gmux_data = gmux_data;
init_completion(&gmux_data->powerchange_done);
- gmux_enable_interrupts(gmux_data);
+ gmux_enable_interrupts(gmux_data, 1);
gmux_read_switch_state(gmux_data);
/*
@@ -1124,6 +1194,9 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
acpi_remove_notify_handler(gmux_data->dhandle,
ACPI_DEVICE_NOTIFY,
&gmux_notify_handler);
+
+ if (gmux_data->type == APPLE_GMUX_TYPE_MMIO)
+ cancel_work_sync(&gmux_data->interrupt_work);
err_notify:
backlight_device_unregister(bdev);
err_unmap:
@@ -1145,7 +1218,6 @@ 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);
@@ -1154,6 +1226,9 @@ static void gmux_remove(struct pnp_dev *pnp)
&gmux_notify_handler);
}
+ if (gmux_data->type == APPLE_GMUX_TYPE_MMIO)
+ cancel_work_sync(&gmux_data->interrupt_work);
+ pci_dev_put(gmux_data->discrete_pdev);
backlight_device_unregister(gmux_data->bdev);
if (gmux_data->iomem_base) {
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 05/12] platform/apple-gmux: Write switch state to EFI on MMIO devices
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (3 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 04/12] platform/apple-gmux: Fix Interrupts for MMIO systems Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:25 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 06/12] platform/apple-gmux: Add check to detect FSS Atharva Tiwari
` (6 subsequent siblings)
11 siblings, 1 reply; 24+ messages in thread
From: Atharva Tiwari @ 2026-11-14 16:06 UTC (permalink / raw)
Cc: Andre Eikmeyer, Sebastian Zygadlo, Yossef Osman, 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,
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
From: Andre Eikmeyer <dev@deq.rocks>
On T2 devices (MMIO), the T2 coprocessor reads gpu-active efi
variable, and macOS seems to write it, so to properly communciate
to the T2 coprocessor, we write gpu-active.
Tested-by: Sebastian Zygadlo <szgit@icloud.com>
Tested-by: Yossef Osman <yossef.osman@proton.me>
Co-developed: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
drivers/platform/x86/Kconfig | 1 +
drivers/platform/x86/apple-gmux.c | 40 +++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index c37e76066..01895d511 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -217,6 +217,7 @@ config APPLE_GMUX
depends on ACPI && PCI
depends on PNP
depends on BACKLIGHT_CLASS_DEVICE
+ depends on EFI
help
This driver provides support for the gmux device found on many
Apple laptops, which controls the display mux for the hybrid
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 37ba20779..c4a953b5b 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -26,6 +26,7 @@
#include <linux/debugfs.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
+#include <linux/efi.h>
#include <acpi/video.h>
#include <asm/io.h>
@@ -489,6 +490,32 @@ static void gmux_write_switch_state(struct apple_gmux_data *gmux_data)
gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
}
+#define APPLE_GPU_VARS_GUID EFI_GUID(0xfa4ce28d, 0xb62f, 0x4c99, \
+ 0x9c, 0xc3, 0x68, 0x15, 0x68, 0x6e, 0x30, 0xf9)
+
+static efi_char16_t gmux_gpu_active_name[] = L"gpu-active";
+
+static void gmux_write_switch_state_efi(struct apple_gmux_data *gmux_data)
+{
+ efi_guid_t guid = APPLE_GPU_VARS_GUID;
+ u32 active = gmux_data->switch_state_display == VGA_SWITCHEROO_IGD;
+ efi_status_t status;
+
+ if (!efivar_is_available() ||
+ !efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
+ return;
+
+ status = efivar_set_variable(gmux_gpu_active_name, &guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(active), &active);
+ if (status != EFI_SUCCESS)
+ pr_warn("writing gpu-active failed: 0x%lx\n", status);
+ else
+ pr_debug("gpu-active: %u\n", active);
+}
+
static int gmux_switchto(enum vga_switcheroo_client_id id)
{
apple_gmux_data->switch_state_ddc = id;
@@ -497,6 +524,8 @@ static int gmux_switchto(enum vga_switcheroo_client_id id)
apple_gmux_data->switch_state_external = id;
gmux_write_switch_state(apple_gmux_data);
+ if (apple_gmux_data->type == APPLE_GMUX_TYPE_MMIO)
+ gmux_write_switch_state_efi(apple_gmux_data);
return 0;
}
@@ -1240,6 +1269,15 @@ static void gmux_remove(struct pnp_dev *pnp)
kfree(gmux_data);
}
+static void gmux_shutdown(struct pnp_dev *pnp)
+{
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+
+ gmux_write_switch_state_efi(gmux_data);
+
+ gmux_remove(pnp);
+}
+
static const struct pnp_device_id gmux_device_ids[] = {
{ .id = GMUX_ACPI_HID },
{ }
@@ -1254,6 +1292,7 @@ static struct pnp_driver gmux_pnp_driver = {
.name = "apple-gmux",
.probe = gmux_probe,
.remove = gmux_remove,
+ .shutdown = gmux_shutdown,
.id_table = gmux_device_ids,
.driver = {
.pm = &gmux_dev_pm_ops,
@@ -1265,3 +1304,4 @@ MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
MODULE_DESCRIPTION("Apple Gmux Driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pnp, gmux_device_ids);
+MODULE_IMPORT_NS("EFIVAR");
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 06/12] platform/apple-gmux: Add check to detect FSS
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (4 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 05/12] platform/apple-gmux: Write switch state to EFI on MMIO devices Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:17 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 07/12] platform/apple-gmux: Add documentation about iMacs Atharva Tiwari
` (5 subsequent siblings)
11 siblings, 1 reply; 24+ messages in thread
From: Atharva Tiwari @ 2026-11-14 16:06 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
On MMIO macs, there is feature called FSS, we assume its
a display syncing technology between internal links
(for example 5K iMacs).
We detect it and then warn the user about it, if its broken/unavailable.
Tested-by: Sebastian Zygadlo <szgit@icloud.com>
Tested-by: Yossef Osman <yossef.osman@proton.me>
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
drivers/platform/x86/apple-gmux.c | 2 ++
include/linux/apple-gmux.h | 1 +
2 files changed, 3 insertions(+)
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index c4a953b5b..a1b7a4709 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -1196,6 +1196,8 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
gmux_enable_interrupts(gmux_data, 1);
gmux_read_switch_state(gmux_data);
+ if (type == APPLE_GMUX_TYPE_MMIO && gmux_read8(gmux_data, GMUX_PORT_FSS) & 1)
+ pr_warn("FSS Failure, display tearing expected\n");
/*
* Retina MacBook Pros cannot switch the panel's AUX separately
* and need eDP pre-calibration. They are distinguishable from
diff --git a/include/linux/apple-gmux.h b/include/linux/apple-gmux.h
index 206d97ffd..a4fa84500 100644
--- a/include/linux/apple-gmux.h
+++ b/include/linux/apple-gmux.h
@@ -22,6 +22,7 @@
#define GMUX_PORT_VERSION_RELEASE 0x06
#define GMUX_PORT_SWITCH_DISPLAY 0x10
#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
+#define GMUX_PORT_FSS 0x12
#define GMUX_PORT_INTERRUPT_ENABLE 0x14
#define GMUX_PORT_INTERRUPT_STATUS 0x16
#define GMUX_PORT_SWITCH_DDC 0x28
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 07/12] platform/apple-gmux: Add documentation about iMacs
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (5 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 06/12] platform/apple-gmux: Add check to detect FSS Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:40 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 08/12] drm/amdgpu: Add Apple GMUX runtime PM support Atharva Tiwari
` (4 subsequent siblings)
11 siblings, 1 reply; 24+ 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
Add documentation about iMacs
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
drivers/platform/x86/apple-gmux.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index a1b7a4709..4f5c98445 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -444,6 +444,11 @@ static const struct backlight_ops gmux_bl_ops = {
* switch the panel and the external DP connector and allocates a framebuffer
* for the selected GPU.
*
+ * There is iGPU present on iMac's aswell, and we can enable it using apple-set-os.
+ * But there arent any display lines going from the iGPU to the display. iGPU is only
+ * used for offloading then, it makes sense as iMac's display are massive (4K or 5K).
+ * A integrated GPU won't be able to handle that.
+ *
* .. _US 8,687,007 B2: https://pimg-fpiw.uspto.gov/fdd/07/870/086/0.pdf
* .. _NXP CBTL06141: https://www.nxp.com/documents/data_sheet/CBTL06141.pdf
* .. _NXP CBTL06142: https://www.nxp.com/documents/data_sheet/CBTL06141.pdf
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 08/12] drm/amdgpu: Add Apple GMUX runtime PM support
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (6 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 07/12] platform/apple-gmux: Add documentation about iMacs Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:44 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 09/12] drm/amdgpu: Catch register access while GMUX keeps the GPU off Atharva Tiwari
` (3 subsequent siblings)
11 siblings, 1 reply; 24+ messages in thread
From: Atharva Tiwari @ 2026-11-14 16:06 UTC (permalink / raw)
Cc: Andre Eikmeyer, Sebastian Zygadlo, Yossef Osman, 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,
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
From: Andre Eikmeyer <dev@deq.rocks>
The T2 MacBookPro's uses Apple GMUX to control power to the discrete GPU,
but it does not expose the ATPX or ACPI power-resource interfaces currently
recognized by amdgpu. The driver therefore leaves runtime PM disabled even
though vga_switcheroo can switch the GPU reliably.
A dedicated GMUX runtime PM mode uses the existing vga_switcheroo PM domain
to sequence GPU power. A runtime-suspended GMUX GPU can remain powered off
across system sleep, following the smart-suspend handling already used by
BOCO devices.
On muxed systems the inactive GPU cannot reach the panel AUX channel. Like
the macOS driver, tell Display Core that eDP is disconnected before
dc_create() when the GMUX GPU is not the boot display device, and skip eDP
detection at init as the resume path already does. This avoids powering the
panel from the inactive GPU and retaining a fail-safe sink that reports as
connected and keeps the GPU runtime busy.
This allows the discrete GPU to enter D3cold while the integrated GPU is
primary and allows PRIME workloads and external displays to wake it on
demand.
This was tested on MacBookPro15,1, 16,1, 16,4 together
with the corresponding apple-gmux power-sequencing support.
Tested-by: Sebastian Zygadlo <szgit@icloud.com>
Tested-by: Yossef Osman <yossef.osman@proton.me>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 +
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 56 +++++++++++++++++--
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 43 +++++++++-----
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 14 +++++
drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 1 +
5 files changed, 97 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index a9c6f5d4a..6fd607587 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -1194,6 +1194,8 @@ int amdgpu_device_mode1_reset(struct amdgpu_device *adev);
int amdgpu_device_link_reset(struct amdgpu_device *adev);
bool amdgpu_device_supports_atpx(struct amdgpu_device *adev);
bool amdgpu_device_supports_px(struct amdgpu_device *adev);
+bool amdgpu_device_supports_gmux(struct amdgpu_device *adev);
+bool amdgpu_device_has_gmux_edp(struct amdgpu_device *adev);
bool amdgpu_device_supports_boco(struct amdgpu_device *adev);
bool amdgpu_device_supports_smart_shift(struct amdgpu_device *adev);
int amdgpu_device_supports_baco(struct amdgpu_device *adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 9269e780f..ac336594b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <linux/iommu.h>
#include <linux/pci.h>
+#include <linux/dmi.h>
#include <linux/pci-p2pdma.h>
#include <linux/apple-gmux.h>
#include <linux/nospec.h>
@@ -570,6 +571,46 @@ bool amdgpu_device_supports_px(struct amdgpu_device *adev)
return false;
}
+static const struct dmi_system_id amdgpu_gmux_runpm_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro15,1"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro15,3"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,1"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,4"),
+ },
+ },
+ { }
+};
+
+bool amdgpu_device_supports_gmux(struct amdgpu_device *adev)
+{
+ return dmi_check_system(amdgpu_gmux_runpm_dmi_table) &&
+ !pci_is_thunderbolt_attached(adev->pdev) &&
+ !dev_is_removable(&adev->pdev->dev) &&
+ apple_gmux_detect(NULL, NULL);
+}
+
+bool amdgpu_device_has_gmux_edp(struct amdgpu_device *adev)
+{
+ return amdgpu_device_supports_gmux(adev) &&
+ (dmi_match(DMI_PRODUCT_NAME, "MacBookPro15,3") ||
+ dmi_match(DMI_PRODUCT_NAME, "MacBookPro16,1") ||
+ dmi_match(DMI_PRODUCT_NAME, "MacBookPro16,4"));
+}
+
/**
* amdgpu_device_supports_boco - Is the device a dGPU with ACPI power resources
*
@@ -640,6 +681,9 @@ void amdgpu_device_detect_runtime_pm_mode(struct amdgpu_device *adev)
/* enable PX as runtime mode */
adev->pm.rpm_mode = AMDGPU_RUNPM_PX;
dev_info(adev->dev, "Using ATPX for runtime pm\n");
+ } else if (amdgpu_device_supports_gmux(adev)) {
+ adev->pm.rpm_mode = AMDGPU_RUNPM_GMUX;
+ dev_info(adev->dev, "Using Apple GMUX for runtime pm\n");
} else if (amdgpu_device_supports_boco(adev)) {
/* enable boco as runtime mode */
adev->pm.rpm_mode = AMDGPU_RUNPM_BOCO;
@@ -3767,7 +3811,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
{
struct pci_dev *pdev = adev->pdev;
int r, i;
- bool px = false;
+ bool gmux, px = false;
u32 max_MBps;
int tmp;
@@ -4202,12 +4246,13 @@ int amdgpu_device_init(struct amdgpu_device *adev,
vga_client_register(adev->pdev, amdgpu_device_vga_set_decode);
px = amdgpu_device_supports_px(adev);
+ gmux = amdgpu_device_supports_gmux(adev);
if (amdgpu_device_should_register_switcheroo(adev, px))
vga_switcheroo_register_client(adev->pdev,
- &amdgpu_switcheroo_ops, px);
+ &amdgpu_switcheroo_ops, px || gmux);
- if (px)
+ if (px || gmux)
vga_switcheroo_init_domain_pm_ops(adev->dev, &adev->vga_pm_domain);
adev->pm_nb.notifier_call = amdgpu_device_pm_notifier;
@@ -4336,7 +4381,7 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
void amdgpu_device_fini_sw(struct amdgpu_device *adev)
{
int i;
- bool px;
+ bool gmux, px;
amdgpu_device_ip_fini(adev);
amdgpu_fence_driver_sw_fini(adev);
@@ -4367,11 +4412,12 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev)
adev->xcp_mgr = NULL;
px = amdgpu_device_supports_px(adev);
+ gmux = amdgpu_device_supports_gmux(adev);
if (amdgpu_device_should_register_switcheroo(adev, px))
vga_switcheroo_unregister_client(adev->pdev);
- if (px)
+ if (px || gmux)
vga_switcheroo_fini_domain_pm_ops(adev->dev);
if ((adev->pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index d9d794a7d..3978ed284 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2398,6 +2398,9 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
return -EINVAL;
}
+ if (vga_switcheroo_client_probe_defer(pdev))
+ return -EPROBE_DEFER;
+
/* skip devices which are owned by radeon */
for (i = 0; i < ARRAY_SIZE(amdgpu_unsupported_pciidlist); i++) {
if (amdgpu_unsupported_pciidlist[i] == pdev->device)
@@ -2494,11 +2497,12 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
DRM_ERROR("Creating debugfs files failed (%d).\n", ret);
if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) {
- /* only need to skip on ATPX */
+ /* ATPX requires a full system-sleep transition. */
if (amdgpu_device_supports_px(adev))
dev_pm_set_driver_flags(ddev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
- /* we want direct complete for BOCO */
- if (amdgpu_device_supports_boco(adev))
+ /* BOCO and GMUX can remain runtime suspended across system sleep. */
+ if (amdgpu_device_supports_boco(adev) ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
dev_pm_set_driver_flags(ddev->dev, DPM_FLAG_SMART_PREPARE |
DPM_FLAG_SMART_SUSPEND |
DPM_FLAG_MAY_SKIP_RESUME);
@@ -2610,7 +2614,9 @@ static int amdgpu_pmops_prepare(struct device *dev)
/* Return a positive number here so
* DPM_FLAG_SMART_SUSPEND works properly
*/
- if (amdgpu_device_supports_boco(adev) && pm_runtime_suspended(dev))
+ if ((amdgpu_device_supports_boco(adev) ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX) &&
+ pm_runtime_suspended(dev))
return 1;
/* if we will not support s3 or s2i for the device
@@ -2881,6 +2887,7 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
}
adev->in_runpm = true;
+ /* GMUX dGPUs drive displays, so hotplug handlers may reopen them. */
if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
@@ -2891,7 +2898,8 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
* platforms.
* TODO: this may be also needed for PX capable platform.
*/
- if (adev->pm.rpm_mode == AMDGPU_RUNPM_BOCO)
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_BOCO ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
adev->mp1_state = PP_MP1_STATE_UNLOAD;
ret = amdgpu_device_prepare(drm_dev);
@@ -2900,16 +2908,19 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
ret = amdgpu_device_suspend(drm_dev, false);
if (ret) {
adev->in_runpm = false;
- if (adev->pm.rpm_mode == AMDGPU_RUNPM_BOCO)
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_BOCO ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
adev->mp1_state = PP_MP1_STATE_NONE;
return ret;
}
- if (adev->pm.rpm_mode == AMDGPU_RUNPM_BOCO)
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_BOCO ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
adev->mp1_state = PP_MP1_STATE_NONE;
- if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX) {
- /* Only need to handle PCI state in the driver for ATPX
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX) {
+ /* Only need to handle PCI state in the driver for ATPX and GMUX.
* PCI core handles it for _PR3.
*/
amdgpu_device_cache_pci_state(pdev);
@@ -2956,10 +2967,12 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
if (!pci_device_is_present(adev->pdev))
adev->no_hw_access = true;
- if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX) {
- drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX) {
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
+ drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
- /* Only need to handle PCI state in the driver for ATPX
+ /* Only need to handle PCI state in the driver for ATPX and GMUX.
* PCI core handles it for _PR3.
*/
pci_set_power_state(pdev, PCI_D0);
@@ -2979,12 +2992,14 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
}
ret = amdgpu_device_resume(drm_dev, false);
if (ret) {
- if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
pci_disable_device(pdev);
return ret;
}
- if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX ||
+ adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
amdgpu_restore_umd_profile_pstate_after_runpm(adev);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 47a0ff2ed..aee12cd76 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -81,6 +81,7 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/pm_runtime.h>
+#include <linux/vgaarb.h>
#include <linux/pci.h>
#include <linux/power_supply.h>
#include <linux/firmware.h>
@@ -623,6 +624,10 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
else
init_data.bb_from_dmub = NULL;
+ if (amdgpu_device_has_gmux_edp(adev) &&
+ adev->pdev != vga_default_device())
+ init_data.flags.edp_not_connected = true;
+
/* Display Core create. */
adev->dm.dc = dc_create(&init_data);
@@ -2545,6 +2550,15 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
dm->hpd_rx_offload_wq[aconnector->base.index].aconnector =
aconnector;
+ /* Like the resume path, skip eDP detection without a sink. */
+ if (link->connector_signal == SIGNAL_TYPE_EDP &&
+ amdgpu_device_has_gmux_edp(adev) &&
+ !link->edp_sink_present) {
+ drm_info(adev_to_drm(adev), "Skipping detection of inactive GMUX eDP link %d\n",
+ i);
+ continue;
+ }
+
if (!dc_link_detect_connection_type(link, &new_connection_type))
drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index 8e8a052e1..025fa806d 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -48,6 +48,7 @@ enum amdgpu_int_thermal_type {
enum amdgpu_runpm_mode {
AMDGPU_RUNPM_NONE,
AMDGPU_RUNPM_PX,
+ AMDGPU_RUNPM_GMUX,
AMDGPU_RUNPM_BOCO,
AMDGPU_RUNPM_BACO,
AMDGPU_RUNPM_BAMACO,
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 09/12] drm/amdgpu: Catch register access while GMUX keeps the GPU off
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (7 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 08/12] drm/amdgpu: Add Apple GMUX runtime PM support Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:33 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 10/12] drm/amd/display: Refuse DP AUX " Atharva Tiwari
` (2 subsequent siblings)
11 siblings, 1 reply; 24+ messages in thread
From: Atharva Tiwari @ 2026-11-14 16:06 UTC (permalink / raw)
Cc: Andre Eikmeyer, Sebastian Zygadlo, Yossef Osman, 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,
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
From: Andre Eikmeyer <dev@deq.rocks>
As mentioned previous any access to the GPU when the GMUX has
shut off the rails results in a CPU CATTER, skip and report
any register access after the GPU was runtime suspended for GMUX power-off.
Tested-by: Sebastian Zygadlo <szgit@icloud.com>
Tested-by: Yossef Osman <yossef.osman@proton.me>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 1 +
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 5 ++++
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 5 ++++
.../gpu/drm/amd/amdgpu/amdgpu_reg_access.c | 24 +++++++++++++++++++
4 files changed, 35 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 6fd607587..9938ae48f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -881,6 +881,7 @@ struct amdgpu_device {
/* enable runtime pm on the device */
bool in_runpm;
bool has_pr3;
+ bool runpm_active;
bool ucode_sysfs_en;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index ac336594b..ed205352f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -947,6 +947,10 @@ bool amdgpu_device_skip_hw_access(struct amdgpu_device *adev)
if (adev->no_hw_access)
return true;
+ if (unlikely(!READ_ONCE(adev->runpm_active))) {
+ WARN_RATELIMIT(1, "%s: register access while powered off\n", dev_name(adev->dev));
+ return true;
+ }
#ifdef CONFIG_LOCKDEP
/*
* This is a bit complicated to understand, so worth a comment. What we assert
@@ -2029,6 +2033,7 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev)
int i, r;
amdgpu_device_enable_virtual_display(adev);
+ adev->runpm_active = true;
if (amdgpu_sriov_vf(adev)) {
/*
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 3978ed284..d1b0c6c73 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2935,6 +2935,9 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
amdgpu_device_baco_enter(adev);
}
+ if (adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
+ WRITE_ONCE(adev->runpm_active, false);
+
dev_dbg(&pdev->dev, "asic/device is runtime suspended\n");
return 0;
@@ -2963,6 +2966,8 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
if (adev->pm.rpm_mode == AMDGPU_RUNPM_NONE)
return -EINVAL;
+ WRITE_ONCE(adev->runpm_active, true);
+
/* Avoids registers access if device is physically gone */
if (!pci_device_is_present(adev->pdev))
adev->no_hw_access = true;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_reg_access.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_reg_access.c
index 7468855c1..64eba8a97 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_reg_access.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_reg_access.c
@@ -594,6 +594,9 @@ u32 amdgpu_device_indirect_rreg(struct amdgpu_device *adev, u32 reg_addr)
void __iomem *pcie_data_offset;
u32 r;
+ if (amdgpu_device_skip_hw_access(adev))
+ return 0;
+
pcie_index = adev->nbio.funcs->get_pcie_index_offset(adev);
pcie_data = adev->nbio.funcs->get_pcie_data_offset(adev);
@@ -617,6 +620,9 @@ u32 amdgpu_device_indirect_rreg_ext(struct amdgpu_device *adev, u64 reg_addr)
void __iomem *pcie_index_hi_offset;
void __iomem *pcie_data_offset;
+ if (amdgpu_device_skip_hw_access(adev))
+ return 0;
+
if (unlikely(!adev->nbio.funcs)) {
pcie_index = AMDGPU_PCIE_INDEX_FALLBACK;
pcie_data = AMDGPU_PCIE_DATA_FALLBACK;
@@ -677,6 +683,9 @@ u64 amdgpu_device_indirect_rreg64(struct amdgpu_device *adev, u32 reg_addr)
void __iomem *pcie_data_offset;
u64 r;
+ if (amdgpu_device_skip_hw_access(adev))
+ return 0;
+
pcie_index = adev->nbio.funcs->get_pcie_index_offset(adev);
pcie_data = adev->nbio.funcs->get_pcie_data_offset(adev);
@@ -706,6 +715,9 @@ u64 amdgpu_device_indirect_rreg64_ext(struct amdgpu_device *adev, u64 reg_addr)
void __iomem *pcie_data_offset;
u64 r;
+ if (amdgpu_device_skip_hw_access(adev))
+ return 0;
+
pcie_index = adev->nbio.funcs->get_pcie_index_offset(adev);
pcie_data = adev->nbio.funcs->get_pcie_data_offset(adev);
if ((reg_addr >> 32) && (adev->nbio.funcs->get_pcie_index_hi_offset))
@@ -762,6 +774,9 @@ void amdgpu_device_indirect_wreg(struct amdgpu_device *adev, u32 reg_addr,
void __iomem *pcie_index_offset;
void __iomem *pcie_data_offset;
+ if (amdgpu_device_skip_hw_access(adev))
+ return;
+
pcie_index = adev->nbio.funcs->get_pcie_index_offset(adev);
pcie_data = adev->nbio.funcs->get_pcie_data_offset(adev);
@@ -784,6 +799,9 @@ void amdgpu_device_indirect_wreg_ext(struct amdgpu_device *adev, u64 reg_addr,
void __iomem *pcie_index_hi_offset;
void __iomem *pcie_data_offset;
+ if (amdgpu_device_skip_hw_access(adev))
+ return;
+
pcie_index = adev->nbio.funcs->get_pcie_index_offset(adev);
pcie_data = adev->nbio.funcs->get_pcie_data_offset(adev);
if ((reg_addr >> 32) && (adev->nbio.funcs->get_pcie_index_hi_offset))
@@ -832,6 +850,9 @@ void amdgpu_device_indirect_wreg64(struct amdgpu_device *adev, u32 reg_addr,
void __iomem *pcie_index_offset;
void __iomem *pcie_data_offset;
+ if (amdgpu_device_skip_hw_access(adev))
+ return;
+
pcie_index = adev->nbio.funcs->get_pcie_index_offset(adev);
pcie_data = adev->nbio.funcs->get_pcie_data_offset(adev);
@@ -861,6 +882,9 @@ void amdgpu_device_indirect_wreg64_ext(struct amdgpu_device *adev, u64 reg_addr,
void __iomem *pcie_index_hi_offset;
void __iomem *pcie_data_offset;
+ if (amdgpu_device_skip_hw_access(adev))
+ return;
+
pcie_index = adev->nbio.funcs->get_pcie_index_offset(adev);
pcie_data = adev->nbio.funcs->get_pcie_data_offset(adev);
if ((reg_addr >> 32) && (adev->nbio.funcs->get_pcie_index_hi_offset))
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 10/12] drm/amd/display: Refuse DP AUX while GMUX keeps the GPU off
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (8 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 09/12] drm/amdgpu: Catch register access while GMUX keeps the GPU off Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:34 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 11/12] drm/amdgpu: Skip AC/DC notification while the GPU is runtime suspended Atharva Tiwari
2026-11-14 16:06 ` [PATCH v2 12/12] drm/amd/display: Skip panel power savings on eDP links without a panel Atharva Tiwari
11 siblings, 1 reply; 24+ messages in thread
From: Atharva Tiwari @ 2026-11-14 16:06 UTC (permalink / raw)
Cc: Andre Eikmeyer, Sebastian Zygadlo, Yossef Osman, 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,
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
From: Andre Eikmeyer <dev@deq.rocks>
fwupd reads /dev/drm_dp_aux* while the GPU is runtime suspended. The AUX
device holds no runtime PM reference, so the transfer touched registers of
the powered off GPU and caused a CPU CATTER.
Tested-by: Sebastian Zygadlo <szgit@icloud.com>
Tested-by: Yossef Osman <yossef.osman@proton.me>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
.../display/amdgpu_dm/amdgpu_dm_mst_types.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 7261317d4..0d11a9443 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -24,6 +24,7 @@
*
*/
+#include <linux/pm_runtime.h>
#include <linux/vmalloc.h>
#include <drm/display/drm_dp_helper.h>
#include <drm/display/drm_dp_mst_helper.h>
@@ -111,6 +112,20 @@ STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
if (WARN_ON(msg->size > 16))
return -E2BIG;
+ ddc = TO_DM_AUX(aux)->ddc_service;
+ adev = ddc->ctx->driver_context;
+
+ /*
+ * User space reaches this through the drm_dp_aux_dev character device
+ * check if the gpu is active or not.
+ */
+ if (adev->dev) {
+ pm_runtime_get_noresume(adev->dev);
+ if (!READ_ONCE(adev->runpm_active)) {
+ pm_runtime_put_noidle(adev->dev);
+ return -EBUSY;
+ }
+ }
payload.address = msg->address;
payload.data = msg->buffer;
payload.length = msg->size;
@@ -126,13 +141,13 @@ STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
result = dc_link_aux_transfer_raw(TO_DM_AUX(aux)->ddc_service, &payload,
&operation_result);
+ if (adev->dev)
+ pm_runtime_put_autosuspend(adev->dev);
/*
* w/a on certain intel platform where hpd is unexpected to pull low during
* 1st sideband message transaction by return AUX_RET_ERROR_HPD_DISCON
* aux transaction is succuess in such case, therefore bypass the error
*/
- ddc = TO_DM_AUX(aux)->ddc_service;
- adev = ddc->ctx->driver_context;
if (adev->dm.aux_hpd_discon_quirk) {
if (msg->address == DP_SIDEBAND_MSG_DOWN_REQ_BASE &&
operation_result == AUX_RET_ERROR_HPD_DISCON) {
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 11/12] drm/amdgpu: Skip AC/DC notification while the GPU is runtime suspended
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (9 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 10/12] drm/amd/display: Refuse DP AUX " Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:06 ` [PATCH v2 12/12] drm/amd/display: Skip panel power savings on eDP links without a panel Atharva Tiwari
11 siblings, 0 replies; 24+ messages in thread
From: Atharva Tiwari @ 2026-11-14 16:06 UTC (permalink / raw)
Cc: Andre Eikmeyer, Sebastian Zygadlo, Yossef Osman, 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,
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
From: Andre Eikmeyer <dev@deq.rocks>
Plugging or unplugging the charger raises an ACPI AC event. The handler
sent the new power source to the SMU without a runtime PM reference, so
with GMUX keeping the GPU off every SMU message spun on skipped register
reads for seconds. A runtime resume racing with that left the SMU
firmware unable to start on MacBookPro15,1 and the GPU in runtime PM
error state. Record the power source only and let the resume apply it.
Tested-by: Sebastian Zygadlo <szgit@icloud.com>
Tested-by: Yossef Osman <yossef.osman@proton.me>
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
---
drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index 808be6c42..3c6dde386 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -30,6 +30,7 @@
#include "amd_pcie.h"
#include "amdgpu_display.h"
#include "hwmgr.h"
+#include <linux/pm_runtime.h>
#include <linux/power_supply.h>
#include "amdgpu_smu.h"
@@ -498,19 +499,27 @@ int amdgpu_dpm_smu_i2c_bus_access(struct amdgpu_device *adev,
void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev)
{
+ int active = 0;
+
if (adev->pm.dpm_enabled) {
mutex_lock(&adev->pm.mutex);
+ active = pm_runtime_get_if_active(adev->dev);
+
if (power_supply_is_system_supplied() > 0)
adev->pm.ac_power = true;
else
adev->pm.ac_power = false;
- if (adev->powerplay.pp_funcs &&
- adev->powerplay.pp_funcs->notify_ac_dc)
- amdgpu_dpm_notify_ac_dc(adev);
+ if (active > 0) {
+ if (adev->powerplay.pp_funcs &&
+ adev->powerplay.pp_funcs->notify_ac_dc)
+ amdgpu_dpm_notify_ac_dc(adev);
- if (is_support_sw_smu(adev))
- smu_set_ac_dc(adev->powerplay.pp_handle, true);
+ if (is_support_sw_smu(adev))
+ smu_set_ac_dc(adev->powerplay.pp_handle, true);
+
+ pm_runtime_put(adev->dev);
+ }
mutex_unlock(&adev->pm.mutex);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v2 12/12] drm/amd/display: Skip panel power savings on eDP links without a panel
2026-11-14 16:06 [PATCH v2 00/12] Fixes for apple-gmux and runtime support Atharva Tiwari
` (10 preceding siblings ...)
2026-11-14 16:06 ` [PATCH v2 11/12] drm/amdgpu: Skip AC/DC notification while the GPU is runtime suspended Atharva Tiwari
@ 2026-11-14 16:06 ` Atharva Tiwari
2026-11-14 16:37 ` sashiko-bot
11 siblings, 1 reply; 24+ messages in thread
From: Atharva Tiwari @ 2026-11-14 16:06 UTC (permalink / raw)
Cc: Andre Eikmeyer, Sebastian Zygadlo, Yossef Osman, 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,
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
From: Andre Eikmeyer <dev@deq.rocks>
When GMUX routes the internal panel to the integrated GPU, the eDP link
of the AMD GPU has no sink. It still exposed panel_power_savings, and
tuned writes it on every switch between AC and battery. Each write sends
a hotplug event and the following connector probe runtime resumes the
GPU that GMUX had powered off. Only create the attribute when the link
has a panel.
Tested-by: Sebastian Zygadlo <szgit@icloud.com>
Tested-by: Yossef Osman <yossef.osman@proton.me>
Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
---
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c | 3 +++
.../drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c | 1 +
2 files changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
index 703d0617d..c8fbd2167 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
@@ -761,6 +761,9 @@ amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector)
if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
return false;
+ if (!amdgpu_dm_connector->dc_link->edp_sink_present)
+ return false;
+
if (link->panel_type != PANEL_TYPE_LCD && !link->panel_config.cacp.cacp_supported)
return false;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
index 7ca17f803..d71f5e5da 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
@@ -52,6 +52,7 @@ static void setup_test_connector(struct kunit *test,
fixture->aconnector->dc_link = fixture->link;
fixture->aconnector->base.dev = &fixture->adev->ddev;
fixture->link->connector_signal = signal;
+ fixture->link->edp_sink_present = true;
}
static void setup_test_dm_ddev(struct kunit *test, struct amdgpu_display_manager *dm)
--
2.55.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v2 06/12] platform/apple-gmux: Add check to detect FSS
2026-11-14 16:06 ` [PATCH v2 06/12] platform/apple-gmux: Add check to detect FSS Atharva Tiwari
@ 2026-11-14 16:17 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:17 UTC (permalink / raw)
To: Atharva Tiwari; +Cc: open list:PCI SUBSYSTEM
> On MMIO macs, there is feature called FSS, we assume its
> a display syncing technology between internal links
> (for example 5K iMacs).
>
> We detect it and then warn the user about it, if its broken/unavailable.
>
> Tested-by: Sebastian Zygadlo <szgit@icloud.com>
> Tested-by: Yossef Osman <yossef.osman@proton.me>
> Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=6
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 03/12] platform/apple-gmux: Fix MMIO interface
2026-11-14 16:06 ` [PATCH v2 03/12] platform/apple-gmux: Fix MMIO interface Atharva Tiwari
@ 2026-11-14 16:17 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:17 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 1 potential
issue to consider:
- [Low] platform/x86: apple-gmux: missing Fixes trailer for commit
0c18184de990
--
commit b19f0c98a4551005b56d57291e3256d29b977707
Author: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
> platform/apple-gmux: Fix MMIO interface
>
> This patch brings a lot of fixes to MMIO interface such as:
> 1. Using 1ms waits instead of 100us.
> 2. Checking if Timed out before reading and writing
> 3. Adding missing wait before gmux_mmio_write32
>
> 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>
[Severity: Low]
This fixes the Apple T2 MMIO gmux wait logic, timeout checks, and the
missing pre-write wait in gmux_mmio_write32(). Should this include:
Fixes: 0c18184de990 ("platform/x86: apple-gmux: support MMIO gmux on T2 Macs")
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=3
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 05/12] platform/apple-gmux: Write switch state to EFI on MMIO devices
2026-11-14 16:06 ` [PATCH v2 05/12] platform/apple-gmux: Write switch state to EFI on MMIO devices Atharva Tiwari
@ 2026-11-14 16:25 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:25 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 4 potential
issues to consider:
- [High] platform/x86: apple-gmux: premature teardown in gmux_shutdown()
- [Medium] platform/x86: apple-gmux: unconditional EFI write in
gmux_shutdown()
- [Medium] platform/x86: apple-gmux: redundant NVRAM writes in
gmux_switchto()
- [Low] platform/x86: apple-gmux: malformed Co-developed trailer in
commit
--
commit d3f4b4e97cdb8c1cdd8c1017127d8aa165b31134
Author: Andre Eikmeyer <dev@deq.rocks>
> platform/apple-gmux: Write switch state to EFI on MMIO devices
>
> On T2 devices (MMIO), the T2 coprocessor reads gpu-active efi
> variable, and macOS seems to write it, so to properly communciate
> to the T2 coprocessor, we write gpu-active.
>
> Tested-by: Sebastian Zygadlo <szgit@icloud.com>
> Tested-by: Yossef Osman <yossef.osman@proton.me>
> Co-developed: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
> Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
> Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
[Severity: Low]
This isn't a bug, but the Co-developed: trailer should be
Co-developed-by:.
> diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
> index 37ba207793a2e..c4a953b5b3e2d 100644
> --- a/drivers/platform/x86/apple-gmux.c
> +++ b/drivers/platform/x86/apple-gmux.c
[ ... ]
> @@ -489,6 +490,32 @@ static void gmux_write_switch_state(struct apple_gmux_data *gmux_data)
> gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
> }
>
> +#define APPLE_GPU_VARS_GUID EFI_GUID(0xfa4ce28d, 0xb62f, 0x4c99, \
> + 0x9c, 0xc3, 0x68, 0x15, 0x68, 0x6e, 0x30, 0xf9)
> +
> +static efi_char16_t gmux_gpu_active_name[] = L"gpu-active";
> +
> +static void gmux_write_switch_state_efi(struct apple_gmux_data *gmux_data)
> +{
> + efi_guid_t guid = APPLE_GPU_VARS_GUID;
> + u32 active = gmux_data->switch_state_display == VGA_SWITCHEROO_IGD;
> + efi_status_t status;
> +
> + if (!efivar_is_available() ||
> + !efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
> + return;
> +
> + status = efivar_set_variable(gmux_gpu_active_name, &guid,
> + EFI_VARIABLE_NON_VOLATILE |
> + EFI_VARIABLE_BOOTSERVICE_ACCESS |
> + EFI_VARIABLE_RUNTIME_ACCESS,
> + sizeof(active), &active);
> + if (status != EFI_SUCCESS)
> + pr_warn("writing gpu-active failed: 0x%lx\n", status);
> + else
> + pr_debug("gpu-active: %u\n", active);
> +}
> +
> static int gmux_switchto(enum vga_switcheroo_client_id id)
> {
> apple_gmux_data->switch_state_ddc = id;
> @@ -497,6 +524,8 @@ static int gmux_switchto(enum vga_switcheroo_client_id id)
> apple_gmux_data->switch_state_external = id;
>
> gmux_write_switch_state(apple_gmux_data);
> + if (apple_gmux_data->type == APPLE_GMUX_TYPE_MMIO)
> + gmux_write_switch_state_efi(apple_gmux_data);
>
> return 0;
> }
[Severity: Medium]
Does gmux_switchto() write the non-volatile gpu-active EFI variable to
NVRAM even when switch_state_display has not changed?
Whenever the discrete GPU runtime-suspends,
vga_switcheroo_runtime_suspend() calls
vgasr_priv.handler->switchto(VGA_SWITCHEROO_IGD) regardless of the
current display mux state. Calling efivar_set_variable() with
EFI_VARIABLE_NON_VOLATILE when switch_state_display is already id causes
redundant NVRAM flash wear and extra runtime suspend latency on every
discrete GPU wake/suspend cycle.
> @@ -1240,6 +1269,15 @@ static void gmux_remove(struct pnp_dev *pnp)
> kfree(gmux_data);
> }
>
> +static void gmux_shutdown(struct pnp_dev *pnp)
> +{
> + struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
> +
> + gmux_write_switch_state_efi(gmux_data);
[Severity: Medium]
Should gmux_shutdown() check gmux_data->type == APPLE_GMUX_TYPE_MMIO
before calling gmux_write_switch_state_efi(gmux_data), matching
gmux_switchto()? Without that check, every shutdown or reboot on
non-MMIO (PIO and indexed) gmux systems writes the T2-specific
gpu-active variable to NVRAM.
> +
> + gmux_remove(pnp);
> +}
[Severity: High]
Can calling gmux_remove(pnp) from gmux_shutdown() cause a NULL pointer
dereference when pci_device_shutdown() runs for the discrete GPU?
PCI devices are registered in subsys_initcall(acpi_init), before PNPACPI
devices are registered in fs_initcall(pnpacpi_init). During shutdown,
device_shutdown() walks devices_kset in reverse registration order, so
pnp_device_shutdown() -> gmux_shutdown() -> gmux_remove() runs before
pci_device_shutdown() on the discrete GPU.
gmux_remove() calls vga_switcheroo_unregister_handler(), which clears
vgasr_priv.handler to NULL, and frees gmux_data. When
pci_device_shutdown() then calls pm_runtime_resume() on a
runtime-suspended discrete GPU, vga_switcheroo_runtime_resume() ->
vga_switcheroo_power_switch() dereferences vgasr_priv.handler:
drivers/gpu/vga/vga_switcheroo.c:vga_switcheroo_power_switch() {
...
if (!vgasr_priv.handler->power_state)
return;
...
}
In addition, pnp_device_shutdown() does not unbind the driver, so
freeing gmux_data here leaves a dangling drvdata pointer in pnp->data
that will be used and freed again if pnp_device_remove() is called.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=5
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 04/12] platform/apple-gmux: Fix Interrupts for MMIO systems
2026-11-14 16:06 ` [PATCH v2 04/12] platform/apple-gmux: Fix Interrupts for MMIO systems Atharva Tiwari
@ 2026-11-14 16:27 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:27 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 4 potential
issues to consider:
- [Medium] platform/apple-gmux: uninitialized interrupt_work in
gmux_probe() and remove
- [Medium] platform/apple-gmux: gmux_notify_work() races with suspend
and teardown
- [Medium] platform/apple-gmux: lost interrupt bits and unbounded loop
in gmux_notify_work()
- [Medium] platform/apple-gmux: early GPE enable in gmux_probe() and
unhandled status=0 GPEs
--
commit 77837b33a80099f01ff89538d43f85130095f9ea
Author: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
platform/apple-gmux: Fix Interrupts for MMIO systems
This commit moves MMIO gmux interrupt handling to a workqueue, checks for
failed interrupt status bits, and requests a runtime resume of the discrete
GPU on display hotplug interrupts.
> diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
> index 9625551e54bfa..37ba207793a2e 100644
> --- a/drivers/platform/x86/apple-gmux.c
> +++ b/drivers/platform/x86/apple-gmux.c
[ ... ]
> @@ -806,23 +819,78 @@ static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
> /* to clear interrupts write back current status */
> status = gmux_interrupt_get_status(gmux_data);
> gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
> - /* Prevent flood of status=0 interrupts */
> - if (gmux_data->config == &apple_gmux_mmio)
> - acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
> }
>
> +static void gmux_notify_work(struct work_struct *work)
> +{
> + struct apple_gmux_data *gmux_data =
> + container_of(work, struct apple_gmux_data, interrupt_work);
> + u8 status;
> + bool turn_on_gpu = false;
> +
> + status = gmux_interrupt_get_status(gmux_data);
> + if (!status)
> + return;
> +
> + gmux_disable_interrupts(gmux_data);
> + pr_debug("Notify handler called: status %d\n", status);
> + acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
[Severity: Medium]
Does returning early when status is 0 leave status=0 GPE floods uncleared on
MMIO systems?
Previously, gmux_clear_interrupts() called GMSP(0) unconditionally, even when
status was 0. Now, if a GPE fires while GMUX_PORT_INTERRUPT_STATUS reads 0
(for example, during system resume before gmux_resume() runs, or in
gmux_probe() between acpi_enable_gpe() and gmux_enable_interrupts(gmux_data,
1)), gmux_notify_work() returns before calling GMSP(0) to reset the GPIO
polarity.
In addition, gmux_probe() enables the notify handler and GPE before
initializing gmux_data->external_switchable and gmux_data->powerchange_done:
drivers/platform/x86/apple-gmux.c:gmux_probe() {
...
status = acpi_enable_gpe(NULL, gmux_data->gpe);
...
gmux_data->external_switchable =
!bus_for_each_dev(&pci_bus_type, NULL, NULL, is_thunderbolt);
if (!gmux_data->external_switchable)
gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
apple_gmux_data = gmux_data;
init_completion(&gmux_data->powerchange_done);
gmux_enable_interrupts(gmux_data, 1);
}
If a GPE fires in that window, gmux_notify_handler() or gmux_notify_work() can
call complete(&gmux_data->powerchange_done) before init_completion() runs,
causing a NULL pointer dereference in swake_up_locked(), or read
gmux_data->external_switchable before it is set.
> +
> + status = gmux_interrupt_get_status(gmux_data);
> + while (status) {
> + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
> + if (status & GMUX_INTERRUPT_STATUS_FAILED) {
> + pr_warn_ratelimited("interrupt failed: status: 0x%x\n", status);
> + status = gmux_interrupt_get_status(gmux_data);
> + continue;
> + }
[Severity: Medium]
Can this drop GMUX_INTERRUPT_STATUS_POWER or GMUX_INTERRUPT_STATUS_HOTPLUG
events when GMUX_INTERRUPT_STATUS_FAILED is set at the same time?
Writing status to GMUX_PORT_INTERRUPT_STATUS clears all set bits in hardware,
so jumping to the next iteration with continue skips the POWER and HOTPLUG
checks for bits that were just cleared, causing gmux_set_discrete_state() to
time out waiting on powerchange_done or missing a hotplug wakeup.
Also, while (status) has no retry limit; if a status bit fails to clear in
hardware, gmux_notify_work() spins forever on system_percpu_wq and hangs
cancel_work_sync() on teardown.
> + if (status & GMUX_INTERRUPT_STATUS_POWER)
> + complete(&gmux_data->powerchange_done);
> +
> + if (status & GMUX_INTERRUPT_STATUS_HOTPLUG) {
> + /*
> + * We get an interrupt every time a display connects or disconnects, but
> + * the GET_DISPLAY port only tells us if a display is connected or not,
> + * so we resume every time a display is connected or disconnected.
> + */
> + u8 external_state = gmux_read8(gmux_data,
> + GMUX_PORT_SWITCH_GET_DISPLAY);
> + if (external_state != gmux_data->external_state) {
> + gmux_data->external_state = external_state;
> + if (external_state)
> + pr_debug("A display has been connected\n");
> + else
> + pr_debug("All displays has been disconnected\n");
> + }
> + turn_on_gpu = true;
> + }
> + status = gmux_interrupt_get_status(gmux_data);
> + }
> +
> + gmux_enable_interrupts(gmux_data, 0);
> +
> + if (turn_on_gpu && !gmux_data->external_switchable && gmux_data->discrete_pdev &&
> + pm_runtime_enabled(&gmux_data->discrete_pdev->dev))
> + pm_request_resume(&gmux_data->discrete_pdev->dev);
> +
> +}
[Severity: Medium]
Can gmux_notify_work() race with gmux_suspend() and driver teardown to
re-enable interrupts and wake the discrete GPU after interrupts were disabled?
CPU0 CPU1 (kworker)
gmux_notify_handler()
schedule_work(&interrupt_work)
gmux_suspend()
gmux_disable_interrupts()
gmux_notify_work()
...
gmux_enable_interrupts(gmux_data, 0)
pm_request_resume(&discrete_pdev->dev)
gmux_suspend() does not cancel or flush gmux_data->interrupt_work, so a queued
work item can re-enable gmux interrupts and request a runtime resume of
discrete_pdev during system suspend. Similarly, gmux_remove() and the
err_register_handler path in gmux_probe() call gmux_disable_interrupts()
before disabling the GPE, removing the notify handler, and calling
cancel_work_sync(), so an in-flight gmux_notify_work() will re-enable hardware
interrupts before cancel_work_sync() returns.
[ ... ]
> @@ -1072,6 +1140,8 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
> goto err_notify;
> }
>
> + if (type == APPLE_GMUX_TYPE_MMIO)
> + INIT_WORK(&gmux_data->interrupt_work, gmux_notify_work);
> status = acpi_enable_gpe(NULL, gmux_data->gpe);
> if (ACPI_FAILURE(status)) {
> pr_err("Cannot enable gpe: %s\n",
[ ... ]
> @@ -1124,6 +1194,9 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
> acpi_remove_notify_handler(gmux_data->dhandle,
> ACPI_DEVICE_NOTIFY,
> &gmux_notify_handler);
> +
> + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO)
> + cancel_work_sync(&gmux_data->interrupt_work);
[Severity: Medium]
Can cancel_work_sync() and schedule_work() be called on an uninitialized
gmux_data->interrupt_work?
INIT_WORK(&gmux_data->interrupt_work, gmux_notify_work) is only called inside
the if (ACPI_SUCCESS(status)) branch for GMGP, and after
acpi_install_notify_handler() has already registered gmux_notify_handler().
If evaluating GMGP fails on an MMIO system, gmux_probe() sets gmux_data->gpe =
-1 and continues without initializing interrupt_work, but both gmux_remove()
and the err_enable_gpe path in gmux_probe() unconditionally call
cancel_work_sync(&gmux_data->interrupt_work), triggering WARN_ON(!work->func)
in __flush_work(). In addition, if an ACPI notification arrives between
acpi_install_notify_handler() and INIT_WORK(), gmux_notify_handler() schedules
interrupt_work while its func pointer is still NULL.
Initializing interrupt_work before evaluating GMGP in gmux_probe() would
avoid both issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=4
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 09/12] drm/amdgpu: Catch register access while GMUX keeps the GPU off
2026-11-14 16:06 ` [PATCH v2 09/12] drm/amdgpu: Catch register access while GMUX keeps the GPU off Atharva Tiwari
@ 2026-11-14 16:33 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:33 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 2 potential
issues to consider:
- [High] drm/amdgpu: adev->runpm_active initialized after
amdgpu_virt_init()
- [Medium] drm/amdgpu: premature runpm_active set and missing unwind on
resume
--
commit 720e36a43938e97cb3f99b288c561e7448e2fbc3
Author: Andre Eikmeyer <dev@deq.rocks>
drm/amdgpu: Catch register access while GMUX keeps the GPU off
This commit adds a runpm_active flag to struct amdgpu_device and checks it
in amdgpu_device_skip_hw_access() and the indirect register accessors to
skip and warn on register accesses while the GPU is runtime suspended via
GMUX.
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> index ac336594bf055..ed205352f0f0a 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> @@ -947,6 +947,10 @@ bool amdgpu_device_skip_hw_access(struct amdgpu_device *adev)
> if (adev->no_hw_access)
> return true;
>
> + if (unlikely(!READ_ONCE(adev->runpm_active))) {
> + WARN_RATELIMIT(1, "%s: register access while powered off\n", dev_name(adev->dev));
> + return true;
> + }
> #ifdef CONFIG_LOCKDEP
> /*
> * This is a bit complicated to understand, so worth a comment. What we assert
> @@ -2029,6 +2033,7 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev)
> int i, r;
>
> amdgpu_device_enable_virtual_display(adev);
> + adev->runpm_active = true;
>
> if (amdgpu_sriov_vf(adev)) {
> /*
[Severity: High]
Is adev->runpm_active initialized too late during device probe?
In amdgpu_device_init(), amdgpu_virt_init() runs before
amdgpu_device_ip_early_init():
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c:amdgpu_device_init() {
...
/* detect hw virtualization here */
amdgpu_virt_init(adev);
...
/* early init functions */
r = amdgpu_device_ip_early_init(adev);
}
Inside amdgpu_virt_init() -> amdgpu_virt_init_detect_asic(), the driver
calls RREG32(mmRCC_IOV_FUNC_IDENTIFIER) (or mmBIF_IOV_FUNC_IDENTIFIER on
Tonga/Fiji). Because adev is zero-allocated and adev->runpm_active is
still false at that point, amdgpu_device_skip_hw_access() fires a
WARN_RATELIMIT splat on every probe of those GPUs and makes RREG32()
return 0, which also prevents SR-IOV VF detection from setting
AMDGPU_SRIOV_CAPS_IS_VF.
Setting adev->runpm_active = true in amdgpu_device_init() before
amdgpu_virt_init() would avoid this regression.
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> index 3978ed284048f..d1b0c6c7301ea 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> @@ -2935,6 +2935,9 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
> amdgpu_device_baco_enter(adev);
> }
>
> + if (adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
> + WRITE_ONCE(adev->runpm_active, false);
> +
> dev_dbg(&pdev->dev, "asic/device is runtime suspended\n");
>
> return 0;
> @@ -2963,6 +2966,8 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
> if (adev->pm.rpm_mode == AMDGPU_RUNPM_NONE)
> return -EINVAL;
>
> + WRITE_ONCE(adev->runpm_active, true);
> +
> /* Avoids registers access if device is physically gone */
> if (!pci_device_is_present(adev->pdev))
> adev->no_hw_access = true;
[Severity: Medium]
Should amdgpu_pmops_runtime_resume() set adev->runpm_active only after
restoring PCI power and config state, and clear it again if resume fails?
In the AMDGPU_RUNPM_GMUX branch right below, the device is still in
PCI_D3cold with PCI memory decoding disabled until pci_set_power_state(),
amdgpu_device_load_pci_state(), and pci_enable_device() complete. If
pci_enable_device() fails, or if amdgpu_device_resume() fails and calls
pci_disable_device(pdev), amdgpu_pmops_runtime_resume() returns an error
with adev->runpm_active left set to true while the PCI device is disabled,
allowing subsequent register accesses to bypass
amdgpu_device_skip_hw_access().
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=9
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices
2026-11-14 16:06 ` [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices Atharva Tiwari
@ 2026-11-14 16:34 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:34 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 2 potential
issues to consider:
- [High] PCI: no_pcie_port leaves D3cold bridges exposed in suspend and
sysfs
- [Medium] x86/PCI: quirk_disable_pcie_port() disables pcieport on
Thunderbolt eGPUs
--
commit ceffb6c4c39f628d202fdc8e2a5f341d822f54ff
Author: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
PCI: Disable pcie port on specific amd devices
This commit adds a no_pcie_port flag to struct pci_dev and sets it via an
early PCI fixup on the upstream bridges of certain AMD GPUs on Apple
MacBookPro models so pcie_portdrv does not bind to them.
> diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c
> index 795d81c7a4de4..b1697cc4aed56 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);
[Severity: Medium]
Can quirk_disable_pcie_port() also disable pcieport on a Thunderbolt
hierarchy when an external GPU with one of these retail AMD PCI device IDs
(such as Radeon RX 460/560 0x67ef or Radeon RX 5500 XT 0x7340) is attached?
Walking every upstream bridge without checking
!pci_is_thunderbolt_attached(pdev) sets no_pcie_port = 1 on the Thunderbolt
Root Port and switch bridges, breaking pciehp, PME, AER, and bridge runtime
D3 across that hierarchy.
> diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c
> index a9cbfc1d2bc75..d2f19e5666022 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) &&
[Severity: High]
Does returning -ENODEV here leave the upstream bridges exposed to config
space accesses while powered off by apple-gmux via PWRD(1)?
Without pcie_portdrv bound, each upstream bridge has dev->driver == NULL
(so pm == NULL) and remains in RPM_ACTIVE with runtime PM forbidden. When
apple-gmux powers off the discrete GPU in gmux_set_discrete_state(), it
saves state, sets pdev->current_state = PCI_D3cold on the bridges, and cuts
power via PWRD(1).
During system suspend while the bridges are off, pci_pm_prepare() sees
!pm_runtime_suspended(dev) and skips direct_complete, and then
pci_pm_suspend_noirq() accesses the dead bridge directly:
drivers/pci/pci-driver.c:pci_pm_suspend_noirq() {
...
if (!pm) {
pci_save_state(pci_dev);
goto set_unknown;
}
...
}
pci_pm_resume_noirq() similarly accesses config space and PCIe capabilities
on the dead bridges before gmux_resume() restores power.
At runtime, any userspace read of /sys/bus/pci/devices/.../config (such as
lspci) calls pci_read_config() -> pci_config_pm_runtime_get(), where
pm_runtime_resume(dev) is a no-op because the unbound bridge is still in
RPM_ACTIVE, so pci_user_read_config_*() reads directly from the powered-off
bridge and triggers a CPU CATERR.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=2
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 10/12] drm/amd/display: Refuse DP AUX while GMUX keeps the GPU off
2026-11-14 16:06 ` [PATCH v2 10/12] drm/amd/display: Refuse DP AUX " Atharva Tiwari
@ 2026-11-14 16:34 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:34 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 1 potential
issue to consider:
- [High] drm/amd/display: unsynchronized runtime PM check in
dm_dp_aux_transfer()
--
commit 7acdd21f6d25e531ba11673cecccfbbda8f8f481
Author: Andre Eikmeyer <dev@deq.rocks>
drm/amd/display: Refuse DP AUX while GMUX keeps the GPU off
This commit adds a runtime PM check around dc_link_aux_transfer_raw() in
dm_dp_aux_transfer() using pm_runtime_get_noresume() and adev->runpm_active.
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> index 7261317d4c0f4..0d11a9443ca01 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
[ ... ]
> @@ -111,6 +112,20 @@ STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
> if (WARN_ON(msg->size > 16))
> return -E2BIG;
>
> + ddc = TO_DM_AUX(aux)->ddc_service;
> + adev = ddc->ctx->driver_context;
> +
> + /*
> + * User space reaches this through the drm_dp_aux_dev character device
> + * check if the gpu is active or not.
> + */
> + if (adev->dev) {
> + pm_runtime_get_noresume(adev->dev);
> + if (!READ_ONCE(adev->runpm_active)) {
> + pm_runtime_put_noidle(adev->dev);
> + return -EBUSY;
> + }
> + }
> payload.address = msg->address;
> payload.data = msg->buffer;
> payload.length = msg->size;
> @@ -126,13 +141,13 @@ STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
> result = dc_link_aux_transfer_raw(TO_DM_AUX(aux)->ddc_service, &payload,
> &operation_result);
>
> + if (adev->dev)
> + pm_runtime_put_autosuspend(adev->dev);
[Severity: High]
As reported by Alex Deucher on v1 [1], why gate this on GMUX-specific
adev->runpm_active and pm_runtime_get_noresume() instead of waking or
synchronizing with runtime PM across all runtime PM modes?
pm_runtime_get_noresume() only increments dev->power.usage_count without
checking runtime_status or waiting on in-progress suspend or resume
transitions, and adev->runpm_active is only cleared at the very end of
amdgpu_pmops_runtime_suspend() when rpm_mode == AMDGPU_RUNPM_GMUX:
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c:amdgpu_pmops_runtime_suspend() {
...
if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX ||
adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX) {
amdgpu_device_cache_pci_state(pdev);
pci_disable_device(pdev);
pci_ignore_hotplug(pdev);
pci_set_power_state(pdev, PCI_D3cold);
drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
...
if (adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
WRITE_ONCE(adev->runpm_active, false);
}
Once rpm_suspend() sets runtime_status to RPM_SUSPENDING, a concurrent
/dev/drm_dp_aux* transfer still races with PCI disable and GMUX power-off:
CPU0 CPU1
rpm_suspend()
runtime_status = RPM_SUSPENDING
vga_switcheroo_runtime_suspend()
amdgpu_pmops_runtime_suspend()
amdgpu_device_suspend()
pci_disable_device(pdev)
pci_set_power_state(..., PCI_D3cold)
aux_dev_read_iter()
...
dm_dp_aux_transfer()
pm_runtime_get_noresume()
READ_ONCE(adev->runpm_active)
/* still true */
WRITE_ONCE(adev->runpm_active, false)
vga_switcheroo_power_switch(..., OFF)
dc_link_aux_transfer_raw()
/* MMIO on powered-off GPU */
The same race occurs during amdgpu_pmops_runtime_resume(), which sets
adev->runpm_active = true at the start of the callback before PCI power
state, BAR state, and DMUB are restored.
On non-GMUX systems (PX, BOCO, BACO, BAMACO), adev->runpm_active is never
cleared on runtime suspend, so pm_runtime_get_noresume() leaves the GPU
suspended while allowing dc_link_aux_transfer_raw() to touch powered-down
registers.
In addition, dm_dp_aux_transfer() is called by internal display paths during
dm_suspend() and dm_resume(), where unconditional pm_runtime_get_noresume()
and pm_runtime_put_autosuspend() manipulate usage_count and last_busy in the
middle of a runtime PM transition.
[1] https://lore.kernel.org/r/CADnq5_PRG_O7jy9P+mf7AQLerpfz6up=BejRSPMgYDh9xigm=A@mail.gmail.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=10
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 12/12] drm/amd/display: Skip panel power savings on eDP links without a panel
2026-11-14 16:06 ` [PATCH v2 12/12] drm/amd/display: Skip panel power savings on eDP links without a panel Atharva Tiwari
@ 2026-11-14 16:37 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:37 UTC (permalink / raw)
To: Atharva Tiwari; +Cc: open list:PCI SUBSYSTEM
> From: Andre Eikmeyer <dev@deq.rocks>
>
> When GMUX routes the internal panel to the integrated GPU, the eDP link
> of the AMD GPU has no sink. It still exposed panel_power_savings, and
> tuned writes it on every switch between AC and battery. Each write sends
> a hotplug event and the following connector probe runtime resumes the
> GPU that GMUX had powered off. Only create the attribute when the link
> has a panel.
>
> Tested-by: Sebastian Zygadlo <szgit@icloud.com>
> Tested-by: Yossef Osman <yossef.osman@proton.me>
> Signed-off-by: Andre Eikmeyer <dev@deq.rocks>
> Signed-off-by: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=12
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 07/12] platform/apple-gmux: Add documentation about iMacs
2026-11-14 16:06 ` [PATCH v2 07/12] platform/apple-gmux: Add documentation about iMacs Atharva Tiwari
@ 2026-11-14 16:40 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:40 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 5 potential
issues to consider:
- [High] platform/x86: apple-gmux: premature teardown and EFI write in
gmux_shutdown()
- [Medium] platform/x86: apple-gmux: uninitialized work and race in
gmux_probe()
- [Medium] platform/x86: apple-gmux: state save bugs in
gmux_set_discrete_state()
- [Medium] platform/x86: apple-gmux: lost irq bits and loop in
gmux_notify_work()
- [Low] platform/x86: apple-gmux: unused variable result in
gmux_set_discrete_state()
--
commit 765faab19bedc6dfe41f21d3ab1e32b30a85f20e
Author: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>
platform/apple-gmux: Add documentation about iMacs
This series updates the apple-gmux driver to support power control,
interrupt handling, EFI switch state persistence, and documentation for
MMIO/T2 Macs.
> diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
> index 9c728ac4e045a..4f5c984450580 100644
> --- a/drivers/platform/x86/apple-gmux.c
> +++ b/drivers/platform/x86/apple-gmux.c
[ ... ]
> @@ -502,23 +557,158 @@ static int gmux_switch_ddc(enum vga_switcheroo_client_id id)
[ ... ]
> 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]
result is never used in gmux_set_discrete_state(), and these declarations
are placed after a statement, triggering compiler warnings.
> +
> + 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;
> + }
[Severity: Medium]
Does requiring gmux_data->discrete_pdev and discrete_handle at the start of
gmux_set_discrete_state() break power control on non-MMIO Macs when
discrete_pdev has not been populated yet or lacks an ACPI companion handle,
even though only the APPLE_GMUX_TYPE_MMIO paths use them?
Also, in the MMIO power-off loop, pci_save_state(gmux_data->discrete_pdev)
is gated on gmux_save_restore_pci(parent_handle, "HDAU", true, NULL)
succeeding, so the discrete GPU's PCI state is not saved before cutting
power if HDAU is absent or fails to save. Finally, if an MMIO power-on
fails after gmux_save_restore_pci() restores PEG0, EGP0, and EGP1 (which
clears pdev->state_saved), returning early leaves gmux_data->power_state as
VGA_SWITCHEROO_OFF, causing the next power-off to jump to do_pwrd without
saving the bridges' PCI state again.
[ ... ]
> @@ -655,23 +853,78 @@ static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
> /* to clear interrupts write back current status */
> status = gmux_interrupt_get_status(gmux_data);
> gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
> - /* Prevent flood of status=0 interrupts */
> - if (gmux_data->config == &apple_gmux_mmio)
> - acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
> }
>
> +static void gmux_notify_work(struct work_struct *work)
> +{
> + struct apple_gmux_data *gmux_data =
> + container_of(work, struct apple_gmux_data, interrupt_work);
> + u8 status;
> + bool turn_on_gpu = false;
> +
> + status = gmux_interrupt_get_status(gmux_data);
> + if (!status)
> + return;
> +
> + gmux_disable_interrupts(gmux_data);
> + pr_debug("Notify handler called: status %d\n", status);
> + acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
> +
> + status = gmux_interrupt_get_status(gmux_data);
> + while (status) {
> + gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
> + if (status & GMUX_INTERRUPT_STATUS_FAILED) {
> + pr_warn_ratelimited("interrupt failed: status: 0x%x\n", status);
> + status = gmux_interrupt_get_status(gmux_data);
> + continue;
> + }
> + if (status & GMUX_INTERRUPT_STATUS_POWER)
> + complete(&gmux_data->powerchange_done);
[Severity: Medium]
Does writing status to GMUX_PORT_INTERRUPT_STATUS before the continue on
GMUX_INTERRUPT_STATUS_FAILED drop GMUX_INTERRUPT_STATUS_POWER or
GMUX_INTERRUPT_STATUS_HOTPLUG events that were set in the same status read?
Clearing all asserted bits in hardware and skipping the remaining checks
can cause powerchange_done timeouts or missed hotplug wakeups. Also,
while (status) has no bound, so a stuck status bit will spin indefinitely on
the system workqueue.
[ ... ]
> @@ -920,6 +1174,8 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
> goto err_notify;
> }
>
> + if (type == APPLE_GMUX_TYPE_MMIO)
> + INIT_WORK(&gmux_data->interrupt_work, gmux_notify_work);
> status = acpi_enable_gpe(NULL, gmux_data->gpe);
[ ... ]
> @@ -972,6 +1230,9 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
> acpi_remove_notify_handler(gmux_data->dhandle,
> ACPI_DEVICE_NOTIFY,
> &gmux_notify_handler);
> +
> + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO)
> + cancel_work_sync(&gmux_data->interrupt_work);
> err_notify:
> backlight_device_unregister(bdev);
> err_unmap:
> @@ -1001,6 +1262,9 @@ static void gmux_remove(struct pnp_dev *pnp)
> &gmux_notify_handler);
> }
>
> + if (gmux_data->type == APPLE_GMUX_TYPE_MMIO)
> + cancel_work_sync(&gmux_data->interrupt_work);
[Severity: Medium]
Can gmux_data->interrupt_work be scheduled or canceled before INIT_WORK()
runs?
INIT_WORK(&gmux_data->interrupt_work, gmux_notify_work) is only called when
evaluating GMGP succeeds, and after acpi_install_notify_handler() is
registered. If GMGP fails on an MMIO system, err_enable_gpe and
gmux_remove() call cancel_work_sync(&gmux_data->interrupt_work) on an
uninitialized work_struct, and an ACPI notification arriving between
acpi_install_notify_handler() and INIT_WORK() (or before
init_completion(&gmux_data->powerchange_done)) will schedule uninitialized
work. Also, gmux_suspend() disables interrupts without canceling
interrupt_work, allowing an in-flight gmux_notify_work() to call
gmux_enable_interrupts() and re-enable interrupts while suspended.
> pci_dev_put(gmux_data->discrete_pdev);
> backlight_device_unregister(gmux_data->bdev);
>
> if (gmux_data->iomem_base) {
> @@ -1012,6 +1276,15 @@ static void gmux_remove(struct pnp_dev *pnp)
> kfree(gmux_data);
> }
>
> +static void gmux_shutdown(struct pnp_dev *pnp)
> +{
> + struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
> +
> + gmux_write_switch_state_efi(gmux_data);
> +
> + gmux_remove(pnp);
> +}
[Severity: High]
Does calling gmux_remove() from gmux_shutdown() cause a NULL pointer
dereference when pci_device_shutdown() runs afterward?
gmux_remove() calls vga_switcheroo_unregister_handler() (setting
vgasr_priv.handler to NULL), unmaps MMIO, and frees gmux_data. Because PNP
devices are registered after PCI devices, device_shutdown() runs
gmux_shutdown() before pci_device_shutdown() on the discrete GPU. If the
discrete GPU is runtime-suspended at shutdown, pci_device_shutdown() calls
pm_runtime_resume() -> vga_switcheroo_runtime_resume() ->
vga_switcheroo_power_switch(), which dereferences vgasr_priv.handler:
drivers/gpu/vga/vga_switcheroo.c:vga_switcheroo_power_switch() {
struct vga_switcheroo_client *client;
if (!vgasr_priv.handler->power_state)
return;
...
}
Also, gmux_shutdown() calls gmux_write_switch_state_efi() without checking
gmux_data->type == APPLE_GMUX_TYPE_MMIO (unlike gmux_switchto()), writing
the T2 gpu-active EFI variable on classic and indexed gmux Macs as well.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=7
^ permalink raw reply [flat|nested] 24+ 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; 24+ 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] 24+ messages in thread
* Re: [PATCH v2 08/12] drm/amdgpu: Add Apple GMUX runtime PM support
2026-11-14 16:06 ` [PATCH v2 08/12] drm/amdgpu: Add Apple GMUX runtime PM support Atharva Tiwari
@ 2026-11-14 16:44 ` sashiko-bot
0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-14 16:44 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 4 potential
issues to consider:
- [High] drm/amdgpu: missing switcheroo readiness checks in
amdgpu_device_supports_gmux()
- [High] drm/amdgpu: broken smart-suspend handling for GMUX in
amdgpu_pci_probe()
- [High] drm/amdgpu: NULL deref on shutdown when GMUX GPU is
runtime-suspended
- [Medium] drm/amdgpu: amdgpu_device_init() ignores runpm param for GMUX
switcheroo
--
commit c24cbddf263bec215538d74185508de97d4f9ff8
Author: Andre Eikmeyer <dev@deq.rocks>
drm/amdgpu: Add Apple GMUX runtime PM support
This commit adds runtime PM support for T2 MacBook Pro discrete GPUs
using the vga_switcheroo PM domain and skips initial eDP detection when
the GMUX GPU is not the boot display device.
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> index 9269e780feb73..ac336594bf055 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
[ ... ]
> @@ -570,6 +571,46 @@ bool amdgpu_device_supports_px(struct amdgpu_device *adev)
> return false;
> }
>
> +static const struct dmi_system_id amdgpu_gmux_runpm_dmi_table[] = {
> + {
> + .matches = {
> + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro15,1"),
> + },
> + },
> + {
> + .matches = {
> + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro15,3"),
> + },
> + },
> + {
> + .matches = {
> + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,1"),
> + },
> + },
> + {
> + .matches = {
> + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,4"),
> + },
> + },
> + { }
> +};
> +
> +bool amdgpu_device_supports_gmux(struct amdgpu_device *adev)
> +{
> + return dmi_check_system(amdgpu_gmux_runpm_dmi_table) &&
> + !pci_is_thunderbolt_attached(adev->pdev) &&
> + !dev_is_removable(&adev->pdev->dev) &&
> + apple_gmux_detect(NULL, NULL);
> +}
[Severity: High]
Can amdgpu_device_supports_gmux() return true before the vga_switcheroo
handler and peer GPU client are registered?
When the system boots with the discrete GPU as the default VGA device
(pdev == vga_default_device()), vga_switcheroo_client_probe_defer() does
not defer probing even if apple-gmux has not registered its handler yet.
Because apple_gmux_detect(NULL, NULL) only checks the PNP MMIO resource,
amdgpu attaches vga_pm_domain and enables runtime autosuspend; when the
autosuspend timer fires while vgasr_priv.handler is NULL,
vga_switcheroo_runtime_suspend() dereferences vgasr_priv.handler->switchto
and crashes:
drivers/gpu/vga/vga_switcheroo.c:vga_switcheroo_runtime_suspend() {
...
mutex_lock(&vgasr_mutex);
if (vgasr_priv.handler->switchto) {
mutex_lock(&vgasr_priv.mux_hw_lock);
vgasr_priv.handler->switchto(VGA_SWITCHEROO_IGD);
...
}
Even after apple-gmux registers its handler,
vga_switcheroo_client_probe_defer() does not wait for the integrated GPU
client (i915) to register, so vga_switcheroo_enable() has not run and
gmux_set_discrete_state() fails with -ENODEV during runtime suspend
without saving bridge state or cutting power. Also, unlike
amdgpu_device_supports_px(), amdgpu_device_supports_gmux() does not check
IS_ENABLED(CONFIG_VGA_SWITCHEROO), and calling
vga_switcheroo_client_probe_defer() unconditionally in amdgpu_pci_probe()
defers Thunderbolt eGPUs or systems with stub APP000B ACPI devices where
gmux_probe() returns -ENODEV.
[ ... ]
> @@ -4202,12 +4246,13 @@ int amdgpu_device_init(struct amdgpu_device *adev,
> vga_client_register(adev->pdev, amdgpu_device_vga_set_decode);
>
> px = amdgpu_device_supports_px(adev);
> + gmux = amdgpu_device_supports_gmux(adev);
>
> if (amdgpu_device_should_register_switcheroo(adev, px))
> vga_switcheroo_register_client(adev->pdev,
> - &amdgpu_switcheroo_ops, px);
> + &amdgpu_switcheroo_ops, px || gmux);
>
> - if (px)
> + if (px || gmux)
> vga_switcheroo_init_domain_pm_ops(adev->dev, &adev->vga_pm_domain);
[Severity: Medium]
Does enabling driver_power_control and attaching vga_pm_domain here ignore
the amdgpu.runpm module parameter?
amdgpu_device_init() runs before amdgpu_device_detect_runtime_pm_mode(),
which only selects AMDGPU_RUNPM_GMUX when amdgpu_runtime_pm is -1 or -2.
Booting with amdgpu.runpm=0 leaves runtime PM disabled while
driver_power_control is true, turning manual vga_switcheroo OFF/ON
commands into no-ops. Booting with amdgpu.runpm=1 or 2 selects
AMDGPU_RUNPM_BACO or AMDGPU_RUNPM_BAMACO while vga_pm_domain is still
attached, so runtime suspend enters BACO without caching PCI state or
setting PP_MP1_STATE_UNLOAD before vga_switcheroo_runtime_suspend() cuts
power via GMUX.
[Severity: High]
Can pci_device_shutdown() trigger a NULL pointer dereference in
vga_switcheroo_power_switch() when the GMUX GPU is runtime-suspended at
shutdown?
PNP devices are registered after PCI devices in devices_kset, and no
device_link orders the PCI GPU before the apple-gmux PNP device. During
device_shutdown(), gmux_shutdown() -> gmux_remove() ->
vga_switcheroo_unregister_handler() runs first and sets
vgasr_priv.handler = NULL while the GPU remains in D3cold. When
pci_device_shutdown() then runs on the GPU, it calls pm_runtime_resume()
-> vga_switcheroo_runtime_resume() -> vga_switcheroo_power_switch(), which
dereferences vgasr_priv.handler->power_state:
drivers/gpu/vga/vga_switcheroo.c:vga_switcheroo_power_switch() {
if (!vgasr_priv.handler->power_state)
return;
...
}
In addition, amdgpu_pci_shutdown() then calls amdgpu_device_prepare() and
amdgpu_device_suspend(), which only check for DRM_SWITCH_POWER_OFF (not
DRM_SWITCH_POWER_DYNAMIC_OFF) and access hardware on the powered-off GPU.
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> index d9d794a7db964..3978ed284048f 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
[ ... ]
> @@ -2494,11 +2497,12 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
> DRM_ERROR("Creating debugfs files failed (%d).\n", ret);
>
> if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) {
> - /* only need to skip on ATPX */
> + /* ATPX requires a full system-sleep transition. */
> if (amdgpu_device_supports_px(adev))
> dev_pm_set_driver_flags(ddev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
> - /* we want direct complete for BOCO */
> - if (amdgpu_device_supports_boco(adev))
> + /* BOCO and GMUX can remain runtime suspended across system sleep. */
> + if (amdgpu_device_supports_boco(adev) ||
> + adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX)
> dev_pm_set_driver_flags(ddev->dev, DPM_FLAG_SMART_PREPARE |
> DPM_FLAG_SMART_SUSPEND |
> DPM_FLAG_MAY_SKIP_RESUME);
> @@ -2610,7 +2614,9 @@ static int amdgpu_pmops_prepare(struct device *dev)
> /* Return a positive number here so
> * DPM_FLAG_SMART_SUSPEND works properly
> */
> - if (amdgpu_device_supports_boco(adev) && pm_runtime_suspended(dev))
> + if ((amdgpu_device_supports_boco(adev) ||
> + adev->pm.rpm_mode == AMDGPU_RUNPM_GMUX) &&
> + pm_runtime_suspended(dev))
> return 1;
[Severity: High]
What happens during system sleep or hibernation when direct_complete is
not used on a runtime-suspended GMUX GPU?
When the HDA audio consumer (snd_hda_intel) is bound, its child PCM
devices clear direct_complete during dpm_prepare() after
amdgpu_pmops_prepare() on the supplier GPU has already returned 1 and
skipped amdgpu_device_prepare(). In dpm_suspend(), pci_pm_suspend() on
the HDA device runtime-resumes the GMUX GPU and clears its
direct_complete, so amdgpu_pmops_suspend() -> amdgpu_device_suspend() runs
without amdgpu_device_prepare() having executed, and because
vga_pm_domain only implements runtime PM callbacks, GMUX never powers off
the GPU for system sleep.
Conversely, if direct_complete is cleared while the GMUX GPU stays
runtime-suspended (for example via a child backlight device, wakeup
settings, or during hibernation in pci_pm_poweroff()), DPM_FLAG_SMART_SUSPEND
causes pci_pm_suspend() and pci_pm_poweroff() to skip pm_runtime_resume()
and call amdgpu_device_suspend() while the GPU is powered off in
DRM_SWITCH_POWER_DYNAMIC_OFF. On resume, pci_pm_resume() sees
pci_dev->state_saved == true from runtime suspend and calls
amdgpu_pmops_resume() while GMUX still has power cut, where
pci_device_is_present() reads 0xffff, sets adev->no_hw_access = true, and
runs amdgpu_device_resume() on the powered-off device.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114160731.3756-1-atharvatiwarilinuxdev@gmail.com?part=8
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2026-11-14 16:44 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
2026-11-14 16:06 ` [PATCH v2 02/12] PCI: Disable pcie port on specific amd devices Atharva Tiwari
2026-11-14 16:34 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 03/12] platform/apple-gmux: Fix MMIO interface Atharva Tiwari
2026-11-14 16:17 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 04/12] platform/apple-gmux: Fix Interrupts for MMIO systems Atharva Tiwari
2026-11-14 16:27 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 05/12] platform/apple-gmux: Write switch state to EFI on MMIO devices Atharva Tiwari
2026-11-14 16:25 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 06/12] platform/apple-gmux: Add check to detect FSS Atharva Tiwari
2026-11-14 16:17 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 07/12] platform/apple-gmux: Add documentation about iMacs Atharva Tiwari
2026-11-14 16:40 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 08/12] drm/amdgpu: Add Apple GMUX runtime PM support Atharva Tiwari
2026-11-14 16:44 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 09/12] drm/amdgpu: Catch register access while GMUX keeps the GPU off Atharva Tiwari
2026-11-14 16:33 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 10/12] drm/amd/display: Refuse DP AUX " Atharva Tiwari
2026-11-14 16:34 ` sashiko-bot
2026-11-14 16:06 ` [PATCH v2 11/12] drm/amdgpu: Skip AC/DC notification while the GPU is runtime suspended Atharva Tiwari
2026-11-14 16:06 ` [PATCH v2 12/12] drm/amd/display: Skip panel power savings on eDP links without a panel Atharva Tiwari
2026-11-14 16:37 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox