1280183Sdumbbell/**
2280183Sdumbbell * \file drm_fops.c
3280183Sdumbbell * File operations for DRM
4280183Sdumbbell *
5280183Sdumbbell * \author Rickard E. (Rik) Faith <faith@valinux.com>
6280183Sdumbbell * \author Daryll Strauss <daryll@valinux.com>
7280183Sdumbbell * \author Gareth Hughes <gareth@valinux.com>
8280183Sdumbbell */
9280183Sdumbbell
10280183Sdumbbell/*
11280183Sdumbbell * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
12280183Sdumbbell *
13235783Skib * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
14235783Skib * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
15235783Skib * All Rights Reserved.
16235783Skib *
17235783Skib * Permission is hereby granted, free of charge, to any person obtaining a
18235783Skib * copy of this software and associated documentation files (the "Software"),
19235783Skib * to deal in the Software without restriction, including without limitation
20235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21235783Skib * and/or sell copies of the Software, and to permit persons to whom the
22235783Skib * Software is furnished to do so, subject to the following conditions:
23235783Skib *
24235783Skib * The above copyright notice and this permission notice (including the next
25235783Skib * paragraph) shall be included in all copies or substantial portions of the
26235783Skib * Software.
27235783Skib *
28235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
31235783Skib * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
32235783Skib * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
33235783Skib * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
34235783Skib * OTHER DEALINGS IN THE SOFTWARE.
35235783Skib */
36235783Skib
37235783Skib#include <sys/cdefs.h>
38235783Skib__FBSDID("$FreeBSD: releng/11.0/sys/dev/drm2/drm_fops.c 288653 2015-10-04 07:45:36Z adrian $");
39235783Skib
40280183Sdumbbell#include <dev/drm2/drmP.h>
41280183Sdumbbell
42280183Sdumbbellstatic int drm_open_helper(struct cdev *kdev, int flags, int fmt,
43280183Sdumbbell			   DRM_STRUCTPROC *p, struct drm_device *dev);
44280183Sdumbbell
45280183Sdumbbellstatic int drm_setup(struct drm_device * dev)
46280183Sdumbbell{
47280183Sdumbbell	int i;
48280183Sdumbbell	int ret;
49280183Sdumbbell
50280183Sdumbbell	if (dev->driver->firstopen) {
51280183Sdumbbell		ret = dev->driver->firstopen(dev);
52280183Sdumbbell		if (ret != 0)
53280183Sdumbbell			return ret;
54280183Sdumbbell	}
55280183Sdumbbell
56280183Sdumbbell	atomic_set(&dev->ioctl_count, 0);
57280183Sdumbbell	atomic_set(&dev->vma_count, 0);
58280183Sdumbbell
59280183Sdumbbell	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
60280183Sdumbbell	    !drm_core_check_feature(dev, DRIVER_MODESET)) {
61280183Sdumbbell		dev->buf_use = 0;
62280183Sdumbbell		atomic_set(&dev->buf_alloc, 0);
63280183Sdumbbell
64280183Sdumbbell		i = drm_dma_setup(dev);
65280183Sdumbbell		if (i < 0)
66280183Sdumbbell			return i;
67280183Sdumbbell	}
68280183Sdumbbell
69280183Sdumbbell	/*
70280183Sdumbbell	 * FIXME Linux<->FreeBSD: counter incremented in drm_open() and
71280183Sdumbbell	 * reset to 0 here.
72280183Sdumbbell	 */
73280183Sdumbbell#if 0
74280183Sdumbbell	for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
75280183Sdumbbell		atomic_set(&dev->counts[i], 0);
76280183Sdumbbell#endif
77280183Sdumbbell
78280183Sdumbbell	dev->sigdata.lock = NULL;
79280183Sdumbbell
80280183Sdumbbell	dev->context_flag = 0;
81280183Sdumbbell	dev->interrupt_flag = 0;
82280183Sdumbbell	dev->dma_flag = 0;
83280183Sdumbbell	dev->last_context = 0;
84280183Sdumbbell	dev->last_switch = 0;
85280183Sdumbbell	dev->last_checked = 0;
86280183Sdumbbell	DRM_INIT_WAITQUEUE(&dev->context_wait);
87280183Sdumbbell	dev->if_version = 0;
88280183Sdumbbell
89280183Sdumbbell#ifdef FREEBSD_NOTYET
90280183Sdumbbell	dev->ctx_start = 0;
91280183Sdumbbell	dev->lck_start = 0;
92280183Sdumbbell
93280183Sdumbbell	dev->buf_async = NULL;
94280183Sdumbbell	DRM_INIT_WAITQUEUE(&dev->buf_readers);
95280183Sdumbbell	DRM_INIT_WAITQUEUE(&dev->buf_writers);
96280183Sdumbbell#endif /* FREEBSD_NOTYET */
97280183Sdumbbell
98280183Sdumbbell	DRM_DEBUG("\n");
99280183Sdumbbell
100280183Sdumbbell	/*
101280183Sdumbbell	 * The kernel's context could be created here, but is now created
102280183Sdumbbell	 * in drm_dma_enqueue.  This is more resource-efficient for
103280183Sdumbbell	 * hardware that does not do DMA, but may mean that
104280183Sdumbbell	 * drm_select_queue fails between the time the interrupt is
105280183Sdumbbell	 * initialized and the time the queues are initialized.
106280183Sdumbbell	 */
107280183Sdumbbell
108280183Sdumbbell	return 0;
109280183Sdumbbell}
110280183Sdumbbell
111280183Sdumbbell/**
112280183Sdumbbell * Open file.
113280183Sdumbbell *
114280183Sdumbbell * \param inode device inode
115280183Sdumbbell * \param filp file pointer.
116280183Sdumbbell * \return zero on success or a negative number on failure.
117280183Sdumbbell *
118280183Sdumbbell * Searches the DRM device with the same minor number, calls open_helper(), and
119280183Sdumbbell * increments the device open count. If the open count was previous at zero,
120280183Sdumbbell * i.e., it's the first that the device is open, then calls setup().
121235783Skib */
122280183Sdumbbellint drm_open(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p)
123280183Sdumbbell{
124280183Sdumbbell	struct drm_device *dev = NULL;
125280183Sdumbbell	struct drm_minor *minor;
126280183Sdumbbell	int retcode = 0;
127280183Sdumbbell	int need_setup = 0;
128235783Skib
129280183Sdumbbell	minor = kdev->si_drv1;
130280183Sdumbbell	if (!minor)
131280183Sdumbbell		return ENODEV;
132235783Skib
133280183Sdumbbell	if (!(dev = minor->dev))
134280183Sdumbbell		return ENODEV;
135280183Sdumbbell
136280183Sdumbbell	sx_xlock(&drm_global_mutex);
137280183Sdumbbell
138280183Sdumbbell	/*
139288653Sadrian	 * FIXME Linux<->FreeBSD: On Linux, counter updated outside
140280183Sdumbbell	 * global mutex.
141280183Sdumbbell	 */
142280183Sdumbbell	if (!dev->open_count++)
143280183Sdumbbell		need_setup = 1;
144280183Sdumbbell
145280183Sdumbbell	retcode = drm_open_helper(kdev, flags, fmt, p, dev);
146280183Sdumbbell	if (retcode) {
147280183Sdumbbell		sx_xunlock(&drm_global_mutex);
148280183Sdumbbell		return (-retcode);
149280183Sdumbbell	}
150280183Sdumbbell	atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
151280183Sdumbbell	if (need_setup) {
152280183Sdumbbell		retcode = drm_setup(dev);
153280183Sdumbbell		if (retcode)
154280183Sdumbbell			goto err_undo;
155280183Sdumbbell	}
156280183Sdumbbell	sx_xunlock(&drm_global_mutex);
157280183Sdumbbell	return 0;
158280183Sdumbbell
159280183Sdumbbellerr_undo:
160280183Sdumbbell	mtx_lock(&Giant); /* FIXME: Giant required? */
161280183Sdumbbell	device_unbusy(dev->dev);
162280183Sdumbbell	mtx_unlock(&Giant);
163280183Sdumbbell	dev->open_count--;
164280183Sdumbbell	sx_xunlock(&drm_global_mutex);
165280183Sdumbbell	return -retcode;
166280183Sdumbbell}
167280183SdumbbellEXPORT_SYMBOL(drm_open);
168280183Sdumbbell
169280183Sdumbbell/**
170280183Sdumbbell * Called whenever a process opens /dev/drm.
171280183Sdumbbell *
172280183Sdumbbell * \param inode device inode.
173280183Sdumbbell * \param filp file pointer.
174280183Sdumbbell * \param dev device.
175280183Sdumbbell * \return zero on success or a negative number on failure.
176280183Sdumbbell *
177280183Sdumbbell * Creates and initializes a drm_file structure for the file private data in \p
178280183Sdumbbell * filp and add it into the double linked list in \p dev.
179280183Sdumbbell */
180280183Sdumbbellstatic int drm_open_helper(struct cdev *kdev, int flags, int fmt,
181280183Sdumbbell			   DRM_STRUCTPROC *p, struct drm_device *dev)
182235783Skib{
183235783Skib	struct drm_file *priv;
184280183Sdumbbell	int ret;
185235783Skib
186235783Skib	if (flags & O_EXCL)
187280183Sdumbbell		return -EBUSY;	/* No exclusive opens */
188280183Sdumbbell	if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
189280183Sdumbbell		return -EINVAL;
190235783Skib
191235783Skib	DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev));
192235783Skib
193235783Skib	priv = malloc(sizeof(*priv), DRM_MEM_FILES, M_NOWAIT | M_ZERO);
194280183Sdumbbell	if (!priv)
195280183Sdumbbell		return -ENOMEM;
196235783Skib
197280183Sdumbbell	priv->uid = p->td_ucred->cr_svuid;
198280183Sdumbbell	priv->pid = p->td_proc->p_pid;
199280183Sdumbbell	priv->minor = kdev->si_drv1;
200280183Sdumbbell	priv->ioctl_count = 0;
201235783Skib	/* for compatibility root is always authenticated */
202280183Sdumbbell	priv->authenticated = DRM_SUSER(p);
203280183Sdumbbell	priv->lock_count = 0;
204235783Skib
205280183Sdumbbell	INIT_LIST_HEAD(&priv->lhead);
206235783Skib	INIT_LIST_HEAD(&priv->fbs);
207235783Skib	INIT_LIST_HEAD(&priv->event_list);
208235783Skib	priv->event_space = 4096; /* set aside 4k for event buffer */
209235783Skib
210235783Skib	if (dev->driver->driver_features & DRIVER_GEM)
211235783Skib		drm_gem_open(dev, priv);
212235783Skib
213280183Sdumbbell#ifdef FREEBSD_NOTYET
214280183Sdumbbell	if (drm_core_check_feature(dev, DRIVER_PRIME))
215280183Sdumbbell		drm_prime_init_file_private(&priv->prime);
216280183Sdumbbell#endif /* FREEBSD_NOTYET */
217280183Sdumbbell
218235783Skib	if (dev->driver->open) {
219280183Sdumbbell		ret = dev->driver->open(dev, priv);
220280183Sdumbbell		if (ret < 0)
221280183Sdumbbell			goto out_free;
222280183Sdumbbell	}
223280183Sdumbbell
224280183Sdumbbell
225280183Sdumbbell	/* if there is no current master make this fd it */
226280183Sdumbbell	DRM_LOCK(dev);
227280183Sdumbbell	if (!priv->minor->master) {
228280183Sdumbbell		/* create a new master */
229280183Sdumbbell		priv->minor->master = drm_master_create(priv->minor);
230280183Sdumbbell		if (!priv->minor->master) {
231235783Skib			DRM_UNLOCK(dev);
232280183Sdumbbell			ret = -ENOMEM;
233280183Sdumbbell			goto out_free;
234235783Skib		}
235280183Sdumbbell
236280183Sdumbbell		priv->is_master = 1;
237280183Sdumbbell		/* take another reference for the copy in the local file priv */
238280183Sdumbbell		priv->master = drm_master_get(priv->minor->master);
239280183Sdumbbell
240280183Sdumbbell		priv->authenticated = 1;
241280183Sdumbbell
242280183Sdumbbell		DRM_UNLOCK(dev);
243280183Sdumbbell		if (dev->driver->master_create) {
244280183Sdumbbell			ret = dev->driver->master_create(dev, priv->master);
245280183Sdumbbell			if (ret) {
246280183Sdumbbell				DRM_LOCK(dev);
247280183Sdumbbell				/* drop both references if this fails */
248280183Sdumbbell				drm_master_put(&priv->minor->master);
249280183Sdumbbell				drm_master_put(&priv->master);
250280183Sdumbbell				DRM_UNLOCK(dev);
251280183Sdumbbell				goto out_free;
252280183Sdumbbell			}
253280183Sdumbbell		}
254280183Sdumbbell		DRM_LOCK(dev);
255280183Sdumbbell		if (dev->driver->master_set) {
256280183Sdumbbell			ret = dev->driver->master_set(dev, priv, true);
257280183Sdumbbell			if (ret) {
258280183Sdumbbell				/* drop both references if this fails */
259280183Sdumbbell				drm_master_put(&priv->minor->master);
260280183Sdumbbell				drm_master_put(&priv->master);
261280183Sdumbbell				DRM_UNLOCK(dev);
262280183Sdumbbell				goto out_free;
263280183Sdumbbell			}
264280183Sdumbbell		}
265280183Sdumbbell		DRM_UNLOCK(dev);
266280183Sdumbbell	} else {
267280183Sdumbbell		/* get a reference to the master */
268280183Sdumbbell		priv->master = drm_master_get(priv->minor->master);
269280183Sdumbbell		DRM_UNLOCK(dev);
270235783Skib	}
271235783Skib
272280183Sdumbbell	DRM_LOCK(dev);
273280183Sdumbbell	list_add(&priv->lhead, &dev->filelist);
274280183Sdumbbell	DRM_UNLOCK(dev);
275235783Skib
276280183Sdumbbell	mtx_lock(&Giant); /* FIXME: Giant required? */
277280183Sdumbbell	device_busy(dev->dev);
278280183Sdumbbell	mtx_unlock(&Giant);
279280183Sdumbbell
280280183Sdumbbell	ret = devfs_set_cdevpriv(priv, drm_release);
281280183Sdumbbell	if (ret != 0)
282280183Sdumbbell		drm_release(priv);
283280183Sdumbbell
284280183Sdumbbell	return ret;
285280183Sdumbbell      out_free:
286280183Sdumbbell	free(priv, DRM_MEM_FILES);
287280183Sdumbbell	return ret;
288280183Sdumbbell}
289280183Sdumbbell
290280183Sdumbbellstatic void drm_master_release(struct drm_device *dev, struct drm_file *file_priv)
291280183Sdumbbell{
292280183Sdumbbell
293280183Sdumbbell	if (drm_i_have_hw_lock(dev, file_priv)) {
294280183Sdumbbell		DRM_DEBUG("File %p released, freeing lock for context %d\n",
295280183Sdumbbell			  file_priv, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
296280183Sdumbbell		drm_lock_free(&file_priv->master->lock,
297280183Sdumbbell			      _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
298280183Sdumbbell	}
299280183Sdumbbell}
300280183Sdumbbell
301280183Sdumbbellstatic void drm_events_release(struct drm_file *file_priv)
302280183Sdumbbell{
303280183Sdumbbell	struct drm_device *dev = file_priv->minor->dev;
304280183Sdumbbell	struct drm_pending_event *e, *et;
305280183Sdumbbell	struct drm_pending_vblank_event *v, *vt;
306280183Sdumbbell	unsigned long flags;
307280183Sdumbbell
308280183Sdumbbell	DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags);
309280183Sdumbbell
310280183Sdumbbell	/* Remove pending flips */
311280183Sdumbbell	list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
312280183Sdumbbell		if (v->base.file_priv == file_priv) {
313280183Sdumbbell			list_del(&v->base.link);
314280183Sdumbbell			drm_vblank_put(dev, v->pipe);
315280183Sdumbbell			v->base.destroy(&v->base);
316280183Sdumbbell		}
317280183Sdumbbell
318280183Sdumbbell	/* Remove unconsumed events */
319280183Sdumbbell	list_for_each_entry_safe(e, et, &file_priv->event_list, link)
320280183Sdumbbell		e->destroy(e);
321280183Sdumbbell
322280183Sdumbbell	DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags);
323280183Sdumbbell}
324280183Sdumbbell
325280183Sdumbbell/**
326280183Sdumbbell * Release file.
327280183Sdumbbell *
328280183Sdumbbell * \param inode device inode
329280183Sdumbbell * \param file_priv DRM file private.
330280183Sdumbbell * \return zero on success or a negative number on failure.
331280183Sdumbbell *
332280183Sdumbbell * If the hardware lock is held then free it, and take it again for the kernel
333280183Sdumbbell * context since it's necessary to reclaim buffers. Unlink the file private
334280183Sdumbbell * data from its list and free it. Decreases the open count and if it reaches
335280183Sdumbbell * zero calls drm_lastclose().
336280183Sdumbbell */
337280183Sdumbbellvoid drm_release(void *data)
338280183Sdumbbell{
339280183Sdumbbell	struct drm_file *file_priv = data;
340280183Sdumbbell	struct drm_device *dev = file_priv->minor->dev;
341280183Sdumbbell
342280183Sdumbbell	sx_xlock(&drm_global_mutex);
343280183Sdumbbell
344280183Sdumbbell	DRM_DEBUG("open_count = %d\n", dev->open_count);
345280183Sdumbbell
346280183Sdumbbell	if (dev->driver->preclose)
347280183Sdumbbell		dev->driver->preclose(dev, file_priv);
348280183Sdumbbell
349280183Sdumbbell	/* ========================================================
350280183Sdumbbell	 * Begin inline drm_release
351280183Sdumbbell	 */
352280183Sdumbbell
353280183Sdumbbell	DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
354280183Sdumbbell		  DRM_CURRENTPID,
355280183Sdumbbell		  (long)file_priv->minor->device,
356280183Sdumbbell		  dev->open_count);
357280183Sdumbbell
358280183Sdumbbell	/* Release any auth tokens that might point to this file_priv,
359280183Sdumbbell	   (do that under the drm_global_mutex) */
360280183Sdumbbell	if (file_priv->magic)
361280183Sdumbbell		(void) drm_remove_magic(file_priv->master, file_priv->magic);
362280183Sdumbbell
363280183Sdumbbell	/* if the master has gone away we can't do anything with the lock */
364280183Sdumbbell	if (file_priv->minor->master)
365280183Sdumbbell		drm_master_release(dev, file_priv);
366280183Sdumbbell
367280183Sdumbbell	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
368280183Sdumbbell		drm_core_reclaim_buffers(dev, file_priv);
369280183Sdumbbell
370280183Sdumbbell	drm_events_release(file_priv);
371280183Sdumbbell
372280183Sdumbbell	seldrain(&file_priv->event_poll);
373280183Sdumbbell
374280183Sdumbbell	if (dev->driver->driver_features & DRIVER_MODESET)
375280183Sdumbbell		drm_fb_release(file_priv);
376280183Sdumbbell
377280183Sdumbbell	if (dev->driver->driver_features & DRIVER_GEM)
378280183Sdumbbell		drm_gem_release(dev, file_priv);
379280183Sdumbbell
380280183Sdumbbell#ifdef FREEBSD_NOTYET
381280183Sdumbbell	mutex_lock(&dev->ctxlist_mutex);
382280183Sdumbbell	if (!list_empty(&dev->ctxlist)) {
383280183Sdumbbell		struct drm_ctx_list *pos, *n;
384280183Sdumbbell
385280183Sdumbbell		list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
386280183Sdumbbell			if (pos->tag == file_priv &&
387280183Sdumbbell			    pos->handle != DRM_KERNEL_CONTEXT) {
388280183Sdumbbell				if (dev->driver->context_dtor)
389280183Sdumbbell					dev->driver->context_dtor(dev,
390280183Sdumbbell								  pos->handle);
391280183Sdumbbell
392280183Sdumbbell				drm_ctxbitmap_free(dev, pos->handle);
393280183Sdumbbell
394280183Sdumbbell				list_del(&pos->head);
395280183Sdumbbell				kfree(pos);
396280183Sdumbbell				--dev->ctx_count;
397280183Sdumbbell			}
398280183Sdumbbell		}
399280183Sdumbbell	}
400280183Sdumbbell	mutex_unlock(&dev->ctxlist_mutex);
401280183Sdumbbell#endif /* FREEBSD_NOTYET */
402280183Sdumbbell
403280183Sdumbbell	DRM_LOCK(dev);
404280183Sdumbbell
405280183Sdumbbell	if (file_priv->is_master) {
406280183Sdumbbell		struct drm_master *master = file_priv->master;
407280183Sdumbbell		struct drm_file *temp;
408280183Sdumbbell		list_for_each_entry(temp, &dev->filelist, lhead) {
409280183Sdumbbell			if ((temp->master == file_priv->master) &&
410280183Sdumbbell			    (temp != file_priv))
411280183Sdumbbell				temp->authenticated = 0;
412280183Sdumbbell		}
413280183Sdumbbell
414280183Sdumbbell		/**
415280183Sdumbbell		 * Since the master is disappearing, so is the
416280183Sdumbbell		 * possibility to lock.
417280183Sdumbbell		 */
418280183Sdumbbell
419280183Sdumbbell		if (master->lock.hw_lock) {
420280183Sdumbbell			if (dev->sigdata.lock == master->lock.hw_lock)
421280183Sdumbbell				dev->sigdata.lock = NULL;
422280183Sdumbbell			master->lock.hw_lock = NULL;
423280183Sdumbbell			master->lock.file_priv = NULL;
424280183Sdumbbell			DRM_WAKEUP_INT(&master->lock.lock_queue);
425280183Sdumbbell		}
426280183Sdumbbell
427280183Sdumbbell		if (file_priv->minor->master == file_priv->master) {
428280183Sdumbbell			/* drop the reference held my the minor */
429280183Sdumbbell			if (dev->driver->master_drop)
430280183Sdumbbell				dev->driver->master_drop(dev, file_priv, true);
431280183Sdumbbell			drm_master_put(&file_priv->minor->master);
432280183Sdumbbell		}
433280183Sdumbbell	}
434280183Sdumbbell
435280183Sdumbbell	/* drop the reference held my the file priv */
436280183Sdumbbell	drm_master_put(&file_priv->master);
437280183Sdumbbell	file_priv->is_master = 0;
438280183Sdumbbell	list_del(&file_priv->lhead);
439235783Skib	DRM_UNLOCK(dev);
440239303Shselasky
441280183Sdumbbell	if (dev->driver->postclose)
442280183Sdumbbell		dev->driver->postclose(dev, file_priv);
443239303Shselasky
444280183Sdumbbell#ifdef FREEBSD_NOTYET
445280183Sdumbbell	if (drm_core_check_feature(dev, DRIVER_PRIME))
446280183Sdumbbell		drm_prime_destroy_file_private(&file_priv->prime);
447280183Sdumbbell#endif /* FREEBSD_NOTYET */
448280183Sdumbbell
449280183Sdumbbell	free(file_priv, DRM_MEM_FILES);
450280183Sdumbbell
451280183Sdumbbell	/* ========================================================
452280183Sdumbbell	 * End inline drm_release
453280183Sdumbbell	 */
454280183Sdumbbell
455280183Sdumbbell	atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
456280183Sdumbbell	mtx_lock(&Giant);
457280183Sdumbbell	device_unbusy(dev->dev);
458280183Sdumbbell	mtx_unlock(&Giant);
459280183Sdumbbell	if (!--dev->open_count) {
460280183Sdumbbell		if (atomic_read(&dev->ioctl_count)) {
461280183Sdumbbell			DRM_ERROR("Device busy: %d\n",
462280183Sdumbbell				  atomic_read(&dev->ioctl_count));
463280183Sdumbbell		} else
464280183Sdumbbell			drm_lastclose(dev);
465280183Sdumbbell	}
466280183Sdumbbell	sx_xunlock(&drm_global_mutex);
467235783Skib}
468280183SdumbbellEXPORT_SYMBOL(drm_release);
469235783Skib
470235783Skibstatic bool
471280183Sdumbbelldrm_dequeue_event(struct drm_file *file_priv, struct uio *uio,
472280183Sdumbbell    struct drm_pending_event **out)
473235783Skib{
474235783Skib	struct drm_pending_event *e;
475280183Sdumbbell	bool ret = false;
476235783Skib
477280183Sdumbbell	/* Already locked in drm_read(). */
478280183Sdumbbell	/* DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); */
479280183Sdumbbell
480280183Sdumbbell	*out = NULL;
481235783Skib	if (list_empty(&file_priv->event_list))
482280183Sdumbbell		goto out;
483235783Skib	e = list_first_entry(&file_priv->event_list,
484280183Sdumbbell			     struct drm_pending_event, link);
485235783Skib	if (e->event->length > uio->uio_resid)
486280183Sdumbbell		goto out;
487235783Skib
488235783Skib	file_priv->event_space += e->event->length;
489235783Skib	list_del(&e->link);
490235783Skib	*out = e;
491280183Sdumbbell	ret = true;
492280183Sdumbbell
493280183Sdumbbellout:
494280183Sdumbbell	/* DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); */
495280183Sdumbbell	return ret;
496235783Skib}
497235783Skib
498235783Skibint
499235783Skibdrm_read(struct cdev *kdev, struct uio *uio, int ioflag)
500235783Skib{
501235783Skib	struct drm_file *file_priv;
502235783Skib	struct drm_device *dev;
503235783Skib	struct drm_pending_event *e;
504280183Sdumbbell	ssize_t error;
505235783Skib
506235783Skib	error = devfs_get_cdevpriv((void **)&file_priv);
507235783Skib	if (error != 0) {
508235783Skib		DRM_ERROR("can't find authenticator\n");
509235783Skib		return (EINVAL);
510235783Skib	}
511280183Sdumbbell
512235783Skib	dev = drm_get_device_from_kdev(kdev);
513235783Skib	mtx_lock(&dev->event_lock);
514235783Skib	while (list_empty(&file_priv->event_list)) {
515235783Skib		if ((ioflag & O_NONBLOCK) != 0) {
516235783Skib			error = EAGAIN;
517235783Skib			goto out;
518235783Skib		}
519235783Skib		error = msleep(&file_priv->event_space, &dev->event_lock,
520235783Skib	           PCATCH, "drmrea", 0);
521235783Skib	       if (error != 0)
522235783Skib		       goto out;
523235783Skib	}
524280183Sdumbbell
525280183Sdumbbell	while (drm_dequeue_event(file_priv, uio, &e)) {
526235783Skib		mtx_unlock(&dev->event_lock);
527235783Skib		error = uiomove(e->event, e->event->length, uio);
528235783Skib		CTR3(KTR_DRM, "drm_event_dequeued %d %d %d", curproc->p_pid,
529235783Skib		    e->event->type, e->event->length);
530280183Sdumbbell
531235783Skib		e->destroy(e);
532235783Skib		if (error != 0)
533235783Skib			return (error);
534235783Skib		mtx_lock(&dev->event_lock);
535235783Skib	}
536280183Sdumbbell
537235783Skibout:
538235783Skib	mtx_unlock(&dev->event_lock);
539235783Skib	return (error);
540235783Skib}
541280183SdumbbellEXPORT_SYMBOL(drm_read);
542235783Skib
543235783Skibvoid
544235783Skibdrm_event_wakeup(struct drm_pending_event *e)
545235783Skib{
546235783Skib	struct drm_file *file_priv;
547235783Skib	struct drm_device *dev;
548235783Skib
549235783Skib	file_priv = e->file_priv;
550280183Sdumbbell	dev = file_priv->minor->dev;
551235783Skib	mtx_assert(&dev->event_lock, MA_OWNED);
552235783Skib
553235783Skib	wakeup(&file_priv->event_space);
554235783Skib	selwakeup(&file_priv->event_poll);
555235783Skib}
556235783Skib
557235783Skibint
558235783Skibdrm_poll(struct cdev *kdev, int events, struct thread *td)
559235783Skib{
560235783Skib	struct drm_file *file_priv;
561235783Skib	struct drm_device *dev;
562235783Skib	int error, revents;
563235783Skib
564235783Skib	error = devfs_get_cdevpriv((void **)&file_priv);
565235783Skib	if (error != 0) {
566235783Skib		DRM_ERROR("can't find authenticator\n");
567235783Skib		return (EINVAL);
568235783Skib	}
569280183Sdumbbell
570235783Skib	dev = drm_get_device_from_kdev(kdev);
571235783Skib
572235783Skib	revents = 0;
573235783Skib	mtx_lock(&dev->event_lock);
574235783Skib	if ((events & (POLLIN | POLLRDNORM)) != 0) {
575235783Skib		if (list_empty(&file_priv->event_list)) {
576235783Skib			CTR0(KTR_DRM, "drm_poll empty list");
577235783Skib			selrecord(td, &file_priv->event_poll);
578235783Skib		} else {
579235783Skib			revents |= events & (POLLIN | POLLRDNORM);
580235783Skib			CTR1(KTR_DRM, "drm_poll revents %x", revents);
581235783Skib		}
582235783Skib	}
583235783Skib	mtx_unlock(&dev->event_lock);
584235783Skib	return (revents);
585235783Skib}
586280183SdumbbellEXPORT_SYMBOL(drm_poll);
587280183Sdumbbell
588280183Sdumbbellint
589280183Sdumbbelldrm_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size,
590280183Sdumbbell    struct vm_object **obj_res, int nprot)
591280183Sdumbbell{
592280183Sdumbbell	struct drm_device *dev;
593280183Sdumbbell
594280183Sdumbbell	dev = drm_get_device_from_kdev(kdev);
595280183Sdumbbell	if (dev->drm_ttm_bdev != NULL) {
596280183Sdumbbell		return (-ttm_bo_mmap_single(dev->drm_ttm_bdev, offset, size,
597280183Sdumbbell		    obj_res, nprot));
598280183Sdumbbell	} else if ((dev->driver->driver_features & DRIVER_GEM) != 0) {
599280183Sdumbbell		return (-drm_gem_mmap_single(dev, offset, size, obj_res, nprot));
600280183Sdumbbell	} else {
601280183Sdumbbell		return (ENODEV);
602280183Sdumbbell	}
603280183Sdumbbell}
604