Merge branch 'vendor/GCC50'
[dragonfly.git] / sys / dev / drm / 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  * $FreeBSD: src/sys/dev/drm2/drm_fops.c,v 1.1 2012/05/22 11:07:44 kib Exp $
37  */
38
39 #include <sys/types.h>
40 #include <sys/conf.h>
41 #include <sys/devfs.h>
42
43 #include <drm/drmP.h>
44
45 extern drm_pci_id_list_t *drm_find_description(int vendor, int device,
46     drm_pci_id_list_t *idlist);
47 extern devclass_t drm_devclass;
48
49 static int drm_setup(struct drm_device *dev)
50 {
51         drm_local_map_t *map;
52         int i;
53
54         DRM_LOCK_ASSERT(dev);
55
56         /* prebuild the SAREA */
57         i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM,
58             _DRM_CONTAINS_LOCK, &map);
59         if (i != 0)
60                 return i;
61
62         if (dev->driver->firstopen)
63                 dev->driver->firstopen(dev);
64
65         dev->buf_use = 0;
66
67         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) {
68                 i = drm_dma_setup(dev);
69                 if (i != 0)
70                         return i;
71         }
72
73         for (i = 0; i < DRM_HASH_SIZE; i++) {
74                 dev->magiclist[i].head = NULL;
75                 dev->magiclist[i].tail = NULL;
76         }
77
78         init_waitqueue_head(&dev->lock.lock_queue);
79         if (!drm_core_check_feature(dev, DRIVER_MODESET))
80                 dev->irq_enabled = 0;
81         dev->context_flag = 0;
82         dev->last_context = 0;
83         dev->if_version = 0;
84
85         dev->buf_sigio = NULL;
86
87         DRM_DEBUG("\n");
88
89         return 0;
90 }
91
92 #define DRIVER_SOFTC(unit) \
93         ((struct drm_device *)devclass_get_softc(drm_devclass, unit))
94
95 int
96 drm_open(struct dev_open_args *ap)
97 {
98         struct cdev *kdev = ap->a_head.a_dev;
99         int flags = ap->a_oflags;
100         int fmt = 0;
101         struct thread *p = curthread;
102         struct drm_device *dev;
103         int retcode;
104
105         dev = DRIVER_SOFTC(minor(kdev));
106         if (dev == NULL)
107                 return (ENXIO);
108
109         DRM_DEBUG("open_count = %d\n", dev->open_count);
110
111         retcode = drm_open_helper(kdev, flags, fmt, p, dev, ap->a_fp);
112
113         if (retcode == 0) {
114                 atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
115                 DRM_LOCK(dev);
116                 device_busy(dev->dev);
117                 if (!dev->open_count++)
118                         retcode = drm_setup(dev);
119                 DRM_UNLOCK(dev);
120         }
121
122         DRM_DEBUG("return %d\n", retcode);
123
124         return (retcode);
125 }
126
127 /* drm_open_helper is called whenever a process opens /dev/drm. */
128 int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p,
129                     struct drm_device *dev, struct file *fp)
130 {
131         struct drm_file *priv;
132         int retcode;
133
134         if (flags & O_EXCL)
135                 return EBUSY; /* No exclusive opens */
136         dev->flags = flags;
137
138         DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev));
139
140         priv = kmalloc(sizeof(*priv), M_DRM, M_WAITOK | M_NULLOK | M_ZERO);
141         if (priv == NULL) {
142                 return ENOMEM;
143         }
144         
145         DRM_LOCK(dev);
146         priv->dev               = dev;
147         priv->uid               = p->td_proc->p_ucred->cr_svuid;
148         priv->pid               = p->td_proc->p_pid;
149         priv->ioctl_count       = 0;
150
151         /* for compatibility root is always authenticated */
152         priv->authenticated     = DRM_SUSER(p);
153
154         INIT_LIST_HEAD(&priv->fbs);
155         INIT_LIST_HEAD(&priv->event_list);
156         init_waitqueue_head(&priv->event_wait);
157         priv->event_space = 4096; /* set aside 4k for event buffer */
158
159         if (dev->driver->driver_features & DRIVER_GEM)
160                 drm_gem_open(dev, priv);
161
162         if (dev->driver->open) {
163                 /* shared code returns -errno */
164                 retcode = -dev->driver->open(dev, priv);
165                 if (retcode != 0) {
166                         drm_free(priv, M_DRM);
167                         DRM_UNLOCK(dev);
168                         return retcode;
169                 }
170         }
171
172         /* first opener automatically becomes master */
173         priv->master = list_empty(&dev->filelist);
174
175         list_add(&priv->lhead, &dev->filelist);
176         DRM_UNLOCK(dev);
177         kdev->si_drv1 = dev;
178
179         retcode = devfs_set_cdevpriv(fp, priv, &drm_cdevpriv_dtor);
180         if (retcode != 0)
181                 drm_cdevpriv_dtor(priv);
182
183         return retcode;
184 }
185
186 /**
187  * Release file.
188  *
189  * \param inode device inode
190  * \param file_priv DRM file private.
191  * \return zero on success or a negative number on failure.
192  *
193  * If the hardware lock is held then free it, and take it again for the kernel
194  * context since it's necessary to reclaim buffers. Unlink the file private
195  * data from its list and free it. Decreases the open count and if it reaches
196  * zero calls drm_lastclose().
197  */
198
199 #if 0 /* old drm_release equivalent from DragonFly */
200 void drm_cdevpriv_dtor(void *cd)
201 {
202         struct drm_file *file_priv = cd;
203         struct drm_device *dev = file_priv->dev;
204         int retcode = 0;
205
206         DRM_DEBUG("open_count = %d\n", dev->open_count);
207
208         DRM_LOCK(dev);
209
210         if (dev->driver->preclose != NULL)
211                 dev->driver->preclose(dev, file_priv);
212
213         /* ========================================================
214          * Begin inline drm_release
215          */
216
217         DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
218             DRM_CURRENTPID, (long)dev->dev, dev->open_count);
219
220         if (dev->driver->driver_features & DRIVER_GEM)
221                 drm_gem_release(dev, file_priv);
222
223         if (dev->primary->master->lock.hw_lock
224             && _DRM_LOCK_IS_HELD(dev->primary->master->lock.hw_lock->lock)
225             && dev->primary->master->lock.file_priv == file_priv) {
226                 DRM_DEBUG("Process %d dead, freeing lock for context %d\n",
227                           DRM_CURRENTPID,
228                           _DRM_LOCKING_CONTEXT(dev->primary->master->lock.hw_lock->lock));
229                 if (dev->driver->reclaim_buffers_locked != NULL)
230                         dev->driver->reclaim_buffers_locked(dev, file_priv);
231
232                 drm_lock_free(&dev->primary->master->lock,
233                     _DRM_LOCKING_CONTEXT(dev->primary->master->lock.hw_lock->lock));
234
235                                 /* FIXME: may require heavy-handed reset of
236                                    hardware at this point, possibly
237                                    processed via a callback to the X
238                                    server. */
239         } else if (dev->driver->reclaim_buffers_locked != NULL &&
240             dev->primary->master->lock.hw_lock != NULL) {
241                 /* The lock is required to reclaim buffers */
242                 for (;;) {
243                         if (!dev->primary->master->lock.hw_lock) {
244                                 /* Device has been unregistered */
245                                 retcode = EINTR;
246                                 break;
247                         }
248                         if (drm_lock_take(&dev->primary->master->lock, DRM_KERNEL_CONTEXT)) {
249                                 dev->primary->master->lock.file_priv = file_priv;
250                                 dev->primary->master->lock.lock_time = jiffies;
251                                 atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
252                                 break;  /* Got lock */
253                         }
254                         /* Contention */
255                         retcode = DRM_LOCK_SLEEP(dev, &dev->primary->master->lock.lock_queue,
256                             PCATCH, "drmlk2", 0);
257                         if (retcode)
258                                 break;
259                 }
260                 if (retcode == 0) {
261                         dev->driver->reclaim_buffers_locked(dev, file_priv);
262                         drm_lock_free(&dev->primary->master->lock, DRM_KERNEL_CONTEXT);
263                 }
264         }
265
266         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
267             !dev->driver->reclaim_buffers_locked)
268                 drm_reclaim_buffers(dev, file_priv);
269
270         funsetown(&dev->buf_sigio);
271
272         if (dev->driver->postclose != NULL)
273                 dev->driver->postclose(dev, file_priv);
274         list_del(&file_priv->lhead);
275
276
277         /* ========================================================
278          * End inline drm_release
279          */
280
281         atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
282         device_unbusy(dev->dev);
283         if (--dev->open_count == 0) {
284                 retcode = drm_lastclose(dev);
285         }
286
287         DRM_UNLOCK(dev);
288 }
289 #endif
290
291 static void drm_unload(struct drm_device *dev)
292 {
293         int i;
294
295         DRM_DEBUG("\n");
296
297         drm_sysctl_cleanup(dev);
298         if (dev->devnode != NULL)
299                 destroy_dev(dev->devnode);
300
301         drm_ctxbitmap_cleanup(dev);
302
303         if (dev->driver->driver_features & DRIVER_GEM)
304                 drm_gem_destroy(dev);
305
306         if (dev->agp && dev->agp->agp_mtrr) {
307                 int __unused retcode;
308
309                 retcode = drm_mtrr_del(0, dev->agp->agp_info.ai_aperture_base,
310                     dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC);
311                 DRM_DEBUG("mtrr_del = %d", retcode);
312         }
313
314         drm_vblank_cleanup(dev);
315
316         DRM_LOCK(dev);
317         drm_lastclose(dev);
318         DRM_UNLOCK(dev);
319
320         /* Clean up PCI resources allocated by drm_bufs.c.  We're not really
321          * worried about resource consumption while the DRM is inactive (between
322          * lastclose and firstopen or unload) because these aren't actually
323          * taking up KVA, just keeping the PCI resource allocated.
324          */
325         for (i = 0; i < DRM_MAX_PCI_RESOURCE; i++) {
326                 if (dev->pcir[i] == NULL)
327                         continue;
328                 bus_release_resource(dev->dev, SYS_RES_MEMORY,
329                     dev->pcirid[i], dev->pcir[i]);
330                 dev->pcir[i] = NULL;
331         }
332
333         if (dev->agp) {
334                 drm_free(dev->agp, M_DRM);
335                 dev->agp = NULL;
336         }
337
338         if (dev->driver->unload != NULL) {
339                 DRM_LOCK(dev);
340                 dev->driver->unload(dev);
341                 DRM_UNLOCK(dev);
342         }
343
344         drm_mem_uninit();
345
346         if (pci_disable_busmaster(dev->dev))
347                 DRM_ERROR("Request to disable bus-master failed.\n");
348
349         lockuninit(&dev->vbl_lock);
350         lockuninit(&dev->dev_lock);
351         lockuninit(&dev->event_lock);
352         lockuninit(&dev->struct_mutex);
353 }
354
355 int drm_release(device_t kdev)
356 {
357         struct drm_device *dev;
358
359         dev = device_get_softc(kdev);
360         drm_unload(dev);
361         if (dev->irqr) {
362                 bus_release_resource(dev->dev, SYS_RES_IRQ, dev->irqrid,
363                     dev->irqr);
364                 if (dev->msi_enabled) {
365                         pci_release_msi(dev->dev);
366                         DRM_INFO("MSI released\n");
367                 }
368         }
369         return (0);
370 }
371
372 static bool
373 drm_dequeue_event(struct drm_device *dev, struct drm_file *file_priv,
374     struct uio *uio, struct drm_pending_event **out)
375 {
376         struct drm_pending_event *e;
377
378         if (list_empty(&file_priv->event_list))
379                 return (false);
380         e = list_first_entry(&file_priv->event_list,
381             struct drm_pending_event, link);
382         if (e->event->length > uio->uio_resid)
383                 return (false);
384
385         file_priv->event_space += e->event->length;
386         list_del(&e->link);
387         *out = e;
388         return (true);
389 }
390
391 int
392 drm_read(struct dev_read_args *ap)
393 {
394         struct cdev *kdev = ap->a_head.a_dev;
395         struct uio *uio = ap->a_uio;
396         int ioflag = ap->a_ioflag;
397         struct drm_file *file_priv;
398         struct drm_device *dev;
399         struct drm_pending_event *e;
400         int error;
401
402         error = devfs_get_cdevpriv(ap->a_fp, (void **)&file_priv);
403         if (error != 0) {
404                 DRM_ERROR("can't find authenticator\n");
405                 return (EINVAL);
406         }
407         dev = drm_get_device_from_kdev(kdev);
408         lockmgr(&dev->event_lock, LK_EXCLUSIVE);
409         while (list_empty(&file_priv->event_list)) {
410                 if ((ioflag & O_NONBLOCK) != 0) {
411                         error = EAGAIN;
412                         goto out;
413                 }
414                 error = lksleep(&file_priv->event_space, &dev->event_lock,
415                    PCATCH, "drmrea", 0);
416                if (error != 0)
417                        goto out;
418         }
419         while (drm_dequeue_event(dev, file_priv, uio, &e)) {
420                 lockmgr(&dev->event_lock, LK_RELEASE);
421                 error = uiomove((caddr_t)e->event, e->event->length, uio);
422                 e->destroy(e);
423                 if (error != 0)
424                         return (error);
425                 lockmgr(&dev->event_lock, LK_EXCLUSIVE);
426         }
427 out:
428         lockmgr(&dev->event_lock, LK_RELEASE);
429         return (error);
430 }
431
432 void
433 drm_event_wakeup(struct drm_pending_event *e)
434 {
435         struct drm_file *file_priv;
436         struct drm_device *dev;
437
438         file_priv = e->file_priv;
439         dev = file_priv->dev;
440         KKASSERT(lockstatus(&dev->event_lock, curthread) != 0);
441
442         wakeup(&file_priv->event_space);
443         KNOTE(&file_priv->dkq.ki_note, 0);
444 }
445
446 static int
447 drmfilt(struct knote *kn, long hint)
448 {
449         struct drm_file *file_priv;
450         struct drm_device *dev;
451         int ready = 0;
452
453         file_priv = (struct drm_file *)kn->kn_hook;
454         dev = file_priv->dev;
455         lockmgr(&dev->event_lock, LK_EXCLUSIVE);
456         if (!list_empty(&file_priv->event_list))
457                 ready = 1;
458         lockmgr(&dev->event_lock, LK_RELEASE);
459
460         return (ready);
461 }
462
463 static void
464 drmfilt_detach(struct knote *kn)
465 {
466         struct drm_file *file_priv;
467         struct drm_device *dev;
468         struct klist *klist;
469
470         file_priv = (struct drm_file *)kn->kn_hook;
471         dev = file_priv->dev;
472
473         lockmgr(&dev->event_lock, LK_EXCLUSIVE);
474         klist = &file_priv->dkq.ki_note;
475         knote_remove(klist, kn);
476         lockmgr(&dev->event_lock, LK_RELEASE);
477 }
478
479 static struct filterops drmfiltops =
480         { FILTEROP_ISFD, NULL, drmfilt_detach, drmfilt };
481
482 int
483 drm_kqfilter(struct dev_kqfilter_args *ap)
484 {
485         struct cdev *kdev = ap->a_head.a_dev;
486         struct drm_file *file_priv;
487         struct drm_device *dev;
488         struct knote *kn = ap->a_kn;
489         struct klist *klist;
490         int error;
491
492         error = devfs_get_cdevpriv(ap->a_fp, (void **)&file_priv);
493         if (error != 0) {
494                 DRM_ERROR("can't find authenticator\n");
495                 return (EINVAL);
496         }
497         dev = drm_get_device_from_kdev(kdev);
498
499         ap->a_result = 0;
500
501         switch (kn->kn_filter) {
502         case EVFILT_READ:
503         case EVFILT_WRITE:
504                 kn->kn_fop = &drmfiltops;
505                 kn->kn_hook = (caddr_t)file_priv;
506                 break;
507         default:
508                 ap->a_result = EOPNOTSUPP;
509                 return (0);
510         }
511
512         lockmgr(&dev->event_lock, LK_EXCLUSIVE);
513         klist = &file_priv->dkq.ki_note;
514         knote_insert(klist, kn);
515         lockmgr(&dev->event_lock, LK_RELEASE);
516
517         return (0);
518 }