From: Dave Airlie <airl...@redhat.com>

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

Signed-off-by: Dave Airlie <airlied at redhat.com
---
 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(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c 
b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 702e2a7..6bb6250 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -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
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c 
b/drivers/gpu/drm/nouveau/nouveau_drm.c
index bb51a00..63b1b7c 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>
@@ -334,6 +337,13 @@ nouveau_drm_load(struct drm_device *dev, unsigned long 
flags)

        nouveau_accel_init(drm);
        nouveau_fbcon_init(dev);
+
+       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_put_autosuspend(dev->dev);
+       pm_runtime_mark_last_busy(dev->dev);
        return 0;

 fail_dispinit:
@@ -349,6 +359,7 @@ fail_ttm:
        nouveau_vga_fini(drm);
 fail_device:
        nouveau_cli_destroy(&drm->client);
+
        return ret;
 }

@@ -357,6 +368,7 @@ nouveau_drm_unload(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);

+       pm_runtime_get_noresume(dev->dev);
        nouveau_fbcon_fini(dev);
        nouveau_accel_fini(drm);

@@ -523,16 +535,21 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file 
*fpriv)
        struct nouveau_cli *cli;
        int ret;

+       /* need to bring up power immediately if opening device */
+       ret = pm_runtime_get_sync(dev->dev);
+       if (ret < 0)
+               return ret;
+
        ret = nouveau_cli_create(pdev, fpriv->pid, 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;
                }
        }

@@ -541,7 +558,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
@@ -550,12 +572,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
@@ -563,6 +588,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
@@ -581,12 +608,29 @@ 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(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,
@@ -664,6 +708,60 @@ 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;
+       printk("runtime suspend called\n");
+
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+       drm_kms_helper_poll_disable(drm_dev);
+       vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF, false);
+       nouveau_switcheroo_optimus_dsm();
+       ret = nouveau_do_suspend(drm_dev);
+       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;
+       printk("runtime resume called\n");
+       drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
+       ret = nouveau_do_resume(drm_dev);
+       vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF, false);
+       drm_kms_helper_poll_enable(drm_dev);
+       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;
+
+       /* are we optimus enabled? */
+       if (!nouveau_is_optimus()) {
+               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;
+               }
+       }
+       printk("runtime idle called\n");
+       WARN_ON(1);
+       return 0;
+}
+
 static const struct dev_pm_ops nouveau_pm_ops = {
        .suspend = nouveau_pmops_suspend,
        .resume = nouveau_pmops_resume,
@@ -671,6 +769,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 ab0c174..215c5ea 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.h
@@ -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,                    \
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c 
b/drivers/gpu/drm/nouveau/nouveau_irq.c
index 9ca8afd..5938f65 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"
@@ -70,6 +70,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
                        nv50_display_intr(dev);
        }

+       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 3c55ec2..509bb77 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -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>
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c 
b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 37fcc9d..47e23b5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -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
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index d6b67bb..698fb3e 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -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)
-- 
1.7.12

Reply via email to