From: Dave Airlie <airlied@xxxxxxxxxx> This adds the interfaces for nouveau to dynamically power off the GPU in an optimus system. It's based on nouveau git so may not apply everywhere. Signed-off-by: Dave Airlie <airlied@xxxxxxxxxx --- drivers/gpu/drm/nouveau/nouveau_drm.c | 2 ++ drivers/gpu/drm/nouveau/nouveau_drm.h | 3 ++ drivers/gpu/drm/nouveau/nouveau_pm.c | 57 +++++++++++++++++++++++++++++++++-- drivers/gpu/drm/nouveau/nouveau_vga.c | 4 ++- 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 9fb56b3..e7735a8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -604,6 +604,8 @@ driver = { .dumb_map_offset = nouveau_display_dumb_map_offset, .dumb_destroy = nouveau_display_dumb_destroy, + .dynamic_off_check = nouveau_dynamic_power_check, + .dynamic_set_state = nouveau_dynamic_power_set_state, .name = DRIVER_NAME, .desc = DRIVER_DESC, #ifdef GIT_REVISION diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index ab0c174..f39f814 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -148,4 +148,7 @@ int nouveau_drm_resume(struct pci_dev *); NV_PRINTK(KERN_DEBUG, "D", drm, fmt, ##args); \ } while (0) +bool nouveau_dynamic_power_check(struct drm_device *dev); +int nouveau_dynamic_power_set_state(struct drm_device *dev, int state); + #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 3c55ec2..fc132c3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -28,12 +28,12 @@ #include <linux/power_supply.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> - +#include <linux/vga_switcheroo.h> #include "drmP.h" - +#include "drm_crtc_helper.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> @@ -962,6 +962,7 @@ nouveau_pm_init(struct drm_device *dev) register_acpi_notifier(&pm->acpi_nb); #endif + drm_dynamic_power_init(dev); return 0; } @@ -1007,3 +1008,53 @@ nouveau_pm_resume(struct drm_device *dev) nouveau_pm_perflvl_set(dev, perflvl); nouveau_pwmfan_set(dev, pm->fan.percent); } + +/* rules for what we want + if we are an optimus laptop, + with no active crtc/encoders/connectors + with no channel activity for 4-5s +*/ + +bool nouveau_dynamic_power_check(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_crtc *crtc; + + /* are we optimus enabled? */ + if (!nouveau_is_optimus()) { + DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); + return false; + } + + list_for_each_entry(crtc, &drm->dev->mode_config.crtc_list, head) { + if (crtc->enabled) { + DRM_DEBUG_DRIVER("failing to power off - crtc active\n"); + return false; + } + } + return true; +} + +int nouveau_dynamic_power_set_state(struct drm_device *dev, int state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; + + if (state == DRM_SWITCH_POWER_DYNAMIC_OFF) { + dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; + drm_kms_helper_poll_disable(drm->dev); + vga_switcheroo_set_dynamic_switch(dev->pdev, VGA_SWITCHEROO_OFF, false); + nouveau_switcheroo_optimus_dsm(); + nouveau_drm_suspend(drm->dev->pdev, pmm); + vga_switcheroo_set_dynamic_switch(dev->pdev, VGA_SWITCHEROO_OFF, true); + } else if (state == DRM_SWITCH_POWER_ON) { + vga_switcheroo_set_dynamic_switch(dev->pdev, VGA_SWITCHEROO_ON, true); + nouveau_drm_resume(dev->pdev); + vga_switcheroo_set_dynamic_switch(dev->pdev, VGA_SWITCHEROO_ON, false); + drm_kms_helper_poll_enable(dev); + dev->switch_power_state = DRM_SWITCH_POWER_ON; + } + + return 0; +} + diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index 37fcc9d..539722f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -33,6 +33,8 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev, 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; @@ -80,7 +82,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 -- 1.7.12 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel