2 * Copyright 2003 Eric Anholt
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:
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
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.
24 * Eric Anholt <anholt@FreeBSD.org>
29 * Support code for handling setup/teardown of interrupt handlers and
30 * handing interrupt handlers off to the drivers.
36 int drm_irq_by_busid(struct drm_device
*dev
, void *data
,
37 struct drm_file
*file_priv
)
39 struct drm_irq_busid
*irq
= data
;
41 if ((irq
->busnum
>> 8) != dev
->pci_domain
||
42 (irq
->busnum
& 0xff) != dev
->pci_bus
||
43 irq
->devnum
!= dev
->pci_slot
||
44 irq
->funcnum
!= dev
->pci_func
)
49 DRM_DEBUG("%d:%d:%d => IRQ %d\n",
50 irq
->busnum
, irq
->devnum
, irq
->funcnum
, irq
->irq
);
55 #if defined(__FreeBSD__)
57 drm_irq_handler_wrap(DRM_IRQ_ARGS
)
59 struct drm_device
*dev
= arg
;
61 DRM_SPINLOCK(&dev
->irq_lock
);
62 dev
->driver
->irq_handler(arg
);
63 DRM_SPINUNLOCK(&dev
->irq_lock
);
65 #elif defined(__NetBSD__)
67 drm_irq_handler_wrap(DRM_IRQ_ARGS
)
70 struct drm_device
*dev
= arg
;
72 DRM_SPINLOCK(&dev
->irq_lock
);
73 ret
= dev
->driver
->irq_handler(arg
);
74 DRM_SPINUNLOCK(&dev
->irq_lock
);
79 static void vblank_disable_fn(void *arg
)
81 struct drm_device
*dev
= (struct drm_device
*)arg
;
84 #if defined(__NetBSD__)
85 mutex_enter(&dev
->vbl_lock
);
87 if (callout_pending(&dev
->vblank_disable_timer
)) {
88 /* callout was reset */
89 #if defined(__NetBSD__)
90 mutex_exit(&dev
->vbl_lock
);
94 if (!callout_active(&dev
->vblank_disable_timer
)) {
95 /* callout was stopped */
96 #if defined(__NetBSD__)
97 mutex_exit(&dev
->vbl_lock
);
101 callout_deactivate(&dev
->vblank_disable_timer
);
103 DRM_DEBUG("vblank_disable_allowed=%d\n", dev
->vblank_disable_allowed
);
104 if (!dev
->vblank_disable_allowed
) {
105 #if defined(__NetBSD__)
106 mutex_exit(&dev
->vbl_lock
);
111 for (i
= 0; i
< dev
->num_crtcs
; i
++) {
112 if (atomic_read(&dev
->vblank
[i
].refcount
) == 0 &&
113 dev
->vblank
[i
].enabled
) {
114 DRM_DEBUG("disabling vblank on crtc %d\n", i
);
115 dev
->vblank
[i
].last
=
116 dev
->driver
->get_vblank_counter(dev
, i
);
117 dev
->driver
->disable_vblank(dev
, i
);
118 dev
->vblank
[i
].enabled
= 0;
121 #if defined(__NetBSD__)
122 mutex_exit(&dev
->vbl_lock
);
126 void drm_vblank_cleanup(struct drm_device
*dev
)
128 unsigned long irqflags
;
130 /* Bail if the driver didn't call drm_vblank_init() */
131 if (dev
->num_crtcs
== 0)
134 DRM_SPINLOCK_IRQSAVE(&dev
->vbl_lock
, irqflags
);
135 callout_stop(&dev
->vblank_disable_timer
);
136 DRM_SPINUNLOCK_IRQRESTORE(&dev
->vbl_lock
, irqflags
);
138 callout_drain(&dev
->vblank_disable_timer
);
140 vblank_disable_fn((void *)dev
);
142 free(dev
->vblank
, DRM_MEM_DRIVER
);
147 int drm_vblank_init(struct drm_device
*dev
, int num_crtcs
)
151 #if defined(__FreeBSD__)
152 callout_init_mtx(&dev
->vblank_disable_timer
, &dev
->vbl_lock
, 0);
154 callout_init(&dev
->vblank_disable_timer
, 0);
156 atomic_set(&dev
->vbl_signal_pending
, 0);
157 dev
->num_crtcs
= num_crtcs
;
159 dev
->vblank
= malloc(sizeof(struct drm_vblank_info
) * num_crtcs
,
160 DRM_MEM_DRIVER
, M_NOWAIT
| M_ZERO
);
166 /* Zero per-crtc vblank stuff */
167 for (i
= 0; i
< num_crtcs
; i
++) {
168 DRM_INIT_WAITQUEUE(&dev
->vblank
[i
].queue
);
169 TAILQ_INIT(&dev
->vblank
[i
].sigs
);
170 atomic_set(&dev
->vblank
[i
].count
, 0);
171 atomic_set(&dev
->vblank
[i
].refcount
, 0);
174 dev
->vblank_disable_allowed
= 0;
179 drm_vblank_cleanup(dev
);
183 int drm_irq_install(struct drm_device
*dev
)
187 pci_intr_handle_t ih
;
191 if (dev
->irq
== 0 || dev
->dev_private
== NULL
)
194 DRM_DEBUG("irq=%d\n", dev
->irq
);
197 if (dev
->irq_enabled
) {
201 dev
->irq_enabled
= 1;
203 dev
->context_flag
= 0;
205 /* Before installing handler */
206 dev
->driver
->irq_preinstall(dev
);
209 /* Install handler */
210 #if defined(__FreeBSD__)
211 #if __FreeBSD_version >= 700031
212 retcode
= bus_setup_intr(dev
->device
, dev
->irqr
,
213 INTR_TYPE_TTY
| INTR_MPSAFE
,
214 NULL
, drm_irq_handler_wrap
, dev
, &dev
->irqh
);
216 retcode
= bus_setup_intr(dev
->device
, dev
->irqr
,
217 INTR_TYPE_TTY
| INTR_MPSAFE
,
218 drm_irq_handler_wrap
, dev
, &dev
->irqh
);
222 #elif defined(__NetBSD__)
223 if (pci_intr_map(&dev
->pa
, &ih
) != 0) {
227 istr
= pci_intr_string(dev
->pa
.pa_pc
, ih
);
228 dev
->irqh
= pci_intr_establish(dev
->pa
.pa_pc
, ih
, IPL_TTY
,
229 drm_irq_handler_wrap
, dev
);
234 aprint_normal_dev(dev
->device
, "interrupting at %s\n", istr
);
237 /* After installing handler */
239 dev
->driver
->irq_postinstall(dev
);
245 dev
->irq_enabled
= 0;
251 int drm_irq_uninstall(struct drm_device
*dev
)
253 if (!dev
->irq_enabled
)
256 dev
->irq_enabled
= 0;
258 DRM_DEBUG("irq=%d\n", dev
->irq
);
260 dev
->driver
->irq_uninstall(dev
);
262 #if defined(__FreeBSD__)
264 bus_teardown_intr(dev
->device
, dev
->irqr
, dev
->irqh
);
266 #elif defined(__NetBSD__)
267 pci_intr_disestablish(dev
->pa
.pa_pc
, dev
->irqh
);
273 int drm_control(struct drm_device
*dev
, void *data
, struct drm_file
*file_priv
)
275 struct drm_control
*ctl
= data
;
279 case DRM_INST_HANDLER
:
280 /* Handle drivers whose DRM used to require IRQ setup but the
283 if (!drm_core_check_feature(dev
, DRIVER_HAVE_IRQ
))
285 if (dev
->if_version
< DRM_IF_VERSION(1, 2) &&
286 ctl
->irq
!= dev
->irq
)
288 return drm_irq_install(dev
);
289 case DRM_UNINST_HANDLER
:
290 if (!drm_core_check_feature(dev
, DRIVER_HAVE_IRQ
))
293 err
= drm_irq_uninstall(dev
);
301 u32
drm_vblank_count(struct drm_device
*dev
, int crtc
)
303 return atomic_read(&dev
->vblank
[crtc
].count
);
306 static void drm_update_vblank_count(struct drm_device
*dev
, int crtc
)
308 u32 cur_vblank
, diff
;
311 * Interrupts were disabled prior to this call, so deal with counter
313 * NOTE! It's possible we lost a full dev->max_vblank_count events
314 * here if the register is small or we had vblank interrupts off for
317 cur_vblank
= dev
->driver
->get_vblank_counter(dev
, crtc
);
318 diff
= cur_vblank
- dev
->vblank
[crtc
].last
;
319 if (cur_vblank
< dev
->vblank
[crtc
].last
) {
320 diff
+= dev
->max_vblank_count
;
322 DRM_DEBUG("vblank[%d].last=0x%x, cur_vblank=0x%x => diff=0x%x\n",
323 crtc
, dev
->vblank
[crtc
].last
, cur_vblank
, diff
);
326 DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n",
329 atomic_add(diff
, &dev
->vblank
[crtc
].count
);
332 int drm_vblank_get(struct drm_device
*dev
, int crtc
)
334 unsigned long irqflags
;
337 DRM_SPINLOCK_IRQSAVE(&dev
->vbl_lock
, irqflags
);
338 /* Going from 0->1 means we have to enable interrupts again */
339 atomic_add_acq_int(&dev
->vblank
[crtc
].refcount
, 1);
340 DRM_DEBUG("vblank refcount = %d\n", dev
->vblank
[crtc
].refcount
);
341 if (dev
->vblank
[crtc
].refcount
== 1 &&
342 !dev
->vblank
[crtc
].enabled
) {
343 ret
= dev
->driver
->enable_vblank(dev
, crtc
);
345 atomic_dec(&dev
->vblank
[crtc
].refcount
);
347 dev
->vblank
[crtc
].enabled
= 1;
348 drm_update_vblank_count(dev
, crtc
);
351 DRM_SPINUNLOCK_IRQRESTORE(&dev
->vbl_lock
, irqflags
);
356 void drm_vblank_put(struct drm_device
*dev
, int crtc
)
358 unsigned long irqflags
;
360 DRM_SPINLOCK_IRQSAVE(&dev
->vbl_lock
, irqflags
);
361 /* Last user schedules interrupt disable */
362 atomic_subtract_acq_int(&dev
->vblank
[crtc
].refcount
, 1);
363 DRM_DEBUG("vblank refcount = %d\n", dev
->vblank
[crtc
].refcount
);
364 if (dev
->vblank
[crtc
].refcount
== 0)
365 callout_reset(&dev
->vblank_disable_timer
, 5 * DRM_HZ
,
366 #if defined(__FreeBSD__)
369 vblank_disable_fn
, (void *)dev
);
370 DRM_SPINUNLOCK_IRQRESTORE(&dev
->vbl_lock
, irqflags
);
373 int drm_modeset_ctl(struct drm_device
*dev
, void *data
,
374 struct drm_file
*file_priv
)
376 struct drm_modeset_ctl
*modeset
= data
;
377 unsigned long irqflags
;
380 DRM_DEBUG("num_crtcs=%d\n", dev
->num_crtcs
);
381 /* If drm_vblank_init() hasn't been called yet, just no-op */
385 crtc
= modeset
->crtc
;
386 DRM_DEBUG("crtc=%d\n", crtc
);
387 if (crtc
>= dev
->num_crtcs
) {
393 * To avoid all the problems that might happen if interrupts
394 * were enabled/disabled around or between these calls, we just
395 * have the kernel take a reference on the CRTC (just once though
396 * to avoid corrupting the count if multiple, mismatch calls occur),
397 * so that interrupts remain enabled in the interim.
399 switch (modeset
->cmd
) {
400 case _DRM_PRE_MODESET
:
401 DRM_DEBUG("pre-modeset\n");
402 if (!dev
->vblank
[crtc
].inmodeset
) {
403 dev
->vblank
[crtc
].inmodeset
= 1;
404 drm_vblank_get(dev
, crtc
);
407 case _DRM_POST_MODESET
:
408 DRM_DEBUG("post-modeset\n");
409 if (dev
->vblank
[crtc
].inmodeset
) {
410 DRM_SPINLOCK_IRQSAVE(&dev
->vbl_lock
, irqflags
);
411 dev
->vblank_disable_allowed
= 1;
412 dev
->vblank
[crtc
].inmodeset
= 0;
413 DRM_SPINUNLOCK_IRQRESTORE(&dev
->vbl_lock
, irqflags
);
414 drm_vblank_put(dev
, crtc
);
426 int drm_wait_vblank(struct drm_device
*dev
, void *data
, struct drm_file
*file_priv
)
428 union drm_wait_vblank
*vblwait
= data
;
429 unsigned int flags
, seq
, crtc
;
432 if (!dev
->irq_enabled
)
435 if (vblwait
->request
.type
&
436 ~(_DRM_VBLANK_TYPES_MASK
| _DRM_VBLANK_FLAGS_MASK
)) {
437 DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
438 vblwait
->request
.type
,
439 (_DRM_VBLANK_TYPES_MASK
| _DRM_VBLANK_FLAGS_MASK
));
443 flags
= vblwait
->request
.type
& _DRM_VBLANK_FLAGS_MASK
;
444 crtc
= flags
& _DRM_VBLANK_SECONDARY
? 1 : 0;
446 if (crtc
>= dev
->num_crtcs
)
449 ret
= drm_vblank_get(dev
, crtc
);
451 DRM_ERROR("failed to acquire vblank counter, %d\n", ret
);
454 seq
= drm_vblank_count(dev
, crtc
);
456 switch (vblwait
->request
.type
& _DRM_VBLANK_TYPES_MASK
) {
457 case _DRM_VBLANK_RELATIVE
:
458 vblwait
->request
.sequence
+= seq
;
459 vblwait
->request
.type
&= ~_DRM_VBLANK_RELATIVE
;
460 case _DRM_VBLANK_ABSOLUTE
:
467 if ((flags
& _DRM_VBLANK_NEXTONMISS
) &&
468 (seq
- vblwait
->request
.sequence
) <= (1<<23)) {
469 vblwait
->request
.sequence
= seq
+ 1;
472 if (flags
& _DRM_VBLANK_SIGNAL
) {
474 drm_vbl_sig_t
*vbl_sig
= malloc(sizeof(drm_vbl_sig_t
),
475 DRM_MEM_DRIVER
, M_NOWAIT
| M_ZERO
);
479 vbl_sig
->sequence
= vblwait
->request
.sequence
;
480 vbl_sig
->signo
= vblwait
->request
.signal
;
481 vbl_sig
->pid
= DRM_CURRENTPID
;
483 vblwait
->reply
.sequence
= atomic_read(&dev
->vbl_received
);
485 DRM_SPINLOCK(&dev
->vbl_lock
);
486 TAILQ_INSERT_HEAD(&dev
->vbl_sig_list
, vbl_sig
, link
);
487 DRM_SPINUNLOCK(&dev
->vbl_lock
);
492 DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
493 vblwait
->request
.sequence
, crtc
);
494 for ( ret
= 0 ; !ret
&& !((drm_vblank_count(dev
, crtc
) -
495 vblwait
->request
.sequence
) <= (1 << 23)) ; ) {
496 DRM_SPINLOCK(&dev
->irq_lock
);
497 if (!((drm_vblank_count(dev
, crtc
) -
498 vblwait
->request
.sequence
) <= (1 << 23)))
499 #if defined(__FreeBSD__)
500 ret
= mtx_sleep(&dev
->vblank
[crtc
].queue
,
501 &dev
->irq_lock
, PCATCH
, "vblwtq",
503 #elif defined(__NetBSD__)
504 ret
= mtsleep(&dev
->vblank
[crtc
].queue
,
505 PCATCH
, "vblwtq", 3 * DRM_HZ
, &dev
->irq_lock
);
507 DRM_SPINUNLOCK(&dev
->irq_lock
);
510 DRM_DEBUG("return = %d\n", ret
);
515 vblwait
->reply
.tval_sec
= now
.tv_sec
;
516 vblwait
->reply
.tval_usec
= now
.tv_usec
;
517 vblwait
->reply
.sequence
= drm_vblank_count(dev
, crtc
);
518 DRM_DEBUG("returning %d to client\n",
519 vblwait
->reply
.sequence
);
521 DRM_DEBUG("vblank wait interrupted by signal\n");
526 drm_vblank_put(dev
, crtc
);
530 void drm_vbl_send_signals(struct drm_device
*dev
, int crtc
)
535 void drm_vbl_send_signals(struct drm_device
*dev
, int crtc
)
537 drm_vbl_sig_t
*vbl_sig
;
538 unsigned int vbl_seq
= atomic_read( &dev
->vbl_received
);
541 vbl_sig
= TAILQ_FIRST(&dev
->vbl_sig_list
);
542 while (vbl_sig
!= NULL
) {
543 drm_vbl_sig_t
*next
= TAILQ_NEXT(vbl_sig
, link
);
545 if ((vbl_seq
- vbl_sig
->sequence
) <= (1 << 23)) {
546 p
= pfind(vbl_sig
->pid
);
548 psignal(p
, vbl_sig
->signo
);
550 TAILQ_REMOVE(&dev
->vbl_sig_list
, vbl_sig
, link
);
551 DRM_FREE(vbl_sig
,sizeof(*vbl_sig
));
558 void drm_handle_vblank(struct drm_device
*dev
, int crtc
)
560 atomic_inc(&dev
->vblank
[crtc
].count
);
561 DRM_WAKEUP(&dev
->vblank
[crtc
].queue
);
562 drm_vbl_send_signals(dev
, crtc
);