From mjg@redhat.com Fri Sep  4 22:58:45 2009
Return-Path: <anssi.hannula+caf_=anssi=onse.fi@gmail.com>
X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on delta.onse.fi
X-Spam-Level: 
X-Spam-Status: No, score=0.0 required=5.0 tests=BAYES_50,SPF_PASS autolearn=no
	version=3.2.5
X-Original-To: anssi@onse.fi
Delivered-To: anssi@onse.fi
Received: from mail-bw0-f216.google.com (mail-bw0-f216.google.com [209.85.218.216])
	by mail.onse.fi (Postfix) with ESMTP id 1517110DE45B
	for <anssi@onse.fi>; Thu, 10 Sep 2009 02:47:01 +0300 (EEST)
Received: by mail-bw0-f216.google.com with SMTP id 12so1101304bwz.20
        for <anssi@onse.fi>; Wed, 09 Sep 2009 16:47:00 -0700 (PDT)
Received: by 10.223.2.75 with SMTP id 11mr809308fai.54.1252540020733;
        Wed, 09 Sep 2009 16:47:00 -0700 (PDT)
X-Forwarded-To: anssi@onse.fi
X-Forwarded-For: anssi.hannula@gmail.com anssi@onse.fi
Delivered-To: anssi.hannula@gmail.com
Received: by 10.223.111.11 with SMTP id q11cs545528fap;
        Wed, 9 Sep 2009 16:47:00 -0700 (PDT)
Received: by 10.210.96.1 with SMTP id t1mr218567ebb.17.1252540019953;
        Wed, 09 Sep 2009 16:46:59 -0700 (PDT)
Received: from seuraava.iki.fi (seuraava.iki.fi [212.16.98.52])
        by mx.google.com with ESMTP id 6si2471565ewy.100.2009.09.09.16.46.59;
        Wed, 09 Sep 2009 16:46:59 -0700 (PDT)
Received-SPF: neutral (google.com: 212.16.98.52 is neither permitted nor denied by best guess record for domain of nouveau-bounces@lists.freedesktop.org) client-ip=212.16.98.52;
Authentication-Results: mx.google.com; spf=neutral (google.com: 212.16.98.52 is neither permitted nor denied by best guess record for domain of nouveau-bounces@lists.freedesktop.org) smtp.mail=nouveau-bounces@lists.freedesktop.org
Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177])
	by seuraava.iki.fi (8.14.2/8.14.2) with ESMTP id n89NkuTl017745;
	Thu, 10 Sep 2009 02:46:58 +0300 (EEST)
Received: from gabe.freedesktop.org (localhost [127.0.0.1])
	by gabe.freedesktop.org (Postfix) with ESMTP id 2FD87A02F2;
	Wed,  9 Sep 2009 16:46:55 -0700 (PDT)
X-Original-To: nouveau@lists.freedesktop.org
Delivered-To: nouveau@lists.freedesktop.org
Received: from cavan.codon.org.uk (cavan.codon.org.uk [93.93.128.6])
	by gabe.freedesktop.org (Postfix) with ESMTP id 84D529E8B3
	for <nouveau@lists.freedesktop.org>;
	Fri,  4 Sep 2009 12:52:39 -0700 (PDT)
Received: from 146-115-120-20.c3-0.smr-ubr1.sbo-smr.ma.cable.rcn.com
	([146.115.120.20] helo=localhost.localdomain)
	by cavan.codon.org.uk with esmtpsa (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32)
	(Exim 4.69) (envelope-from <mjg@redhat.com>)
	id 1Mjepm-0007pn-9V; Fri, 04 Sep 2009 20:52:34 +0100
From: Matthew Garrett <mjg@redhat.com>
To: nouveau@lists.freedesktop.org
Date: Fri,  4 Sep 2009 15:58:45 -0400
Message-Id: <1252094325-9865-1-git-send-email-mjg@redhat.com>
X-Mailer: git-send-email 1.6.4.1
X-SA-Do-Not-Run: Yes
X-SA-Exim-Connect-IP: 146.115.120.20
X-SA-Exim-Mail-From: mjg@redhat.com
X-SA-Exim-Scanned: No (on cavan.codon.org.uk); SAEximRunCond expanded to false
X-Mailman-Approved-At: Wed, 09 Sep 2009 16:46:52 -0700
Cc: Matthew Garrett <mjg@redhat.com>
Subject: [Nouveau] [PATCH] novueau: Preliminary support for switchable
	graphics
X-BeenThere: nouveau@lists.freedesktop.org
X-Mailman-Version: 2.1.9
Precedence: list
List-Id: Nouveau development list <nouveau.lists.freedesktop.org>
List-Unsubscribe: <http://lists.freedesktop.org/mailman/listinfo/nouveau>,
	<mailto:nouveau-request@lists.freedesktop.org?subject=unsubscribe>
List-Archive: <http://lists.freedesktop.org/archives/nouveau>
List-Post: <mailto:nouveau@lists.freedesktop.org>
List-Help: <mailto:nouveau-request@lists.freedesktop.org?subject=help>
List-Subscribe: <http://lists.freedesktop.org/mailman/listinfo/nouveau>,
	<mailto:nouveau-request@lists.freedesktop.org?subject=subscribe>
MIME-Version: 1.0
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Sender: nouveau-bounces@lists.freedesktop.org
Errors-To: nouveau-bounces@lists.freedesktop.org
X-Length: 10938
X-UID: 3566

Some recent laptops have switchable graphics, providing a discrete nvidia
gpu and either an integrated Intel gpu or a lower-powered nvidia gpu. An
ACPI interface is provided for controlling these devices. BIOSes will
often leave both gpus running, wasting power. This initial support simply
turns off whichever gpu is currently inactive. In future it will be
extended to allow for runtime switching of gpu.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
---
 drivers/gpu/drm/nouveau/Makefile        |    1 +
 drivers/gpu/drm/nouveau/nouveau_acpi.c  |  127 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/nouveau/nouveau_drv.h   |   16 ++++
 drivers/gpu/drm/nouveau/nouveau_state.c |    5 +
 4 files changed, 149 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/nouveau/nouveau_acpi.c

diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 5a46cdd..a4e8223 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -23,5 +23,6 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
 
 nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
 nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
+nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
 
 obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
new file mode 100644
index 0000000..714e5fb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -0,0 +1,127 @@
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_sarea.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nv50_display.h"
+
+#define NOUVEAU_DSM_SUPPORTED 0x00
+#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
+
+#define NOUVEAU_DSM_ACTIVE 0x01
+#define NOUVEAU_DSM_ACTIVE_QUERY 0x00
+
+#define NOUVEAU_DSM_LED 0x02
+#define NOUVEAU_DSM_LED_STATE 0x00
+#define NOUVEAU_DSM_LED_OFF 0x10
+#define NOUVEAU_DSM_LED_STAMINA 0x11
+#define NOUVEAU_DSM_LED_SPEED 0x12
+
+#define NOUVEAU_DSM_POWER 0x03
+#define NOUVEAU_DSM_POWER_STATE 0x00
+#define NOUVEAU_DSM_POWER_SPEED 0x01
+#define NOUVEAU_DSM_POWER_STAMINA 0x02
+
+static int nvidia_dsm(struct pci_dev *dev, int func, int arg, int *result)
+{
+	static char muid[] = {
+		0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
+		0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
+	};
+
+	struct acpi_handle *handle;
+	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_object_list input;
+	union acpi_object params[4];
+	union acpi_object *obj;
+	int err;
+
+	handle = DEVICE_ACPI_HANDLE(&dev->dev);
+
+	if (!handle)
+		return -ENODEV;
+
+	input.count = 4;
+	input.pointer = params;
+	params[0].type = ACPI_TYPE_BUFFER;
+	params[0].buffer.length = sizeof(muid);
+	params[0].buffer.pointer = (char *)muid;
+	params[1].type = ACPI_TYPE_INTEGER;
+	params[1].integer.value = 0x00000102;
+	params[2].type = ACPI_TYPE_INTEGER;
+	params[2].integer.value = func;
+	params[3].type = ACPI_TYPE_INTEGER;
+	params[3].integer.value = arg;
+
+	err = acpi_evaluate_object(handle, "_DSM", &input, &output);
+	if (err) {
+		printk(KERN_ERR "nvidia-control: failed to evaluate _DSM: %d\n",
+		       err);
+		return err;
+	}
+
+	obj = (union acpi_object *)output.pointer;
+
+	if (obj->type == ACPI_TYPE_INTEGER)
+		if (obj->integer.value == 0x80000002)
+			return -ENODEV;
+
+	if (obj->type == ACPI_TYPE_BUFFER) {
+		if (obj->buffer.length == 4 && result) {
+			*result = 0;
+			*result |= obj->buffer.pointer[0];
+			*result |= (obj->buffer.pointer[1] << 8);
+			*result |= (obj->buffer.pointer[2] << 16);
+			*result |= (obj->buffer.pointer[3] << 24);
+		}
+	}
+
+	kfree(output.pointer);
+	return 0;
+}
+
+int nouveau_hybrid_setup(struct drm_device *dev)
+{
+	struct pci_dev *pdev = dev->pdev;
+	int result;
+
+	if (nvidia_dsm(pdev, NOUVEAU_DSM_ACTIVE, NOUVEAU_DSM_ACTIVE_QUERY,
+		       &result))
+		return -ENODEV;
+
+	printk(KERN_INFO "nouveau: _DSM hardware status gave 0x%x\n", result);
+
+	if (result &= 0x1) {	/* Stamina mode - disable the external GPU */
+		nvidia_dsm(pdev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA,
+			   NULL);
+		nvidia_dsm(pdev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA,
+			   NULL);
+	} else {		/* Ensure that the external GPU is enabled */
+		nvidia_dsm(pdev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL);
+		nvidia_dsm(pdev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED,
+			   NULL);
+	}
+
+	return 0;
+}
+
+bool nouveau_dsm_probe(struct drm_device *dev)
+{
+	struct pci_dev *pdev = dev->pdev;
+	int support = 0;
+	
+	if (nvidia_dsm(pdev, NOUVEAU_DSM_SUPPORTED,
+		       NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support))
+		return false;
+
+	if (!support)
+		return false;
+
+	return true;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 4a1efa1..f26d6ff 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -565,6 +565,7 @@ struct drm_nouveau_private {
 	} susres;
 
 	struct backlight_device *backlight;
+	bool acpi_dsm;
 
 	struct nouveau_channel *evo;
 };
@@ -748,6 +749,21 @@ extern struct ttm_backend *nouveau_sgdma_init_ttm(struct drm_device *);
 extern int  nouveau_dma_init(struct nouveau_channel *);
 extern int  nouveau_dma_wait(struct nouveau_channel *, int size);
 
+/* nouveau_acpi.c */
+#ifdef CONFIG_ACPI
+extern int nouveau_hybrid_setup(struct drm_device *dev);
+extern bool nouveau_dsm_probe(struct drm_device *dev);
+#else
+static inline int nouveau_hybrid_setup(struct drm_device *dev)
+{
+	return 0;
+}
+static inline bool nouveau_dsm_probe(struct drm_device *dev)
+{
+	return false;
+}
+#endif
+
 /* nouveau_backlight.c */
 #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
 extern int nouveau_backlight_init(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 9abc7c8..4d91887 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -497,6 +497,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
 	NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
 		 dev->pci_vendor, dev->pci_device, dev->pdev->class);
 
+	dev_priv->acpi_dsm = nouveau_dsm_probe(dev);
+
+	if (dev_priv->acpi_dsm)
+		nouveau_hybrid_setup(dev);
+
 	/* resource 0 is mmio regs */
 	/* resource 1 is linear FB */
 	/* resource 2 is RAMIN (mmio regs + 0x1000000) */
-- 
1.6.4.1

_______________________________________________
Nouveau mailing list
Nouveau@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/nouveau

