]> Pileus Git - ~andy/linux/blobdiff - drivers/gpu/drm/nouveau/nouveau_drm.c
Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[~andy/linux] / drivers / gpu / drm / nouveau / nouveau_drm.c
index c72d42636c89db15337bb011971b0098d2cae8ac..ccae8c26ae2b34ecc25e8312ee6d57c0d14f06e3 100644 (file)
  * Authors: Ben Skeggs
  */
 
+#include <linux/console.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 
 #include <core/device.h>
 #include <core/client.h>
+#include <core/gpuobj.h>
 #include <core/class.h>
 
 #include <subdev/device.h>
+#include <subdev/vm.h>
 
 #include "nouveau_drm.h"
-
-int __devinit nouveau_pci_probe(struct pci_dev *, const struct pci_device_id *);
-void nouveau_pci_remove(struct pci_dev *);
-int nouveau_pci_suspend(struct pci_dev *, pm_message_t);
-int nouveau_pci_resume(struct pci_dev *);
-int __init nouveau_init(struct pci_driver *);
-void __exit nouveau_exit(struct pci_driver *);
-
-int nouveau_load(struct drm_device *, unsigned long);
-int nouveau_unload(struct drm_device *);
-void *nouveau_newpriv(struct drm_device *);
+#include "nouveau_irq.h"
+#include "nouveau_dma.h"
+#include "nouveau_ttm.h"
+#include "nouveau_gem.h"
+#include "nouveau_agp.h"
+#include "nouveau_vga.h"
+#include "nouveau_pm.h"
+#include "nouveau_acpi.h"
+#include "nouveau_bios.h"
+#include "nouveau_ioctl.h"
+#include "nouveau_abi16.h"
+#include "nouveau_fbcon.h"
+#include "nouveau_fence.h"
+
+#include "nouveau_ttm.h"
 
 MODULE_PARM_DESC(config, "option string to pass to driver core");
 static char *nouveau_config;
@@ -52,6 +59,16 @@ MODULE_PARM_DESC(debug, "debug string to pass to driver core");
 static char *nouveau_debug;
 module_param_named(debug, nouveau_debug, charp, 0400);
 
+MODULE_PARM_DESC(noaccel, "disable kernel/abi16 acceleration");
+static int nouveau_noaccel = 0;
+module_param_named(noaccel, nouveau_noaccel, int, 0400);
+
+MODULE_PARM_DESC(modeset, "enable driver");
+static int nouveau_modeset = -1;
+module_param_named(modeset, nouveau_modeset, int, 0400);
+
+static struct drm_driver driver;
+
 static u64
 nouveau_name(struct pci_dev *pdev)
 {
@@ -62,7 +79,8 @@ nouveau_name(struct pci_dev *pdev)
 }
 
 static int
-nouveau_cli_create(struct pci_dev *pdev, u32 name, int size, void **pcli)
+nouveau_cli_create(struct pci_dev *pdev, const char *name,
+                  int size, void **pcli)
 {
        struct nouveau_cli *cli;
        int ret;
@@ -81,17 +99,130 @@ static void
 nouveau_cli_destroy(struct nouveau_cli *cli)
 {
        struct nouveau_object *client = nv_object(cli);
+       nouveau_vm_ref(NULL, &cli->base.vm, NULL);
        nouveau_client_fini(&cli->base, false);
        atomic_set(&client->refcount, 1);
        nouveau_object_ref(NULL, &client);
 }
 
+static void
+nouveau_accel_fini(struct nouveau_drm *drm)
+{
+       nouveau_gpuobj_ref(NULL, &drm->notify);
+       nouveau_channel_del(&drm->channel);
+       nouveau_channel_del(&drm->cechan);
+       if (drm->fence)
+               nouveau_fence(drm)->dtor(drm);
+}
+
+static void
+nouveau_accel_init(struct nouveau_drm *drm)
+{
+       struct nouveau_device *device = nv_device(drm->device);
+       struct nouveau_object *object;
+       u32 arg0, arg1;
+       int ret;
+
+       if (nouveau_noaccel)
+               return;
+
+       /* initialise synchronisation routines */
+       if      (device->card_type < NV_10) ret = nv04_fence_create(drm);
+       else if (device->chipset   <  0x84) ret = nv10_fence_create(drm);
+       else if (device->card_type < NV_C0) ret = nv84_fence_create(drm);
+       else                                ret = nvc0_fence_create(drm);
+       if (ret) {
+               NV_ERROR(drm, "failed to initialise sync subsystem, %d\n", ret);
+               nouveau_accel_fini(drm);
+               return;
+       }
+
+       if (device->card_type >= NV_E0) {
+               ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE,
+                                         NVDRM_CHAN + 1,
+                                         NVE0_CHANNEL_IND_ENGINE_CE0 |
+                                         NVE0_CHANNEL_IND_ENGINE_CE1, 0,
+                                         &drm->cechan);
+               if (ret)
+                       NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
+
+               arg0 = NVE0_CHANNEL_IND_ENGINE_GR;
+               arg1 = 0;
+       } else {
+               arg0 = NvDmaFB;
+               arg1 = NvDmaTT;
+       }
+
+       ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE, NVDRM_CHAN,
+                                 arg0, arg1, &drm->channel);
+       if (ret) {
+               NV_ERROR(drm, "failed to create kernel channel, %d\n", ret);
+               nouveau_accel_fini(drm);
+               return;
+       }
+
+       if (device->card_type < NV_C0) {
+               ret = nouveau_gpuobj_new(drm->device, NULL, 32, 0, 0,
+                                       &drm->notify);
+               if (ret) {
+                       NV_ERROR(drm, "failed to allocate notifier, %d\n", ret);
+                       nouveau_accel_fini(drm);
+                       return;
+               }
+
+               ret = nouveau_object_new(nv_object(drm),
+                                        drm->channel->handle, NvNotify0,
+                                        0x003d, &(struct nv_dma_class) {
+                                               .flags = NV_DMA_TARGET_VRAM |
+                                                        NV_DMA_ACCESS_RDWR,
+                                               .start = drm->notify->addr,
+                                               .limit = drm->notify->addr + 31
+                                               }, sizeof(struct nv_dma_class),
+                                        &object);
+               if (ret) {
+                       nouveau_accel_fini(drm);
+                       return;
+               }
+       }
+
+
+       nouveau_bo_move_init(drm);
+}
+
 static int __devinit
 nouveau_drm_probe(struct pci_dev *pdev, const struct pci_device_id *pent)
 {
        struct nouveau_device *device;
+       struct apertures_struct *aper;
+       bool boot = false;
        int ret;
 
+       /* remove conflicting drivers (vesafb, efifb etc) */
+       aper = alloc_apertures(3);
+       if (!aper)
+               return -ENOMEM;
+
+       aper->ranges[0].base = pci_resource_start(pdev, 1);
+       aper->ranges[0].size = pci_resource_len(pdev, 1);
+       aper->count = 1;
+
+       if (pci_resource_len(pdev, 2)) {
+               aper->ranges[aper->count].base = pci_resource_start(pdev, 2);
+               aper->ranges[aper->count].size = pci_resource_len(pdev, 2);
+               aper->count++;
+       }
+
+       if (pci_resource_len(pdev, 3)) {
+               aper->ranges[aper->count].base = pci_resource_start(pdev, 3);
+               aper->ranges[aper->count].size = pci_resource_len(pdev, 3);
+               aper->count++;
+       }
+
+#ifdef CONFIG_X86
+       boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
+#endif
+       remove_conflicting_framebuffers(aper, "nouveaufb", boot);
+
        ret = nouveau_device_create(pdev, nouveau_name(pdev), pci_name(pdev),
                                    nouveau_config, nouveau_debug, &device);
        if (ret)
@@ -99,30 +230,57 @@ nouveau_drm_probe(struct pci_dev *pdev, const struct pci_device_id *pent)
 
        pci_set_master(pdev);
 
-       ret = nouveau_pci_probe(pdev, pent);
+       ret = drm_get_pci_dev(pdev, pent, &driver);
        if (ret) {
-               nouveau_device_destroy(&device);
+               nouveau_object_ref(NULL, (struct nouveau_object **)&device);
                return ret;
        }
 
        return 0;
 }
 
-int
+static int
 nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 {
        struct pci_dev *pdev = dev->pdev;
+       struct nouveau_device *device;
        struct nouveau_drm *drm;
        int ret;
 
-       ret = nouveau_cli_create(pdev, 0, sizeof(*drm), (void**)&drm);
-       dev->dev_private = drm;
+       ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm);
        if (ret)
                return ret;
 
-       INIT_LIST_HEAD(&drm->clients);
+       dev->dev_private = drm;
        drm->dev = dev;
 
+       INIT_LIST_HEAD(&drm->clients);
+       spin_lock_init(&drm->tile.lock);
+
+       /* make sure AGP controller is in a consistent state before we
+        * (possibly) execute vbios init tables (see nouveau_agp.h)
+        */
+       if (drm_pci_device_is_agp(dev) && dev->agp) {
+               /* dummy device object, doesn't init anything, but allows
+                * agp code access to registers
+                */
+               ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT,
+                                        NVDRM_DEVICE, 0x0080,
+                                        &(struct nv_device_class) {
+                                               .device = ~0,
+                                               .disable =
+                                                ~(NV_DEVICE_DISABLE_MMIO |
+                                                  NV_DEVICE_DISABLE_IDENTIFY),
+                                               .debug0 = ~0,
+                                        }, sizeof(struct nv_device_class),
+                                        &drm->device);
+               if (ret)
+                       goto fail_device;
+
+               nouveau_agp_reset(drm);
+               nouveau_object_del(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE);
+       }
+
        ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE,
                                 0x0080, &(struct nv_device_class) {
                                        .device = ~0,
@@ -133,29 +291,88 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
        if (ret)
                goto fail_device;
 
-       ret = nouveau_load(dev, flags);
+       /* workaround an odd issue on nvc1 by disabling the device's
+        * nosnoop capability.  hopefully won't cause issues until a
+        * better fix is found - assuming there is one...
+        */
+       device = nv_device(drm->device);
+       if (nv_device(drm->device)->chipset == 0xc1)
+               nv_mask(device, 0x00088080, 0x00000800, 0x00000000);
+
+       nouveau_vga_init(drm);
+       nouveau_agp_init(drm);
+
+       if (device->card_type >= NV_50) {
+               ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40),
+                                    0x1000, &drm->client.base.vm);
+               if (ret)
+                       goto fail_device;
+       }
+
+       ret = nouveau_ttm_init(drm);
        if (ret)
-               goto fail_device;
+               goto fail_ttm;
+
+       ret = nouveau_bios_init(dev);
+       if (ret)
+               goto fail_bios;
+
+       ret = nouveau_irq_init(dev);
+       if (ret)
+               goto fail_irq;
+
+       ret = nouveau_display_create(dev);
+       if (ret)
+               goto fail_dispctor;
+
+       if (dev->mode_config.num_crtc) {
+               ret = nouveau_display_init(dev);
+               if (ret)
+                       goto fail_dispinit;
+       }
 
+       nouveau_pm_init(dev);
+
+       nouveau_accel_init(drm);
+       nouveau_fbcon_init(dev);
        return 0;
 
+fail_dispinit:
+       nouveau_display_destroy(dev);
+fail_dispctor:
+       nouveau_irq_fini(dev);
+fail_irq:
+       nouveau_bios_takedown(dev);
+fail_bios:
+       nouveau_ttm_fini(drm);
+fail_ttm:
+       nouveau_agp_fini(drm);
+       nouveau_vga_fini(drm);
 fail_device:
        nouveau_cli_destroy(&drm->client);
        return ret;
 }
 
-int
+static int
 nouveau_drm_unload(struct drm_device *dev)
 {
-       struct nouveau_drm *drm = nouveau_newpriv(dev);
-       struct pci_dev *pdev = dev->pdev;
-       int ret;
+       struct nouveau_drm *drm = nouveau_drm(dev);
 
-       ret = nouveau_unload(dev);
-       if (ret)
-               return ret;
+       nouveau_fbcon_fini(dev);
+       nouveau_accel_fini(drm);
+
+       nouveau_pm_fini(dev);
+
+       nouveau_display_fini(dev);
+       nouveau_display_destroy(dev);
+
+       nouveau_irq_fini(dev);
+       nouveau_bios_takedown(dev);
+
+       nouveau_ttm_fini(drm);
+       nouveau_agp_fini(drm);
+       nouveau_vga_fini(drm);
 
-       pci_set_drvdata(pdev, drm->client.base.device);
        nouveau_cli_destroy(&drm->client);
        return 0;
 }
@@ -163,17 +380,22 @@ nouveau_drm_unload(struct drm_device *dev)
 static void
 nouveau_drm_remove(struct pci_dev *pdev)
 {
-       struct nouveau_device *device;
-       nouveau_pci_remove(pdev);
-       device = pci_get_drvdata(pdev);
-       nouveau_device_destroy(&device);
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_object *device;
+
+       device = drm->client.base.device;
+       drm_put_dev(dev);
+
+       nouveau_object_ref(NULL, &device);
+       nouveau_object_debug();
 }
 
 int
 nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
-       struct nouveau_drm *drm = nouveau_newpriv(dev);
+       struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_cli *cli;
        int ret;
 
@@ -181,10 +403,23 @@ nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
            pm_state.event == PM_EVENT_PRETHAW)
                return 0;
 
-       ret = nouveau_pci_suspend(pdev, pm_state);
+       NV_INFO(drm, "suspending fbcon...\n");
+       nouveau_fbcon_set_suspend(dev, 1);
+
+       NV_INFO(drm, "suspending display...\n");
+       ret = nouveau_display_suspend(dev);
        if (ret)
                return ret;
 
+       NV_INFO(drm, "evicting buffers...\n");
+       ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM);
+
+       if (drm->fence && nouveau_fence(drm)->suspend) {
+               if (!nouveau_fence(drm)->suspend(drm))
+                       return -ENOMEM;
+       }
+
+       NV_INFO(drm, "suspending client object trees...\n");
        list_for_each_entry(cli, &drm->clients, head) {
                ret = nouveau_client_fini(&cli->base, true);
                if (ret)
@@ -195,6 +430,8 @@ nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
        if (ret)
                goto fail_client;
 
+       nouveau_agp_fini(drm);
+
        pci_save_state(pdev);
        if (pm_state.event == PM_EVENT_SUSPEND) {
                pci_disable_device(pdev);
@@ -208,7 +445,8 @@ fail_client:
                nouveau_client_init(&cli->base);
        }
 
-       nouveau_pci_resume(pdev);
+       NV_INFO(drm, "resuming display...\n");
+       nouveau_display_resume(dev);
        return ret;
 }
 
@@ -216,13 +454,14 @@ int
 nouveau_drm_resume(struct pci_dev *pdev)
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
-       struct nouveau_drm *drm = nouveau_newpriv(dev);
+       struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_cli *cli;
        int ret;
 
        if (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);
@@ -230,15 +469,165 @@ nouveau_drm_resume(struct pci_dev *pdev)
                return ret;
        pci_set_master(pdev);
 
+       nouveau_agp_reset(drm);
+
+       NV_INFO(drm, "resuming client object trees...\n");
        nouveau_client_init(&drm->client.base);
+       nouveau_agp_init(drm);
 
        list_for_each_entry(cli, &drm->clients, head) {
                nouveau_client_init(&cli->base);
        }
 
-       return nouveau_pci_resume(pdev);
+       if (drm->fence && nouveau_fence(drm)->resume)
+               nouveau_fence(drm)->resume(drm);
+
+       nouveau_run_vbios_init(dev);
+       nouveau_irq_postinstall(dev);
+       nouveau_pm_resume(dev);
+
+       NV_INFO(drm, "resuming display...\n");
+       nouveau_display_resume(dev);
+       return 0;
 }
 
+static int
+nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
+{
+       struct pci_dev *pdev = dev->pdev;
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_cli *cli;
+       char name[16];
+       int ret;
+
+       snprintf(name, sizeof(name), "%d", pid_nr(fpriv->pid));
+
+       ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli);
+       if (ret)
+               return ret;
+
+       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;
+               }
+       }
+
+       fpriv->driver_priv = cli;
+
+       mutex_lock(&drm->client.mutex);
+       list_add(&cli->head, &drm->clients);
+       mutex_unlock(&drm->client.mutex);
+       return 0;
+}
+
+static void
+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);
+
+       if (cli->abi16)
+               nouveau_abi16_fini(cli->abi16);
+
+       mutex_lock(&drm->client.mutex);
+       list_del(&cli->head);
+       mutex_unlock(&drm->client.mutex);
+}
+
+static void
+nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
+{
+       struct nouveau_cli *cli = nouveau_cli(fpriv);
+       nouveau_cli_destroy(cli);
+}
+
+static 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_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),
+};
+
+static const struct file_operations
+nouveau_driver_fops = {
+       .owner = THIS_MODULE,
+       .open = drm_open,
+       .release = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+       .mmap = nouveau_ttm_mmap,
+       .poll = drm_poll,
+       .fasync = drm_fasync,
+       .read = drm_read,
+#if defined(CONFIG_COMPAT)
+       .compat_ioctl = nouveau_compat_ioctl,
+#endif
+       .llseek = noop_llseek,
+};
+
+static struct drm_driver
+driver = {
+       .driver_features =
+               DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
+               DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM |
+               DRIVER_MODESET | DRIVER_PRIME,
+
+       .load = nouveau_drm_load,
+       .unload = nouveau_drm_unload,
+       .open = nouveau_drm_open,
+       .preclose = nouveau_drm_preclose,
+       .postclose = nouveau_drm_postclose,
+       .lastclose = nouveau_vga_lastclose,
+
+       .irq_preinstall = nouveau_irq_preinstall,
+       .irq_postinstall = nouveau_irq_postinstall,
+       .irq_uninstall = nouveau_irq_uninstall,
+       .irq_handler = nouveau_irq_handler,
+
+       .get_vblank_counter = drm_vblank_count,
+       .enable_vblank = nouveau_vblank_enable,
+       .disable_vblank = nouveau_vblank_disable,
+
+       .ioctls = nouveau_ioctls,
+       .fops = &nouveau_driver_fops,
+
+       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+       .gem_prime_export = nouveau_gem_prime_export,
+       .gem_prime_import = nouveau_gem_prime_import,
+
+       .gem_init_object = nouveau_gem_object_new,
+       .gem_free_object = nouveau_gem_object_del,
+       .gem_open_object = nouveau_gem_object_open,
+       .gem_close_object = nouveau_gem_object_close,
+
+       .dumb_create = nouveau_display_dumb_create,
+       .dumb_map_offset = nouveau_display_dumb_map_offset,
+       .dumb_destroy = nouveau_display_dumb_destroy,
+
+       .name = DRIVER_NAME,
+       .desc = DRIVER_DESC,
+#ifdef GIT_REVISION
+       .date = GIT_REVISION,
+#else
+       .date = DRIVER_DATE,
+#endif
+       .major = DRIVER_MAJOR,
+       .minor = DRIVER_MINOR,
+       .patchlevel = DRIVER_PATCHLEVEL,
+};
+
 static struct pci_device_id
 nouveau_drm_pci_table[] = {
        {
@@ -267,19 +656,38 @@ nouveau_drm_pci_driver = {
 static int __init
 nouveau_drm_init(void)
 {
-       return nouveau_init(&nouveau_drm_pci_driver);
+       driver.num_ioctls = ARRAY_SIZE(nouveau_ioctls);
+
+       if (nouveau_modeset == -1) {
+#ifdef CONFIG_VGA_CONSOLE
+               if (vgacon_text_force())
+                       nouveau_modeset = 0;
+               else
+#endif
+                       nouveau_modeset = 1;
+       }
+
+       if (!nouveau_modeset)
+               return 0;
+
+       nouveau_register_dsm_handler();
+       return drm_pci_init(&driver, &nouveau_drm_pci_driver);
 }
 
 static void __exit
 nouveau_drm_exit(void)
 {
-       nouveau_exit(&nouveau_drm_pci_driver);
+       if (!nouveau_modeset)
+               return;
+
+       drm_pci_exit(&driver, &nouveau_drm_pci_driver);
+       nouveau_unregister_dsm_handler();
 }
 
 module_init(nouveau_drm_init);
 module_exit(nouveau_drm_exit);
 
 MODULE_DEVICE_TABLE(pci, nouveau_drm_pci_table);
-MODULE_AUTHOR("Nouveau Project");
-MODULE_DESCRIPTION("nVidia Riva/TNT/GeForce/Quadro/Tesla");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL and additional rights");