1 | /* $NetBSD: nouveau_drm.c,v 1.8 2016/02/11 04:51:44 riastradh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright 2012 Red Hat Inc. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
22 | * OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * Authors: Ben Skeggs |
25 | */ |
26 | |
27 | #include <sys/cdefs.h> |
28 | __KERNEL_RCSID(0, "$NetBSD: nouveau_drm.c,v 1.8 2016/02/11 04:51:44 riastradh Exp $" ); |
29 | |
30 | #include <linux/console.h> |
31 | #include <linux/module.h> |
32 | #include <linux/pci.h> |
33 | #include <linux/pm_runtime.h> |
34 | #include <linux/vga_switcheroo.h> |
35 | #include "drmP.h" |
36 | #include "drm_crtc_helper.h" |
37 | #include <core/device.h> |
38 | #include <core/client.h> |
39 | #include <core/gpuobj.h> |
40 | #include <core/class.h> |
41 | #include <core/option.h> |
42 | |
43 | #include <engine/device.h> |
44 | #include <engine/disp.h> |
45 | #include <engine/fifo.h> |
46 | #include <engine/software.h> |
47 | |
48 | #include <subdev/vm.h> |
49 | |
50 | #include "nouveau_drm.h" |
51 | #include "nouveau_dma.h" |
52 | #include "nouveau_ttm.h" |
53 | #include "nouveau_gem.h" |
54 | #include "nouveau_agp.h" |
55 | #include "nouveau_vga.h" |
56 | #include "nouveau_sysfs.h" |
57 | #include "nouveau_hwmon.h" |
58 | #include "nouveau_acpi.h" |
59 | #include "nouveau_bios.h" |
60 | #include "nouveau_ioctl.h" |
61 | #include "nouveau_abi16.h" |
62 | #include "nouveau_fbcon.h" |
63 | #include "nouveau_fence.h" |
64 | #include "nouveau_debugfs.h" |
65 | #include "nouveau_ttm.h" |
66 | |
67 | MODULE_PARM_DESC(config, "option string to pass to driver core" ); |
68 | char *nouveau_config; |
69 | module_param_named(config, nouveau_config, charp, 0400); |
70 | |
71 | MODULE_PARM_DESC(debug, "debug string to pass to driver core" ); |
72 | char *nouveau_debug; |
73 | module_param_named(debug, nouveau_debug, charp, 0400); |
74 | |
75 | MODULE_PARM_DESC(noaccel, "disable kernel/abi16 acceleration" ); |
76 | static int nouveau_noaccel = 0; |
77 | module_param_named(noaccel, nouveau_noaccel, int, 0400); |
78 | |
79 | MODULE_PARM_DESC(modeset, "enable driver (default: auto, " |
80 | "0 = disabled, 1 = enabled, 2 = headless)" ); |
81 | int nouveau_modeset = -1; |
82 | module_param_named(modeset, nouveau_modeset, int, 0400); |
83 | |
84 | MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)" ); |
85 | int nouveau_runtime_pm = -1; |
86 | module_param_named(runpm, nouveau_runtime_pm, int, 0400); |
87 | |
88 | static struct drm_driver driver; |
89 | #ifdef __NetBSD__ |
90 | struct drm_driver *const nouveau_drm_driver = &driver; |
91 | |
92 | /* XXX Kludge for the non-GEM GEM that nouveau uses. */ |
93 | static const struct uvm_pagerops nouveau_gem_uvm_ops; |
94 | |
95 | #endif |
96 | |
97 | static u64 |
98 | nouveau_pci_name(struct pci_dev *pdev) |
99 | { |
100 | #ifdef __NetBSD__ |
101 | u64 name = (u64)device_unit(device_parent(pdev->pd_dev)) << 32; |
102 | name |= (u64)pdev->pd_pa.pa_bus << 16; |
103 | #else |
104 | u64 name = (u64)pci_domain_nr(pdev->bus) << 32; |
105 | name |= pdev->bus->number << 16; |
106 | #endif |
107 | name |= PCI_SLOT(pdev->devfn) << 8; |
108 | return name | PCI_FUNC(pdev->devfn); |
109 | } |
110 | |
111 | static u64 |
112 | nouveau_platform_name(struct platform_device *platformdev) |
113 | { |
114 | return platformdev->id; |
115 | } |
116 | |
117 | static u64 |
118 | nouveau_name(struct drm_device *dev) |
119 | { |
120 | if (dev->pdev) |
121 | return nouveau_pci_name(dev->pdev); |
122 | else |
123 | return nouveau_platform_name(dev->platformdev); |
124 | } |
125 | |
126 | static int |
127 | nouveau_cli_create(u64 name, const char *sname, |
128 | int size, void **pcli) |
129 | { |
130 | struct nouveau_cli *cli; |
131 | int ret; |
132 | |
133 | *pcli = NULL; |
134 | ret = nouveau_client_create_(sname, name, nouveau_config, |
135 | nouveau_debug, size, pcli); |
136 | cli = *pcli; |
137 | if (ret) { |
138 | if (cli) |
139 | nouveau_client_destroy(&cli->base); |
140 | *pcli = NULL; |
141 | return ret; |
142 | } |
143 | |
144 | #ifdef __NetBSD__ |
145 | linux_mutex_init(&cli->mutex); |
146 | #else |
147 | mutex_init(&cli->mutex); |
148 | #endif |
149 | return 0; |
150 | } |
151 | |
152 | static void |
153 | nouveau_cli_destroy(struct nouveau_cli *cli) |
154 | { |
155 | struct nouveau_object *client = nv_object(cli); |
156 | #ifdef __NetBSD__ |
157 | linux_mutex_destroy(&cli->mutex); |
158 | #else |
159 | mutex_destroy(&cli->mutex); |
160 | #endif |
161 | nouveau_vm_ref(NULL, &cli->base.vm, NULL); |
162 | nouveau_client_fini(&cli->base, false); |
163 | atomic_set(&client->refcount, 1); |
164 | nouveau_object_ref(NULL, &client); |
165 | } |
166 | |
167 | static void |
168 | nouveau_accel_fini(struct nouveau_drm *drm) |
169 | { |
170 | nouveau_gpuobj_ref(NULL, &drm->notify); |
171 | nouveau_channel_del(&drm->channel); |
172 | nouveau_channel_del(&drm->cechan); |
173 | if (drm->fence) |
174 | nouveau_fence(drm)->dtor(drm); |
175 | } |
176 | |
177 | static void |
178 | nouveau_accel_init(struct nouveau_drm *drm) |
179 | { |
180 | struct nouveau_device *device = nv_device(drm->device); |
181 | struct nouveau_object *object; |
182 | u32 arg0, arg1; |
183 | int ret; |
184 | |
185 | if (nouveau_noaccel || !nouveau_fifo(device) /*XXX*/) |
186 | return; |
187 | |
188 | /* initialise synchronisation routines */ |
189 | if (device->card_type < NV_10) ret = nv04_fence_create(drm); |
190 | else if (device->card_type < NV_11 || |
191 | device->chipset < 0x17) ret = nv10_fence_create(drm); |
192 | else if (device->card_type < NV_50) ret = nv17_fence_create(drm); |
193 | else if (device->chipset < 0x84) ret = nv50_fence_create(drm); |
194 | else if (device->card_type < NV_C0) ret = nv84_fence_create(drm); |
195 | else ret = nvc0_fence_create(drm); |
196 | if (ret) { |
197 | NV_ERROR(drm, "failed to initialise sync subsystem, %d\n" , ret); |
198 | nouveau_accel_fini(drm); |
199 | return; |
200 | } |
201 | |
202 | if (device->card_type >= NV_E0) { |
203 | ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE, |
204 | NVDRM_CHAN + 1, |
205 | NVE0_CHANNEL_IND_ENGINE_CE0 | |
206 | NVE0_CHANNEL_IND_ENGINE_CE1, 0, |
207 | &drm->cechan); |
208 | if (ret) |
209 | NV_ERROR(drm, "failed to create ce channel, %d\n" , ret); |
210 | |
211 | arg0 = NVE0_CHANNEL_IND_ENGINE_GR; |
212 | arg1 = 1; |
213 | } else |
214 | if (device->chipset >= 0xa3 && |
215 | device->chipset != 0xaa && |
216 | device->chipset != 0xac) { |
217 | ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE, |
218 | NVDRM_CHAN + 1, NvDmaFB, NvDmaTT, |
219 | &drm->cechan); |
220 | if (ret) |
221 | NV_ERROR(drm, "failed to create ce channel, %d\n" , ret); |
222 | |
223 | arg0 = NvDmaFB; |
224 | arg1 = NvDmaTT; |
225 | } else { |
226 | arg0 = NvDmaFB; |
227 | arg1 = NvDmaTT; |
228 | } |
229 | |
230 | ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE, NVDRM_CHAN, |
231 | arg0, arg1, &drm->channel); |
232 | if (ret) { |
233 | NV_ERROR(drm, "failed to create kernel channel, %d\n" , ret); |
234 | nouveau_accel_fini(drm); |
235 | return; |
236 | } |
237 | |
238 | ret = nouveau_object_new(nv_object(drm), NVDRM_CHAN, NVDRM_NVSW, |
239 | nouveau_abi16_swclass(drm), NULL, 0, &object); |
240 | if (ret == 0) { |
241 | struct nouveau_software_chan *swch = (void *)object->parent; |
242 | ret = RING_SPACE(drm->channel, 2); |
243 | if (ret == 0) { |
244 | if (device->card_type < NV_C0) { |
245 | BEGIN_NV04(drm->channel, NvSubSw, 0, 1); |
246 | OUT_RING (drm->channel, NVDRM_NVSW); |
247 | } else |
248 | if (device->card_type < NV_E0) { |
249 | BEGIN_NVC0(drm->channel, FermiSw, 0, 1); |
250 | OUT_RING (drm->channel, 0x001f0000); |
251 | } |
252 | } |
253 | swch = (void *)object->parent; |
254 | swch->flip = nouveau_flip_complete; |
255 | swch->flip_data = drm->channel; |
256 | } |
257 | |
258 | if (ret) { |
259 | NV_ERROR(drm, "failed to allocate software object, %d\n" , ret); |
260 | nouveau_accel_fini(drm); |
261 | return; |
262 | } |
263 | |
264 | if (device->card_type < NV_C0) { |
265 | ret = nouveau_gpuobj_new(drm->device, NULL, 32, 0, 0, |
266 | &drm->notify); |
267 | if (ret) { |
268 | NV_ERROR(drm, "failed to allocate notifier, %d\n" , ret); |
269 | nouveau_accel_fini(drm); |
270 | return; |
271 | } |
272 | |
273 | ret = nouveau_object_new(nv_object(drm), |
274 | drm->channel->handle, NvNotify0, |
275 | 0x003d, &(struct nv_dma_class) { |
276 | .flags = NV_DMA_TARGET_VRAM | |
277 | NV_DMA_ACCESS_RDWR, |
278 | .start = drm->notify->addr, |
279 | .limit = drm->notify->addr + 31 |
280 | }, sizeof(struct nv_dma_class), |
281 | &object); |
282 | if (ret) { |
283 | nouveau_accel_fini(drm); |
284 | return; |
285 | } |
286 | } |
287 | |
288 | |
289 | nouveau_bo_move_init(drm); |
290 | } |
291 | |
292 | #ifndef __NetBSD__ |
293 | static int nouveau_drm_probe(struct pci_dev *pdev, |
294 | const struct pci_device_id *pent) |
295 | { |
296 | struct nouveau_device *device; |
297 | struct apertures_struct *aper; |
298 | bool boot = false; |
299 | int ret; |
300 | |
301 | /* remove conflicting drivers (vesafb, efifb etc) */ |
302 | aper = alloc_apertures(3); |
303 | if (!aper) |
304 | return -ENOMEM; |
305 | |
306 | aper->ranges[0].base = pci_resource_start(pdev, 1); |
307 | aper->ranges[0].size = pci_resource_len(pdev, 1); |
308 | aper->count = 1; |
309 | |
310 | if (pci_resource_len(pdev, 2)) { |
311 | aper->ranges[aper->count].base = pci_resource_start(pdev, 2); |
312 | aper->ranges[aper->count].size = pci_resource_len(pdev, 2); |
313 | aper->count++; |
314 | } |
315 | |
316 | if (pci_resource_len(pdev, 3)) { |
317 | aper->ranges[aper->count].base = pci_resource_start(pdev, 3); |
318 | aper->ranges[aper->count].size = pci_resource_len(pdev, 3); |
319 | aper->count++; |
320 | } |
321 | |
322 | #ifdef CONFIG_X86 |
323 | boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; |
324 | #endif |
325 | remove_conflicting_framebuffers(aper, "nouveaufb" , boot); |
326 | kfree(aper); |
327 | |
328 | ret = nouveau_device_create(pdev, NOUVEAU_BUS_PCI, |
329 | nouveau_pci_name(pdev), pci_name(pdev), |
330 | nouveau_config, nouveau_debug, &device); |
331 | if (ret) |
332 | return ret; |
333 | |
334 | pci_set_master(pdev); |
335 | |
336 | ret = drm_get_pci_dev(pdev, pent, &driver); |
337 | if (ret) { |
338 | nouveau_object_ref(NULL, (struct nouveau_object **)&device); |
339 | return ret; |
340 | } |
341 | |
342 | return 0; |
343 | } |
344 | #endif |
345 | |
346 | #define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 |
347 | |
348 | static void |
349 | nouveau_get_hdmi_dev(struct nouveau_drm *drm) |
350 | { |
351 | #ifndef __NetBSD__ /* XXX nouveau hdmi */ |
352 | struct pci_dev *pdev = drm->dev->pdev; |
353 | |
354 | if (!pdev) { |
355 | DRM_INFO("not a PCI device; no HDMI\n" ); |
356 | drm->hdmi_device = NULL; |
357 | return; |
358 | } |
359 | |
360 | /* subfunction one is a hdmi audio device? */ |
361 | drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number, |
362 | PCI_DEVFN(PCI_SLOT(pdev->devfn), 1)); |
363 | |
364 | if (!drm->hdmi_device) { |
365 | NV_DEBUG(drm, "hdmi device not found %d %d %d\n" , pdev->bus->number, PCI_SLOT(pdev->devfn), 1); |
366 | return; |
367 | } |
368 | |
369 | if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) { |
370 | NV_DEBUG(drm, "possible hdmi device not audio %d\n" , drm->hdmi_device->class); |
371 | pci_dev_put(drm->hdmi_device); |
372 | drm->hdmi_device = NULL; |
373 | return; |
374 | } |
375 | #endif |
376 | } |
377 | |
378 | static int |
379 | nouveau_drm_load(struct drm_device *dev, unsigned long flags) |
380 | { |
381 | struct pci_dev *pdev = dev->pdev; |
382 | struct nouveau_device *device; |
383 | struct nouveau_drm *drm; |
384 | int ret; |
385 | |
386 | ret = nouveau_cli_create(nouveau_name(dev), "DRM" , sizeof(*drm), |
387 | (void **)&drm); |
388 | if (ret) |
389 | return ret; |
390 | |
391 | dev->dev_private = drm; |
392 | drm->dev = dev; |
393 | nouveau_client(drm)->debug = nouveau_dbgopt(nouveau_debug, "DRM" ); |
394 | |
395 | INIT_LIST_HEAD(&drm->clients); |
396 | spin_lock_init(&drm->tile.lock); |
397 | |
398 | nouveau_get_hdmi_dev(drm); |
399 | |
400 | /* make sure AGP controller is in a consistent state before we |
401 | * (possibly) execute vbios init tables (see nouveau_agp.h) |
402 | */ |
403 | if (pdev && drm_pci_device_is_agp(dev) && dev->agp) { |
404 | /* dummy device object, doesn't init anything, but allows |
405 | * agp code access to registers |
406 | */ |
407 | ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT, |
408 | NVDRM_DEVICE, 0x0080, |
409 | &(struct nv_device_class) { |
410 | .device = ~0, |
411 | .disable = |
412 | ~(NV_DEVICE_DISABLE_MMIO | |
413 | NV_DEVICE_DISABLE_IDENTIFY), |
414 | .debug0 = ~0, |
415 | }, sizeof(struct nv_device_class), |
416 | &drm->device); |
417 | if (ret) |
418 | goto fail_device; |
419 | |
420 | nouveau_agp_reset(drm); |
421 | nouveau_object_del(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE); |
422 | } |
423 | |
424 | ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE, |
425 | 0x0080, &(struct nv_device_class) { |
426 | .device = ~0, |
427 | .disable = 0, |
428 | .debug0 = 0, |
429 | }, sizeof(struct nv_device_class), |
430 | &drm->device); |
431 | if (ret) |
432 | goto fail_device; |
433 | |
434 | dev->irq_enabled = true; |
435 | |
436 | /* workaround an odd issue on nvc1 by disabling the device's |
437 | * nosnoop capability. hopefully won't cause issues until a |
438 | * better fix is found - assuming there is one... |
439 | */ |
440 | device = nv_device(drm->device); |
441 | if (nv_device(drm->device)->chipset == 0xc1) |
442 | nv_mask(device, 0x00088080, 0x00000800, 0x00000000); |
443 | |
444 | nouveau_vga_init(drm); |
445 | nouveau_agp_init(drm); |
446 | |
447 | if (device->card_type >= NV_50) { |
448 | ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40), |
449 | 0x1000, &drm->client.base.vm); |
450 | if (ret) |
451 | goto fail_device; |
452 | } |
453 | |
454 | ret = nouveau_ttm_init(drm); |
455 | if (ret) |
456 | goto fail_ttm; |
457 | |
458 | ret = nouveau_bios_init(dev); |
459 | if (ret) |
460 | goto fail_bios; |
461 | |
462 | ret = nouveau_display_create(dev); |
463 | if (ret) |
464 | goto fail_dispctor; |
465 | |
466 | if (dev->mode_config.num_crtc) { |
467 | ret = nouveau_display_init(dev); |
468 | if (ret) |
469 | goto fail_dispinit; |
470 | } |
471 | |
472 | nouveau_sysfs_init(dev); |
473 | nouveau_hwmon_init(dev); |
474 | nouveau_accel_init(drm); |
475 | nouveau_fbcon_init(dev); |
476 | |
477 | if (nouveau_runtime_pm != 0) { |
478 | pm_runtime_use_autosuspend(dev->dev); |
479 | pm_runtime_set_autosuspend_delay(dev->dev, 5000); |
480 | pm_runtime_set_active(dev->dev); |
481 | pm_runtime_allow(dev->dev); |
482 | pm_runtime_mark_last_busy(dev->dev); |
483 | pm_runtime_put(dev->dev); |
484 | } |
485 | return 0; |
486 | |
487 | fail_dispinit: |
488 | nouveau_display_destroy(dev); |
489 | fail_dispctor: |
490 | nouveau_bios_takedown(dev); |
491 | fail_bios: |
492 | nouveau_ttm_fini(drm); |
493 | fail_ttm: |
494 | nouveau_agp_fini(drm); |
495 | nouveau_vga_fini(drm); |
496 | fail_device: |
497 | nouveau_cli_destroy(&drm->client); |
498 | return ret; |
499 | } |
500 | |
501 | static int |
502 | nouveau_drm_unload(struct drm_device *dev) |
503 | { |
504 | struct nouveau_drm *drm = nouveau_drm(dev); |
505 | |
506 | pm_runtime_get_sync(dev->dev); |
507 | nouveau_fbcon_fini(dev); |
508 | nouveau_accel_fini(drm); |
509 | nouveau_hwmon_fini(dev); |
510 | nouveau_sysfs_fini(dev); |
511 | |
512 | if (dev->mode_config.num_crtc) |
513 | nouveau_display_fini(dev); |
514 | nouveau_display_destroy(dev); |
515 | |
516 | nouveau_bios_takedown(dev); |
517 | |
518 | nouveau_ttm_fini(drm); |
519 | nouveau_agp_fini(drm); |
520 | nouveau_vga_fini(drm); |
521 | |
522 | #ifndef __NetBSD__ /* XXX nouveau hdmi */ |
523 | if (drm->hdmi_device) |
524 | pci_dev_put(drm->hdmi_device); |
525 | #endif |
526 | nouveau_cli_destroy(&drm->client); |
527 | return 0; |
528 | } |
529 | |
530 | #ifndef __NetBSD__ /* XXX nouveau detach */ |
531 | static void |
532 | nouveau_drm_remove(struct pci_dev *pdev) |
533 | { |
534 | struct drm_device *dev = pci_get_drvdata(pdev); |
535 | struct nouveau_drm *drm = nouveau_drm(dev); |
536 | struct nouveau_object *device; |
537 | |
538 | dev->irq_enabled = false; |
539 | device = drm->client.base.device; |
540 | drm_put_dev(dev); |
541 | |
542 | nouveau_object_ref(NULL, &device); |
543 | nouveau_object_debug(); |
544 | } |
545 | #endif |
546 | |
547 | static int |
548 | nouveau_do_suspend(struct drm_device *dev, bool runtime) |
549 | { |
550 | struct nouveau_drm *drm = nouveau_drm(dev); |
551 | struct nouveau_cli *cli; |
552 | int ret; |
553 | |
554 | if (dev->mode_config.num_crtc && !runtime) { |
555 | NV_INFO(drm, "suspending display...\n" ); |
556 | ret = nouveau_display_suspend(dev); |
557 | if (ret) |
558 | return ret; |
559 | } |
560 | |
561 | NV_INFO(drm, "evicting buffers...\n" ); |
562 | ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM); |
563 | |
564 | NV_INFO(drm, "waiting for kernel channels to go idle...\n" ); |
565 | if (drm->cechan) { |
566 | ret = nouveau_channel_idle(drm->cechan); |
567 | if (ret) |
568 | goto fail_display; |
569 | } |
570 | |
571 | if (drm->channel) { |
572 | ret = nouveau_channel_idle(drm->channel); |
573 | if (ret) |
574 | goto fail_display; |
575 | } |
576 | |
577 | NV_INFO(drm, "suspending client object trees...\n" ); |
578 | if (drm->fence && nouveau_fence(drm)->suspend) { |
579 | if (!nouveau_fence(drm)->suspend(drm)) { |
580 | ret = -ENOMEM; |
581 | goto fail_display; |
582 | } |
583 | } |
584 | |
585 | list_for_each_entry(cli, &drm->clients, head) { |
586 | ret = nouveau_client_fini(&cli->base, true); |
587 | if (ret) |
588 | goto fail_client; |
589 | } |
590 | |
591 | NV_INFO(drm, "suspending kernel object tree...\n" ); |
592 | ret = nouveau_client_fini(&drm->client.base, true); |
593 | if (ret) |
594 | goto fail_client; |
595 | |
596 | nouveau_agp_fini(drm); |
597 | return 0; |
598 | |
599 | fail_client: |
600 | list_for_each_entry_continue_reverse(cli, &drm->clients, head) { |
601 | nouveau_client_init(&cli->base); |
602 | } |
603 | |
604 | if (drm->fence && nouveau_fence(drm)->resume) |
605 | nouveau_fence(drm)->resume(drm); |
606 | |
607 | fail_display: |
608 | if (dev->mode_config.num_crtc) { |
609 | NV_INFO(drm, "resuming display...\n" ); |
610 | nouveau_display_resume(dev); |
611 | } |
612 | return ret; |
613 | } |
614 | |
615 | #ifdef __NetBSD__ |
616 | int nouveau_pmops_suspend(struct drm_device *drm_dev) |
617 | #else |
618 | int nouveau_pmops_suspend(struct device *dev) |
619 | #endif |
620 | { |
621 | #ifndef __NetBSD__ |
622 | struct pci_dev *pdev = to_pci_dev(dev); |
623 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
624 | #endif |
625 | int ret; |
626 | |
627 | if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF || |
628 | drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF) |
629 | return 0; |
630 | |
631 | if (drm_dev->mode_config.num_crtc) |
632 | nouveau_fbcon_set_suspend(drm_dev, 1); |
633 | |
634 | ret = nouveau_do_suspend(drm_dev, false); |
635 | if (ret) |
636 | return ret; |
637 | |
638 | #ifndef __NetBSD__ /* pmf handles this for us. */ |
639 | pci_save_state(pdev); |
640 | pci_disable_device(pdev); |
641 | pci_set_power_state(pdev, PCI_D3hot); |
642 | #endif |
643 | return 0; |
644 | } |
645 | |
646 | static int |
647 | nouveau_do_resume(struct drm_device *dev) |
648 | { |
649 | struct nouveau_drm *drm = nouveau_drm(dev); |
650 | struct nouveau_cli *cli; |
651 | |
652 | NV_INFO(drm, "re-enabling device...\n" ); |
653 | |
654 | nouveau_agp_reset(drm); |
655 | |
656 | NV_INFO(drm, "resuming kernel object tree...\n" ); |
657 | nouveau_client_init(&drm->client.base); |
658 | nouveau_agp_init(drm); |
659 | |
660 | NV_INFO(drm, "resuming client object trees...\n" ); |
661 | if (drm->fence && nouveau_fence(drm)->resume) |
662 | nouveau_fence(drm)->resume(drm); |
663 | |
664 | list_for_each_entry(cli, &drm->clients, head) { |
665 | nouveau_client_init(&cli->base); |
666 | } |
667 | |
668 | nouveau_run_vbios_init(dev); |
669 | |
670 | if (dev->mode_config.num_crtc) { |
671 | NV_INFO(drm, "resuming display...\n" ); |
672 | nouveau_display_repin(dev); |
673 | } |
674 | |
675 | return 0; |
676 | } |
677 | |
678 | #ifdef __NetBSD__ |
679 | int nouveau_pmops_resume(struct drm_device *drm_dev) |
680 | #else |
681 | int nouveau_pmops_resume(struct device *dev) |
682 | #endif |
683 | { |
684 | #ifndef __NetBSD__ |
685 | struct pci_dev *pdev = to_pci_dev(dev); |
686 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
687 | #endif |
688 | int ret; |
689 | |
690 | if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF || |
691 | drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF) |
692 | return 0; |
693 | |
694 | #ifndef __NetBSD__ /* pmf handles this for us */ |
695 | pci_set_power_state(pdev, PCI_D0); |
696 | pci_restore_state(pdev); |
697 | ret = pci_enable_device(pdev); |
698 | if (ret) |
699 | return ret; |
700 | pci_set_master(pdev); |
701 | #endif |
702 | |
703 | ret = nouveau_do_resume(drm_dev); |
704 | if (ret) |
705 | return ret; |
706 | if (drm_dev->mode_config.num_crtc) |
707 | nouveau_fbcon_set_suspend(drm_dev, 0); |
708 | |
709 | nouveau_fbcon_zfill_all(drm_dev); |
710 | if (drm_dev->mode_config.num_crtc) |
711 | nouveau_display_resume(drm_dev); |
712 | return 0; |
713 | } |
714 | |
715 | #ifndef __NetBSD__ /* XXX nouveau pm */ |
716 | static int nouveau_pmops_freeze(struct device *dev) |
717 | { |
718 | struct pci_dev *pdev = to_pci_dev(dev); |
719 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
720 | int ret; |
721 | |
722 | if (drm_dev->mode_config.num_crtc) |
723 | nouveau_fbcon_set_suspend(drm_dev, 1); |
724 | |
725 | ret = nouveau_do_suspend(drm_dev, false); |
726 | return ret; |
727 | } |
728 | |
729 | static int nouveau_pmops_thaw(struct device *dev) |
730 | { |
731 | struct pci_dev *pdev = to_pci_dev(dev); |
732 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
733 | int ret; |
734 | |
735 | ret = nouveau_do_resume(drm_dev); |
736 | if (ret) |
737 | return ret; |
738 | if (drm_dev->mode_config.num_crtc) |
739 | nouveau_fbcon_set_suspend(drm_dev, 0); |
740 | nouveau_fbcon_zfill_all(drm_dev); |
741 | if (drm_dev->mode_config.num_crtc) |
742 | nouveau_display_resume(drm_dev); |
743 | return 0; |
744 | } |
745 | #endif /* XXX nouveau pm */ |
746 | |
747 | static int |
748 | nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) |
749 | { |
750 | struct nouveau_drm *drm = nouveau_drm(dev); |
751 | struct nouveau_cli *cli; |
752 | #ifdef __NetBSD__ |
753 | const char name[] = "user" ; |
754 | #else |
755 | char name[32], tmpname[TASK_COMM_LEN]; |
756 | #endif |
757 | int ret; |
758 | |
759 | /* need to bring up power immediately if opening device */ |
760 | ret = pm_runtime_get_sync(dev->dev); |
761 | if (ret < 0 && ret != -EACCES) |
762 | return ret; |
763 | |
764 | #ifndef __NetBSD__ |
765 | get_task_comm(tmpname, current); |
766 | snprintf(name, sizeof(name), "%s[%d]" , tmpname, pid_nr(fpriv->pid)); |
767 | #endif |
768 | |
769 | ret = nouveau_cli_create(nouveau_name(dev), name, sizeof(*cli), |
770 | (void **)&cli); |
771 | |
772 | if (ret) |
773 | goto out_suspend; |
774 | |
775 | if (nv_device(drm->device)->card_type >= NV_50) { |
776 | ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40), |
777 | 0x1000, &cli->base.vm); |
778 | if (ret) { |
779 | nouveau_cli_destroy(cli); |
780 | goto out_suspend; |
781 | } |
782 | } |
783 | |
784 | fpriv->driver_priv = cli; |
785 | |
786 | mutex_lock(&drm->client.mutex); |
787 | list_add(&cli->head, &drm->clients); |
788 | mutex_unlock(&drm->client.mutex); |
789 | |
790 | out_suspend: |
791 | pm_runtime_mark_last_busy(dev->dev); |
792 | pm_runtime_put_autosuspend(dev->dev); |
793 | |
794 | return ret; |
795 | } |
796 | |
797 | static void |
798 | nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv) |
799 | { |
800 | struct nouveau_cli *cli = nouveau_cli(fpriv); |
801 | struct nouveau_drm *drm = nouveau_drm(dev); |
802 | |
803 | pm_runtime_get_sync(dev->dev); |
804 | |
805 | if (cli->abi16) |
806 | nouveau_abi16_fini(cli->abi16); |
807 | |
808 | mutex_lock(&drm->client.mutex); |
809 | list_del(&cli->head); |
810 | mutex_unlock(&drm->client.mutex); |
811 | |
812 | } |
813 | |
814 | static void |
815 | nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv) |
816 | { |
817 | struct nouveau_cli *cli = nouveau_cli(fpriv); |
818 | nouveau_cli_destroy(cli); |
819 | pm_runtime_mark_last_busy(dev->dev); |
820 | pm_runtime_put_autosuspend(dev->dev); |
821 | } |
822 | |
823 | static const struct drm_ioctl_desc |
824 | nouveau_ioctls[] = { |
825 | DRM_IOCTL_DEF_DRV(NOUVEAU_GETPARAM, nouveau_abi16_ioctl_getparam, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
826 | DRM_IOCTL_DEF_DRV(NOUVEAU_SETPARAM, nouveau_abi16_ioctl_setparam, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), |
827 | DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_ALLOC, nouveau_abi16_ioctl_channel_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
828 | DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_FREE, nouveau_abi16_ioctl_channel_free, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
829 | DRM_IOCTL_DEF_DRV(NOUVEAU_GROBJ_ALLOC, nouveau_abi16_ioctl_grobj_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
830 | DRM_IOCTL_DEF_DRV(NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_abi16_ioctl_notifierobj_alloc, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
831 | DRM_IOCTL_DEF_DRV(NOUVEAU_GPUOBJ_FREE, nouveau_abi16_ioctl_gpuobj_free, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
832 | DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
833 | DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
834 | DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
835 | DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
836 | DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW), |
837 | }; |
838 | |
839 | #ifndef __NetBSD__ /* XXX nouveau pm */ |
840 | long nouveau_drm_ioctl(struct file *filp, |
841 | unsigned int cmd, unsigned long arg) |
842 | { |
843 | struct drm_file *file_priv = filp->private_data; |
844 | struct drm_device *dev; |
845 | long ret; |
846 | dev = file_priv->minor->dev; |
847 | |
848 | ret = pm_runtime_get_sync(dev->dev); |
849 | if (ret < 0 && ret != -EACCES) |
850 | return ret; |
851 | |
852 | ret = drm_ioctl(filp, cmd, arg); |
853 | |
854 | pm_runtime_mark_last_busy(dev->dev); |
855 | pm_runtime_put_autosuspend(dev->dev); |
856 | return ret; |
857 | } |
858 | static const struct file_operations |
859 | nouveau_driver_fops = { |
860 | .owner = THIS_MODULE, |
861 | .open = drm_open, |
862 | .release = drm_release, |
863 | .unlocked_ioctl = nouveau_drm_ioctl, |
864 | .mmap = nouveau_ttm_mmap, |
865 | .poll = drm_poll, |
866 | .read = drm_read, |
867 | #if defined(CONFIG_COMPAT) |
868 | .compat_ioctl = nouveau_compat_ioctl, |
869 | #endif |
870 | .llseek = noop_llseek, |
871 | }; |
872 | #endif |
873 | |
874 | static struct drm_driver |
875 | driver = { |
876 | .driver_features = |
877 | DRIVER_USE_AGP | |
878 | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER, |
879 | |
880 | .load = nouveau_drm_load, |
881 | .unload = nouveau_drm_unload, |
882 | .open = nouveau_drm_open, |
883 | .preclose = nouveau_drm_preclose, |
884 | .postclose = nouveau_drm_postclose, |
885 | .lastclose = nouveau_vga_lastclose, |
886 | |
887 | #if defined(CONFIG_DEBUG_FS) |
888 | .debugfs_init = nouveau_debugfs_init, |
889 | .debugfs_cleanup = nouveau_debugfs_takedown, |
890 | #endif |
891 | |
892 | .get_vblank_counter = drm_vblank_count, |
893 | .enable_vblank = nouveau_display_vblank_enable, |
894 | .disable_vblank = nouveau_display_vblank_disable, |
895 | .get_scanout_position = nouveau_display_scanoutpos, |
896 | .get_vblank_timestamp = nouveau_display_vblstamp, |
897 | |
898 | .ioctls = nouveau_ioctls, |
899 | .num_ioctls = ARRAY_SIZE(nouveau_ioctls), |
900 | #ifdef __NetBSD__ |
901 | .fops = NULL, |
902 | .mmap_object = &nouveau_ttm_mmap_object, |
903 | .gem_uvm_ops = &nouveau_gem_uvm_ops, |
904 | #else |
905 | .fops = &nouveau_driver_fops, |
906 | #endif |
907 | |
908 | #ifndef __NetBSD__ /* XXX drm prime */ |
909 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
910 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
911 | .gem_prime_export = drm_gem_prime_export, |
912 | .gem_prime_import = drm_gem_prime_import, |
913 | .gem_prime_pin = nouveau_gem_prime_pin, |
914 | .gem_prime_unpin = nouveau_gem_prime_unpin, |
915 | .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table, |
916 | .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, |
917 | .gem_prime_vmap = nouveau_gem_prime_vmap, |
918 | .gem_prime_vunmap = nouveau_gem_prime_vunmap, |
919 | #endif |
920 | |
921 | .gem_free_object = nouveau_gem_object_del, |
922 | .gem_open_object = nouveau_gem_object_open, |
923 | .gem_close_object = nouveau_gem_object_close, |
924 | |
925 | .dumb_create = nouveau_display_dumb_create, |
926 | .dumb_map_offset = nouveau_display_dumb_map_offset, |
927 | .dumb_destroy = drm_gem_dumb_destroy, |
928 | |
929 | .name = DRIVER_NAME, |
930 | .desc = DRIVER_DESC, |
931 | #ifdef GIT_REVISION |
932 | .date = GIT_REVISION, |
933 | #else |
934 | .date = DRIVER_DATE, |
935 | #endif |
936 | .major = DRIVER_MAJOR, |
937 | .minor = DRIVER_MINOR, |
938 | .patchlevel = DRIVER_PATCHLEVEL, |
939 | }; |
940 | |
941 | #ifndef __NetBSD__ |
942 | static struct pci_device_id |
943 | nouveau_drm_pci_table[] = { |
944 | { |
945 | PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), |
946 | .class = PCI_BASE_CLASS_DISPLAY << 16, |
947 | .class_mask = 0xff << 16, |
948 | }, |
949 | { |
950 | PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID), |
951 | .class = PCI_BASE_CLASS_DISPLAY << 16, |
952 | .class_mask = 0xff << 16, |
953 | }, |
954 | {} |
955 | }; |
956 | #endif |
957 | |
958 | #ifndef __NetBSD__ /* XXX nouveau pm */ |
959 | static int nouveau_pmops_runtime_suspend(struct device *dev) |
960 | { |
961 | struct pci_dev *pdev = to_pci_dev(dev); |
962 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
963 | int ret; |
964 | |
965 | if (nouveau_runtime_pm == 0) { |
966 | pm_runtime_forbid(dev); |
967 | return -EBUSY; |
968 | } |
969 | |
970 | /* are we optimus enabled? */ |
971 | if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { |
972 | DRM_DEBUG_DRIVER("failing to power off - not optimus\n" ); |
973 | pm_runtime_forbid(dev); |
974 | return -EBUSY; |
975 | } |
976 | |
977 | nv_debug_level(SILENT); |
978 | drm_kms_helper_poll_disable(drm_dev); |
979 | vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); |
980 | nouveau_switcheroo_optimus_dsm(); |
981 | ret = nouveau_do_suspend(drm_dev, true); |
982 | pci_save_state(pdev); |
983 | pci_disable_device(pdev); |
984 | pci_set_power_state(pdev, PCI_D3cold); |
985 | drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; |
986 | return ret; |
987 | } |
988 | |
989 | static int nouveau_pmops_runtime_resume(struct device *dev) |
990 | { |
991 | struct pci_dev *pdev = to_pci_dev(dev); |
992 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
993 | struct nouveau_device *device = nouveau_dev(drm_dev); |
994 | int ret; |
995 | |
996 | if (nouveau_runtime_pm == 0) |
997 | return -EINVAL; |
998 | |
999 | pci_set_power_state(pdev, PCI_D0); |
1000 | pci_restore_state(pdev); |
1001 | ret = pci_enable_device(pdev); |
1002 | if (ret) |
1003 | return ret; |
1004 | pci_set_master(pdev); |
1005 | |
1006 | ret = nouveau_do_resume(drm_dev); |
1007 | drm_kms_helper_poll_enable(drm_dev); |
1008 | /* do magic */ |
1009 | nv_mask(device, 0x88488, (1 << 25), (1 << 25)); |
1010 | vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); |
1011 | drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; |
1012 | nv_debug_level(NORMAL); |
1013 | return ret; |
1014 | } |
1015 | |
1016 | static int nouveau_pmops_runtime_idle(struct device *dev) |
1017 | { |
1018 | struct pci_dev *pdev = to_pci_dev(dev); |
1019 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
1020 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
1021 | struct drm_crtc *crtc; |
1022 | |
1023 | if (nouveau_runtime_pm == 0) { |
1024 | pm_runtime_forbid(dev); |
1025 | return -EBUSY; |
1026 | } |
1027 | |
1028 | /* are we optimus enabled? */ |
1029 | if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { |
1030 | DRM_DEBUG_DRIVER("failing to power off - not optimus\n" ); |
1031 | pm_runtime_forbid(dev); |
1032 | return -EBUSY; |
1033 | } |
1034 | |
1035 | #ifndef __NetBSD__ /* XXX nouveau hdmi */ |
1036 | /* if we have a hdmi audio device - make sure it has a driver loaded */ |
1037 | if (drm->hdmi_device) { |
1038 | if (!drm->hdmi_device->driver) { |
1039 | DRM_DEBUG_DRIVER("failing to power off - no HDMI audio driver loaded\n" ); |
1040 | pm_runtime_mark_last_busy(dev); |
1041 | return -EBUSY; |
1042 | } |
1043 | } |
1044 | #endif |
1045 | |
1046 | list_for_each_entry(crtc, &drm->dev->mode_config.crtc_list, head) { |
1047 | if (crtc->enabled) { |
1048 | DRM_DEBUG_DRIVER("failing to power off - crtc active\n" ); |
1049 | return -EBUSY; |
1050 | } |
1051 | } |
1052 | pm_runtime_mark_last_busy(dev); |
1053 | pm_runtime_autosuspend(dev); |
1054 | /* we don't want the main rpm_idle to call suspend - we want to autosuspend */ |
1055 | return 1; |
1056 | } |
1057 | |
1058 | static const struct dev_pm_ops nouveau_pm_ops = { |
1059 | .suspend = nouveau_pmops_suspend, |
1060 | .resume = nouveau_pmops_resume, |
1061 | .freeze = nouveau_pmops_freeze, |
1062 | .thaw = nouveau_pmops_thaw, |
1063 | .poweroff = nouveau_pmops_freeze, |
1064 | .restore = nouveau_pmops_resume, |
1065 | .runtime_suspend = nouveau_pmops_runtime_suspend, |
1066 | .runtime_resume = nouveau_pmops_runtime_resume, |
1067 | .runtime_idle = nouveau_pmops_runtime_idle, |
1068 | }; |
1069 | #endif /* XXX nouveau pm */ |
1070 | |
1071 | #ifndef __NetBSD__ |
1072 | static struct pci_driver |
1073 | nouveau_drm_pci_driver = { |
1074 | .name = "nouveau" , |
1075 | .id_table = nouveau_drm_pci_table, |
1076 | .probe = nouveau_drm_probe, |
1077 | .remove = nouveau_drm_remove, |
1078 | .driver.pm = &nouveau_pm_ops, |
1079 | }; |
1080 | |
1081 | int nouveau_drm_platform_probe(struct platform_device *pdev) |
1082 | { |
1083 | struct nouveau_device *device; |
1084 | int ret; |
1085 | |
1086 | ret = nouveau_device_create(pdev, NOUVEAU_BUS_PLATFORM, |
1087 | nouveau_platform_name(pdev), |
1088 | dev_name(&pdev->dev), nouveau_config, |
1089 | nouveau_debug, &device); |
1090 | |
1091 | ret = drm_platform_init(&driver, pdev); |
1092 | if (ret) { |
1093 | nouveau_object_ref(NULL, (struct nouveau_object **)&device); |
1094 | return ret; |
1095 | } |
1096 | |
1097 | return ret; |
1098 | } |
1099 | |
1100 | static int __init |
1101 | nouveau_drm_init(void) |
1102 | { |
1103 | if (nouveau_modeset == -1) { |
1104 | #ifdef CONFIG_VGA_CONSOLE |
1105 | if (vgacon_text_force()) |
1106 | nouveau_modeset = 0; |
1107 | #endif |
1108 | } |
1109 | |
1110 | if (!nouveau_modeset) |
1111 | return 0; |
1112 | |
1113 | nouveau_register_dsm_handler(); |
1114 | return drm_pci_init(&driver, &nouveau_drm_pci_driver); |
1115 | } |
1116 | |
1117 | static void __exit |
1118 | nouveau_drm_exit(void) |
1119 | { |
1120 | if (!nouveau_modeset) |
1121 | return; |
1122 | |
1123 | drm_pci_exit(&driver, &nouveau_drm_pci_driver); |
1124 | nouveau_unregister_dsm_handler(); |
1125 | } |
1126 | #endif |
1127 | |
1128 | module_init(nouveau_drm_init); |
1129 | module_exit(nouveau_drm_exit); |
1130 | |
1131 | MODULE_DEVICE_TABLE(pci, nouveau_drm_pci_table); |
1132 | MODULE_AUTHOR(DRIVER_AUTHOR); |
1133 | MODULE_DESCRIPTION(DRIVER_DESC); |
1134 | MODULE_LICENSE("GPL and additional rights" ); |
1135 | |