]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/nouveau/nouveau_drm.c
drm/nouveau: Support render nodes
[karo-tx-linux.git] / drivers / gpu / drm / nouveau / nouveau_drm.c
index 61972668fd0532f9f29e6ea540ab925713d5cf7d..8863644024b78b71d97451d9f6f9407c9c551fe3 100644 (file)
 #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>
@@ -69,6 +72,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 int
@@ -296,6 +303,31 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
        return 0;
 }
 
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+
+static void
+nouveau_get_hdmi_dev(struct drm_device *dev)
+{
+       struct nouveau_drm *drm = dev->dev_private;
+       struct pci_dev *pdev = dev->pdev;
+
+       /* subfunction one is a hdmi audio device? */
+       drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
+                                               PCI_DEVFN(PCI_SLOT(pdev->devfn), 1));
+
+       if (!drm->hdmi_device) {
+               DRM_INFO("hdmi device  not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);
+               return;
+       }
+
+       if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) {
+               DRM_INFO("possible hdmi device  not audio %d\n", drm->hdmi_device->class);
+               pci_dev_put(drm->hdmi_device);
+               drm->hdmi_device = NULL;
+               return;
+       }
+}
+
 static int
 nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 {
@@ -314,6 +346,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
        INIT_LIST_HEAD(&drm->clients);
        spin_lock_init(&drm->tile.lock);
 
+       nouveau_get_hdmi_dev(dev);
+
        /* make sure AGP controller is in a consistent state before we
         * (possibly) execute vbios init tables (see nouveau_agp.h)
         */
@@ -388,6 +422,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(dev->dev);
+       }
        return 0;
 
 fail_dispinit:
@@ -409,6 +452,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);
 
@@ -424,6 +468,8 @@ nouveau_drm_unload(struct drm_device *dev)
        nouveau_agp_fini(drm);
        nouveau_vga_fini(drm);
 
+       if (drm->hdmi_device)
+               pci_dev_put(drm->hdmi_device);
        nouveau_cli_destroy(&drm->client);
        return 0;
 }
@@ -450,19 +496,16 @@ 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);
 
-       NV_INFO(drm, "waiting for kernel channels to go idle...\n");
+       NV_SUSPEND(drm, "waiting for kernel channels to go idle...\n");
        if (drm->cechan) {
                ret = nouveau_channel_idle(drm->cechan);
                if (ret)
@@ -475,7 +518,7 @@ nouveau_do_suspend(struct drm_device *dev)
                        return ret;
        }
 
-       NV_INFO(drm, "suspending client object trees...\n");
+       NV_SUSPEND(drm, "suspending client object trees...\n");
        if (drm->fence && nouveau_fence(drm)->suspend) {
                if (!nouveau_fence(drm)->suspend(drm))
                        return -ENOMEM;
@@ -487,7 +530,7 @@ nouveau_do_suspend(struct drm_device *dev)
                        goto fail_client;
        }
 
-       NV_INFO(drm, "suspending kernel object tree...\n");
+       NV_SUSPEND(drm, "suspending kernel object tree...\n");
        ret = nouveau_client_fini(&drm->client.base, true);
        if (ret)
                goto fail_client;
@@ -501,7 +544,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;
@@ -513,9 +556,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;
@@ -523,6 +571,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;
 }
@@ -533,15 +582,15 @@ 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 kernel object tree...\n");
+       NV_SUSPEND(drm, "resuming kernel object tree...\n");
        nouveau_client_init(&drm->client.base);
        nouveau_agp_init(drm);
 
-       NV_INFO(drm, "resuming client object trees...\n");
+       NV_SUSPEND(drm, "resuming client object trees...\n");
        if (drm->fence && nouveau_fence(drm)->resume)
                nouveau_fence(drm)->resume(drm);
 
@@ -553,9 +602,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;
 }
 
@@ -565,7 +615,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);
@@ -575,23 +626,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;
 }
 
 
@@ -604,19 +686,24 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
        char name[32], tmpname[TASK_COMM_LEN];
        int ret;
 
+       /* need to bring up power immediately if opening device */
+       ret = pm_runtime_get_sync(dev->dev);
+       if (ret < 0)
+               return ret;
+
        get_task_comm(tmpname, current);
        snprintf(name, sizeof(name), "%s[%d]", tmpname, 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;
                }
        }
 
@@ -625,7 +712,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
@@ -634,12 +726,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
@@ -647,33 +742,52 @@ 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
+static const struct drm_ioctl_desc
 nouveau_ioctls[] = {
-       DRM_IOCTL_DEF_DRV(NOUVEAU_GETPARAM, nouveau_abi16_ioctl_getparam, DRM_UNLOCKED|DRM_AUTH),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_GETPARAM, nouveau_abi16_ioctl_getparam, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(NOUVEAU_SETPARAM, nouveau_abi16_ioctl_setparam, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_ALLOC, nouveau_abi16_ioctl_channel_alloc, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_FREE, nouveau_abi16_ioctl_channel_free, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_GROBJ_ALLOC, nouveau_abi16_ioctl_grobj_alloc, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_abi16_ioctl_notifierobj_alloc, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_GPUOBJ_FREE, nouveau_abi16_ioctl_gpuobj_free, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
-       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_ALLOC, nouveau_abi16_ioctl_channel_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_FREE, nouveau_abi16_ioctl_channel_free, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_GROBJ_ALLOC, nouveau_abi16_ioctl_grobj_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_abi16_ioctl_notifierobj_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_GPUOBJ_FREE, nouveau_abi16_ioctl_gpuobj_free, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 };
 
+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,
        .read = drm_read,
 #if defined(CONFIG_COMPAT)
        .compat_ioctl = nouveau_compat_ioctl,
@@ -684,8 +798,8 @@ nouveau_driver_fops = {
 static struct drm_driver
 driver = {
        .driver_features =
-               DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
-               DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME,
+               DRIVER_USE_AGP |
+               DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER,
 
        .load = nouveau_drm_load,
        .unload = nouveau_drm_unload,
@@ -704,6 +818,7 @@ driver = {
        .disable_vblank = nouveau_drm_vblank_disable,
 
        .ioctls = nouveau_ioctls,
+       .num_ioctls = ARRAY_SIZE(nouveau_ioctls),
        .fops = &nouveau_driver_fops,
 
        .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
@@ -724,7 +839,7 @@ driver = {
 
        .dumb_create = nouveau_display_dumb_create,
        .dumb_map_offset = nouveau_display_dumb_map_offset,
-       .dumb_destroy = nouveau_display_dumb_destroy,
+       .dumb_destroy = drm_gem_dumb_destroy,
 
        .name = DRIVER_NAME,
        .desc = DRIVER_DESC,
@@ -753,6 +868,90 @@ 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);
+       struct nouveau_device *device = nouveau_dev(drm_dev);
+       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);
+       /* do magic */
+       nv_mask(device, 0x88488, (1 << 25), (1 << 25));
+       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;
+       }
+
+       /* if we have a hdmi audio device - make sure it has a driver loaded */
+       if (drm->hdmi_device) {
+               if (!drm->hdmi_device->driver) {
+                       DRM_DEBUG_DRIVER("failing to power off - no HDMI audio driver loaded\n");
+                       pm_runtime_mark_last_busy(dev);
+                       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;
+               }
+       }
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_autosuspend(dev);
+       /* we don't want the main rpm_idle to call suspend - we want to autosuspend */
+       return 1;
+}
+
 static const struct dev_pm_ops nouveau_pm_ops = {
        .suspend = nouveau_pmops_suspend,
        .resume = nouveau_pmops_resume,
@@ -760,6 +959,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
@@ -774,8 +976,6 @@ nouveau_drm_pci_driver = {
 static int __init
 nouveau_drm_init(void)
 {
-       driver.num_ioctls = ARRAY_SIZE(nouveau_ioctls);
-
        if (nouveau_modeset == -1) {
 #ifdef CONFIG_VGA_CONSOLE
                if (vgacon_text_force())