+Rafael On 10/29/26 10:14, Mike Lothian wrote:
The AMD CPM D3cold table on the G513QY (BIOS 335) handles the dGPU hotplug GPE with:Method (_L05) { Notify (\_SB.PCI0.GPP0.VGA, 0x81) } On this board the Navi 22 sits behind its on-package PCIe switch, so the dGPU node is \_SB.PCI0.GPP0.SWUS.SWDS.VGA_ and every hotplug on the USB-C DP alt mode port while the dGPU is in D3cold logs: ACPI BIOS Error (bug): Could not resolve symbol [\_SB.PCI0.GPP0.VGA], AE_NOT_FOUND ACPI Error: Aborting method \_GPE._L05 due to previous error (AE_NOT_FOUND) ACPI Error: AE_NOT_FOUND, while evaluating GPE method [_L05] The notify is lost, the dGPU stays powered off and the external displays stay dark. Masking the GPE (acpi_mask_gpe=0x05), the usual workaround, removes the errors but also the hotplug. On boards listed in a DMI table, and only when the notify target does not resolve, install a handler for the GPE in place of the _Lxx method. It requests a runtime resume of the dGPU, which re-detects all links and sends a hotplug event, and re-arms the level-triggered GPE from a delayed work so it cannot storm while the dGPU powers up. If the firmware is fixed, or the path is supplied by an SSDT override, the firmware method is left in charge. Other boards using the same CPM table may need adding to the list. Link: https://forum.endeavouros.com/t/boots-into-black-screen-unresponsive-amd/49430 Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Mike Lothian <[email protected]>
Can you please post an acpidump to a kernel bugzilla or drm bug associated with this issue?
This generally feels like an ACPI interpreter issue to me.
--- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 3 + drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c | 103 ++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index deb63122828d..d8a7fea8be9a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -642,6 +642,9 @@ struct amdgpu_device { bool accel_working; struct notifier_block acpi_nb; bool acpi_dgpu_notify; + bool acpi_hpd_gpe_installed; + u32 acpi_hpd_gpe; + struct delayed_work acpi_hpd_gpe_work; struct notifier_block pm_nb; struct amdgpu_i2c_chan *i2c_bus[AMDGPU_MAX_I2C_BUS]; struct debugfs_blob_wrapper debugfs_vbios_blob; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c index a4041f90b374..182791f4cc86 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c @@ -25,6 +25,7 @@ #include <linux/pci.h> #include <linux/acpi.h> #include <linux/backlight.h> +#include <linux/dmi.h> #include <linux/slab.h> #include <linux/xarray.h> #include <linux/power_supply.h> @@ -1297,6 +1298,103 @@ static void amdgpu_acpi_dgpu_notify(acpi_handle handle, u32 event, void *data) pm_request_resume(adev->dev); }+/*+ * Some AMD CPM D3cold tables handle the dGPU hotplug GPE with + * Notify(\_SB.PCI0.GPP0.VGA, 0x81), but on boards where the dGPU sits behind + * an on-package PCIe switch the node is \_SB.PCI0.GPP0.SWUS.SWDS.VGA_, so the + * _Lxx method aborts with AE_NOT_FOUND and the dGPU is never woken. On those + * boards take over the GPE and do what the notify would have done. + */ +struct amdgpu_acpi_hpd_gpe_quirk { + const char *notify_path; + u32 gpe; +}; + +static const struct amdgpu_acpi_hpd_gpe_quirk g513qy_hpd_gpe = { + .notify_path = "\\_SB.PCI0.GPP0.VGA", + .gpe = 0x05, +}; + +static const struct dmi_system_id amdgpu_acpi_hpd_gpe_quirks[] = { + { + .ident = "ASUS ROG Strix G513QY", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, "G513QY"), + }, + .driver_data = (void *)&g513qy_hpd_gpe, + }, + {} +}; + +/* GPE handlers run in interrupt context; the GPE stays disabled until the work re-arms it */ +static u32 amdgpu_acpi_hpd_gpe_handler(acpi_handle gpe_device, u32 gpe, void *data) +{ + struct amdgpu_device *adev = data; + + pm_request_resume(adev->dev); + schedule_delayed_work(&adev->acpi_hpd_gpe_work, msecs_to_jiffies(500)); + + return 0; +} + +/* + * Re-arm after a delay: the GPE is level triggered and may stay asserted + * while the dGPU powers up, so this bounds the rate at which it refires. + */ +static void amdgpu_acpi_hpd_gpe_work(struct work_struct *work) +{ + struct amdgpu_device *adev = + container_of(work, struct amdgpu_device, acpi_hpd_gpe_work.work); + + acpi_finish_gpe(NULL, adev->acpi_hpd_gpe); +} + +static void amdgpu_acpi_hpd_gpe_init(struct amdgpu_device *adev) +{ + const struct amdgpu_acpi_hpd_gpe_quirk *quirk; + const struct dmi_system_id *id; + acpi_handle handle; + + id = dmi_first_match(amdgpu_acpi_hpd_gpe_quirks); + if (!id) + return; + quirk = id->driver_data; + + /* Firmware (or a table override) already provides the notify target */ + if (ACPI_SUCCESS(acpi_get_handle(NULL, quirk->notify_path, &handle))) + return; + + adev->acpi_hpd_gpe = quirk->gpe; + INIT_DELAYED_WORK(&adev->acpi_hpd_gpe_work, amdgpu_acpi_hpd_gpe_work); + if (ACPI_FAILURE(acpi_install_gpe_handler(NULL, quirk->gpe, + ACPI_GPE_LEVEL_TRIGGERED, + amdgpu_acpi_hpd_gpe_handler, + adev))) + return; + if (ACPI_FAILURE(acpi_enable_gpe(NULL, quirk->gpe))) { + acpi_remove_gpe_handler(NULL, quirk->gpe, + amdgpu_acpi_hpd_gpe_handler); + return; + } + + adev->acpi_hpd_gpe_installed = true; + dev_info(adev->dev, "handling dGPU hotplug GPE 0x%02x (%s missing)\n", + quirk->gpe, quirk->notify_path); +} + +static void amdgpu_acpi_hpd_gpe_fini(struct amdgpu_device *adev) +{ + if (!adev->acpi_hpd_gpe_installed) + return; + + acpi_disable_gpe(NULL, adev->acpi_hpd_gpe); + cancel_delayed_work_sync(&adev->acpi_hpd_gpe_work); + acpi_remove_gpe_handler(NULL, adev->acpi_hpd_gpe, + amdgpu_acpi_hpd_gpe_handler); + adev->acpi_hpd_gpe_installed = false; +} + /* Call all ACPI methods here */ /** * amdgpu_acpi_init - init driver acpi support @@ -1343,12 +1441,14 @@ int amdgpu_acpi_init(struct amdgpu_device *adev) register_acpi_notifier(&adev->acpi_nb);if (!(adev->flags & AMD_IS_APU) && ACPI_COMPANION(adev->dev) &&- adev->pm.rpm_mode == AMDGPU_RUNPM_BOCO) + adev->pm.rpm_mode == AMDGPU_RUNPM_BOCO) { adev->acpi_dgpu_notify = !acpi_dev_install_notify_handler(ACPI_COMPANION(adev->dev), ACPI_DEVICE_NOTIFY, amdgpu_acpi_dgpu_notify, adev); + amdgpu_acpi_hpd_gpe_init(adev); + }return 0;} @@ -1369,6 +1469,7 @@ void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps) */ void amdgpu_acpi_fini(struct amdgpu_device *adev) { + amdgpu_acpi_hpd_gpe_fini(adev); if (adev->acpi_dgpu_notify) { acpi_dev_remove_notify_handler(ACPI_COMPANION(adev->dev), ACPI_DEVICE_NOTIFY,
