drm_stub.c 13.7 KB
Newer Older
L
Linus Torvalds 已提交
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>
L
Linus Torvalds 已提交
39

D
Dave Airlie 已提交
40
unsigned int drm_debug = 0;	/* 1 to enable debug output */
L
Linus Torvalds 已提交
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;

D
Dave Airlie 已提交
58 59
MODULE_AUTHOR(CORE_AUTHOR);
MODULE_DESCRIPTION(CORE_DESC);
L
Linus Torvalds 已提交
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");
L
Linus Torvalds 已提交
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);
L
Linus Torvalds 已提交
72

73 74
struct idr drm_minors_idr;

75
struct class *drm_class;
76
struct dentry *drm_debugfs_root;
J
Joe Perches 已提交
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 102
void drm_ut_debug_printk(unsigned int request_level,
			 const char *prefix,
			 const char *function_name,
			 const char *format, ...)
{
	va_list args;
L
Linus Torvalds 已提交
103

104 105 106 107 108 109 110 111 112
	if (drm_debug & request_level) {
		if (function_name)
			printk(KERN_DEBUG "[%s:%s], ", prefix, function_name);
		va_start(args, format);
		vprintk(format, args);
		va_end(args);
	}
}
EXPORT_SYMBOL(drm_ut_debug_printk);
J
Joe Perches 已提交
113

114 115 116 117 118
static int drm_minor_get_id(struct drm_device *dev, int type)
{
	int ret;
	int base = 0, limit = 63;

D
Dave Airlie 已提交
119
	if (type == DRM_MINOR_CONTROL) {
120 121 122 123 124 125
		base += 64;
		limit = base + 63;
	} else if (type == DRM_MINOR_RENDER) {
		base += 128;
		limit = base + 63;
	}
D
Dave Airlie 已提交
126

127
	mutex_lock(&dev->struct_mutex);
T
Tejun Heo 已提交
128
	ret = idr_alloc(&drm_minors_idr, NULL, base, limit, GFP_KERNEL);
129
	mutex_unlock(&dev->struct_mutex);
T
Tejun Heo 已提交
130 131

	return ret == -ENOSPC ? -EINVAL : ret;
132 133
}

134 135 136 137
struct drm_master *drm_master_create(struct drm_minor *minor)
{
	struct drm_master *master;

138
	master = kzalloc(sizeof(*master), GFP_KERNEL);
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
	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;
}
159
EXPORT_SYMBOL(drm_master_get);
160 161 162 163 164 165

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;
166
	struct drm_map_list *r_list, *list_temp;
167 168 169 170 171 172

	list_del(&master->head);

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

173 174 175 176 177 178 179
	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;
		}
	}

180
	if (master->unique) {
181
		kfree(master->unique);
182 183 184 185
		master->unique = NULL;
		master->unique_len = 0;
	}

186 187 188
	kfree(dev->devname);
	dev->devname = NULL;

189 190 191
	list_for_each_entry_safe(pt, next, &master->magicfree, head) {
		list_del(&pt->head);
		drm_ht_remove_item(&master->magiclist, &pt->hash_item);
192
		kfree(pt);
193 194 195 196
	}

	drm_ht_remove(&master->magiclist);

197
	kfree(master);
198 199 200 201 202 203 204
}

void drm_master_put(struct drm_master **master)
{
	kref_put(&(*master)->refcount, drm_master_destroy);
	*master = NULL;
}
205
EXPORT_SYMBOL(drm_master_put);
206 207 208 209

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

212 213 214
	if (file_priv->is_master)
		return 0;

215 216 217 218 219 220
	if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
		return -EINVAL;

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

221 222 223 224 225 226 227 228 229 230 231
	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);
232
		}
233
	}
234
	mutex_unlock(&dev->struct_mutex);
235

236
	return ret;
237 238 239 240 241
}

int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
{
242
	if (!file_priv->is_master)
243
		return -EINVAL;
244

245 246 247
	if (!file_priv->minor->master)
		return -EINVAL;

248
	mutex_lock(&dev->struct_mutex);
249 250
	if (dev->driver->master_drop)
		dev->driver->master_drop(dev, file_priv, false);
251
	drm_master_put(&file_priv->minor->master);
252
	file_priv->is_master = 0;
253 254 255 256
	mutex_unlock(&dev->struct_mutex);
	return 0;
}

L
Linus Torvalds 已提交
257 258 259 260 261 262 263
/**
 * Get a secondary minor number.
 *
 * \param dev device data structure
 * \param sec-minor structure to hold the assigned minor
 * \return negative number on failure.
 *
D
Daniel Vetter 已提交
264 265
 * Search an empty entry and initialize it to the given parameters. This
 * routines assigns minor numbers to secondary heads of multi-headed cards
L
Linus Torvalds 已提交
266
 */
267
int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
L
Linus Torvalds 已提交
268
{
269
	struct drm_minor *new_minor;
L
Linus Torvalds 已提交
270
	int ret;
271
	int minor_id;
L
Linus Torvalds 已提交
272 273 274

	DRM_DEBUG("\n");

275 276 277 278 279 280 281 282 283 284 285 286 287 288
	minor_id = drm_minor_get_id(dev, type);
	if (minor_id < 0)
		return minor_id;

	new_minor = kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
	if (!new_minor) {
		ret = -ENOMEM;
		goto err_idr;
	}

	new_minor->type = type;
	new_minor->device = MKDEV(DRM_MAJOR, minor_id);
	new_minor->dev = dev;
	new_minor->index = minor_id;
289
	INIT_LIST_HEAD(&new_minor->master_list);
290 291 292

	idr_replace(&drm_minors_idr, new_minor, minor_id);

293 294 295
#if defined(CONFIG_DEBUG_FS)
	ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root);
	if (ret) {
296
		DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
D
Daniel Vetter 已提交
297
		goto err_mem;
298 299
	}
#endif
300 301 302 303 304

	ret = drm_sysfs_device_add(new_minor);
	if (ret) {
		printk(KERN_ERR
		       "DRM: Error sysfs_device_add.\n");
D
Daniel Vetter 已提交
305
		goto err_debugfs;
L
Linus Torvalds 已提交
306
	}
307 308 309 310 311 312
	*minor = new_minor;

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


D
Daniel Vetter 已提交
313 314 315
err_debugfs:
#if defined(CONFIG_DEBUG_FS)
	drm_debugfs_cleanup(new_minor);
316
err_mem:
D
Daniel Vetter 已提交
317
#endif
318 319 320 321
	kfree(new_minor);
err_idr:
	idr_remove(&drm_minors_idr, minor_id);
	*minor = NULL;
L
Linus Torvalds 已提交
322 323
	return ret;
}
324
EXPORT_SYMBOL(drm_get_minor);
D
Dave Airlie 已提交
325

L
Linus Torvalds 已提交
326 327 328 329 330 331
/**
 * Put a secondary minor number.
 *
 * \param sec_minor - structure to be released
 * \return always zero
 */
332
int drm_put_minor(struct drm_minor **minor_p)
L
Linus Torvalds 已提交
333
{
334
	struct drm_minor *minor = *minor_p;
335

336
	DRM_DEBUG("release secondary minor %d\n", minor->index);
D
Dave Airlie 已提交
337

338 339 340 341
#if defined(CONFIG_DEBUG_FS)
	drm_debugfs_cleanup(minor);
#endif

342
	drm_sysfs_device_remove(minor);
L
Linus Torvalds 已提交
343

344
	idr_remove(&drm_minors_idr, minor->index);
D
Dave Airlie 已提交
345

346 347
	kfree(minor);
	*minor_p = NULL;
L
Linus Torvalds 已提交
348 349
	return 0;
}
350
EXPORT_SYMBOL(drm_put_minor);
351

352 353 354 355 356
static void drm_unplug_minor(struct drm_minor *minor)
{
	drm_sysfs_device_remove(minor);
}

357 358 359 360 361 362 363 364 365
/**
 * 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)
{
366
	struct drm_driver *driver;
367 368 369 370 371 372 373 374
	struct drm_map_list *r_list, *list_temp;

	DRM_DEBUG("\n");

	if (!dev) {
		DRM_ERROR("cleanup called no dev\n");
		return;
	}
375
	driver = dev->driver;
376 377 378 379 380 381

	drm_lastclose(dev);

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

382 383
	if (dev->driver->bus->agp_destroy)
		dev->driver->bus->agp_destroy(dev);
384

385 386
	drm_vblank_cleanup(dev);

387 388
	list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
		drm_rmmap(dev, r_list->map);
389 390
	drm_ht_remove(&dev->map_hash);

391
	drm_ctxbitmap_cleanup(dev);
392 393 394 395

	if (drm_core_check_feature(dev, DRIVER_MODESET))
		drm_put_minor(&dev->control);

396 397 398
	if (dev->render)
		drm_put_minor(&dev->render);

399 400 401 402 403
	if (driver->driver_features & DRIVER_GEM)
		drm_gem_destroy(dev);

	drm_put_minor(&dev->primary);

D
Dave Airlie 已提交
404
	list_del(&dev->driver_item);
405
	kfree(dev->devname);
406
	kfree(dev);
407 408
}
EXPORT_SYMBOL(drm_put_dev);
409 410 411 412 413 414

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);
415 416
	if (dev->render)
		drm_unplug_minor(dev->render);
417 418 419 420 421 422 423 424 425 426 427 428
	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);
D
David Herrmann 已提交
429 430 431 432 433 434 435

/**
 * 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.
436 437
 * Call drm_dev_register() to advertice the device to user space and register it
 * with other core subsystems.
D
David Herrmann 已提交
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
 *
 * 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;

	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);

	/* the DRM has 6 basic counters */
	dev->counters = 6;
	dev->types[0] = _DRM_STAT_LOCK;
	dev->types[1] = _DRM_STAT_OPENS;
	dev->types[2] = _DRM_STAT_CLOSES;
	dev->types[3] = _DRM_STAT_IOCTLS;
	dev->types[4] = _DRM_STAT_LOCKS;
	dev->types[5] = _DRM_STAT_UNLOCKS;

	if (drm_ht_create(&dev->map_hash, 12))
		goto err_free;

	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);
err_free:
	kfree(dev);
	return NULL;
}
EXPORT_SYMBOL(drm_dev_alloc);
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582

/**
 * 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);

	if (dev->driver->bus->agp_init) {
		ret = dev->driver->bus->agp_init(dev);
		if (ret)
			goto out_unlock;
	}

	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
		ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
		if (ret)
			goto err_agp;
	}

	if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
		ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
		if (ret)
			goto err_control_node;
	}

	ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
	if (ret)
		goto err_render_node;

	if (dev->driver->load) {
		ret = dev->driver->load(dev, flags);
		if (ret)
			goto err_primary_node;
	}

	/* 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;
	}

	list_add_tail(&dev->driver_item, &dev->driver->device_list);

	ret = 0;
	goto out_unlock;

err_unload:
	if (dev->driver->unload)
		dev->driver->unload(dev);
err_primary_node:
	drm_put_minor(&dev->primary);
err_render_node:
	if (dev->render)
		drm_put_minor(&dev->render);
err_control_node:
	if (dev->control)
		drm_put_minor(&dev->control);
err_agp:
	if (dev->driver->bus->agp_destroy)
		dev->driver->bus->agp_destroy(dev);
out_unlock:
	mutex_unlock(&drm_global_mutex);
	return ret;
}
EXPORT_SYMBOL(drm_dev_register);