DRM update to git snapshot from 2008-01-04.
[dragonfly.git] / sys / dev / drm / drm_irq.c
1 /*-
2  * Copyright 2003 Eric Anholt
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * ERIC ANHOLT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *    Eric Anholt <anholt@FreeBSD.org>
25  *
26  * $DragonFly: src/sys/dev/drm/drm_irq.c,v 1.1 2008/04/05 18:12:29 hasso Exp $
27  */
28
29 /** @file drm_irq.c
30  * Support code for handling setup/teardown of interrupt handlers and
31  * handing interrupt handlers off to the drivers.
32  */
33
34 #include "drmP.h"
35 #include "drm.h"
36
37 static void drm_locked_task(void *context, int pending __unused);
38
39 int drm_irq_by_busid(drm_device_t *dev, void *data, struct drm_file *file_priv)
40 {
41         drm_irq_busid_t *irq = data;
42
43         if ((irq->busnum >> 8) != dev->pci_domain ||
44             (irq->busnum & 0xff) != dev->pci_bus ||
45             irq->devnum != dev->pci_slot ||
46             irq->funcnum != dev->pci_func)
47                 return EINVAL;
48
49         irq->irq = dev->irq;
50
51         DRM_DEBUG("%d:%d:%d => IRQ %d\n",
52                   irq->busnum, irq->devnum, irq->funcnum, irq->irq);
53
54         return 0;
55 }
56
57 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
58 static irqreturn_t
59 drm_irq_handler_wrap(DRM_IRQ_ARGS)
60 {
61         drm_device_t *dev = (drm_device_t *)arg;
62
63         DRM_SPINLOCK(&dev->irq_lock);
64         dev->driver.irq_handler(arg);
65         DRM_SPINUNLOCK(&dev->irq_lock);
66 }
67 #endif
68
69 int drm_irq_install(drm_device_t *dev)
70 {
71         int retcode;
72 #ifdef __NetBSD__
73         pci_intr_handle_t ih;
74 #endif
75
76         if (dev->irq == 0 || dev->dev_private == NULL)
77                 return EINVAL;
78
79         DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq );
80
81         DRM_LOCK();
82         if (dev->irq_enabled) {
83                 DRM_UNLOCK();
84                 return EBUSY;
85         }
86         dev->irq_enabled = 1;
87
88         dev->context_flag = 0;
89
90 #ifndef __DragonFly__
91         DRM_SPININIT(&dev->irq_lock, "DRM IRQ lock");
92 #else
93         lwkt_serialize_init(&dev->irq_lock);
94 #endif
95
96                                 /* Before installing handler */
97         dev->driver.irq_preinstall(dev);
98         DRM_UNLOCK();
99
100                                 /* Install handler */
101 #if defined(__FreeBSD__) || defined(__DragonFly__)
102         dev->irqrid = 0;
103         dev->irqr = bus_alloc_resource_any(dev->device, SYS_RES_IRQ, 
104                                       &dev->irqrid, RF_SHAREABLE);
105         if (!dev->irqr) {
106                 retcode = ENOENT;
107                 goto err;
108         }
109 #if __FreeBSD_version >= 700031
110         retcode = bus_setup_intr(dev->device, dev->irqr,
111                                  INTR_TYPE_TTY | INTR_MPSAFE,
112                                  NULL, drm_irq_handler_wrap, dev, &dev->irqh);
113 #elif defined(__DragonFly__)
114         retcode = bus_setup_intr(dev->device, dev->irqr,
115                                  INTR_MPSAFE,
116                                  dev->driver.irq_handler, dev, &dev->irqh,
117                                  &dev->irq_lock);
118 #else
119         retcode = bus_setup_intr(dev->device, dev->irqr,
120                                  INTR_TYPE_TTY | INTR_MPSAFE,
121                                  drm_irq_handler_wrap, dev, &dev->irqh);
122 #endif
123         if (retcode != 0)
124                 goto err;
125 #elif defined(__NetBSD__) || defined(__OpenBSD__)
126         if (pci_intr_map(&dev->pa, &ih) != 0) {
127                 retcode = ENOENT;
128                 goto err;
129         }
130         dev->irqh = pci_intr_establish(&dev->pa.pa_pc, ih, IPL_TTY,
131             (irqreturn_t (*)(void *))dev->irq_handler, dev);
132         if (!dev->irqh) {
133                 retcode = ENOENT;
134                 goto err;
135         }
136 #endif
137
138                                 /* After installing handler */
139         DRM_LOCK();
140         dev->driver.irq_postinstall(dev);
141         DRM_UNLOCK();
142
143         TASK_INIT(&dev->locked_task, 0, drm_locked_task, dev);
144         return 0;
145 err:
146         DRM_LOCK();
147         dev->irq_enabled = 0;
148 #if defined(___FreeBSD__) || defined(__DragonFly__)
149         if (dev->irqrid != 0) {
150                 bus_release_resource(dev->device, SYS_RES_IRQ, dev->irqrid,
151                     dev->irqr);
152                 dev->irqrid = 0;
153         }
154 #endif
155 #ifndef __DragonFly__
156         DRM_SPINUNINIT(&dev->irq_lock);
157 #endif
158         DRM_UNLOCK();
159         return retcode;
160 }
161
162 int drm_irq_uninstall(drm_device_t *dev)
163 {
164 #if defined(__FreeBSD__) || defined(__DragonFly__)
165         int irqrid;
166 #endif
167
168         if (!dev->irq_enabled)
169                 return EINVAL;
170
171         dev->irq_enabled = 0;
172 #if defined(__FreeBSD__) || defined(__DragonFly__)
173         irqrid = dev->irqrid;
174         dev->irqrid = 0;
175 #endif
176
177         DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq );
178
179         dev->driver.irq_uninstall(dev);
180
181 #if defined(__FreeBSD__) || defined(__DragonFly__)
182         DRM_UNLOCK();
183         bus_teardown_intr(dev->device, dev->irqr, dev->irqh);
184         bus_release_resource(dev->device, SYS_RES_IRQ, irqrid, dev->irqr);
185         DRM_LOCK();
186 #elif defined(__NetBSD__) || defined(__OpenBSD__)
187         pci_intr_disestablish(&dev->pa.pa_pc, dev->irqh);
188 #endif
189 #ifndef __DragonFly__
190         DRM_SPINUNINIT(&dev->irq_lock);
191 #endif
192
193         return 0;
194 }
195
196 int drm_control(drm_device_t *dev, void *data, struct drm_file *file_priv)
197 {
198         drm_control_t *ctl = data;
199         int err;
200
201         switch ( ctl->func ) {
202         case DRM_INST_HANDLER:
203                 /* Handle drivers whose DRM used to require IRQ setup but the
204                  * no longer does.
205                  */
206                 if (!dev->driver.use_irq)
207                         return 0;
208                 if (dev->if_version < DRM_IF_VERSION(1, 2) &&
209                     ctl->irq != dev->irq)
210                         return EINVAL;
211                 return drm_irq_install(dev);
212         case DRM_UNINST_HANDLER:
213                 if (!dev->driver.use_irq)
214                         return 0;
215                 DRM_LOCK();
216                 err = drm_irq_uninstall(dev);
217                 DRM_UNLOCK();
218                 return err;
219         default:
220                 return EINVAL;
221         }
222 }
223
224 int drm_wait_vblank(drm_device_t *dev, void *data, struct drm_file *file_priv)
225 {
226         drm_wait_vblank_t *vblwait = data;
227         struct timeval now;
228         int ret = 0;
229         int flags, seq;
230
231         if (!dev->irq_enabled)
232                 return EINVAL;
233
234         if (vblwait->request.type &
235             ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) {
236                 DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
237                     vblwait->request.type,
238                     (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK));
239                 return EINVAL;
240         }
241
242         flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
243
244         if ((flags & _DRM_VBLANK_SECONDARY) && !dev->driver.use_vbl_irq2)
245                 return EINVAL;
246         
247         seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ?
248             &dev->vbl_received2 : &dev->vbl_received);
249
250         switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
251         case _DRM_VBLANK_RELATIVE:
252                 vblwait->request.sequence += seq;
253                 vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
254         case _DRM_VBLANK_ABSOLUTE:
255                 break;
256         default:
257                 return EINVAL;
258         }
259
260         if ((flags & _DRM_VBLANK_NEXTONMISS) &&
261             (seq - vblwait->request.sequence) <= (1<<23)) {
262                 vblwait->request.sequence = seq + 1;
263         }
264
265         if (flags & _DRM_VBLANK_SIGNAL) {
266 #if 0 /* disabled */
267                 drm_vbl_sig_t *vbl_sig = malloc(sizeof(drm_vbl_sig_t), M_DRM,
268                     M_NOWAIT | M_ZERO);
269                 if (vbl_sig == NULL)
270                         return ENOMEM;
271
272                 vbl_sig->sequence = vblwait->request.sequence;
273                 vbl_sig->signo = vblwait->request.signal;
274                 vbl_sig->pid = DRM_CURRENTPID;
275
276                 vblwait->reply.sequence = atomic_read(&dev->vbl_received);
277                 
278                 DRM_SPINLOCK(&dev->irq_lock);
279                 TAILQ_INSERT_HEAD(&dev->vbl_sig_list, vbl_sig, link);
280                 DRM_SPINUNLOCK(&dev->irq_lock);
281                 ret = 0;
282 #endif
283                 ret = EINVAL;
284         } else {
285                 DRM_LOCK();
286                 /* shared code returns -errno */
287                 if (flags & _DRM_VBLANK_SECONDARY) {
288                         if (dev->driver.vblank_wait2)
289                                 ret = -dev->driver.vblank_wait2(dev,
290                                     &vblwait->request.sequence);
291                 } else if (dev->driver.vblank_wait)
292                         ret = -dev->driver.vblank_wait(dev,
293                             &vblwait->request.sequence);
294
295                 DRM_UNLOCK();
296
297                 microtime(&now);
298                 vblwait->reply.tval_sec = now.tv_sec;
299                 vblwait->reply.tval_usec = now.tv_usec;
300         }
301
302         return ret;
303 }
304
305 void drm_vbl_send_signals(drm_device_t *dev)
306 {
307 }
308
309 #if 0 /* disabled */
310 void drm_vbl_send_signals( drm_device_t *dev )
311 {
312         drm_vbl_sig_t *vbl_sig;
313         unsigned int vbl_seq = atomic_read( &dev->vbl_received );
314         struct proc *p;
315
316         vbl_sig = TAILQ_FIRST(&dev->vbl_sig_list);
317         while (vbl_sig != NULL) {
318                 drm_vbl_sig_t *next = TAILQ_NEXT(vbl_sig, link);
319
320                 if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) {
321                         p = pfind(vbl_sig->pid);
322                         if (p != NULL)
323                                 psignal(p, vbl_sig->signo);
324
325                         TAILQ_REMOVE(&dev->vbl_sig_list, vbl_sig, link);
326                         DRM_FREE(vbl_sig,sizeof(*vbl_sig));
327                 }
328                 vbl_sig = next;
329         }
330 }
331 #endif
332
333 static void drm_locked_task(void *context, int pending __unused)
334 {
335         drm_device_t *dev = context;
336
337         DRM_LOCK();
338         for (;;) {
339                 int ret;
340
341                 if (drm_lock_take(&dev->lock.hw_lock->lock,
342                     DRM_KERNEL_CONTEXT))
343                 {
344                         dev->lock.file_priv = NULL; /* kernel owned */
345                         dev->lock.lock_time = jiffies;
346                         atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
347                         break;  /* Got lock */
348                 }
349
350                 /* Contention */
351 #if defined(__FreeBSD__) && __FreeBSD_version > 500000
352                 ret = mtx_sleep((void *)&dev->lock.lock_queue, &dev->dev_lock,
353                     PZERO | PCATCH, "drmlk2", 0);
354 #elif defined(__DragonFly__)
355                 crit_enter();
356                 tsleep_interlock((void *)&dev->lock.lock_queue);
357                 DRM_UNLOCK();
358                 ret = tsleep((void *)&dev->lock.lock_queue, PCATCH,
359                     "drmlk2", 0);
360                 crit_exit();
361                 DRM_LOCK();
362 #else
363                 ret = tsleep((void *)&dev->lock.lock_queue, PZERO | PCATCH,
364                     "drmlk2", 0);
365 #endif
366                 if (ret != 0)
367                         return;
368         }
369         DRM_UNLOCK();
370
371         dev->locked_task_call(dev);
372
373         drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
374 }
375
376 void
377 drm_locked_tasklet(drm_device_t *dev, void (*tasklet)(drm_device_t *dev))
378 {
379         dev->locked_task_call = tasklet;
380         taskqueue_enqueue(taskqueue_swi, &dev->locked_task);
381 }