The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Andre Eikmeyer <dev@deq.rocks>
To: platform-driver-x86@vger.kernel.org,
	amd-gfx@lists.freedesktop.org, linux-sound@vger.kernel.org
Cc: "Atharva Tiwari" <atharvatiwarilinuxdev@gmail.com>,
	"Hans de Goede" <hansg@kernel.org>,
	"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
	"Alex Deucher" <alexander.deucher@amd.com>,
	"Christian König" <christian.koenig@amd.com>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Kenneth Feng" <kenneth.feng@amd.com>,
	"Jaroslav Kysela" <perex@perex.cz>,
	"Takashi Iwai" <tiwai@suse.de>, "Takashi Iwai" <tiwai@suse.com>,
	"Harry Wentland" <harry.wentland@amd.com>,
	"Leo Li" <sunpeng.li@amd.com>,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	"Andre Eikmeyer" <dev@deq.rocks>
Subject: [PATCH v3 1/3] platform/x86: apple-gmux: add MacBookPro15,1 dGPU power sequence
Date: Wed, 12 Aug 2026 16:47:48 +0200	[thread overview]
Message-ID: <20260812144750.36797-2-dev@deq.rocks> (raw)
In-Reply-To: <20260812144750.36797-1-dev@deq.rocks>

From: Atharva Tiwari <atharvatiwarilinuxdev@gmail.com>

The discrete GPU on the MacBookPro15,1 does not return after the legacy
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 <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 | 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));
+		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) {
+				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);
 		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");
 
 	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);
 }
 
-- 
2.55.0


  reply	other threads:[~2026-08-12 14:48 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-12 12:22 [PATCH 0/3] Apple GMUX hybrid graphics support for MacBookPro15,1 Andre Eikmeyer
2026-08-12 12:22 ` [PATCH 1/3] platform/x86: apple-gmux: add MacBookPro15,1 dGPU power sequence Andre Eikmeyer
2026-08-12 12:22 ` [PATCH 2/3] drm/amdgpu: add Apple GMUX runtime PM support Andre Eikmeyer
2026-08-12 12:22 ` [PATCH 3/3] ALSA: hda: allow direct complete with a powered-off GPU Andre Eikmeyer
2026-08-12 13:45   ` Takashi Iwai
2026-08-12 13:53     ` Takashi Iwai
2026-08-12 14:03   ` [PATCH v2 " Andre Eikmeyer
2026-08-12 14:23     ` [PATCH v3 " Andre Eikmeyer
2026-08-12 14:30       ` Takashi Iwai
2026-08-12 14:04   ` [PATCH " Christian König
2026-08-12 14:42 ` [PATCH v2 0/3] Apple GMUX hybrid graphics support for MacBookPro15,1 Andre Eikmeyer
2026-08-12 14:42   ` [PATCH v2 1/3] platform/x86: apple-gmux: add MacBookPro15,1 dGPU power sequence Andre Eikmeyer
2026-08-12 14:42   ` [PATCH v2 2/3] drm/amdgpu: add Apple GMUX runtime PM support Andre Eikmeyer
2026-08-12 14:42   ` [PATCH v2 3/3] ALSA: hda: allow direct complete with a powered-off GPU Andre Eikmeyer
2026-08-12 14:47   ` [PATCH v3 0/3] Apple GMUX hybrid graphics support for MacBookPro15,1 Andre Eikmeyer
2026-08-12 14:47     ` Andre Eikmeyer [this message]
2026-08-12 14:47     ` [PATCH v3 2/3] drm/amdgpu: add Apple GMUX runtime PM support Andre Eikmeyer
2026-08-12 14:47     ` [PATCH v3 3/3] ALSA: hda: allow direct complete with a powered-off GPU Andre Eikmeyer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260812144750.36797-2-dev@deq.rocks \
    --to=dev@deq.rocks \
    --cc=airlied@gmail.com \
    --cc=alexander.deucher@amd.com \
    --cc=amd-gfx@lists.freedesktop.org \
    --cc=atharvatiwarilinuxdev@gmail.com \
    --cc=christian.koenig@amd.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=hansg@kernel.org \
    --cc=harry.wentland@amd.com \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=kenneth.feng@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=platform-driver-x86@vger.kernel.org \
    --cc=simona@ffwll.ch \
    --cc=sunpeng.li@amd.com \
    --cc=tiwai@suse.com \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox