]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/drm2/drm_fops.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / dev / drm2 / drm_fops.c
1 /**
2  * \file drm_fops.c
3  * File operations for DRM
4  *
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  * \author Daryll Strauss <daryll@valinux.com>
7  * \author Gareth Hughes <gareth@valinux.com>
8  */
9
10 /*
11  * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
12  *
13  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
14  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
15  * All Rights Reserved.
16  *
17  * Permission is hereby granted, free of charge, to any person obtaining a
18  * copy of this software and associated documentation files (the "Software"),
19  * to deal in the Software without restriction, including without limitation
20  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21  * and/or sell copies of the Software, and to permit persons to whom the
22  * Software is furnished to do so, subject to the following conditions:
23  *
24  * The above copyright notice and this permission notice (including the next
25  * paragraph) shall be included in all copies or substantial portions of the
26  * Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
31  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
32  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
33  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
34  * OTHER DEALINGS IN THE SOFTWARE.
35  */
36
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include <dev/drm2/drmP.h>
41
42 static int drm_open_helper(struct cdev *kdev, int flags, int fmt,
43                            DRM_STRUCTPROC *p, struct drm_device *dev);
44
45 static int drm_setup(struct drm_device * dev)
46 {
47         int i;
48         int ret;
49
50         if (dev->driver->firstopen) {
51                 ret = dev->driver->firstopen(dev);
52                 if (ret != 0)
53                         return ret;
54         }
55
56         atomic_set(&dev->ioctl_count, 0);
57         atomic_set(&dev->vma_count, 0);
58
59         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
60             !drm_core_check_feature(dev, DRIVER_MODESET)) {
61                 dev->buf_use = 0;
62                 atomic_set(&dev->buf_alloc, 0);
63
64                 i = drm_dma_setup(dev);
65                 if (i < 0)
66                         return i;
67         }
68
69         /*
70          * FIXME Linux<->FreeBSD: counter incremented in drm_open() and
71          * reset to 0 here.
72          */
73 #if 0
74         for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
75                 atomic_set(&dev->counts[i], 0);
76 #endif
77
78         dev->sigdata.lock = NULL;
79
80         dev->context_flag = 0;
81         dev->interrupt_flag = 0;
82         dev->dma_flag = 0;
83         dev->last_context = 0;
84         dev->last_switch = 0;
85         dev->last_checked = 0;
86         DRM_INIT_WAITQUEUE(&dev->context_wait);
87         dev->if_version = 0;
88
89 #ifdef FREEBSD_NOTYET
90         dev->ctx_start = 0;
91         dev->lck_start = 0;
92
93         dev->buf_async = NULL;
94         DRM_INIT_WAITQUEUE(&dev->buf_readers);
95         DRM_INIT_WAITQUEUE(&dev->buf_writers);
96 #endif /* FREEBSD_NOTYET */
97
98         DRM_DEBUG("\n");
99
100         /*
101          * The kernel's context could be created here, but is now created
102          * in drm_dma_enqueue.  This is more resource-efficient for
103          * hardware that does not do DMA, but may mean that
104          * drm_select_queue fails between the time the interrupt is
105          * initialized and the time the queues are initialized.
106          */
107
108         return 0;
109 }
110
111 /**
112  * Open file.
113  *
114  * \param inode device inode
115  * \param filp file pointer.
116  * \return zero on success or a negative number on failure.
117  *
118  * Searches the DRM device with the same minor number, calls open_helper(), and
119  * increments the device open count. If the open count was previous at zero,
120  * i.e., it's the first that the device is open, then calls setup().
121  */
122 int drm_open(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p)
123 {
124         struct drm_device *dev = NULL;
125         struct drm_minor *minor;
126         int retcode = 0;
127         int need_setup = 0;
128
129         minor = kdev->si_drv1;
130         if (!minor)
131                 return ENODEV;
132
133         if (!(dev = minor->dev))
134                 return ENODEV;
135
136         sx_xlock(&drm_global_mutex);
137
138         /*
139          * FIXME Linux<->FreeBSD: On Linux, counter updated outisde
140          * global mutex.
141          */
142         if (!dev->open_count++)
143                 need_setup = 1;
144
145         retcode = drm_open_helper(kdev, flags, fmt, p, dev);
146         if (retcode) {
147                 sx_xunlock(&drm_global_mutex);
148                 return (-retcode);
149         }
150         atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
151         if (need_setup) {
152                 retcode = drm_setup(dev);
153                 if (retcode)
154                         goto err_undo;
155         }
156         sx_xunlock(&drm_global_mutex);
157         return 0;
158
159 err_undo:
160         mtx_lock(&Giant); /* FIXME: Giant required? */
161         device_unbusy(dev->dev);
162         mtx_unlock(&Giant);
163         dev->open_count--;
164         sx_xunlock(&drm_global_mutex);
165         return -retcode;
166 }
167 EXPORT_SYMBOL(drm_open);
168
169 /**
170  * Called whenever a process opens /dev/drm.
171  *
172  * \param inode device inode.
173  * \param filp file pointer.
174  * \param dev device.
175  * \return zero on success or a negative number on failure.
176  *
177  * Creates and initializes a drm_file structure for the file private data in \p
178  * filp and add it into the double linked list in \p dev.
179  */
180 static int drm_open_helper(struct cdev *kdev, int flags, int fmt,
181                            DRM_STRUCTPROC *p, struct drm_device *dev)
182 {
183         struct drm_file *priv;
184         int ret;
185
186         if (flags & O_EXCL)
187                 return -EBUSY;  /* No exclusive opens */
188         if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
189                 return -EINVAL;
190
191         DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev));
192
193         priv = malloc(sizeof(*priv), DRM_MEM_FILES, M_NOWAIT | M_ZERO);
194         if (!priv)
195                 return -ENOMEM;
196
197         priv->uid = p->td_ucred->cr_svuid;
198         priv->pid = p->td_proc->p_pid;
199         priv->minor = kdev->si_drv1;
200         priv->ioctl_count = 0;
201         /* for compatibility root is always authenticated */
202         priv->authenticated = DRM_SUSER(p);
203         priv->lock_count = 0;
204
205         INIT_LIST_HEAD(&priv->lhead);
206         INIT_LIST_HEAD(&priv->fbs);
207         INIT_LIST_HEAD(&priv->event_list);
208         priv->event_space = 4096; /* set aside 4k for event buffer */
209
210         if (dev->driver->driver_features & DRIVER_GEM)
211                 drm_gem_open(dev, priv);
212
213 #ifdef FREEBSD_NOTYET
214         if (drm_core_check_feature(dev, DRIVER_PRIME))
215                 drm_prime_init_file_private(&priv->prime);
216 #endif /* FREEBSD_NOTYET */
217
218         if (dev->driver->open) {
219                 ret = dev->driver->open(dev, priv);
220                 if (ret < 0)
221                         goto out_free;
222         }
223
224
225         /* if there is no current master make this fd it */
226         DRM_LOCK(dev);
227         if (!priv->minor->master) {
228                 /* create a new master */
229                 priv->minor->master = drm_master_create(priv->minor);
230                 if (!priv->minor->master) {
231                         DRM_UNLOCK(dev);
232                         ret = -ENOMEM;
233                         goto out_free;
234                 }
235
236                 priv->is_master = 1;
237                 /* take another reference for the copy in the local file priv */
238                 priv->master = drm_master_get(priv->minor->master);
239
240                 priv->authenticated = 1;
241
242                 DRM_UNLOCK(dev);
243                 if (dev->driver->master_create) {
244                         ret = dev->driver->master_create(dev, priv->master);
245                         if (ret) {
246                                 DRM_LOCK(dev);
247                                 /* drop both references if this fails */
248                                 drm_master_put(&priv->minor->master);
249                                 drm_master_put(&priv->master);
250                                 DRM_UNLOCK(dev);
251                                 goto out_free;
252                         }
253                 }
254                 DRM_LOCK(dev);
255                 if (dev->driver->master_set) {
256                         ret = dev->driver->master_set(dev, priv, true);
257                         if (ret) {
258                                 /* drop both references if this fails */
259                                 drm_master_put(&priv->minor->master);
260                                 drm_master_put(&priv->master);
261                                 DRM_UNLOCK(dev);
262                                 goto out_free;
263                         }
264                 }
265                 DRM_UNLOCK(dev);
266         } else {
267                 /* get a reference to the master */
268                 priv->master = drm_master_get(priv->minor->master);
269                 DRM_UNLOCK(dev);
270         }
271
272         DRM_LOCK(dev);
273         list_add(&priv->lhead, &dev->filelist);
274         DRM_UNLOCK(dev);
275
276         mtx_lock(&Giant); /* FIXME: Giant required? */
277         device_busy(dev->dev);
278         mtx_unlock(&Giant);
279
280         ret = devfs_set_cdevpriv(priv, drm_release);
281         if (ret != 0)
282                 drm_release(priv);
283
284         return ret;
285       out_free:
286         free(priv, DRM_MEM_FILES);
287         return ret;
288 }
289
290 static void drm_master_release(struct drm_device *dev, struct drm_file *file_priv)
291 {
292
293         if (drm_i_have_hw_lock(dev, file_priv)) {
294                 DRM_DEBUG("File %p released, freeing lock for context %d\n",
295                           file_priv, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
296                 drm_lock_free(&file_priv->master->lock,
297                               _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
298         }
299 }
300
301 static void drm_events_release(struct drm_file *file_priv)
302 {
303         struct drm_device *dev = file_priv->minor->dev;
304         struct drm_pending_event *e, *et;
305         struct drm_pending_vblank_event *v, *vt;
306         unsigned long flags;
307
308         DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags);
309
310         /* Remove pending flips */
311         list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
312                 if (v->base.file_priv == file_priv) {
313                         list_del(&v->base.link);
314                         drm_vblank_put(dev, v->pipe);
315                         v->base.destroy(&v->base);
316                 }
317
318         /* Remove unconsumed events */
319         list_for_each_entry_safe(e, et, &file_priv->event_list, link)
320                 e->destroy(e);
321
322         DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags);
323 }
324
325 /**
326  * Release file.
327  *
328  * \param inode device inode
329  * \param file_priv DRM file private.
330  * \return zero on success or a negative number on failure.
331  *
332  * If the hardware lock is held then free it, and take it again for the kernel
333  * context since it's necessary to reclaim buffers. Unlink the file private
334  * data from its list and free it. Decreases the open count and if it reaches
335  * zero calls drm_lastclose().
336  */
337 void drm_release(void *data)
338 {
339         struct drm_file *file_priv = data;
340         struct drm_device *dev = file_priv->minor->dev;
341
342         sx_xlock(&drm_global_mutex);
343
344         DRM_DEBUG("open_count = %d\n", dev->open_count);
345
346         if (dev->driver->preclose)
347                 dev->driver->preclose(dev, file_priv);
348
349         /* ========================================================
350          * Begin inline drm_release
351          */
352
353         DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
354                   DRM_CURRENTPID,
355                   (long)file_priv->minor->device,
356                   dev->open_count);
357
358         /* Release any auth tokens that might point to this file_priv,
359            (do that under the drm_global_mutex) */
360         if (file_priv->magic)
361                 (void) drm_remove_magic(file_priv->master, file_priv->magic);
362
363         /* if the master has gone away we can't do anything with the lock */
364         if (file_priv->minor->master)
365                 drm_master_release(dev, file_priv);
366
367         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
368                 drm_core_reclaim_buffers(dev, file_priv);
369
370         drm_events_release(file_priv);
371
372         seldrain(&file_priv->event_poll);
373
374         if (dev->driver->driver_features & DRIVER_MODESET)
375                 drm_fb_release(file_priv);
376
377         if (dev->driver->driver_features & DRIVER_GEM)
378                 drm_gem_release(dev, file_priv);
379
380 #ifdef FREEBSD_NOTYET
381         mutex_lock(&dev->ctxlist_mutex);
382         if (!list_empty(&dev->ctxlist)) {
383                 struct drm_ctx_list *pos, *n;
384
385                 list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
386                         if (pos->tag == file_priv &&
387                             pos->handle != DRM_KERNEL_CONTEXT) {
388                                 if (dev->driver->context_dtor)
389                                         dev->driver->context_dtor(dev,
390                                                                   pos->handle);
391
392                                 drm_ctxbitmap_free(dev, pos->handle);
393
394                                 list_del(&pos->head);
395                                 kfree(pos);
396                                 --dev->ctx_count;
397                         }
398                 }
399         }
400         mutex_unlock(&dev->ctxlist_mutex);
401 #endif /* FREEBSD_NOTYET */
402
403         DRM_LOCK(dev);
404
405         if (file_priv->is_master) {
406                 struct drm_master *master = file_priv->master;
407                 struct drm_file *temp;
408                 list_for_each_entry(temp, &dev->filelist, lhead) {
409                         if ((temp->master == file_priv->master) &&
410                             (temp != file_priv))
411                                 temp->authenticated = 0;
412                 }
413
414                 /**
415                  * Since the master is disappearing, so is the
416                  * possibility to lock.
417                  */
418
419                 if (master->lock.hw_lock) {
420                         if (dev->sigdata.lock == master->lock.hw_lock)
421                                 dev->sigdata.lock = NULL;
422                         master->lock.hw_lock = NULL;
423                         master->lock.file_priv = NULL;
424                         DRM_WAKEUP_INT(&master->lock.lock_queue);
425                 }
426
427                 if (file_priv->minor->master == file_priv->master) {
428                         /* drop the reference held my the minor */
429                         if (dev->driver->master_drop)
430                                 dev->driver->master_drop(dev, file_priv, true);
431                         drm_master_put(&file_priv->minor->master);
432                 }
433         }
434
435         /* drop the reference held my the file priv */
436         drm_master_put(&file_priv->master);
437         file_priv->is_master = 0;
438         list_del(&file_priv->lhead);
439         DRM_UNLOCK(dev);
440
441         if (dev->driver->postclose)
442                 dev->driver->postclose(dev, file_priv);
443
444 #ifdef FREEBSD_NOTYET
445         if (drm_core_check_feature(dev, DRIVER_PRIME))
446                 drm_prime_destroy_file_private(&file_priv->prime);
447 #endif /* FREEBSD_NOTYET */
448
449         free(file_priv, DRM_MEM_FILES);
450
451         /* ========================================================
452          * End inline drm_release
453          */
454
455         atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
456         mtx_lock(&Giant);
457         device_unbusy(dev->dev);
458         mtx_unlock(&Giant);
459         if (!--dev->open_count) {
460                 if (atomic_read(&dev->ioctl_count)) {
461                         DRM_ERROR("Device busy: %d\n",
462                                   atomic_read(&dev->ioctl_count));
463                 } else
464                         drm_lastclose(dev);
465         }
466         sx_xunlock(&drm_global_mutex);
467 }
468 EXPORT_SYMBOL(drm_release);
469
470 static bool
471 drm_dequeue_event(struct drm_file *file_priv, struct uio *uio,
472     struct drm_pending_event **out)
473 {
474         struct drm_pending_event *e;
475         bool ret = false;
476
477         /* Already locked in drm_read(). */
478         /* DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); */
479
480         *out = NULL;
481         if (list_empty(&file_priv->event_list))
482                 goto out;
483         e = list_first_entry(&file_priv->event_list,
484                              struct drm_pending_event, link);
485         if (e->event->length > uio->uio_resid)
486                 goto out;
487
488         file_priv->event_space += e->event->length;
489         list_del(&e->link);
490         *out = e;
491         ret = true;
492
493 out:
494         /* DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); */
495         return ret;
496 }
497
498 int
499 drm_read(struct cdev *kdev, struct uio *uio, int ioflag)
500 {
501         struct drm_file *file_priv;
502         struct drm_device *dev;
503         struct drm_pending_event *e;
504         ssize_t error;
505
506         error = devfs_get_cdevpriv((void **)&file_priv);
507         if (error != 0) {
508                 DRM_ERROR("can't find authenticator\n");
509                 return (EINVAL);
510         }
511
512         dev = drm_get_device_from_kdev(kdev);
513         mtx_lock(&dev->event_lock);
514         while (list_empty(&file_priv->event_list)) {
515                 if ((ioflag & O_NONBLOCK) != 0) {
516                         error = EAGAIN;
517                         goto out;
518                 }
519                 error = msleep(&file_priv->event_space, &dev->event_lock,
520                    PCATCH, "drmrea", 0);
521                if (error != 0)
522                        goto out;
523         }
524
525         while (drm_dequeue_event(file_priv, uio, &e)) {
526                 mtx_unlock(&dev->event_lock);
527                 error = uiomove(e->event, e->event->length, uio);
528                 CTR3(KTR_DRM, "drm_event_dequeued %d %d %d", curproc->p_pid,
529                     e->event->type, e->event->length);
530
531                 e->destroy(e);
532                 if (error != 0)
533                         return (error);
534                 mtx_lock(&dev->event_lock);
535         }
536
537 out:
538         mtx_unlock(&dev->event_lock);
539         return (error);
540 }
541 EXPORT_SYMBOL(drm_read);
542
543 void
544 drm_event_wakeup(struct drm_pending_event *e)
545 {
546         struct drm_file *file_priv;
547         struct drm_device *dev;
548
549         file_priv = e->file_priv;
550         dev = file_priv->minor->dev;
551         mtx_assert(&dev->event_lock, MA_OWNED);
552
553         wakeup(&file_priv->event_space);
554         selwakeup(&file_priv->event_poll);
555 }
556
557 int
558 drm_poll(struct cdev *kdev, int events, struct thread *td)
559 {
560         struct drm_file *file_priv;
561         struct drm_device *dev;
562         int error, revents;
563
564         error = devfs_get_cdevpriv((void **)&file_priv);
565         if (error != 0) {
566                 DRM_ERROR("can't find authenticator\n");
567                 return (EINVAL);
568         }
569
570         dev = drm_get_device_from_kdev(kdev);
571
572         revents = 0;
573         mtx_lock(&dev->event_lock);
574         if ((events & (POLLIN | POLLRDNORM)) != 0) {
575                 if (list_empty(&file_priv->event_list)) {
576                         CTR0(KTR_DRM, "drm_poll empty list");
577                         selrecord(td, &file_priv->event_poll);
578                 } else {
579                         revents |= events & (POLLIN | POLLRDNORM);
580                         CTR1(KTR_DRM, "drm_poll revents %x", revents);
581                 }
582         }
583         mtx_unlock(&dev->event_lock);
584         return (revents);
585 }
586 EXPORT_SYMBOL(drm_poll);
587
588 int
589 drm_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size,
590     struct vm_object **obj_res, int nprot)
591 {
592         struct drm_device *dev;
593
594         dev = drm_get_device_from_kdev(kdev);
595         if (dev->drm_ttm_bdev != NULL) {
596                 return (-ttm_bo_mmap_single(dev->drm_ttm_bdev, offset, size,
597                     obj_res, nprot));
598         } else if ((dev->driver->driver_features & DRIVER_GEM) != 0) {
599                 return (-drm_gem_mmap_single(dev, offset, size, obj_res, nprot));
600         } else {
601                 return (ENODEV);
602         }
603 }