[Nouveau] [PATCH RFC] nouveau: Add support for Gmux _DSM method

Pierre Moreau pierre.morrow at free.fr
Fri Jan 16 17:18:48 PST 2015


This patch certainly needs some more work, but I'd like to get some comments.

Not sure if it is really related to the gmux or if it is a different Optimus
_DSM version.

I tested it on my laptop (MCP79/7A + G96) and the G96 successfully goes to
sleep, however it is still marked as PWR in vga_switcheroo, despite
`vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF)` being called.
---
 drm/nouveau_acpi.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 drm/nouveau_acpi.h |  2 ++
 drm/nouveau_drm.c  |  4 +--
 3 files changed, 94 insertions(+), 6 deletions(-)

diff --git a/drm/nouveau_acpi.c b/drm/nouveau_acpi.c
index 6224246..a71229a 100644
--- a/drm/nouveau_acpi.c
+++ b/drm/nouveau_acpi.c
@@ -29,6 +29,8 @@
 
 #define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)
 
+#define NOUVEAU_DSM_GMUX_UNK7 0x7
+
 /* result of the optimus caps function */
 #define OPTIMUS_ENABLED (1 << 0)
 #define OPTIMUS_STATUS_MASK (3 << 3)
@@ -45,6 +47,7 @@
 static struct nouveau_dsm_priv {
 	bool dsm_detected;
 	bool optimus_detected;
+	bool gmux_detected;
 	acpi_handle dhandle;
 	acpi_handle rom_handle;
 } nouveau_dsm_priv;
@@ -57,8 +60,13 @@ bool nouveau_is_v1_dsm(void) {
 	return nouveau_dsm_priv.dsm_detected;
 }
 
-#define NOUVEAU_DSM_HAS_MUX 0x1
-#define NOUVEAU_DSM_HAS_OPT 0x2
+bool nouveau_has_gmux_dsm(void) {
+	return nouveau_dsm_priv.gmux_detected;
+}
+
+#define NOUVEAU_DSM_HAS_MUX  0x1
+#define NOUVEAU_DSM_HAS_OPT  0x2
+#define NOUVEAU_DSM_HAS_GMUX 0x4
 
 #ifdef CONFIG_VGA_SWITCHEROO
 static const char nouveau_dsm_muid[] = {
@@ -71,6 +79,11 @@ static const char nouveau_op_dsm_muid[] = {
 	0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0,
 };
 
+static const char nouveau_gmux_dsm_muid[] = {
+	0xA6, 0x69, 0x86, 0x99, 0xE9, 0x8B, 0xFB, 0x49,
+	0xBD, 0xDB, 0x51, 0xA1, 0xEF, 0xE1, 0x9C, 0x3D,
+};
+
 static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
 {
 	int i;
@@ -212,6 +225,63 @@ static struct vga_switcheroo_handler nouveau_dsm_handler = {
 	.get_client_id = nouveau_dsm_get_client_id,
 };
 
+static int nouveau_gmux_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
+{
+	int i;
+	union acpi_object *obj;
+	char args_buff[4];
+	union acpi_object argv4 = {
+		.buffer.type = ACPI_TYPE_BUFFER,
+		.buffer.length = 4,
+		.buffer.pointer = args_buff
+	};
+
+	/* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */
+	for (i = 0; i < 4; i++)
+		args_buff[i] = (arg >> i * 8) & 0xFF;
+
+	*result = 0;
+	obj = acpi_evaluate_dsm_typed(handle, nouveau_gmux_dsm_muid, 0x00000101,
+				      func, &argv4, ACPI_TYPE_BUFFER);
+	if (!obj) {
+		acpi_handle_info(handle, "failed to evaluate _DSM\n");
+		return AE_ERROR;
+	} else {
+		if (obj->buffer.length == 4) {
+			*result |= obj->buffer.pointer[0];
+			*result |= (obj->buffer.pointer[1] << 8);
+			*result |= (obj->buffer.pointer[2] << 16);
+			*result |= (obj->buffer.pointer[3] << 24);
+		}
+		ACPI_FREE(obj);
+	}
+
+	return 0;
+}
+
+/*
+ * On some platforms, _DSM(nouveau_op_dsm_muid, func0) has special
+ * requirements on the fourth parameter, so a private implementation
+ * instead of using acpi_check_dsm().
+ */
+static int nouveau_check_gmux_dsm(acpi_handle handle)
+{
+	int result;
+
+	/*
+	 * Function 0 returns a Buffer containing available functions.
+	 * The args parameter is ignored for function 0, so just put 0 in it
+	 */
+	if (nouveau_gmux_dsm(handle, 0, 0, &result))
+		return 0;
+
+	/*
+	 * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported.
+	 * If the n-th bit is enabled, function n is supported
+	 */
+	return result & 1 && result & (1 << NOUVEAU_DSM_GMUX_UNK7);
+}
+
 static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
 {
 	acpi_handle dhandle;
@@ -231,6 +301,9 @@ static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
 	if (nouveau_check_optimus_dsm(dhandle))
 		retval |= NOUVEAU_DSM_HAS_OPT;
 
+	if (nouveau_check_gmux_dsm(dhandle))
+		retval |= NOUVEAU_DSM_HAS_GMUX;
+
 	if (retval & NOUVEAU_DSM_HAS_OPT) {
 		uint32_t result;
 		nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
@@ -253,6 +326,7 @@ static bool nouveau_dsm_detect(void)
 	struct pci_dev *pdev = NULL;
 	int has_dsm = 0;
 	int has_optimus = 0;
+	int has_gmux = 0;
 	int vga_count = 0;
 	bool guid_valid;
 	int retval;
@@ -273,6 +347,8 @@ static bool nouveau_dsm_detect(void)
 			has_dsm |= 1;
 		if (retval & NOUVEAU_DSM_HAS_OPT)
 			has_optimus = 1;
+		if (retval & NOUVEAU_DSM_HAS_GMUX)
+			has_gmux = 1;
 	}
 
 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
@@ -283,9 +359,11 @@ static bool nouveau_dsm_detect(void)
 			has_dsm |= 1;
 		if (retval & NOUVEAU_DSM_HAS_OPT)
 			has_optimus = 1;
+		if (retval & NOUVEAU_DSM_HAS_GMUX)
+			has_gmux = 1;
 	}
 
-	/* find the optimus DSM or the old v1 DSM */
+	/* find the optimus DSM, the old v1 DSM or the gmux DSM */
 	if (has_optimus == 1) {
 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
 			&buffer);
@@ -300,6 +378,13 @@ static bool nouveau_dsm_detect(void)
 			acpi_method_name);
 		nouveau_dsm_priv.dsm_detected = true;
 		ret = true;
+	} else if (has_gmux == 1) {
+		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
+			&buffer);
+		printk(KERN_INFO "VGA switcheroo: detected Gmux DSM switching method %s handle\n",
+			acpi_method_name);
+		nouveau_dsm_priv.gmux_detected = true;
+		ret = true;
 	}
 
 
@@ -311,7 +396,8 @@ void nouveau_register_dsm_handler(void)
 	bool r;
 
 	r = nouveau_dsm_detect();
-	if (!r)
+	/* Gmux will register its own handler to vga_switcheroo */
+	if (!r || nouveau_dsm_priv.gmux_detected)
 		return;
 
 	vga_switcheroo_register_handler(&nouveau_dsm_handler);
diff --git a/drm/nouveau_acpi.h b/drm/nouveau_acpi.h
index 74acf0f..3668dce 100644
--- a/drm/nouveau_acpi.h
+++ b/drm/nouveau_acpi.h
@@ -6,6 +6,7 @@
 #if defined(CONFIG_ACPI) && defined(CONFIG_X86)
 bool nouveau_is_optimus(void);
 bool nouveau_is_v1_dsm(void);
+bool nouveau_has_gmux_dsm(void);
 void nouveau_register_dsm_handler(void);
 void nouveau_unregister_dsm_handler(void);
 void nouveau_switcheroo_optimus_dsm(void);
@@ -15,6 +16,7 @@ void *nouveau_acpi_edid(struct drm_device *, struct drm_connector *);
 #else
 static inline bool nouveau_is_optimus(void) { return false; };
 static inline bool nouveau_is_v1_dsm(void) { return false; };
+static inline bool nouveau_has_gmux_dsm(void) { return false; };
 static inline void nouveau_register_dsm_handler(void) {}
 static inline void nouveau_unregister_dsm_handler(void) {}
 static inline void nouveau_switcheroo_optimus_dsm(void) {}
diff --git a/drm/nouveau_drm.c b/drm/nouveau_drm.c
index 9eb90a6..414acc4 100644
--- a/drm/nouveau_drm.c
+++ b/drm/nouveau_drm.c
@@ -719,7 +719,7 @@ nouveau_pmops_runtime_suspend(struct device *dev)
 	}
 
 	/* are we optimus enabled? */
-	if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) {
+	if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm() && !nouveau_has_gmux_dsm()) {
 		DRM_DEBUG_DRIVER("failing to power off - not optimus\n");
 		pm_runtime_forbid(dev);
 		return -EBUSY;
@@ -780,7 +780,7 @@ nouveau_pmops_runtime_idle(struct device *dev)
 	}
 
 	/* are we optimus enabled? */
-	if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) {
+	if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm() && !nouveau_has_gmux_dsm()) {
 		DRM_DEBUG_DRIVER("failing to power off - not optimus\n");
 		pm_runtime_forbid(dev);
 		return -EBUSY;
-- 
2.2.2



More information about the Nouveau mailing list