[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