xref: /freebsd/sys/dev/drm2/drm_stub.c (revision fb3ef04d2028110f06d68b09009f1f2ca0f4128e)
1 /**
2  * \file drm_stub.h
3  * Stub support
4  *
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  */
7 
8 /*
9  * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
10  *
11  * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
12  * All Rights Reserved.
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice (including the next
22  * paragraph) shall be included in all copies or substantial portions of the
23  * Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  */
33 
34 #include <sys/cdefs.h>
35 #include <dev/drm2/drmP.h>
36 #include <dev/drm2/drm_core.h>
37 
38 #ifdef DRM_DEBUG_DEFAULT_ON
39 unsigned int drm_debug = (DRM_DEBUGBITS_DEBUG | DRM_DEBUGBITS_KMS |
40     DRM_DEBUGBITS_FAILED_IOCTL);
41 #else
42 unsigned int drm_debug = 0;	/* 1 to enable debug output */
43 #endif
44 EXPORT_SYMBOL(drm_debug);
45 
46 unsigned int drm_notyet = 0;
47 
48 unsigned int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
49 EXPORT_SYMBOL(drm_vblank_offdelay);
50 
51 unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
52 EXPORT_SYMBOL(drm_timestamp_precision);
53 
54 /*
55  * Default to use monotonic timestamps for wait-for-vblank and page-flip
56  * complete events.
57  */
58 unsigned int drm_timestamp_monotonic = 1;
59 
60 MODULE_AUTHOR(CORE_AUTHOR);
61 MODULE_DESCRIPTION(CORE_DESC);
62 MODULE_LICENSE("GPL and additional rights");
63 MODULE_PARM_DESC(debug, "Enable debug output");
64 MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
65 MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
66 MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
67 
68 module_param_named(debug, drm_debug, int, 0600);
69 module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
70 module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
71 module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
72 
73 static struct cdevsw drm_cdevsw = {
74 	.d_version =	D_VERSION,
75 	.d_open =	drm_open,
76 	.d_read =	drm_read,
77 	.d_ioctl =	drm_ioctl,
78 	.d_poll =	drm_poll,
79 	.d_mmap_single = drm_mmap_single,
80 	.d_name =	"drm",
81 	.d_flags =	D_TRACKCLOSE
82 };
83 
84 static int drm_minor_get_id(struct drm_device *dev, int type)
85 {
86 	int new_id;
87 
88 	new_id = device_get_unit(dev->dev);
89 
90 	if (new_id >= 64)
91 		return -EINVAL;
92 
93 	if (type == DRM_MINOR_CONTROL) {
94 		new_id += 64;
95 	} else if (type == DRM_MINOR_RENDER) {
96 		new_id += 128;
97 	}
98 
99 	return new_id;
100 }
101 
102 struct drm_master *drm_master_create(struct drm_minor *minor)
103 {
104 	struct drm_master *master;
105 
106 	master = malloc(sizeof(*master), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
107 	if (!master)
108 		return NULL;
109 
110 	refcount_init(&master->refcount, 1);
111 	mtx_init(&master->lock.spinlock, "drm_master__lock__spinlock",
112 	    NULL, MTX_DEF);
113 	DRM_INIT_WAITQUEUE(&master->lock.lock_queue);
114 	drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER);
115 	INIT_LIST_HEAD(&master->magicfree);
116 	master->minor = minor;
117 
118 	list_add_tail(&master->head, &minor->master_list);
119 
120 	return master;
121 }
122 
123 struct drm_master *drm_master_get(struct drm_master *master)
124 {
125 	refcount_acquire(&master->refcount);
126 	return master;
127 }
128 EXPORT_SYMBOL(drm_master_get);
129 
130 static void drm_master_destroy(struct drm_master *master)
131 {
132 	struct drm_magic_entry *pt, *next;
133 	struct drm_device *dev = master->minor->dev;
134 	struct drm_map_list *r_list, *list_temp;
135 
136 	list_del(&master->head);
137 
138 	if (dev->driver->master_destroy)
139 		dev->driver->master_destroy(dev, master);
140 
141 	list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) {
142 		if (r_list->master == master) {
143 			drm_rmmap_locked(dev, r_list->map);
144 			r_list = NULL;
145 		}
146 	}
147 
148 	if (master->unique) {
149 		free(master->unique, DRM_MEM_DRIVER);
150 		master->unique = NULL;
151 		master->unique_len = 0;
152 	}
153 
154 	list_for_each_entry_safe(pt, next, &master->magicfree, head) {
155 		list_del(&pt->head);
156 		drm_ht_remove_item(&master->magiclist, &pt->hash_item);
157 		free(pt, DRM_MEM_MAGIC);
158 	}
159 
160 	drm_ht_remove(&master->magiclist);
161 
162 	free(master, DRM_MEM_KMS);
163 }
164 
165 void drm_master_put(struct drm_master **master)
166 {
167 	if (refcount_release(&(*master)->refcount))
168 		drm_master_destroy(*master);
169 	*master = NULL;
170 }
171 EXPORT_SYMBOL(drm_master_put);
172 
173 int drm_setmaster_ioctl(struct drm_device *dev, void *data,
174 			struct drm_file *file_priv)
175 {
176 	int ret;
177 
178 	if (file_priv->is_master)
179 		return 0;
180 
181 	if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
182 		return -EINVAL;
183 
184 	if (!file_priv->master)
185 		return -EINVAL;
186 
187 	if (file_priv->minor->master)
188 		return -EINVAL;
189 
190 	DRM_LOCK(dev);
191 	file_priv->minor->master = drm_master_get(file_priv->master);
192 	file_priv->is_master = 1;
193 	if (dev->driver->master_set) {
194 		ret = dev->driver->master_set(dev, file_priv, false);
195 		if (unlikely(ret != 0)) {
196 			file_priv->is_master = 0;
197 			drm_master_put(&file_priv->minor->master);
198 		}
199 	}
200 	DRM_UNLOCK(dev);
201 
202 	return 0;
203 }
204 
205 int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
206 			 struct drm_file *file_priv)
207 {
208 	if (!file_priv->is_master)
209 		return -EINVAL;
210 
211 	if (!file_priv->minor->master)
212 		return -EINVAL;
213 
214 	DRM_LOCK(dev);
215 	if (dev->driver->master_drop)
216 		dev->driver->master_drop(dev, file_priv, false);
217 	drm_master_put(&file_priv->minor->master);
218 	file_priv->is_master = 0;
219 	DRM_UNLOCK(dev);
220 	return 0;
221 }
222 
223 int drm_fill_in_dev(struct drm_device *dev,
224 			   struct drm_driver *driver)
225 {
226 	int retcode, i;
227 
228 	INIT_LIST_HEAD(&dev->filelist);
229 	INIT_LIST_HEAD(&dev->ctxlist);
230 	INIT_LIST_HEAD(&dev->maplist);
231 	INIT_LIST_HEAD(&dev->vblank_event_list);
232 
233 	mtx_init(&dev->irq_lock, "drmirq", NULL, MTX_DEF);
234 	mtx_init(&dev->count_lock, "drmcount", NULL, MTX_DEF);
235 	mtx_init(&dev->event_lock, "drmev", NULL, MTX_DEF);
236 	sx_init(&dev->dev_struct_lock, "drmslk");
237 	mtx_init(&dev->ctxlist_mutex, "drmctxlist", NULL, MTX_DEF);
238 	mtx_init(&dev->pcir_lock, "drmpcir", NULL, MTX_DEF);
239 
240 	if (drm_ht_create(&dev->map_hash, 12)) {
241 		return -ENOMEM;
242 	}
243 
244 	/* the DRM has 6 basic counters */
245 	dev->counters = 6;
246 	dev->types[0] = _DRM_STAT_LOCK;
247 	dev->types[1] = _DRM_STAT_OPENS;
248 	dev->types[2] = _DRM_STAT_CLOSES;
249 	dev->types[3] = _DRM_STAT_IOCTLS;
250 	dev->types[4] = _DRM_STAT_LOCKS;
251 	dev->types[5] = _DRM_STAT_UNLOCKS;
252 
253 	/*
254 	 * FIXME Linux<->FreeBSD: this is done in drm_setup() on Linux.
255 	 */
256 	for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
257 		atomic_set(&dev->counts[i], 0);
258 
259 	dev->driver = driver;
260 
261 	retcode = drm_pci_agp_init(dev);
262 	if (retcode)
263 		goto error_out_unreg;
264 
265 
266 
267 	retcode = drm_ctxbitmap_init(dev);
268 	if (retcode) {
269 		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
270 		goto error_out_unreg;
271 	}
272 
273 	if (driver->driver_features & DRIVER_GEM) {
274 		retcode = drm_gem_init(dev);
275 		if (retcode) {
276 			DRM_ERROR("Cannot initialize graphics execution "
277 				  "manager (GEM)\n");
278 			goto error_out_unreg;
279 		}
280 	}
281 
282 	retcode = drm_sysctl_init(dev);
283 	if (retcode != 0) {
284 		DRM_ERROR("Failed to create hw.dri sysctl entry: %d\n",
285 		    retcode);
286 	}
287 
288 	return 0;
289 
290       error_out_unreg:
291 	drm_cancel_fill_in_dev(dev);
292 	return retcode;
293 }
294 EXPORT_SYMBOL(drm_fill_in_dev);
295 
296 void drm_cancel_fill_in_dev(struct drm_device *dev)
297 {
298 	struct drm_driver *driver;
299 
300 	driver = dev->driver;
301 
302 	drm_sysctl_cleanup(dev);
303 	if (driver->driver_features & DRIVER_GEM)
304 		drm_gem_destroy(dev);
305 	drm_ctxbitmap_cleanup(dev);
306 
307 	if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) &&
308 	    dev->agp && dev->agp->agp_mtrr >= 0) {
309 		int retval;
310 		retval = drm_mtrr_del(dev->agp->agp_mtrr,
311 				  dev->agp->agp_info.ai_aperture_base,
312 				  dev->agp->agp_info.ai_aperture_size,
313 				  DRM_MTRR_WC);
314 		DRM_DEBUG("mtrr_del=%d\n", retval);
315 	}
316 	free(dev->agp, DRM_MEM_AGPLISTS);
317 	dev->agp = NULL;
318 
319 	drm_ht_remove(&dev->map_hash);
320 
321 	mtx_destroy(&dev->irq_lock);
322 	mtx_destroy(&dev->count_lock);
323 	mtx_destroy(&dev->event_lock);
324 	sx_destroy(&dev->dev_struct_lock);
325 	mtx_destroy(&dev->ctxlist_mutex);
326 	mtx_destroy(&dev->pcir_lock);
327 }
328 
329 /**
330  * Get a secondary minor number.
331  *
332  * \param dev device data structure
333  * \param sec-minor structure to hold the assigned minor
334  * \return negative number on failure.
335  *
336  * Search an empty entry and initialize it to the given parameters, and
337  * create the proc init entry via proc_init(). This routines assigns
338  * minor numbers to secondary heads of multi-headed cards
339  */
340 int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
341 {
342 	struct drm_minor *new_minor;
343 	int ret;
344 	int minor_id;
345 	const char *minor_devname;
346 
347 	DRM_DEBUG("\n");
348 
349 	minor_id = drm_minor_get_id(dev, type);
350 	if (minor_id < 0)
351 		return minor_id;
352 
353 	new_minor = malloc(sizeof(struct drm_minor), DRM_MEM_MINOR,
354 	    M_NOWAIT | M_ZERO);
355 	if (!new_minor) {
356 		ret = -ENOMEM;
357 		goto err_idr;
358 	}
359 
360 	new_minor->type = type;
361 	new_minor->dev = dev;
362 	new_minor->index = minor_id;
363 	INIT_LIST_HEAD(&new_minor->master_list);
364 
365 	new_minor->buf_sigio = NULL;
366 
367 	switch (type) {
368 	case DRM_MINOR_CONTROL:
369 		minor_devname = "dri/controlD%d";
370 		break;
371 	case DRM_MINOR_RENDER:
372 		minor_devname = "dri/renderD%d";
373 		break;
374 	default:
375 		minor_devname = "dri/card%d";
376 		break;
377 	}
378 
379 	ret = make_dev_p(MAKEDEV_WAITOK | MAKEDEV_CHECKNAME, &new_minor->device,
380 	    &drm_cdevsw, 0, DRM_DEV_UID, DRM_DEV_GID,
381 	    DRM_DEV_MODE, minor_devname, minor_id);
382 	if (ret) {
383 		DRM_ERROR("Failed to create cdev: %d\n", ret);
384 		goto err_mem;
385 	}
386 	new_minor->device->si_drv1 = new_minor;
387 	*minor = new_minor;
388 
389 	DRM_DEBUG("new minor assigned %d\n", minor_id);
390 	return 0;
391 
392 
393 err_mem:
394 	free(new_minor, DRM_MEM_MINOR);
395 err_idr:
396 	*minor = NULL;
397 	return ret;
398 }
399 EXPORT_SYMBOL(drm_get_minor);
400 
401 /**
402  * Put a secondary minor number.
403  *
404  * \param sec_minor - structure to be released
405  * \return always zero
406  *
407  * Cleans up the proc resources. Not legal for this to be the
408  * last minor released.
409  *
410  */
411 int drm_put_minor(struct drm_minor **minor_p)
412 {
413 	struct drm_minor *minor = *minor_p;
414 
415 	DRM_DEBUG("release secondary minor %d\n", minor->index);
416 
417 	funsetown(&minor->buf_sigio);
418 
419 	destroy_dev(minor->device);
420 
421 	free(minor, DRM_MEM_MINOR);
422 	*minor_p = NULL;
423 	return 0;
424 }
425 EXPORT_SYMBOL(drm_put_minor);
426 
427 /**
428  * Called via drm_exit() at module unload time or when pci device is
429  * unplugged.
430  *
431  * Cleans up all DRM device, calling drm_lastclose().
432  *
433  */
434 void drm_put_dev(struct drm_device *dev)
435 {
436 	struct drm_driver *driver;
437 	struct drm_map_list *r_list, *list_temp;
438 
439 	DRM_DEBUG("\n");
440 
441 	if (!dev) {
442 		DRM_ERROR("cleanup called no dev\n");
443 		return;
444 	}
445 	driver = dev->driver;
446 
447 	drm_lastclose(dev);
448 
449 	if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) &&
450 	    dev->agp && dev->agp->agp_mtrr >= 0) {
451 		int retval;
452 		retval = drm_mtrr_del(dev->agp->agp_mtrr,
453 				  dev->agp->agp_info.ai_aperture_base,
454 				  dev->agp->agp_info.ai_aperture_size,
455 				  DRM_MTRR_WC);
456 		DRM_DEBUG("mtrr_del=%d\n", retval);
457 	}
458 
459 	if (drm_core_check_feature(dev, DRIVER_MODESET))
460 		drm_mode_group_free(&dev->primary->mode_group);
461 
462 	if (dev->driver->unload)
463 		dev->driver->unload(dev);
464 
465 	drm_sysctl_cleanup(dev);
466 
467 	if (drm_core_has_AGP(dev) && dev->agp) {
468 		free(dev->agp, DRM_MEM_AGPLISTS);
469 		dev->agp = NULL;
470 	}
471 
472 	drm_vblank_cleanup(dev);
473 
474 	list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
475 		drm_rmmap(dev, r_list->map);
476 	drm_ht_remove(&dev->map_hash);
477 
478 	drm_ctxbitmap_cleanup(dev);
479 
480 	if (drm_core_check_feature(dev, DRIVER_MODESET))
481 		drm_put_minor(&dev->control);
482 
483 	if (driver->driver_features & DRIVER_GEM)
484 		drm_gem_destroy(dev);
485 
486 	drm_put_minor(&dev->primary);
487 
488 	mtx_destroy(&dev->irq_lock);
489 	mtx_destroy(&dev->count_lock);
490 	mtx_destroy(&dev->event_lock);
491 	sx_destroy(&dev->dev_struct_lock);
492 	mtx_destroy(&dev->ctxlist_mutex);
493 	mtx_destroy(&dev->pcir_lock);
494 
495 #ifdef FREEBSD_NOTYET
496 	list_del(&dev->driver_item);
497 #endif /* FREEBSD_NOTYET */
498 }
499 EXPORT_SYMBOL(drm_put_dev);
500