2 * drm_irq.c -- IRQ IOCTL and function support
3 * Created: Fri Oct 18 2003 by anholt@FreeBSD.org
6 * Copyright 2003 Eric Anholt
7 * Copyright (c) 2009, Intel Corporation.
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
17 * The above copyright notice and this permission notice (including the next
18 * paragraph) shall be included in all copies or substantial portions of the
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * ERIC ANHOLT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 * Eric Anholt <anholt@FreeBSD.org>
34 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
35 * Use is subject to license terms.
44 drm_irq_by_busid(DRM_IOCTL_ARGS
)
49 DRM_COPYFROM_WITH_RETURN(&irq
, (void *)data
, sizeof (irq
));
51 if ((irq
.busnum
>> 8) != dev
->pci_domain
||
52 (irq
.busnum
& 0xff) != dev
->pci_bus
||
53 irq
.devnum
!= dev
->pci_slot
||
54 irq
.funcnum
!= dev
->pci_func
)
59 DRM_DEBUG("%d:%d:%d => IRQ %d\n",
60 irq
.busnum
, irq
.devnum
, irq
.funcnum
, irq
.irq
);
62 DRM_COPYTO_WITH_RETURN((void *)data
, &irq
, sizeof (irq
));
69 drm_irq_handler_wrap(DRM_IRQ_ARGS
)
71 drm_device_t
*dev
= (void *)arg
;
74 mutex_enter(&dev
->irq_lock
);
75 ret
= dev
->driver
->irq_handler(arg
);
76 mutex_exit(&dev
->irq_lock
);
81 static void vblank_disable_fn(void *arg
)
83 struct drm_device
*dev
= (struct drm_device
*)arg
;
86 if (!dev
->vblank_disable_allowed
)
89 for (i
= 0; i
< dev
->num_crtcs
; i
++) {
90 if (atomic_read(&dev
->vblank_refcount
[i
]) == 0 &&
91 atomic_read(&dev
->vblank_enabled
[i
]) == 1) {
93 dev
->driver
->get_vblank_counter(dev
, i
);
94 dev
->driver
->disable_vblank(dev
, i
);
95 atomic_set(&dev
->vblank_enabled
[i
], 0);
96 DRM_DEBUG("disable vblank");
102 drm_vblank_cleanup(struct drm_device
*dev
)
105 /* Bail if the driver didn't call drm_vblank_init() */
106 if (dev
->num_crtcs
== 0)
109 vblank_disable_fn((void *)dev
);
111 drm_free(dev
->vbl_queues
, sizeof (wait_queue_head_t
) * dev
->num_crtcs
,
113 drm_free(dev
->vbl_sigs
, sizeof (struct drm_vbl_sig
) * dev
->num_crtcs
,
115 drm_free(dev
->_vblank_count
, sizeof (atomic_t
) *
116 dev
->num_crtcs
, DRM_MEM_DRIVER
);
117 drm_free(dev
->vblank_refcount
, sizeof (atomic_t
) *
118 dev
->num_crtcs
, DRM_MEM_DRIVER
);
119 drm_free(dev
->vblank_enabled
, sizeof (int) *
120 dev
->num_crtcs
, DRM_MEM_DRIVER
);
121 drm_free(dev
->last_vblank
, sizeof (u32
) * dev
->num_crtcs
,
123 drm_free(dev
->vblank_inmodeset
, sizeof (*dev
->vblank_inmodeset
) *
124 dev
->num_crtcs
, DRM_MEM_DRIVER
);
129 drm_vblank_init(struct drm_device
*dev
, int num_crtcs
)
133 atomic_set(&dev
->vbl_signal_pending
, 0);
134 dev
->num_crtcs
= num_crtcs
;
137 dev
->vbl_queues
= drm_alloc(sizeof (wait_queue_head_t
) * num_crtcs
,
139 if (!dev
->vbl_queues
)
142 dev
->vbl_sigs
= drm_alloc(sizeof (struct drm_vbl_sig
) * num_crtcs
,
147 dev
->_vblank_count
= drm_alloc(sizeof (atomic_t
) * num_crtcs
,
149 if (!dev
->_vblank_count
)
152 dev
->vblank_refcount
= drm_alloc(sizeof (atomic_t
) * num_crtcs
,
154 if (!dev
->vblank_refcount
)
157 dev
->vblank_enabled
= drm_alloc(num_crtcs
* sizeof (int),
159 if (!dev
->vblank_enabled
)
162 dev
->last_vblank
= drm_alloc(num_crtcs
* sizeof (u32
), DRM_MEM_DRIVER
);
163 if (!dev
->last_vblank
)
166 dev
->vblank_inmodeset
= drm_alloc(num_crtcs
* sizeof (int),
168 if (!dev
->vblank_inmodeset
)
171 /* Zero per-crtc vblank stuff */
172 for (i
= 0; i
< num_crtcs
; i
++) {
173 DRM_INIT_WAITQUEUE(&dev
->vbl_queues
[i
], DRM_INTR_PRI(dev
));
174 TAILQ_INIT(&dev
->vbl_sigs
[i
]);
175 atomic_set(&dev
->_vblank_count
[i
], 0);
176 atomic_set(&dev
->vblank_refcount
[i
], 0);
179 dev
->vblank_disable_allowed
= 1;
183 DRM_ERROR("drm_vblank_init: alloc error");
184 drm_vblank_cleanup(dev
);
190 drm_install_irq_handle(drm_device_t
*dev
)
192 dev_info_t
*dip
= dev
->dip
;
195 DRM_ERROR("drm_install_irq_handle: cannot get vgatext's dip");
196 return (DDI_FAILURE
);
199 if (ddi_intr_hilevel(dip
, 0) != 0) {
200 DRM_ERROR("drm_install_irq_handle: "
201 "high-level interrupts are not supported");
202 return (DDI_FAILURE
);
205 if (ddi_get_iblock_cookie(dip
, 0,
206 &dev
->intr_block
) != DDI_SUCCESS
) {
207 DRM_ERROR("drm_install_irq_handle: cannot get iblock cookie");
208 return (DDI_FAILURE
);
211 /* setup the interrupt handler */
212 if (ddi_add_intr(dip
, 0, &dev
->intr_block
,
213 (ddi_idevice_cookie_t
*)NULL
, drm_irq_handler_wrap
,
214 (caddr_t
)dev
) != DDI_SUCCESS
) {
215 DRM_ERROR("drm_install_irq_handle: ddi_add_intr failed");
216 return (DDI_FAILURE
);
219 return (DDI_SUCCESS
);
224 drm_irq_install(drm_device_t
*dev
)
228 if (dev
->dev_private
== NULL
) {
229 DRM_ERROR("drm_irq_install: dev_private is NULL");
233 if (dev
->irq_enabled
) {
234 DRM_ERROR("drm_irq_install: irq already enabled");
238 DRM_DEBUG("drm_irq_install irq=%d\n", dev
->irq
);
240 /* before installing handler */
241 ret
= dev
->driver
->irq_preinstall(dev
);
245 /* install handler */
246 ret
= drm_install_irq_handle(dev
);
247 if (ret
!= DDI_SUCCESS
) {
248 DRM_ERROR("drm_irq_install: drm_install_irq_handle failed");
252 /* after installing handler */
253 dev
->driver
->irq_postinstall(dev
);
255 dev
->irq_enabled
= 1;
256 dev
->context_flag
= 0;
262 drm_uninstall_irq_handle(drm_device_t
*dev
)
265 ddi_remove_intr(dev
->dip
, 0, dev
->intr_block
);
271 drm_irq_uninstall(drm_device_t
*dev
)
274 if (!dev
->irq_enabled
) {
277 dev
->irq_enabled
= 0;
280 * Wake up any waiters so they don't hang.
282 DRM_SPINLOCK(&dev
->vbl_lock
);
283 for (i
= 0; i
< dev
->num_crtcs
; i
++) {
284 DRM_WAKEUP(&dev
->vbl_queues
[i
]);
285 dev
->vblank_enabled
[i
] = 0;
287 DRM_SPINUNLOCK(&dev
->vbl_lock
);
289 dev
->driver
->irq_uninstall(dev
);
290 drm_uninstall_irq_handle(dev
);
291 dev
->locked_tasklet_func
= NULL
;
293 return (DDI_SUCCESS
);
298 drm_control(DRM_IOCTL_ARGS
)
304 DRM_COPYFROM_WITH_RETURN(&ctl
, (void *)data
, sizeof (ctl
));
307 case DRM_INST_HANDLER
:
309 * Handle drivers whose DRM used to require IRQ setup but the
312 return (drm_irq_install(dev
));
313 case DRM_UNINST_HANDLER
:
314 err
= drm_irq_uninstall(dev
);
322 drm_vblank_count(struct drm_device
*dev
, int crtc
)
324 return (atomic_read(&dev
->_vblank_count
[crtc
]));
327 static void drm_update_vblank_count(struct drm_device
*dev
, int crtc
)
329 u32 cur_vblank
, diff
;
331 * Interrupts were disabled prior to this call, so deal with counter
333 * NOTE! It's possible we lost a full dev->max_vblank_count events
334 * here if the register is small or we had vblank interrupts off for
337 cur_vblank
= dev
->driver
->get_vblank_counter(dev
, crtc
);
338 diff
= cur_vblank
- dev
->last_vblank
[crtc
];
339 if (cur_vblank
< dev
->last_vblank
[crtc
]) {
340 diff
+= dev
->max_vblank_count
;
341 DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n",
342 crtc
, dev
->last_vblank
[crtc
], cur_vblank
, diff
);
345 atomic_add(diff
, &dev
->_vblank_count
[crtc
]);
348 static timeout_id_t timer_id
= NULL
;
351 drm_vblank_get(struct drm_device
*dev
, int crtc
)
355 DRM_SPINLOCK(&dev
->vbl_lock
);
357 if (timer_id
!= NULL
) {
358 (void) untimeout(timer_id
);
362 /* Going from 0->1 means we have to enable interrupts again */
363 atomic_add(1, &dev
->vblank_refcount
[crtc
]);
364 if (dev
->vblank_refcount
[crtc
] == 1 &&
365 atomic_read(&dev
->vblank_enabled
[crtc
]) == 0) {
366 ret
= dev
->driver
->enable_vblank(dev
, crtc
);
368 atomic_dec(&dev
->vblank_refcount
[crtc
]);
370 atomic_set(&dev
->vblank_enabled
[crtc
], 1);
371 drm_update_vblank_count(dev
, crtc
);
374 DRM_SPINUNLOCK(&dev
->vbl_lock
);
380 drm_vblank_put(struct drm_device
*dev
, int crtc
)
382 DRM_SPINLOCK(&dev
->vbl_lock
);
383 /* Last user schedules interrupt disable */
384 atomic_dec(&dev
->vblank_refcount
[crtc
]);
386 if (dev
->vblank_refcount
[crtc
] == 0)
387 timer_id
= timeout(vblank_disable_fn
, (void *) dev
, 5*DRM_HZ
);
389 DRM_SPINUNLOCK(&dev
->vbl_lock
);
393 * drm_modeset_ctl - handle vblank event counter changes across mode switch
394 * @DRM_IOCTL_ARGS: standard ioctl arguments
396 * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET
397 * ioctls around modesetting so that any lost vblank events are accounted for.
399 * Generally the counter will reset across mode sets. If interrupts are
400 * enabled around this call, we don't have to do anything since the counter
401 * will have already been incremented.
405 drm_modeset_ctl(DRM_IOCTL_ARGS
)
408 struct drm_modeset_ctl modeset
;
411 /* If drm_vblank_init() hasn't been called yet, just no-op */
415 DRM_COPYFROM_WITH_RETURN(&modeset
, (void *)data
,
419 if (crtc
>= dev
->num_crtcs
) {
425 * To avoid all the problems that might happen if interrupts
426 * were enabled/disabled around or between these calls, we just
427 * have the kernel take a reference on the CRTC (just once though
428 * to avoid corrupting the count if multiple, mismatch calls occur),
429 * so that interrupts remain enabled in the interim.
431 switch (modeset
.cmd
) {
432 case _DRM_PRE_MODESET
:
433 if (!dev
->vblank_inmodeset
[crtc
]) {
434 dev
->vblank_inmodeset
[crtc
] = 1;
435 ret
= drm_vblank_get(dev
, crtc
);
438 case _DRM_POST_MODESET
:
439 if (dev
->vblank_inmodeset
[crtc
]) {
440 DRM_SPINLOCK(&dev
->vbl_lock
);
441 dev
->vblank_disable_allowed
= 1;
442 dev
->vblank_inmodeset
[crtc
] = 0;
443 DRM_SPINUNLOCK(&dev
->vbl_lock
);
444 drm_vblank_put(dev
, crtc
);
458 drm_wait_vblank(DRM_IOCTL_ARGS
)
461 drm_wait_vblank_t vblwait
;
462 int ret
, flags
, crtc
;
463 unsigned int sequence
;
465 if (!dev
->irq_enabled
) {
466 DRM_ERROR("wait vblank, EINVAL");
469 #ifdef _MULTI_DATAMODEL
470 if (ddi_model_convert_from(mode
& FMODELS
) == DDI_MODEL_ILP32
) {
471 drm_wait_vblank_32_t vblwait32
;
472 DRM_COPYFROM_WITH_RETURN(&vblwait32
, (void *)data
,
474 vblwait
.request
.type
= vblwait32
.request
.type
;
475 vblwait
.request
.sequence
= vblwait32
.request
.sequence
;
476 vblwait
.request
.signal
= vblwait32
.request
.signal
;
479 DRM_COPYFROM_WITH_RETURN(&vblwait
, (void *)data
,
481 #ifdef _MULTI_DATAMODEL
485 if (vblwait
.request
.type
&
486 ~(_DRM_VBLANK_TYPES_MASK
| _DRM_VBLANK_FLAGS_MASK
)) {
487 DRM_ERROR("drm_wait_vblank: wrong request type 0x%x",
488 vblwait
.request
.type
);
492 flags
= vblwait
.request
.type
& _DRM_VBLANK_FLAGS_MASK
;
493 crtc
= flags
& _DRM_VBLANK_SECONDARY
? 1 : 0;
494 if (crtc
>= dev
->num_crtcs
) {
495 DRM_ERROR("wait vblank operation not support");
498 ret
= drm_vblank_get(dev
, crtc
);
500 DRM_ERROR("can't get drm vblank %d", ret
);
503 sequence
= drm_vblank_count(dev
, crtc
);
505 switch (vblwait
.request
.type
& _DRM_VBLANK_TYPES_MASK
) {
506 case _DRM_VBLANK_RELATIVE
:
507 vblwait
.request
.sequence
+= sequence
;
508 vblwait
.request
.type
&= ~_DRM_VBLANK_RELATIVE
;
510 case _DRM_VBLANK_ABSOLUTE
:
513 DRM_DEBUG("wait vblank return EINVAL");
517 if ((flags
& _DRM_VBLANK_NEXTONMISS
) &&
518 (sequence
- vblwait
.request
.sequence
) <= (1<<23)) {
519 vblwait
.request
.sequence
= sequence
+ 1;
522 if (flags
& _DRM_VBLANK_SIGNAL
) {
524 * Don't block process, send signal when vblank interrupt
526 DRM_ERROR("NOT SUPPORT YET, SHOULD BE ADDED");
527 cmn_err(CE_WARN
, "NOT SUPPORT YET, SHOULD BE ADDED");
531 /* block until vblank interupt */
532 /* shared code returns -errno */
533 DRM_WAIT_ON(ret
, &dev
->vbl_queues
[crtc
], 3 * DRM_HZ
,
534 (((drm_vblank_count(dev
, crtc
)
535 - vblwait
.request
.sequence
) <= (1 << 23)) ||
539 (void) uniqtime(&now
);
540 vblwait
.reply
.tval_sec
= now
.tv_sec
;
541 vblwait
.reply
.tval_usec
= now
.tv_usec
;
542 vblwait
.reply
.sequence
= drm_vblank_count(dev
, crtc
);
547 #ifdef _MULTI_DATAMODEL
548 if (ddi_model_convert_from(mode
& FMODELS
) == DDI_MODEL_ILP32
) {
549 drm_wait_vblank_32_t vblwait32
;
550 vblwait32
.reply
.type
= vblwait
.reply
.type
;
551 vblwait32
.reply
.sequence
= vblwait
.reply
.sequence
;
552 vblwait32
.reply
.tval_sec
= (int32_t)vblwait
.reply
.tval_sec
;
553 vblwait32
.reply
.tval_usec
= (int32_t)vblwait
.reply
.tval_usec
;
554 DRM_COPYTO_WITH_RETURN((void *)data
, &vblwait32
,
558 DRM_COPYTO_WITH_RETURN((void *)data
, &vblwait
,
560 #ifdef _MULTI_DATAMODEL
564 drm_vblank_put(dev
, crtc
);
571 drm_vbl_send_signals(drm_device_t
*dev
)
573 DRM_DEBUG("drm_vbl_send_signals");
577 drm_handle_vblank(struct drm_device
*dev
, int crtc
)
579 atomic_inc(&dev
->_vblank_count
[crtc
]);
580 DRM_WAKEUP(&dev
->vbl_queues
[crtc
]);