drm_stub.c 11.4 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
36
37
38
/**
 * \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>
#include "drmP.h"
#include "drm_core.h"

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

Dave Airlie's avatar
Dave Airlie committed
42
43
MODULE_AUTHOR(CORE_AUTHOR);
MODULE_DESCRIPTION(CORE_DESC);
Linus Torvalds's avatar
Linus Torvalds committed
44
45
46
MODULE_LICENSE("GPL and additional rights");
MODULE_PARM_DESC(debug, "Enable debug output");

47
module_param_named(debug, drm_debug, int, 0600);
Linus Torvalds's avatar
Linus Torvalds committed
48

49
50
struct idr drm_minors_idr;

51
struct class *drm_class;
Linus Torvalds's avatar
Linus Torvalds committed
52
53
struct proc_dir_entry *drm_proc_root;

54
55
56
57
58
59
static int drm_minor_get_id(struct drm_device *dev, int type)
{
	int new_id;
	int ret;
	int base = 0, limit = 63;

Dave Airlie's avatar
Dave Airlie committed
60
61
62
63
64
65
66
67
	if (type == DRM_MINOR_CONTROL) {
                base += 64;
                limit = base + 127;
        } else if (type == DRM_MINOR_RENDER) {
                base += 128;
                limit = base + 255;
        }

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
again:
	if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) {
		DRM_ERROR("Out of memory expanding drawable idr\n");
		return -ENOMEM;
	}
	mutex_lock(&dev->struct_mutex);
	ret = idr_get_new_above(&drm_minors_idr, NULL,
				base, &new_id);
	mutex_unlock(&dev->struct_mutex);
	if (ret == -EAGAIN) {
		goto again;
	} else if (ret) {
		return ret;
	}

	if (new_id >= limit) {
		idr_remove(&drm_minors_idr, new_id);
		return -EINVAL;
	}
	return new_id;
}

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
struct drm_master *drm_master_create(struct drm_minor *minor)
{
	struct drm_master *master;

	master = drm_calloc(1, sizeof(*master), DRM_MEM_DRIVER);
	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;
}

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;
121
	struct drm_map_list *r_list, *list_temp;
122
123
124
125
126
127

	list_del(&master->head);

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

128
129
130
131
132
133
134
	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;
		}
	}

135
	if (master->unique) {
136
		drm_free(master->unique, master->unique_size, DRM_MEM_DRIVER);
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
		master->unique = NULL;
		master->unique_len = 0;
	}

	list_for_each_entry_safe(pt, next, &master->magicfree, head) {
		list_del(&pt->head);
		drm_ht_remove_item(&master->magiclist, &pt->hash_item);
		drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
	}

	drm_ht_remove(&master->magiclist);

	if (master->lock.hw_lock) {
		if (dev->sigdata.lock == master->lock.hw_lock)
			dev->sigdata.lock = NULL;
		master->lock.hw_lock = NULL;
		master->lock.file_priv = NULL;
154
		wake_up_interruptible_all(&master->lock.lock_queue);
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
	}

	drm_free(master, sizeof(*master), DRM_MEM_DRIVER);
}

void drm_master_put(struct drm_master **master)
{
	kref_put(&(*master)->refcount, drm_master_destroy);
	*master = NULL;
}

int drm_setmaster_ioctl(struct drm_device *dev, void *data,
			struct drm_file *file_priv)
{
	if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
		return -EINVAL;

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

	if (!file_priv->minor->master &&
	    file_priv->minor->master != file_priv->master) {
		mutex_lock(&dev->struct_mutex);
		file_priv->minor->master = drm_master_get(file_priv->master);
		mutex_lock(&dev->struct_mutex);
	}

	return 0;
}

int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
{
	if (!file_priv->master)
		return -EINVAL;
	mutex_lock(&dev->struct_mutex);
	drm_master_put(&file_priv->minor->master);
	mutex_unlock(&dev->struct_mutex);
	return 0;
}

196
static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
Dave Airlie's avatar
Dave Airlie committed
197
198
			   const struct pci_device_id *ent,
			   struct drm_driver *driver)
Linus Torvalds's avatar
Linus Torvalds committed
199
200
201
{
	int retcode;

202
	INIT_LIST_HEAD(&dev->filelist);
Dave Airlie's avatar
Dave Airlie committed
203
204
205
206
	INIT_LIST_HEAD(&dev->ctxlist);
	INIT_LIST_HEAD(&dev->vmalist);
	INIT_LIST_HEAD(&dev->maplist);

Linus Torvalds's avatar
Linus Torvalds committed
207
	spin_lock_init(&dev->count_lock);
208
	spin_lock_init(&dev->drw_lock);
Dave Airlie's avatar
Dave Airlie committed
209
	init_timer(&dev->timer);
Dave Airlie's avatar
Dave Airlie committed
210
211
	mutex_init(&dev->struct_mutex);
	mutex_init(&dev->ctxlist_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
212

Dave Airlie's avatar
Dave Airlie committed
213
214
	idr_init(&dev->drw_idr);

Dave Airlie's avatar
Dave Airlie committed
215
	dev->pdev = pdev;
216
217
	dev->pci_device = pdev->device;
	dev->pci_vendor = pdev->vendor;
Linus Torvalds's avatar
Linus Torvalds committed
218
219

#ifdef __alpha__
Dave Airlie's avatar
Dave Airlie committed
220
	dev->hose = pdev->sysdata;
Linus Torvalds's avatar
Linus Torvalds committed
221
222
#endif

223
224
225
	if (drm_ht_create(&dev->map_hash, 12)) {
		return -ENOMEM;
	}
Dave Airlie's avatar
Dave Airlie committed
226

Linus Torvalds's avatar
Linus Torvalds committed
227
228
	/* the DRM has 6 basic counters */
	dev->counters = 6;
Dave Airlie's avatar
Dave Airlie committed
229
230
231
232
233
234
	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;
Linus Torvalds's avatar
Linus Torvalds committed
235
236

	dev->driver = driver;
Dave Airlie's avatar
Dave Airlie committed
237

Linus Torvalds's avatar
Linus Torvalds committed
238
	if (drm_core_has_AGP(dev)) {
239
240
		if (drm_device_is_agp(dev))
			dev->agp = drm_agp_init(dev);
Dave Airlie's avatar
Dave Airlie committed
241
242
243
		if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP)
		    && (dev->agp == NULL)) {
			DRM_ERROR("Cannot initialize the agpgart module.\n");
Linus Torvalds's avatar
Linus Torvalds committed
244
245
246
247
248
			retcode = -EINVAL;
			goto error_out_unreg;
		}
		if (drm_core_has_MTRR(dev)) {
			if (dev->agp)
Dave Airlie's avatar
Dave Airlie committed
249
250
251
252
				dev->agp->agp_mtrr =
				    mtrr_add(dev->agp->agp_info.aper_base,
					     dev->agp->agp_info.aper_size *
					     1024 * 1024, MTRR_TYPE_WRCOMB, 1);
Linus Torvalds's avatar
Linus Torvalds committed
253
254
255
		}
	}

256

Dave Airlie's avatar
Dave Airlie committed
257
258
259
	retcode = drm_ctxbitmap_init(dev);
	if (retcode) {
		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
Linus Torvalds's avatar
Linus Torvalds committed
260
261
262
		goto error_out_unreg;
	}

263
264
265
266
267
268
269
270
271
	if (driver->driver_features & DRIVER_GEM) {
		retcode = drm_gem_init(dev);
		if (retcode) {
			DRM_ERROR("Cannot initialize graphics execution "
				  "manager (GEM)\n");
			goto error_out_unreg;
		}
	}

Linus Torvalds's avatar
Linus Torvalds committed
272
	return 0;
Dave Airlie's avatar
Dave Airlie committed
273
274

      error_out_unreg:
275
	drm_lastclose(dev);
Linus Torvalds's avatar
Linus Torvalds committed
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
	return retcode;
}


/**
 * Get a secondary minor number.
 *
 * \param dev device data structure
 * \param sec-minor structure to hold the assigned minor
 * \return negative number on failure.
 *
 * Search an empty entry and initialize it to the given parameters, and
 * create the proc init entry via proc_init(). This routines assigns
 * minor numbers to secondary heads of multi-headed cards
 */
291
static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
Linus Torvalds's avatar
Linus Torvalds committed
292
{
293
	struct drm_minor *new_minor;
Linus Torvalds's avatar
Linus Torvalds committed
294
	int ret;
295
	int minor_id;
Linus Torvalds's avatar
Linus Torvalds committed
296
297
298

	DRM_DEBUG("\n");

299
300
301
302
303
304
305
306
307
308
309
310
311
312
	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;
313
	INIT_LIST_HEAD(&new_minor->master_list);
314
315
316
317
318
319
320
321

	idr_replace(&drm_minors_idr, new_minor, minor_id);

	if (type == DRM_MINOR_LEGACY) {
		ret = drm_proc_init(new_minor, minor_id, drm_proc_root);
		if (ret) {
			DRM_ERROR("DRM: Failed to initialize /proc/dri.\n");
			goto err_mem;
Linus Torvalds's avatar
Linus Torvalds committed
322
		}
323
324
325
326
327
328
329
330
	} else
		new_minor->dev_root = NULL;

	ret = drm_sysfs_device_add(new_minor);
	if (ret) {
		printk(KERN_ERR
		       "DRM: Error sysfs_device_add.\n");
		goto err_g2;
Linus Torvalds's avatar
Linus Torvalds committed
331
	}
332
333
334
335
336
337
338
339
340
341
342
343
344
345
	*minor = new_minor;

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


err_g2:
	if (new_minor->type == DRM_MINOR_LEGACY)
		drm_proc_cleanup(new_minor, drm_proc_root);
err_mem:
	kfree(new_minor);
err_idr:
	idr_remove(&drm_minors_idr, minor_id);
	*minor = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
346
347
	return ret;
}
Dave Airlie's avatar
Dave Airlie committed
348

Dave Airlie's avatar
Dave Airlie committed
349
350
351
352
353
354
355
356
357
358
359
360
/**
 * Register.
 *
 * \param pdev - PCI device structure
 * \param ent entry from the PCI ID table with device type flags
 * \return zero on success or a negative number on failure.
 *
 * Attempt to gets inter module "drm" information. If we are first
 * then register the character device and inter module information.
 * Try and register, if we fail to register, backout previous work.
 */
int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
Dave Airlie's avatar
Dave Airlie committed
361
		struct drm_driver *driver)
Dave Airlie's avatar
Dave Airlie committed
362
{
363
	struct drm_device *dev;
Dave Airlie's avatar
Dave Airlie committed
364
365
366
367
368
369
370
371
	int ret;

	DRM_DEBUG("\n");

	dev = drm_calloc(1, sizeof(*dev), DRM_MEM_STUB);
	if (!dev)
		return -ENOMEM;

372
373
374
	ret = pci_enable_device(pdev);
	if (ret)
		goto err_g1;
Dave Airlie's avatar
Dave Airlie committed
375

376
	pci_set_master(pdev);
Dave Airlie's avatar
Dave Airlie committed
377
378
	if ((ret = drm_fill_in_dev(dev, pdev, ent, driver))) {
		printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
379
		goto err_g2;
Dave Airlie's avatar
Dave Airlie committed
380
	}
Dave Airlie's avatar
Dave Airlie committed
381
382
383
384
385
386
387

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

388
	if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
Dave Airlie's avatar
Dave Airlie committed
389
		goto err_g3;
390

Dave Airlie's avatar
Dave Airlie committed
391
392
393
	if (dev->driver->load) {
		ret = dev->driver->load(dev, ent->driver_data);
		if (ret)
Dave Airlie's avatar
Dave Airlie committed
394
			goto err_g3;
Dave Airlie's avatar
Dave Airlie committed
395
396
397
398
399
400
401
402
	}

        /* setup the grouping for the legacy output */
	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
		ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
		if (ret)
			goto err_g3;
	}
Dave Airlie's avatar
Dave Airlie committed
403

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

406
407
	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
		 driver->name, driver->major, driver->minor, driver->patchlevel,
408
		 driver->date, dev->primary->index);
Dave Airlie's avatar
Dave Airlie committed
409
410
411

	return 0;

Dave Airlie's avatar
Dave Airlie committed
412
413
err_g3:
	drm_put_minor(&dev->primary);
414
415
416
err_g2:
	pci_disable_device(pdev);
err_g1:
Dave Airlie's avatar
Dave Airlie committed
417
418
419
	drm_free(dev, sizeof(*dev), DRM_MEM_STUB);
	return ret;
}
Dave Airlie's avatar
Dave Airlie committed
420

Linus Torvalds's avatar
Linus Torvalds committed
421
422
423
424
425
426
427
428
429
430
/**
 * Put a device minor number.
 *
 * \param dev device data structure
 * \return always zero
 *
 * Cleans up the proc resources. If it is the last minor then release the foreign
 * "drm" data, otherwise unregisters the "drm" data, frees the dev list and
 * unregisters the character device.
 */
431
int drm_put_dev(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
{
	DRM_DEBUG("release primary %s\n", dev->driver->pci_driver.name);

	if (dev->devname) {
		drm_free(dev->devname, strlen(dev->devname) + 1,
			 DRM_MEM_DRIVER);
		dev->devname = NULL;
	}
	drm_free(dev, sizeof(*dev), DRM_MEM_STUB);
	return 0;
}

/**
 * Put a secondary minor number.
 *
 * \param sec_minor - structure to be released
 * \return always zero
 *
 * Cleans up the proc resources. Not legal for this to be the
 * last minor released.
 *
 */
454
int drm_put_minor(struct drm_minor **minor_p)
Linus Torvalds's avatar
Linus Torvalds committed
455
{
456
	struct drm_minor *minor = *minor_p;
457

458
	DRM_DEBUG("release secondary minor %d\n", minor->index);
Dave Airlie's avatar
Dave Airlie committed
459

460
461
462
	if (minor->type == DRM_MINOR_LEGACY)
		drm_proc_cleanup(minor, drm_proc_root);
	drm_sysfs_device_remove(minor);
Linus Torvalds's avatar
Linus Torvalds committed
463

464
	idr_remove(&drm_minors_idr, minor->index);
Dave Airlie's avatar
Dave Airlie committed
465

466
467
	kfree(minor);
	*minor_p = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
468
469
	return 0;
}