nouveau runtime PM support (take two)
Subsystems (3)
Dave Airlie
maintainerSo 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.
Dave Airlie
maintainerPATCH 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
@@ -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
Dave Airlie
maintainerPATCH 2/2 nouveau: add runtime PM support (v0.1)
In reply to Dave AirlieFrom: 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 3static __inline__int drm_core_check_feature (struct drm_device *dev,int feature)
Signature · 1 line
1.7.12