On Wed, 12 Aug 2026, Andre Eikmeyer wrote:

> From: Atharva Tiwari <[email protected]>
> 
> The discrete GPU on the MacBookPro15,1 does not return after the legacy

"return" seems wrong term in this sentence.

> GMUX power-on sequence. PCI configuration space remains inaccessible, so
> runtime PM cannot provide usable hybrid graphics with the integrated GPU
> as primary.
> 
> Evaluate the firmware PWG1 and PWG3 link methods around the GMUX
> transition and wait for PCI configuration space before completing power-on.
> Keep the sequence limited to the MacBookPro15,1 and retain the existing
> path for every other model.



> This was tested on both the 2018 and 2019 MacBookPro15,1 revisions with the
> integrated GPU as primary. The discrete GPU transitions between DynOff and
> DynPwr, and external displays work across the transitions.
> 
> Co-developed-by: Andre Eikmeyer <[email protected]>
> Signed-off-by: Andre Eikmeyer <[email protected]>
> Signed-off-by: Atharva Tiwari <[email protected]>
> ---
>  drivers/platform/x86/apple-gmux.c | 86 ++++++++++++++++++++++++++++---
>  1 file changed, 80 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/platform/x86/apple-gmux.c 
> b/drivers/platform/x86/apple-gmux.c
> index 9c728ac..ccb059a 100644
> --- a/drivers/platform/x86/apple-gmux.c
> +++ b/drivers/platform/x86/apple-gmux.c
> @@ -22,6 +22,7 @@
>  #include <linux/pci.h>
>  #include <linux/vga_switcheroo.h>
>  #include <linux/debugfs.h>
> +#include <linux/dmi.h>
>  #include <acpi/video.h>
>  #include <asm/io.h>
>  
> @@ -74,6 +75,8 @@ struct apple_gmux_data {
>       enum vga_switcheroo_client_id switch_state_external;
>       enum vga_switcheroo_state power_state;
>       struct completion powerchange_done;
> +     struct pci_dev *discrete_pdev;
> +     bool use_pwg_power_sequence;
>  
>       /* debugfs data */
>       u8 selected_port;
> @@ -82,6 +85,34 @@ struct apple_gmux_data {
>  
>  static struct apple_gmux_data *apple_gmux_data;
>  
> +static int gmux_call_pwg(struct apple_gmux_data *gmux_data,
> +                      const char *method)
> +{
> +     acpi_handle handle = ACPI_HANDLE(&gmux_data->discrete_pdev->dev);
> +     unsigned long long result;
> +     acpi_status status;
> +
> +     if (!handle)
> +             return -ENODEV;
> +
> +     status = acpi_evaluate_integer(handle, (acpi_string)method, NULL,
> +                                    &result);
> +     if (ACPI_FAILURE(status)) {
> +             dev_err(&gmux_data->discrete_pdev->dev,
> +                     "failed to evaluate %s: %s\n", method,
> +                     acpi_format_exception(status));

Add hinclude for dev_err().

> +             return -EIO;
> +     }
> +
> +     if (result) {
> +             dev_err(&gmux_data->discrete_pdev->dev,
> +                     "%s failed: %llu\n", method, result);
> +             return -EIO;
> +     }
> +
> +     return 0;
> +}
> +
>  struct apple_gmux_config {
>       u8 (*read8)(struct apple_gmux_data *gmux_data, int port);
>       void (*write8)(struct apple_gmux_data *gmux_data, int port, u8 val);
> @@ -510,14 +541,49 @@ 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)
>  {
> +     int ret;
> +
>       reinit_completion(&gmux_data->powerchange_done);
>  
>       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->use_pwg_power_sequence &&
> +                 gmux_data->discrete_pdev) {
> +                     u16 vendor;
> +                     int i;
> +
> +                     gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 2);
> +                     msleep(100);
> +                     gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
> +
> +                     ret = gmux_call_pwg(gmux_data, "PWG1");
> +                     if (ret)
> +                             return ret;
> +
> +                     for (i = 0; i < 1000; i++) {
> +                             pci_read_config_word(gmux_data->discrete_pdev,
> +                                                  PCI_VENDOR_ID, &vendor);
> +                             if (vendor != 0xffff)
> +                                     break;
> +                             usleep_range(1000, 2000);
> +                     }
> +                     if (vendor == 0xffff) {

Does this duplicate pci_bus_read_dev_vendor_id() ?

> +                             dev_err(&gmux_data->discrete_pdev->dev,
> +                                     "timed out waiting for PCI config 
> space\n");
> +                             return -ETIMEDOUT;
> +                     }
> +
> +                     ret = gmux_call_pwg(gmux_data, "PWG3");
> +                     if (ret)
> +                             return ret;
> +             } 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);
> +             if (gmux_data->use_pwg_power_sequence)
> +                     usleep_range(10000, 11000);

Where do these numbers come from?

>               gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
>               pr_debug("Discrete card powered down\n");
>       }
> @@ -549,11 +615,16 @@ static enum vga_switcheroo_client_id 
> gmux_get_client_id(struct pci_dev *pdev)
>        */
>       if (pdev->vendor == PCI_VENDOR_ID_INTEL)
>               return VGA_SWITCHEROO_IGD;
> -     else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
> -              pdev->device == 0x0863)
> +     if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && pdev->device == 0x0863)
>               return VGA_SWITCHEROO_IGD;
> -     else
> -             return VGA_SWITCHEROO_DIS;
> +
> +     if (apple_gmux_data->use_pwg_power_sequence &&
> +         apple_gmux_data->discrete_pdev != pdev) {
> +             pci_dev_put(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 = {
> @@ -803,6 +874,8 @@ 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->use_pwg_power_sequence = type == APPLE_GMUX_TYPE_MMIO &&
> +             dmi_match(DMI_PRODUCT_NAME, "MacBookPro15,1");

This formatting is hard to read.

>       switch (type) {
>       case APPLE_GMUX_TYPE_MMIO:
> @@ -1009,6 +1082,7 @@ static void gmux_remove(struct pnp_dev *pnp)
>       } else
>               release_region(gmux_data->iostart, gmux_data->iolen);
>       apple_gmux_data = NULL;
> +     pci_dev_put(gmux_data->discrete_pdev);
>       kfree(gmux_data);
>  }
>  
> 

-- 
 i.

Reply via email to