diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/nouveau/core/core/printk.c | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/include/core/printk.h | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/bios/init.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_connector.c | 28 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.c | 201 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.h | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_irq.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_vga.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv04_crtc.c | 49 |
12 files changed, 318 insertions, 36 deletions
diff --git a/drivers/gpu/drm/nouveau/core/core/printk.c b/drivers/gpu/drm/nouveau/core/core/printk.c index 6161eaf5447c..514c26a39449 100644 --- a/drivers/gpu/drm/nouveau/core/core/printk.c +++ b/drivers/gpu/drm/nouveau/core/core/printk.c @@ -27,6 +27,8 @@ #include <core/subdev.h> #include <core/printk.h> +int nv_printk_suspend_level = NV_DBG_DEBUG; + void nv_printk_(struct nouveau_object *object, const char *pfx, int level, const char *fmt, ...) @@ -72,3 +74,20 @@ nv_printk_(struct nouveau_object *object, const char *pfx, int level, vprintk(mfmt, args); va_end(args); } + +#define CONV_LEVEL(x) case NV_DBG_##x: return NV_PRINTK_##x + +const char *nv_printk_level_to_pfx(int level) +{ + switch (level) { + CONV_LEVEL(FATAL); + CONV_LEVEL(ERROR); + CONV_LEVEL(WARN); + CONV_LEVEL(INFO); + CONV_LEVEL(DEBUG); + CONV_LEVEL(PARANOIA); + CONV_LEVEL(TRACE); + CONV_LEVEL(SPAM); + } + return NV_PRINTK_DEBUG; +} diff --git a/drivers/gpu/drm/nouveau/core/include/core/printk.h b/drivers/gpu/drm/nouveau/core/include/core/printk.h index 1d629664f32d..7738e892a0e5 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/printk.h +++ b/drivers/gpu/drm/nouveau/core/include/core/printk.h @@ -15,6 +15,12 @@ struct nouveau_object; #define NV_PRINTK_TRACE KERN_DEBUG #define NV_PRINTK_SPAM KERN_DEBUG +extern int nv_printk_suspend_level; + +#define NV_DBG_SUSPEND (nv_printk_suspend_level) +#define NV_PRINTK_SUSPEND (nv_printk_level_to_pfx(nv_printk_suspend_level)) + +const char *nv_printk_level_to_pfx(int level); void nv_printk_(struct nouveau_object *, const char *, int, const char *, ...); #define nv_printk(o,l,f,a...) do { \ @@ -30,6 +36,13 @@ void nv_printk_(struct nouveau_object *, const char *, int, const char *, ...); #define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a) #define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a) +#define nv_suspend(o,f,a...) nv_printk((o), SUSPEND, f, ##a) + +static inline void nv_suspend_set_printk_level(int level) +{ + nv_printk_suspend_level = level; +} + #define nv_assert(f,a...) do { \ if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG) \ nv_printk_(NULL, NV_PRINTK_FATAL, NV_DBG_FATAL, f "\n", ##a); \ diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 690ed438b2ad..bdcc88f20079 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -2147,7 +2147,7 @@ nvbios_init(struct nouveau_subdev *subdev, bool execute) u16 data; if (execute) - nv_info(bios, "running init tables\n"); + nv_suspend(bios, "running init tables\n"); while (!ret && (data = (init_script(bios, ++i)))) { struct nvbios_init init = { .subdev = subdev, diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index e620ba8271b4..52b9db0ce113 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -26,6 +26,8 @@ #include <acpi/button.h> +#include <linux/pm_runtime.h> + #include <drm/drmP.h> #include <drm/drm_edid.h> #include <drm/drm_crtc_helper.h> @@ -260,6 +262,8 @@ 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; /* Cleanup the previous EDID block. */ if (nv_connector->edid) { @@ -268,6 +272,11 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) nv_connector->edid = NULL; } + ret = pm_runtime_get_sync(connector->dev->dev); + if (ret < 0) + return conn_status; + + i2c = nouveau_connector_ddc_detect(connector, &nv_encoder); if (i2c) { nv_connector->edid = drm_get_edid(connector, &i2c->adapter); @@ -283,7 +292,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 @@ -310,13 +320,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: @@ -331,12 +343,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 diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 508b00a2ce0d..216b00685058 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -406,7 +406,7 @@ nouveau_display_suspend(struct drm_device *dev) nouveau_display_fini(dev); - NV_INFO(drm, "unpinning framebuffer(s)...\n"); + NV_SUSPEND(drm, "unpinning framebuffer(s)...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; @@ -428,7 +428,7 @@ nouveau_display_suspend(struct drm_device *dev) } void -nouveau_display_resume(struct drm_device *dev) +nouveau_display_repin(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); struct drm_crtc *crtc; @@ -453,10 +453,12 @@ nouveau_display_resume(struct drm_device *dev) if (ret) NV_ERROR(drm, "Could not pin/map cursor.\n"); } +} - nouveau_fbcon_set_suspend(dev, 0); - nouveau_fbcon_zfill_all(dev); - +void +nouveau_display_resume(struct drm_device *dev) +{ + struct drm_crtc *crtc; nouveau_display_init(dev); /* Force CLUT to get re-loaded during modeset */ diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 722548bb3bd3..96e29ab200d7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -57,6 +57,7 @@ void nouveau_display_destroy(struct drm_device *dev); int nouveau_display_init(struct drm_device *dev); void nouveau_display_fini(struct drm_device *dev); int nouveau_display_suspend(struct drm_device *dev); +void nouveau_display_repin(struct drm_device *dev); void nouveau_display_resume(struct drm_device *dev); int nouveau_vblank_enable(struct drm_device *dev, int crtc); @@ -76,6 +77,7 @@ int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *, void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); +int nouveau_crtc_set_config(struct drm_mode_set *set); #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT extern int nouveau_backlight_init(struct drm_device *); extern void nouveau_backlight_exit(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 8b090f1eb51d..db0d17a6f92e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -25,7 +25,10 @@ #include <linux/console.h> #include <linux/module.h> #include <linux/pci.h> - +#include <linux/pm_runtime.h> +#include <linux/vga_switcheroo.h> +#include "drmP.h" +#include "drm_crtc_helper.h" #include <core/device.h> #include <core/client.h> #include <core/gpuobj.h> @@ -66,6 +69,10 @@ MODULE_PARM_DESC(modeset, "enable driver (default: auto, " int nouveau_modeset = -1; module_param_named(modeset, nouveau_modeset, int, 0400); +MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)"); +int nouveau_runtime_pm = -1; +module_param_named(runpm, nouveau_runtime_pm, int, 0400); + static struct drm_driver driver; static u64 @@ -341,6 +348,15 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) nouveau_accel_init(drm); nouveau_fbcon_init(dev); + + if (nouveau_runtime_pm != 0) { + pm_runtime_use_autosuspend(dev->dev); + pm_runtime_set_autosuspend_delay(dev->dev, 5000); + pm_runtime_set_active(dev->dev); + pm_runtime_allow(dev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + } return 0; fail_dispinit: @@ -356,6 +372,7 @@ fail_ttm: nouveau_vga_fini(drm); fail_device: nouveau_cli_destroy(&drm->client); + return ret; } @@ -364,6 +381,7 @@ nouveau_drm_unload(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); + pm_runtime_get_sync(dev->dev); nouveau_fbcon_fini(dev); nouveau_accel_fini(drm); @@ -406,16 +424,13 @@ nouveau_do_suspend(struct drm_device *dev) int ret; if (dev->mode_config.num_crtc) { - NV_INFO(drm, "suspending fbcon...\n"); - nouveau_fbcon_set_suspend(dev, 1); - - NV_INFO(drm, "suspending display...\n"); + NV_SUSPEND(drm, "suspending display...\n"); ret = nouveau_display_suspend(dev); if (ret) return ret; } - NV_INFO(drm, "evicting buffers...\n"); + NV_SUSPEND(drm, "evicting buffers...\n"); ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM); if (drm->fence && nouveau_fence(drm)->suspend) { @@ -423,7 +438,7 @@ nouveau_do_suspend(struct drm_device *dev) return -ENOMEM; } - NV_INFO(drm, "suspending client object trees...\n"); + NV_SUSPEND(drm, "suspending client object trees...\n"); list_for_each_entry(cli, &drm->clients, head) { ret = nouveau_client_fini(&cli->base, true); if (ret) @@ -443,7 +458,7 @@ fail_client: } if (dev->mode_config.num_crtc) { - NV_INFO(drm, "resuming display...\n"); + NV_SUSPEND(drm, "resuming display...\n"); nouveau_display_resume(dev); } return ret; @@ -455,9 +470,14 @@ int nouveau_pmops_suspend(struct device *dev) struct drm_device *drm_dev = pci_get_drvdata(pdev); int ret; - if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF || + drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF) return 0; + if (drm_dev->mode_config.num_crtc) + nouveau_fbcon_set_suspend(drm_dev, 1); + + nv_suspend_set_printk_level(NV_DBG_INFO); ret = nouveau_do_suspend(drm_dev); if (ret) return ret; @@ -465,6 +485,7 @@ int nouveau_pmops_suspend(struct device *dev) pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); + nv_suspend_set_printk_level(NV_DBG_DEBUG); return 0; } @@ -475,11 +496,11 @@ 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"); + NV_SUSPEND(drm, "re-enabling device...\n"); nouveau_agp_reset(drm); - NV_INFO(drm, "resuming client object trees...\n"); + NV_SUSPEND(drm, "resuming client object trees...\n"); nouveau_client_init(&drm->client.base); nouveau_agp_init(drm); @@ -495,9 +516,10 @@ nouveau_do_resume(struct drm_device *dev) nouveau_pm_resume(dev); if (dev->mode_config.num_crtc) { - NV_INFO(drm, "resuming display...\n"); - nouveau_display_resume(dev); + NV_SUSPEND(drm, "resuming display...\n"); + nouveau_display_repin(dev); } + return 0; } @@ -507,7 +529,8 @@ int nouveau_pmops_resume(struct device *dev) struct drm_device *drm_dev = pci_get_drvdata(pdev); int ret; - if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF || + drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF) return 0; pci_set_power_state(pdev, PCI_D0); @@ -517,23 +540,54 @@ int nouveau_pmops_resume(struct device *dev) return ret; pci_set_master(pdev); - return nouveau_do_resume(drm_dev); + nv_suspend_set_printk_level(NV_DBG_INFO); + ret = nouveau_do_resume(drm_dev); + if (ret) { + nv_suspend_set_printk_level(NV_DBG_DEBUG); + return ret; + } + if (drm_dev->mode_config.num_crtc) + nouveau_fbcon_set_suspend(drm_dev, 0); + + nouveau_fbcon_zfill_all(drm_dev); + nouveau_display_resume(drm_dev); + nv_suspend_set_printk_level(NV_DBG_DEBUG); + return 0; } 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); + int ret; + + nv_suspend_set_printk_level(NV_DBG_INFO); + if (drm_dev->mode_config.num_crtc) + nouveau_fbcon_set_suspend(drm_dev, 1); - return nouveau_do_suspend(drm_dev); + ret = nouveau_do_suspend(drm_dev); + nv_suspend_set_printk_level(NV_DBG_DEBUG); + return ret; } 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); + int ret; - return nouveau_do_resume(drm_dev); + nv_suspend_set_printk_level(NV_DBG_INFO); + ret = nouveau_do_resume(drm_dev); + if (ret) { + nv_suspend_set_printk_level(NV_DBG_DEBUG); + return ret; + } + if (drm_dev->mode_config.num_crtc) + nouveau_fbcon_set_suspend(drm_dev, 0); + nouveau_fbcon_zfill_all(drm_dev); + nouveau_display_resume(drm_dev); + nv_suspend_set_printk_level(NV_DBG_DEBUG); + return 0; } @@ -546,18 +600,23 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) char name[16]; int ret; + /* need to bring up power immediately if opening device */ + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) + return ret; + snprintf(name, sizeof(name), "%d", pid_nr(fpriv->pid)); ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli); if (ret) - return ret; + goto out_suspend; if (nv_device(drm->device)->card_type >= NV_50) { ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40), 0x1000, &cli->base.vm); if (ret) { nouveau_cli_destroy(cli); - return ret; + goto out_suspend; } } @@ -566,7 +625,12 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) mutex_lock(&drm->client.mutex); list_add(&cli->head, &drm->clients); mutex_unlock(&drm->client.mutex); - return 0; + +out_suspend: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; } static void @@ -575,12 +639,15 @@ nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv) struct nouveau_cli *cli = nouveau_cli(fpriv); struct nouveau_drm *drm = nouveau_drm(dev); + pm_runtime_get_sync(dev->dev); + if (cli->abi16) nouveau_abi16_fini(cli->abi16); mutex_lock(&drm->client.mutex); list_del(&cli->head); mutex_unlock(&drm->client.mutex); + } static void @@ -588,6 +655,8 @@ nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv) { struct nouveau_cli *cli = nouveau_cli(fpriv); nouveau_cli_destroy(cli); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); } static struct drm_ioctl_desc @@ -606,12 +675,30 @@ nouveau_ioctls[] = { DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH), }; +long nouveau_drm_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_device *dev; + long ret; + dev = file_priv->minor->dev; + + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) + return ret; + + ret = drm_ioctl(filp, cmd, arg); + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + return ret; +} static const struct file_operations nouveau_driver_fops = { .owner = THIS_MODULE, .open = drm_open, .release = drm_release, - .unlocked_ioctl = drm_ioctl, + .unlocked_ioctl = nouveau_drm_ioctl, .mmap = nouveau_ttm_mmap, .poll = drm_poll, .fasync = drm_fasync, @@ -689,6 +776,75 @@ nouveau_drm_pci_table[] = { {} }; +static int nouveau_pmops_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int ret; + + if (nouveau_runtime_pm == 0) + return -EINVAL; + + drm_kms_helper_poll_disable(drm_dev); + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); + nouveau_switcheroo_optimus_dsm(); + ret = nouveau_do_suspend(drm_dev); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3cold); + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; + return ret; +} + +static int nouveau_pmops_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int ret; + + if (nouveau_runtime_pm == 0) + return -EINVAL; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; + pci_set_master(pdev); + + ret = nouveau_do_resume(drm_dev); + nouveau_display_resume(drm_dev); + drm_kms_helper_poll_enable(drm_dev); + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; + return ret; +} + +static int nouveau_pmops_runtime_idle(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct drm_crtc *crtc; + + if (nouveau_runtime_pm == 0) + return -EBUSY; + + /* are we optimus enabled? */ + if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { + DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); + return -EBUSY; + } + + 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 -EBUSY; + } + } + return 0; +} + static const struct dev_pm_ops nouveau_pm_ops = { .suspend = nouveau_pmops_suspend, .resume = nouveau_pmops_resume, @@ -696,6 +852,9 @@ static const struct dev_pm_ops nouveau_pm_ops = { .thaw = nouveau_pmops_thaw, .poweroff = nouveau_pmops_freeze, .restore = nouveau_pmops_resume, + .runtime_suspend = nouveau_pmops_runtime_suspend, + .runtime_resume = nouveau_pmops_runtime_resume, + .runtime_idle = nouveau_pmops_runtime_idle, }; static struct pci_driver diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index aa89eb938b47..0398639371ee 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -58,6 +58,8 @@ nouveau_cli(struct drm_file *fpriv) return fpriv ? fpriv->driver_priv : NULL; } +extern int nouveau_runtime_pm; + struct nouveau_drm { struct nouveau_cli client; struct drm_device *dev; @@ -115,6 +117,11 @@ struct nouveau_drm { /* power management */ struct nouveau_pm *pm; + + /* display power reference */ + bool have_disp_power_ref; + + struct dev_pm_domain vga_pm_domain; }; static inline struct nouveau_drm * @@ -132,6 +139,7 @@ nouveau_dev(struct drm_device *dev) int nouveau_pmops_suspend(struct device *); int nouveau_pmops_resume(struct device *); +#define NV_SUSPEND(cli, fmt, args...) nv_suspend((cli), fmt, ##args) #define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args) #define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args) #define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args) diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c index 1303680affd3..1abc32b6e8e9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_irq.c +++ b/drivers/gpu/drm/nouveau/nouveau_irq.c @@ -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" @@ -60,6 +60,8 @@ nouveau_irq_handler(DRM_IRQ_ARGS) return IRQ_NONE; nv_subdev(pmc)->intr(nv_subdev(pmc)); + + pm_runtime_mark_last_busy(dev->dev); return IRQ_HANDLED; } diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index a701ff5ffa5b..7e1058394692 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -33,7 +33,7 @@ #include "nouveau_drm.h" #include "nouveau_pm.h" - +#include "nouveau_acpi.h" #include <subdev/gpio.h> #include <subdev/timer.h> #include <subdev/therm.h> diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index 40a09f11a600..81638d7f2eff 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -32,6 +32,9 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev, { struct drm_device *dev = pci_get_drvdata(pdev); + if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && 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; @@ -78,8 +81,17 @@ void nouveau_vga_init(struct nouveau_drm *drm) { struct drm_device *dev = drm->dev; + bool runtime = false; vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); - vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, false); + + if (nouveau_runtime_pm == 1) + runtime = true; + if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm())) + runtime = true; + vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime); + + if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) + vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain); } void diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index 6578cd28c556..b6ecc9eb34ff 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -22,6 +22,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +#include <linux/pm_runtime.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> @@ -1007,13 +1008,59 @@ nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) return 0; } +int +nouveau_crtc_set_config(struct drm_mode_set *set) +{ + struct drm_device *dev; + struct nouveau_drm *drm; + int ret; + struct drm_crtc *crtc; + bool active = false; + if (!set || !set->crtc) + return -EINVAL; + + dev = set->crtc->dev; + + /* get a pm reference here */ + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) + return ret; + + ret = drm_crtc_helper_set_config(set); + + drm = nouveau_drm(dev); + + /* if we get here with no crtcs active then we can drop a reference */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->enabled) + active = true; + } + + pm_runtime_mark_last_busy(dev->dev); + /* if we have active crtcs and we don't have a power ref, + take the current one */ + if (active && !drm->have_disp_power_ref) { + drm->have_disp_power_ref = true; + return ret; + } + /* if we have no active crtcs, then drop the power ref + we got before */ + if (!active && drm->have_disp_power_ref) { + pm_runtime_put_autosuspend(dev->dev); + drm->have_disp_power_ref = false; + } + /* drop the power reference we got coming in here */ + pm_runtime_put_autosuspend(dev->dev); + return ret; +} + static const struct drm_crtc_funcs nv04_crtc_funcs = { .save = nv_crtc_save, .restore = nv_crtc_restore, .cursor_set = nv04_crtc_cursor_set, .cursor_move = nv04_crtc_cursor_move, .gamma_set = nv_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, + .set_config = nouveau_crtc_set_config, .page_flip = nouveau_crtc_page_flip, .destroy = nv_crtc_destroy, }; |