Skip to contentSkip to conversation

nouveau runtime PM support (take two)

PATCH New Dave Airlie 1 participant 3 messages Sep 11, 2012

Subsystems (3)
lore
Share & tools
lore

Dave Airlie

author maintainer
#0 lore raw

So I started investigating the kernel runtime PM support as per Alan's suggestion. An API with lots of interface docs and not enough examples :-)

So I've mostly implemented autosuspend for nouveau with this, it could still be pushed down a lot further into the code I suspect.

Also this doesn't actually turn the power switch off since runtime PM currently have no hook to do that, so it only D3s the GPU.

Dave.

author maintainer
#1 lore raw

PATCH 1/2 nouveau: convert to dev_pm_ops

From: Dave Airlie <[email protected]>

---
 drivers/gpu/drm/nouveau/nouveau_drm.c | 93 +++++++++++++++++++++++++----------
 1 file changed, 67 insertions(+), 26 deletions(-)
drivers/gpu/drm/nouveau/nouveau_drm.c +67 -26
index 9fb56b3..bb51a00 100644
@@ -391,17 +391,12 @@ nouveau_drm_remove(struct pci_dev *pdev)
 }
 
 int
-nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
+nouveau_do_suspend(struct drm_device *dev)
 {
-	struct drm_device *dev = pci_get_drvdata(pdev);
 	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct nouveau_cli *cli;
 	int ret;
 
-	if (dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
-	    pm_state.event == PM_EVENT_PRETHAW)
-		return 0;
-
 	NV_INFO(drm, "suspending fbcon...\n");
 	nouveau_fbcon_set_suspend(dev, 1);
 
@@ -425,13 +420,6 @@ nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
 		goto fail_client;
 
 	nouveau_agp_fini(drm);
-
-	pci_save_state(pdev);
-	if (pm_state.event == PM_EVENT_SUSPEND) {
-		pci_disable_device(pdev);
-		pci_set_power_state(pdev, PCI_D3hot);
-	}
-
 	return 0;
 
 fail_client:
@@ -444,24 +432,33 @@ fail_client:
 	return ret;
 }
 
-int
-nouveau_drm_resume(struct pci_dev *pdev)
+int nouveau_pmops_suspend(struct device *dev)
 {
-	struct drm_device *dev = pci_get_drvdata(pdev);
-	struct nouveau_drm *drm = nouveau_drm(dev);
-	struct nouveau_cli *cli;
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct drm_device *drm_dev = pci_get_drvdata(pdev);
 	int ret;
 
-	if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+	if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
 		return 0;
 
-	NV_INFO(drm, "re-enabling device...\n");
-	pci_set_power_state(pdev, PCI_D0);
-	pci_restore_state(pdev);
-	ret = pci_enable_device(pdev);
+	ret = nouveau_do_suspend(drm_dev);
 	if (ret)
 		return ret;
-	pci_set_master(pdev);
+
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, PCI_D3hot);
+
+	return 0;
+}
+
+int
+nouveau_do_resume(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_drm(dev);
+	struct nouveau_cli *cli;
+
+	NV_INFO(drm, "re-enabling device...\n");
 
 	nouveau_agp_reset(drm);
 
@@ -482,6 +479,42 @@ nouveau_drm_resume(struct pci_dev *pdev)
 	return 0;
 }
 
+int nouveau_pmops_resume(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct drm_device *drm_dev = pci_get_drvdata(pdev);
+	int ret;
+
+	if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+		return 0;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return ret;
+	pci_set_master(pdev);
+
+	return nouveau_do_resume(drm_dev);
+}
+
+static int nouveau_pmops_freeze(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+	return nouveau_do_suspend(drm_dev);
+}
+
+static int nouveau_pmops_thaw(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+	return nouveau_do_resume(drm_dev);
+}
+
+
 static int
 nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
 {
@@ -631,14 +664,22 @@ nouveau_drm_pci_table[] = {
 	{}
 };
 
+static const struct dev_pm_ops nouveau_pm_ops = {
+	.suspend = nouveau_pmops_suspend,
+	.resume = nouveau_pmops_resume,
+	.freeze = nouveau_pmops_freeze,
+	.thaw = nouveau_pmops_thaw,
+	.poweroff = nouveau_pmops_freeze,
+	.restore = nouveau_pmops_resume,
+};
+
 static struct pci_driver
 nouveau_drm_pci_driver = {
 	.name = "nouveau",
 	.id_table = nouveau_drm_pci_table,
 	.probe = nouveau_drm_probe,
 	.remove = nouveau_drm_remove,
-	.suspend = nouveau_drm_suspend,
-	.resume = nouveau_drm_resume,
+	.driver.pm = &nouveau_pm_ops,
 };
 
 static int __init
Signature · 1 line
1.7.12
author maintainer
#2 lore raw

PATCH 2/2 nouveau: add runtime PM support (v0.1)

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

This adds hook into the system runtime PM support to enable dynamic PM on the nvidia device.

It doesn't hook into the power switch yet, only D3s the card.

TODO:
keep a reference if we have enabled outputs/framebuffers (??) keep a reference if we have any requests in flight. sprinkle mark last busy for autosuspend timeout

---
 drivers/gpu/drm/nouveau/nouveau_connector.c |  27 +++++--
 drivers/gpu/drm/nouveau/nouveau_drm.c       | 111 ++++++++++++++++++++++++++--
 drivers/gpu/drm/nouveau/nouveau_drm.h       |   4 +-
 drivers/gpu/drm/nouveau/nouveau_irq.c       |   3 +-
 drivers/gpu/drm/nouveau/nouveau_pm.c        |   4 +-
 drivers/gpu/drm/nouveau/nouveau_vga.c       |  10 +--
 include/drm/drmP.h                          |   1 +
 7 files changed, 138 insertions(+), 22 deletions(-)
drivers/gpu/drm/nouveau/nouveau_connector.c +21 -6
index 702e2a7..6bb6250 100644
@@ -25,7 +25,7 @@
  */
 
 #include <acpi/button.h>
-
+#include <linux/pm_runtime.h>
 #include "drmP.h"
 #include "drm_edid.h"
 #include "drm_crtc_helper.h"
@@ -239,6 +239,12 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 	struct nouveau_encoder *nv_partner;
 	struct nouveau_i2c_port *i2c;
 	int type;
+	int ret;
+	enum drm_connector_status conn_status = connector_status_disconnected;
+
+	ret = pm_runtime_get_sync(connector->dev->dev);
+	if (ret < 0)
+		return ret;
 
 	/* Cleanup the previous EDID block. */
 	if (nv_connector->edid) {
@@ -262,7 +268,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 		    !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
 			NV_ERROR(drm, "Detected %s, but failed init\n",
 				 drm_get_connector_name(connector));
-			return connector_status_disconnected;
+			conn_status = connector_status_disconnected;
+			goto out;
 		}
 
 		/* Override encoder type for DVI-I based on whether EDID
@@ -289,13 +296,15 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 		}
 
 		nouveau_connector_set_encoder(connector, nv_encoder);
-		return connector_status_connected;
+		conn_status = connector_status_connected;
+		goto out;
 	}
 
 	nv_encoder = nouveau_connector_of_detect(connector);
 	if (nv_encoder) {
 		nouveau_connector_set_encoder(connector, nv_encoder);
-		return connector_status_connected;
+		conn_status = connector_status_connected;
+		goto out;
 	}
 
 detect_analog:
@@ -310,12 +319,18 @@ detect_analog:
 		if (helper->detect(encoder, connector) ==
 						connector_status_connected) {
 			nouveau_connector_set_encoder(connector, nv_encoder);
-			return connector_status_connected;
+			conn_status = connector_status_connected;
+			goto out;
 		}
 
 	}
 
-	return connector_status_disconnected;
+ out:
+	
+	pm_runtime_mark_last_busy(connector->dev->dev);
+	pm_runtime_put_autosuspend(connector->dev->dev);
+
+	return conn_status;
 }
 
 static enum drm_connector_status
drivers/gpu/drm/nouveau/nouveau_drm.c +106 -5

196 lines not on this page · raw message

drivers/gpu/drm/nouveau/nouveau_drm.h +2 -2
index ab0c174..215c5ea 100644
@@ -129,8 +129,8 @@ nouveau_dev(struct drm_device *dev)
 	return nv_device(nouveau_drm(dev)->device);
 }
 
-int nouveau_drm_suspend(struct pci_dev *, pm_message_t);
-int nouveau_drm_resume(struct pci_dev *);
+int nouveau_pmops_suspend(struct device *dev);
+int nouveau_pmops_resume(struct device *dev);
 
 #define NV_PRINTK(level, code, drm, fmt, args...)                              \
 	printk(level "nouveau " code "[     DRM][%s] " fmt,                    \
drivers/gpu/drm/nouveau/nouveau_irq.c +2 -1
index 9ca8afd..5938f65 100644
@@ -23,7 +23,7 @@
  */
 
 #include <subdev/mc.h>
-
+#include <linux/pm_runtime.h>
 #include "nouveau_drm.h"
 #include "nouveau_irq.h"
 #include "nv50_display.h"
@@ -70,6 +70,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
 			nv50_display_intr(dev);
 	}
 
+	pm_runtime_mark_last_busy(dev->dev);
 	return IRQ_HANDLED;
 }
 
drivers/gpu/drm/nouveau/nouveau_pm.c +1 -3
index 3c55ec2..509bb77 100644
@@ -28,12 +28,10 @@
 #include <linux/power_supply.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
-
 #include "drmP.h"
-
 #include "nouveau_drm.h"
 #include "nouveau_pm.h"
-
+#include "nouveau_acpi.h"
 #include <subdev/bios/gpio.h>
 #include <subdev/gpio.h>
 #include <subdev/timer.h>
drivers/gpu/drm/nouveau/nouveau_vga.c +5 -5
index 37fcc9d..47e23b5 100644
@@ -31,12 +31,12 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev,
 			     enum vga_switcheroo_state state)
 {
 	struct drm_device *dev = pci_get_drvdata(pdev);
-	pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
-
+	if (nouveau_is_optimus() && state == VGA_SWITCHEROO_OFF)
+		return;
 	if (state == VGA_SWITCHEROO_ON) {
 		printk(KERN_ERR "VGA switcheroo: switched nouveau on\n");
 		dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
-		nouveau_drm_resume(pdev);
+		nouveau_pmops_resume(&pdev->dev);
 		drm_kms_helper_poll_enable(dev);
 		dev->switch_power_state = DRM_SWITCH_POWER_ON;
 	} else {
@@ -44,7 +44,7 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev,
 		dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
 		drm_kms_helper_poll_disable(dev);
 		nouveau_switcheroo_optimus_dsm();
-		nouveau_drm_suspend(pdev, pmm);
+		nouveau_pmops_suspend(&pdev->dev);
 		dev->switch_power_state = DRM_SWITCH_POWER_OFF;
 	}
 }
@@ -80,7 +80,7 @@ nouveau_vga_init(struct nouveau_drm *drm)
 {
 	struct drm_device *dev = drm->dev;
 	vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
-	vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, false);
+	vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, nouveau_is_optimus());
 }
 
 void
include/drm/drmP.h +1 -0
index d6b67bb..698fb3e 100644
@@ -1202,6 +1202,7 @@ struct drm_device {
 #define DRM_SWITCH_POWER_ON 0
 #define DRM_SWITCH_POWER_OFF 1
 #define DRM_SWITCH_POWER_CHANGING 2
+#define DRM_SWITCH_POWER_DYNAMIC_OFF 3
 
 static __inline__ int drm_core_check_feature(struct drm_device *dev,
 					     int feature)
Signature · 1 line
1.7.12