drm_stub.c 16.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/**
 * \file drm_stub.h
 * Stub support
 *
 * \author Rickard E. (Rik) Faith <faith@valinux.com>
 */

/*
 * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
 *
 * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
36
#include <linux/slab.h>
37 38
#include <drm/drmP.h>
#include <drm/drm_core.h>
Linus Torvalds's avatar
Linus Torvalds committed
39

40
unsigned int drm_debug = 0;	/* 1 to enable debug output */
Linus Torvalds's avatar
Linus Torvalds committed
41 42
EXPORT_SYMBOL(drm_debug);

43 44 45
unsigned int drm_rnodes = 0;	/* 1 to enable experimental render nodes API */
EXPORT_SYMBOL(drm_rnodes);

46 47 48 49 50 51
unsigned int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
EXPORT_SYMBOL(drm_vblank_offdelay);

unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
EXPORT_SYMBOL(drm_timestamp_precision);

52 53 54 55 56 57
/*
 * Default to use monotonic timestamps for wait-for-vblank and page-flip
 * complete events.
 */
unsigned int drm_timestamp_monotonic = 1;

58 59
MODULE_AUTHOR(CORE_AUTHOR);
MODULE_DESCRIPTION(CORE_DESC);
Linus Torvalds's avatar
Linus Torvalds committed
60 61
MODULE_LICENSE("GPL and additional rights");
MODULE_PARM_DESC(debug, "Enable debug output");
62
MODULE_PARM_DESC(rnodes, "Enable experimental render nodes API");
63 64
MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
65
MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
Linus Torvalds's avatar
Linus Torvalds committed
66

67
module_param_named(debug, drm_debug, int, 0600);
68
module_param_named(rnodes, drm_rnodes, int, 0600);
69 70
module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
71
module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
Linus Torvalds's avatar
Linus Torvalds committed
72

73 74
struct idr drm_minors_idr;

75
struct class *drm_class;
76
struct dentry *drm_debugfs_root;
Joe Perches's avatar
Joe Perches committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

int drm_err(const char *func, const char *format, ...)
{
	struct va_format vaf;
	va_list args;
	int r;

	va_start(args, format);

	vaf.fmt = format;
	vaf.va = &args;

	r = printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf);

	va_end(args);

	return r;
}
EXPORT_SYMBOL(drm_err);

97 98 99 100 101
void drm_ut_debug_printk(unsigned int request_level,
			 const char *prefix,
			 const char *function_name,
			 const char *format, ...)
{
102
	struct va_format vaf;
103
	va_list args;
Linus Torvalds's avatar
Linus Torvalds committed
104

105 106
	if (drm_debug & request_level) {
		va_start(args, format);
107 108 109 110 111 112 113 114
		vaf.fmt = format;
		vaf.va = &args;

		if (function_name)
			printk(KERN_DEBUG "[%s:%s], %pV", prefix,
			       function_name, &vaf);
		else
			printk(KERN_DEBUG "%pV", &vaf);
115 116 117 118
		va_end(args);
	}
}
EXPORT_SYMBOL(drm_ut_debug_printk);
Joe Perches's avatar
Joe Perches committed
119

120 121 122 123 124
static int drm_minor_get_id(struct drm_device *dev, int type)
{
	int ret;
	int base = 0, limit = 63;

Dave Airlie's avatar
Dave Airlie committed
125
	if (type == DRM_MINOR_CONTROL) {
126 127 128 129 130 131
		base += 64;
		limit = base + 63;
	} else if (type == DRM_MINOR_RENDER) {
		base += 128;
		limit = base + 63;
	}
Dave Airlie's avatar
Dave Airlie committed
132

133
	mutex_lock(&dev->struct_mutex);
Tejun Heo's avatar
Tejun Heo committed
134
	ret = idr_alloc(&drm_minors_idr, NULL, base, limit, GFP_KERNEL);
135
	mutex_unlock(&dev->struct_mutex);
Tejun Heo's avatar
Tejun Heo committed
136 137

	return ret == -ENOSPC ? -EINVAL : ret;
138 139
}

140 141 142 143
struct drm_master *drm_master_create(struct drm_minor *minor)
{
	struct drm_master *master;

144
	master = kzalloc(sizeof(*master), GFP_KERNEL);
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	if (!master)
		return NULL;

	kref_init(&master->refcount);
	spin_lock_init(&master->lock.spinlock);
	init_waitqueue_head(&master->lock.lock_queue);
	drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER);
	INIT_LIST_HEAD(&master->magicfree);
	master->minor = minor;

	list_add_tail(&master->head, &minor->master_list);

	return master;
}

struct drm_master *drm_master_get(struct drm_master *master)
{
	kref_get(&master->refcount);
	return master;
}
165
EXPORT_SYMBOL(drm_master_get);
166 167 168 169 170 171

static void drm_master_destroy(struct kref *kref)
{
	struct drm_master *master = container_of(kref, struct drm_master, refcount);
	struct drm_magic_entry *pt, *next;
	struct drm_device *dev = master->minor->dev;
172
	struct drm_map_list *r_list, *list_temp;
173 174 175 176 177 178

	list_del(&master->head);

	if (dev->driver->master_destroy)
		dev->driver->master_destroy(dev, master);

179 180 181 182 183 184 185
	list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) {
		if (r_list->master == master) {
			drm_rmmap_locked(dev, r_list->map);
			r_list = NULL;
		}
	}

186
	if (master->unique) {
187
		kfree(master->unique);
188 189 190 191
		master->unique = NULL;
		master->unique_len = 0;
	}

192 193 194
	kfree(dev->devname);
	dev->devname = NULL;

195 196 197
	list_for_each_entry_safe(pt, next, &master->magicfree, head) {
		list_del(&pt->head);
		drm_ht_remove_item(&master->magiclist, &pt->hash_item);
198
		kfree(pt);
199 200 201 202
	}

	drm_ht_remove(&master->magiclist);

203
	kfree(master);
204 205 206 207 208 209 210
}

void drm_master_put(struct drm_master **master)
{
	kref_put(&(*master)->refcount, drm_master_destroy);
	*master = NULL;
}
211
EXPORT_SYMBOL(drm_master_put);
212 213 214 215

int drm_setmaster_ioctl(struct drm_device *dev, void *data,
			struct drm_file *file_priv)
{
216
	int ret = 0;
217

218 219 220
	if (file_priv->is_master)
		return 0;

221 222 223 224 225 226
	if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
		return -EINVAL;

	if (!file_priv->master)
		return -EINVAL;

227 228 229 230 231 232 233 234 235 236 237
	if (file_priv->minor->master)
		return -EINVAL;

	mutex_lock(&dev->struct_mutex);
	file_priv->minor->master = drm_master_get(file_priv->master);
	file_priv->is_master = 1;
	if (dev->driver->master_set) {
		ret = dev->driver->master_set(dev, file_priv, false);
		if (unlikely(ret != 0)) {
			file_priv->is_master = 0;
			drm_master_put(&file_priv->minor->master);
238
		}
239
	}
240
	mutex_unlock(&dev->struct_mutex);
241

242
	return ret;
243 244 245 246 247
}

int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
{
248
	if (!file_priv->is_master)
249
		return -EINVAL;
250

251 252 253
	if (!file_priv->minor->master)
		return -EINVAL;

254
	mutex_lock(&dev->struct_mutex);
255 256
	if (dev->driver->master_drop)
		dev->driver->master_drop(dev, file_priv, false);
257
	drm_master_put(&file_priv->minor->master);
258
	file_priv->is_master = 0;
259 260 261 262
	mutex_unlock(&dev->struct_mutex);
	return 0;
}

David Herrmann's avatar
David Herrmann committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
					     unsigned int type)
{
	switch (type) {
	case DRM_MINOR_LEGACY:
		return &dev->primary;
	case DRM_MINOR_RENDER:
		return &dev->render;
	case DRM_MINOR_CONTROL:
		return &dev->control;
	default:
		return NULL;
	}
}

static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
{
	struct drm_minor *minor;

	minor = kzalloc(sizeof(*minor), GFP_KERNEL);
	if (!minor)
		return -ENOMEM;

	minor->type = type;
	minor->dev = dev;
	INIT_LIST_HEAD(&minor->master_list);

	*drm_minor_get_slot(dev, type) = minor;
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
294
/**
David Herrmann's avatar
David Herrmann committed
295
 * drm_get_minor - Register DRM minor
296 297
 * @dev: DRM device
 * @type: Type of minor
Linus Torvalds's avatar
Linus Torvalds committed
298
 *
David Herrmann's avatar
David Herrmann committed
299
 * Register minor of given type.
300
 * Caller must hold the global DRM mutex.
Linus Torvalds's avatar
Linus Torvalds committed
301
 *
302 303
 * RETURNS:
 * 0 on success, negative error code on failure.
Linus Torvalds's avatar
Linus Torvalds committed
304
 */
David Herrmann's avatar
David Herrmann committed
305
static int drm_get_minor(struct drm_device *dev, unsigned int type)
Linus Torvalds's avatar
Linus Torvalds committed
306
{
307
	struct drm_minor *new_minor;
Linus Torvalds's avatar
Linus Torvalds committed
308
	int ret;
309
	int minor_id;
Linus Torvalds's avatar
Linus Torvalds committed
310 311 312

	DRM_DEBUG("\n");

David Herrmann's avatar
David Herrmann committed
313 314 315 316
	new_minor = *drm_minor_get_slot(dev, type);
	if (!new_minor)
		return 0;

317 318 319 320 321 322 323 324 325
	minor_id = drm_minor_get_id(dev, type);
	if (minor_id < 0)
		return minor_id;

	new_minor->device = MKDEV(DRM_MAJOR, minor_id);
	new_minor->index = minor_id;

	idr_replace(&drm_minors_idr, new_minor, minor_id);

326 327 328
#if defined(CONFIG_DEBUG_FS)
	ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root);
	if (ret) {
329
		DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
330
		goto err_mem;
331 332
	}
#endif
333 334 335 336 337

	ret = drm_sysfs_device_add(new_minor);
	if (ret) {
		printk(KERN_ERR
		       "DRM: Error sysfs_device_add.\n");
338
		goto err_debugfs;
Linus Torvalds's avatar
Linus Torvalds committed
339
	}
340 341 342 343 344

	DRM_DEBUG("new minor assigned %d\n", minor_id);
	return 0;


345 346 347
err_debugfs:
#if defined(CONFIG_DEBUG_FS)
	drm_debugfs_cleanup(new_minor);
348
err_mem:
349
#endif
350
	idr_remove(&drm_minors_idr, minor_id);
Linus Torvalds's avatar
Linus Torvalds committed
351 352
	return ret;
}
353

Linus Torvalds's avatar
Linus Torvalds committed
354
/**
355 356
 * drm_unplug_minor - Unplug DRM minor
 * @minor: Minor to unplug
Linus Torvalds's avatar
Linus Torvalds committed
357
 *
358 359 360 361 362 363 364 365
 * Unplugs the given DRM minor but keeps the object. So after this returns,
 * minor->dev is still valid so existing open-files can still access it to get
 * device information from their drm_file ojects.
 * If the minor is already unplugged or if @minor is NULL, nothing is done.
 * The global DRM mutex must be held by the caller.
 */
static void drm_unplug_minor(struct drm_minor *minor)
{
366
	if (!minor || !minor->kdev)
367 368
		return;

369 370 371 372
#if defined(CONFIG_DEBUG_FS)
	drm_debugfs_cleanup(minor);
#endif

373
	drm_sysfs_device_remove(minor);
374
	idr_remove(&drm_minors_idr, minor->index);
375 376
}

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
/**
 * drm_minor_acquire - Acquire a DRM minor
 * @minor_id: Minor ID of the DRM-minor
 *
 * Looks up the given minor-ID and returns the respective DRM-minor object. The
 * refence-count of the underlying device is increased so you must release this
 * object with drm_minor_release().
 *
 * As long as you hold this minor, it is guaranteed that the object and the
 * minor->dev pointer will stay valid! However, the device may get unplugged and
 * unregistered while you hold the minor.
 *
 * Returns:
 * Pointer to minor-object with increased device-refcount, or PTR_ERR on
 * failure.
 */
struct drm_minor *drm_minor_acquire(unsigned int minor_id)
{
	struct drm_minor *minor;

	minor = idr_find(&drm_minors_idr, minor_id);
	if (!minor)
		return ERR_PTR(-ENODEV);

	drm_dev_ref(minor->dev);
	return minor;
}

/**
 * drm_minor_release - Release DRM minor
 * @minor: Pointer to DRM minor object
 *
 * Release a minor that was previously acquired via drm_minor_acquire().
 */
void drm_minor_release(struct drm_minor *minor)
{
	drm_dev_unref(minor->dev);
}

416 417
/**
 * drm_put_minor - Destroy DRM minor
418
 * @minor: Minor to destroy
419
 *
420 421 422
 * This calls drm_unplug_minor() on the given minor and then frees it. Nothing
 * is done if @minor is NULL. It is fine to call this on already unplugged
 * minors.
423
 * The global DRM mutex must be held by the caller.
Linus Torvalds's avatar
Linus Torvalds committed
424
 */
425
static void drm_put_minor(struct drm_minor *minor)
Linus Torvalds's avatar
Linus Torvalds committed
426
{
427 428
	if (!minor)
		return;
429

430
	DRM_DEBUG("release secondary minor %d\n", minor->index);
431

432
	drm_unplug_minor(minor);
433
	kfree(minor);
Linus Torvalds's avatar
Linus Torvalds committed
434
}
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451

/**
 * Called via drm_exit() at module unload time or when pci device is
 * unplugged.
 *
 * Cleans up all DRM device, calling drm_lastclose().
 *
 */
void drm_put_dev(struct drm_device *dev)
{
	DRM_DEBUG("\n");

	if (!dev) {
		DRM_ERROR("cleanup called no dev\n");
		return;
	}

452
	drm_dev_unregister(dev);
453
	drm_dev_unref(dev);
454 455
}
EXPORT_SYMBOL(drm_put_dev);
456 457 458 459 460 461

void drm_unplug_dev(struct drm_device *dev)
{
	/* for a USB device */
	if (drm_core_check_feature(dev, DRIVER_MODESET))
		drm_unplug_minor(dev->control);
462 463
	if (dev->render)
		drm_unplug_minor(dev->render);
464 465 466 467 468 469 470 471 472 473 474 475
	drm_unplug_minor(dev->primary);

	mutex_lock(&drm_global_mutex);

	drm_device_set_unplugged(dev);

	if (dev->open_count == 0) {
		drm_put_dev(dev);
	}
	mutex_unlock(&drm_global_mutex);
}
EXPORT_SYMBOL(drm_unplug_dev);
476 477 478 479 480 481 482

/**
 * drm_dev_alloc - Allocate new drm device
 * @driver: DRM driver to allocate device for
 * @parent: Parent device object
 *
 * Allocate and initialize a new DRM device. No device registration is done.
483 484
 * Call drm_dev_register() to advertice the device to user space and register it
 * with other core subsystems.
485
 *
486 487 488
 * The initial ref-count of the object is 1. Use drm_dev_ref() and
 * drm_dev_unref() to take and drop further ref-counts.
 *
489 490 491 492 493 494 495 496 497 498 499 500 501
 * RETURNS:
 * Pointer to new DRM device, or NULL if out of memory.
 */
struct drm_device *drm_dev_alloc(struct drm_driver *driver,
				 struct device *parent)
{
	struct drm_device *dev;
	int ret;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return NULL;

502
	kref_init(&dev->ref);
503 504 505 506 507 508 509 510 511 512 513 514 515 516
	dev->dev = parent;
	dev->driver = driver;

	INIT_LIST_HEAD(&dev->filelist);
	INIT_LIST_HEAD(&dev->ctxlist);
	INIT_LIST_HEAD(&dev->vmalist);
	INIT_LIST_HEAD(&dev->maplist);
	INIT_LIST_HEAD(&dev->vblank_event_list);

	spin_lock_init(&dev->count_lock);
	spin_lock_init(&dev->event_lock);
	mutex_init(&dev->struct_mutex);
	mutex_init(&dev->ctxlist_mutex);

David Herrmann's avatar
David Herrmann committed
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
		ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL);
		if (ret)
			goto err_minors;
	}

	if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
		ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
		if (ret)
			goto err_minors;
	}

	ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY);
	if (ret)
		goto err_minors;

533
	if (drm_ht_create(&dev->map_hash, 12))
David Herrmann's avatar
David Herrmann committed
534
		goto err_minors;
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555

	ret = drm_ctxbitmap_init(dev);
	if (ret) {
		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
		goto err_ht;
	}

	if (driver->driver_features & DRIVER_GEM) {
		ret = drm_gem_init(dev);
		if (ret) {
			DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
			goto err_ctxbitmap;
		}
	}

	return dev;

err_ctxbitmap:
	drm_ctxbitmap_cleanup(dev);
err_ht:
	drm_ht_remove(&dev->map_hash);
David Herrmann's avatar
David Herrmann committed
556 557 558 559
err_minors:
	drm_put_minor(dev->control);
	drm_put_minor(dev->render);
	drm_put_minor(dev->primary);
560 561 562 563
	kfree(dev);
	return NULL;
}
EXPORT_SYMBOL(drm_dev_alloc);
564

565
static void drm_dev_release(struct kref *ref)
566
{
567 568
	struct drm_device *dev = container_of(ref, struct drm_device, ref);

569 570 571 572
	drm_put_minor(dev->control);
	drm_put_minor(dev->render);
	drm_put_minor(dev->primary);

573 574 575 576 577 578 579 580 581
	if (dev->driver->driver_features & DRIVER_GEM)
		drm_gem_destroy(dev);

	drm_ctxbitmap_cleanup(dev);
	drm_ht_remove(&dev->map_hash);

	kfree(dev->devname);
	kfree(dev);
}
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614

/**
 * drm_dev_ref - Take reference of a DRM device
 * @dev: device to take reference of or NULL
 *
 * This increases the ref-count of @dev by one. You *must* already own a
 * reference when calling this. Use drm_dev_unref() to drop this reference
 * again.
 *
 * This function never fails. However, this function does not provide *any*
 * guarantee whether the device is alive or running. It only provides a
 * reference to the object and the memory associated with it.
 */
void drm_dev_ref(struct drm_device *dev)
{
	if (dev)
		kref_get(&dev->ref);
}
EXPORT_SYMBOL(drm_dev_ref);

/**
 * drm_dev_unref - Drop reference of a DRM device
 * @dev: device to drop reference of or NULL
 *
 * This decreases the ref-count of @dev by one. The device is destroyed if the
 * ref-count drops to zero.
 */
void drm_dev_unref(struct drm_device *dev)
{
	if (dev)
		kref_put(&dev->ref, drm_dev_release);
}
EXPORT_SYMBOL(drm_dev_unref);
615

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
/**
 * drm_dev_register - Register DRM device
 * @dev: Device to register
 *
 * Register the DRM device @dev with the system, advertise device to user-space
 * and start normal device operation. @dev must be allocated via drm_dev_alloc()
 * previously.
 *
 * Never call this twice on any device!
 *
 * RETURNS:
 * 0 on success, negative error code on failure.
 */
int drm_dev_register(struct drm_device *dev, unsigned long flags)
{
	int ret;

	mutex_lock(&drm_global_mutex);

David Herrmann's avatar
David Herrmann committed
635 636 637
	ret = drm_get_minor(dev, DRM_MINOR_CONTROL);
	if (ret)
		goto err_minors;
638

David Herrmann's avatar
David Herrmann committed
639 640 641
	ret = drm_get_minor(dev, DRM_MINOR_RENDER);
	if (ret)
		goto err_minors;
642

David Herrmann's avatar
David Herrmann committed
643
	ret = drm_get_minor(dev, DRM_MINOR_LEGACY);
644
	if (ret)
David Herrmann's avatar
David Herrmann committed
645
		goto err_minors;
646 647 648 649

	if (dev->driver->load) {
		ret = dev->driver->load(dev, flags);
		if (ret)
David Herrmann's avatar
David Herrmann committed
650
			goto err_minors;
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
	}

	/* setup grouping for legacy outputs */
	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
		ret = drm_mode_group_init_legacy_group(dev,
				&dev->primary->mode_group);
		if (ret)
			goto err_unload;
	}

	ret = 0;
	goto out_unlock;

err_unload:
	if (dev->driver->unload)
		dev->driver->unload(dev);
David Herrmann's avatar
David Herrmann committed
667
err_minors:
668
	drm_unplug_minor(dev->control);
David Herrmann's avatar
David Herrmann committed
669 670
	drm_unplug_minor(dev->render);
	drm_unplug_minor(dev->primary);
671 672 673 674 675
out_unlock:
	mutex_unlock(&drm_global_mutex);
	return ret;
}
EXPORT_SYMBOL(drm_dev_register);
676 677 678 679 680 681 682

/**
 * drm_dev_unregister - Unregister DRM device
 * @dev: Device to unregister
 *
 * Unregister the DRM device from the system. This does the reverse of
 * drm_dev_register() but does not deallocate the device. The caller must call
683
 * drm_dev_unref() to drop their final reference.
684 685 686 687 688 689 690 691 692 693
 */
void drm_dev_unregister(struct drm_device *dev)
{
	struct drm_map_list *r_list, *list_temp;

	drm_lastclose(dev);

	if (dev->driver->unload)
		dev->driver->unload(dev);

694 695
	if (dev->agp)
		drm_pci_agp_destroy(dev);
696 697 698 699 700 701

	drm_vblank_cleanup(dev);

	list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
		drm_rmmap(dev, r_list->map);

702 703 704
	drm_unplug_minor(dev->control);
	drm_unplug_minor(dev->render);
	drm_unplug_minor(dev->primary);
705 706
}
EXPORT_SYMBOL(drm_dev_unregister);