drm_irq.c revision 194746
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 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/dev/drm/drm_irq.c 194746 2009-06-23 17:52:41Z rnoland $"); 30 31/** @file drm_irq.c 32 * Support code for handling setup/teardown of interrupt handlers and 33 * handing interrupt handlers off to the drivers. 34 */ 35 36#include "dev/drm/drmP.h" 37#include "dev/drm/drm.h" 38 39int drm_irq_by_busid(struct drm_device *dev, void *data, 40 struct drm_file *file_priv) 41{ 42 struct drm_irq_busid *irq = data; 43 44 if ((irq->busnum >> 8) != dev->pci_domain || 45 (irq->busnum & 0xff) != dev->pci_bus || 46 irq->devnum != dev->pci_slot || 47 irq->funcnum != dev->pci_func) 48 return EINVAL; 49 50 irq->irq = dev->irq; 51 52 DRM_DEBUG("%d:%d:%d => IRQ %d\n", 53 irq->busnum, irq->devnum, irq->funcnum, irq->irq); 54 55 return 0; 56} 57 58static irqreturn_t 59drm_irq_handler_wrap(DRM_IRQ_ARGS) 60{ 61 struct drm_device *dev = arg; 62 63 DRM_SPINLOCK(&dev->irq_lock); 64 dev->driver->irq_handler(arg); 65 DRM_SPINUNLOCK(&dev->irq_lock); 66} 67 68static void vblank_disable_fn(void *arg) 69{ 70 struct drm_device *dev = (struct drm_device *)arg; 71 int i; 72 73 if (callout_pending(&dev->vblank_disable_timer)) { 74 /* callout was reset */ 75 return; 76 } 77 if (!callout_active(&dev->vblank_disable_timer)) { 78 /* callout was stopped */ 79 return; 80 } 81 callout_deactivate(&dev->vblank_disable_timer); 82 83 DRM_DEBUG("vblank_disable: %s\n", dev->vblank_disable_allowed ? 84 "allowed" : "denied"); 85 if (!dev->vblank_disable_allowed) 86 return; 87 88 for (i = 0; i < dev->num_crtcs; i++) { 89 if (atomic_read(&dev->vblank[i].refcount) == 0 && 90 dev->vblank[i].enabled && !dev->vblank[i].inmodeset) { 91 DRM_DEBUG("disabling vblank on crtc %d\n", i); 92 dev->vblank[i].last = 93 dev->driver->get_vblank_counter(dev, i); 94 dev->driver->disable_vblank(dev, i); 95 dev->vblank[i].enabled = 0; 96 } 97 } 98} 99 100void drm_vblank_cleanup(struct drm_device *dev) 101{ 102 /* Bail if the driver didn't call drm_vblank_init() */ 103 if (dev->num_crtcs == 0) 104 return; 105 106 DRM_SPINLOCK(&dev->vbl_lock); 107 callout_stop(&dev->vblank_disable_timer); 108 DRM_SPINUNLOCK(&dev->vbl_lock); 109 110 callout_drain(&dev->vblank_disable_timer); 111 112 vblank_disable_fn((void *)dev); 113 114 free(dev->vblank, DRM_MEM_DRIVER); 115 116 dev->num_crtcs = 0; 117} 118 119int drm_vblank_init(struct drm_device *dev, int num_crtcs) 120{ 121 int i, ret = ENOMEM; 122 123 callout_init_mtx(&dev->vblank_disable_timer, &dev->vbl_lock, 0); 124 atomic_set(&dev->vbl_signal_pending, 0); 125 dev->num_crtcs = num_crtcs; 126 127 dev->vblank = malloc(sizeof(struct drm_vblank_info) * num_crtcs, 128 DRM_MEM_DRIVER, M_NOWAIT | M_ZERO); 129 if (!dev->vblank) 130 goto err; 131 132 DRM_DEBUG("\n"); 133 134 /* Zero per-crtc vblank stuff */ 135 for (i = 0; i < num_crtcs; i++) { 136 DRM_INIT_WAITQUEUE(&dev->vblank[i].queue); 137 TAILQ_INIT(&dev->vblank[i].sigs); 138 atomic_set(&dev->vblank[i].count, 0); 139 atomic_set(&dev->vblank[i].refcount, 0); 140 } 141 142 dev->vblank_disable_allowed = 0; 143 144 return 0; 145 146err: 147 drm_vblank_cleanup(dev); 148 return ret; 149} 150 151int drm_irq_install(struct drm_device *dev) 152{ 153 int crtc, retcode; 154 155 if (dev->irq == 0 || dev->dev_private == NULL) 156 return EINVAL; 157 158 DRM_DEBUG("irq=%d\n", dev->irq); 159 160 DRM_LOCK(); 161 if (dev->irq_enabled) { 162 DRM_UNLOCK(); 163 return EBUSY; 164 } 165 dev->irq_enabled = 1; 166 167 dev->context_flag = 0; 168 169 /* Before installing handler */ 170 dev->driver->irq_preinstall(dev); 171 DRM_UNLOCK(); 172 173 /* Install handler */ 174#if __FreeBSD_version >= 700031 175 retcode = bus_setup_intr(dev->device, dev->irqr, 176 INTR_TYPE_TTY | INTR_MPSAFE, 177 NULL, drm_irq_handler_wrap, dev, &dev->irqh); 178#else 179 retcode = bus_setup_intr(dev->device, dev->irqr, 180 INTR_TYPE_TTY | INTR_MPSAFE, 181 drm_irq_handler_wrap, dev, &dev->irqh); 182#endif 183 if (retcode != 0) 184 goto err; 185 186 /* After installing handler */ 187 DRM_LOCK(); 188 dev->driver->irq_postinstall(dev); 189 DRM_UNLOCK(); 190 if (dev->driver->enable_vblank) { 191 DRM_SPINLOCK(&dev->vbl_lock); 192 for( crtc = 0 ; crtc < dev->num_crtcs ; crtc++) { 193 if (dev->driver->enable_vblank(dev, crtc) == 0) { 194 dev->vblank[crtc].enabled = 1; 195 } 196 } 197 callout_reset(&dev->vblank_disable_timer, 5 * DRM_HZ, 198 (timeout_t *)vblank_disable_fn, (void *)dev); 199 DRM_SPINUNLOCK(&dev->vbl_lock); 200 } 201 202 return 0; 203err: 204 DRM_LOCK(); 205 dev->irq_enabled = 0; 206 DRM_UNLOCK(); 207 208 return retcode; 209} 210 211int drm_irq_uninstall(struct drm_device *dev) 212{ 213 int crtc; 214 215 if (!dev->irq_enabled) 216 return EINVAL; 217 218 dev->irq_enabled = 0; 219 220 /* 221 * Wake up any waiters so they don't hang. 222 */ 223 DRM_SPINLOCK(&dev->vbl_lock); 224 for (crtc = 0; crtc < dev->num_crtcs; crtc++) { 225 if (dev->vblank[crtc].enabled) { 226 DRM_WAKEUP(&dev->vblank[crtc].queue); 227 dev->vblank[crtc].last = 228 dev->driver->get_vblank_counter(dev, crtc); 229 dev->vblank[crtc].enabled = 0; 230 } 231 } 232 DRM_SPINUNLOCK(&dev->vbl_lock); 233 234 DRM_DEBUG("irq=%d\n", dev->irq); 235 236 dev->driver->irq_uninstall(dev); 237 238 DRM_UNLOCK(); 239 bus_teardown_intr(dev->device, dev->irqr, dev->irqh); 240 DRM_LOCK(); 241 242 return 0; 243} 244 245int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv) 246{ 247 struct drm_control *ctl = data; 248 int err; 249 250 switch (ctl->func) { 251 case DRM_INST_HANDLER: 252 /* Handle drivers whose DRM used to require IRQ setup but the 253 * no longer does. 254 */ 255 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) 256 return 0; 257 if (dev->if_version < DRM_IF_VERSION(1, 2) && 258 ctl->irq != dev->irq) 259 return EINVAL; 260 return drm_irq_install(dev); 261 case DRM_UNINST_HANDLER: 262 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) 263 return 0; 264 DRM_LOCK(); 265 err = drm_irq_uninstall(dev); 266 DRM_UNLOCK(); 267 return err; 268 default: 269 return EINVAL; 270 } 271} 272 273u32 drm_vblank_count(struct drm_device *dev, int crtc) 274{ 275 return atomic_read(&dev->vblank[crtc].count); 276} 277 278static void drm_update_vblank_count(struct drm_device *dev, int crtc) 279{ 280 u32 cur_vblank, diff; 281 282 /* 283 * Interrupts were disabled prior to this call, so deal with counter 284 * wrap if needed. 285 * NOTE! It's possible we lost a full dev->max_vblank_count events 286 * here if the register is small or we had vblank interrupts off for 287 * a long time. 288 */ 289 cur_vblank = dev->driver->get_vblank_counter(dev, crtc); 290 diff = cur_vblank - dev->vblank[crtc].last; 291 if (cur_vblank < dev->vblank[crtc].last) { 292 diff += dev->max_vblank_count; 293 294 DRM_DEBUG("vblank[%d].last=0x%x, cur_vblank=0x%x => diff=0x%x\n", 295 crtc, dev->vblank[crtc].last, cur_vblank, diff); 296 } 297 298 DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n", 299 crtc, diff); 300 301 atomic_add(diff, &dev->vblank[crtc].count); 302} 303 304int drm_vblank_get(struct drm_device *dev, int crtc) 305{ 306 int ret = 0; 307 308 DRM_SPINLOCK(&dev->vbl_lock); 309 /* Going from 0->1 means we have to enable interrupts again */ 310 atomic_add_acq_int(&dev->vblank[crtc].refcount, 1); 311 if (dev->vblank[crtc].refcount == 1 && 312 !dev->vblank[crtc].enabled) { 313 ret = dev->driver->enable_vblank(dev, crtc); 314 DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret); 315 if (ret) 316 atomic_dec(&dev->vblank[crtc].refcount); 317 else { 318 dev->vblank[crtc].enabled = 1; 319 drm_update_vblank_count(dev, crtc); 320 } 321 } 322 DRM_SPINUNLOCK(&dev->vbl_lock); 323 324 return ret; 325} 326 327void drm_vblank_put(struct drm_device *dev, int crtc) 328{ 329 KASSERT(atomic_read(&dev->vblank[crtc].refcount) > 0, 330 ("invalid refcount")); 331 332 /* Last user schedules interrupt disable */ 333 atomic_subtract_acq_int(&dev->vblank[crtc].refcount, 1); 334 335 DRM_SPINLOCK(&dev->vbl_lock); 336 if (dev->vblank[crtc].refcount == 0) 337 callout_reset(&dev->vblank_disable_timer, 5 * DRM_HZ, 338 (timeout_t *)vblank_disable_fn, (void *)dev); 339 DRM_SPINUNLOCK(&dev->vbl_lock); 340} 341 342int drm_modeset_ctl(struct drm_device *dev, void *data, 343 struct drm_file *file_priv) 344{ 345 struct drm_modeset_ctl *modeset = data; 346 int crtc, ret = 0; 347 348 DRM_DEBUG("num_crtcs=%d\n", dev->num_crtcs); 349 /* If drm_vblank_init() hasn't been called yet, just no-op */ 350 if (!dev->num_crtcs) 351 goto out; 352 353 crtc = modeset->crtc; 354 DRM_DEBUG("crtc=%d\n", crtc); 355 if (crtc >= dev->num_crtcs) { 356 ret = EINVAL; 357 goto out; 358 } 359 360 /* 361 * To avoid all the problems that might happen if interrupts 362 * were enabled/disabled around or between these calls, we just 363 * have the kernel take a reference on the CRTC (just once though 364 * to avoid corrupting the count if multiple, mismatch calls occur), 365 * so that interrupts remain enabled in the interim. 366 */ 367 switch (modeset->cmd) { 368 case _DRM_PRE_MODESET: 369 DRM_DEBUG("pre-modeset\n"); 370 if (!dev->vblank[crtc].inmodeset) { 371 dev->vblank[crtc].inmodeset = 0x1; 372 if (drm_vblank_get(dev, crtc) == 0) 373 dev->vblank[crtc].inmodeset |= 0x2; 374 } 375 break; 376 case _DRM_POST_MODESET: 377 DRM_DEBUG("post-modeset\n"); 378 if (dev->vblank[crtc].inmodeset) { 379 DRM_SPINLOCK(&dev->vbl_lock); 380 dev->vblank_disable_allowed = 1; 381 DRM_SPINUNLOCK(&dev->vbl_lock); 382 383 if (dev->vblank[crtc].inmodeset & 0x2) 384 drm_vblank_put(dev, crtc); 385 386 dev->vblank[crtc].inmodeset = 0; 387 } 388 break; 389 default: 390 ret = EINVAL; 391 break; 392 } 393 394out: 395 return ret; 396} 397 398int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) 399{ 400 union drm_wait_vblank *vblwait = data; 401 unsigned int flags, seq, crtc; 402 int ret = 0; 403 404 if (!dev->irq_enabled) 405 return EINVAL; 406 407 if (vblwait->request.type & 408 ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { 409 DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", 410 vblwait->request.type, 411 (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)); 412 return EINVAL; 413 } 414 415 flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; 416 crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; 417 418 if (crtc >= dev->num_crtcs) 419 return EINVAL; 420 421 ret = drm_vblank_get(dev, crtc); 422 if (ret) { 423 DRM_ERROR("failed to acquire vblank counter, %d\n", ret); 424 return ret; 425 } 426 seq = drm_vblank_count(dev, crtc); 427 428 switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { 429 case _DRM_VBLANK_RELATIVE: 430 vblwait->request.sequence += seq; 431 vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; 432 case _DRM_VBLANK_ABSOLUTE: 433 break; 434 default: 435 ret = EINVAL; 436 goto done; 437 } 438 439 if ((flags & _DRM_VBLANK_NEXTONMISS) && 440 (seq - vblwait->request.sequence) <= (1<<23)) { 441 vblwait->request.sequence = seq + 1; 442 } 443 444 if (flags & _DRM_VBLANK_SIGNAL) { 445#if 0 /* disabled */ 446 drm_vbl_sig_t *vbl_sig = malloc(sizeof(drm_vbl_sig_t), 447 DRM_MEM_DRIVER, M_NOWAIT | M_ZERO); 448 if (vbl_sig == NULL) 449 return ENOMEM; 450 451 vbl_sig->sequence = vblwait->request.sequence; 452 vbl_sig->signo = vblwait->request.signal; 453 vbl_sig->pid = DRM_CURRENTPID; 454 455 vblwait->reply.sequence = atomic_read(&dev->vbl_received); 456 457 DRM_SPINLOCK(&dev->vbl_lock); 458 TAILQ_INSERT_HEAD(&dev->vbl_sig_list, vbl_sig, link); 459 DRM_SPINUNLOCK(&dev->vbl_lock); 460 ret = 0; 461#endif 462 ret = EINVAL; 463 } else { 464 DRM_DEBUG("waiting on vblank count %d, crtc %d\n", 465 vblwait->request.sequence, crtc); 466 for ( ret = 0 ; !ret && !(((drm_vblank_count(dev, crtc) - 467 vblwait->request.sequence) <= (1 << 23)) || 468 !dev->irq_enabled) ; ) { 469 mtx_lock(&dev->irq_lock); 470 if (!(((drm_vblank_count(dev, crtc) - 471 vblwait->request.sequence) <= (1 << 23)) || 472 !dev->irq_enabled)) 473 ret = mtx_sleep(&dev->vblank[crtc].queue, 474 &dev->irq_lock, PCATCH, "vblwtq", 475 DRM_HZ); 476 mtx_unlock(&dev->irq_lock); 477 } 478 479 if (ret != EINTR && ret != ERESTART) { 480 struct timeval now; 481 482 microtime(&now); 483 vblwait->reply.tval_sec = now.tv_sec; 484 vblwait->reply.tval_usec = now.tv_usec; 485 vblwait->reply.sequence = drm_vblank_count(dev, crtc); 486 DRM_DEBUG("returning %d to client, irq_enabled %d\n", 487 vblwait->reply.sequence, dev->irq_enabled); 488 } else { 489 DRM_DEBUG("vblank wait interrupted by signal\n"); 490 } 491 } 492 493done: 494 drm_vblank_put(dev, crtc); 495 return ret; 496} 497 498void drm_vbl_send_signals(struct drm_device *dev, int crtc) 499{ 500} 501 502#if 0 /* disabled */ 503void drm_vbl_send_signals(struct drm_device *dev, int crtc ) 504{ 505 drm_vbl_sig_t *vbl_sig; 506 unsigned int vbl_seq = atomic_read( &dev->vbl_received ); 507 struct proc *p; 508 509 vbl_sig = TAILQ_FIRST(&dev->vbl_sig_list); 510 while (vbl_sig != NULL) { 511 drm_vbl_sig_t *next = TAILQ_NEXT(vbl_sig, link); 512 513 if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { 514 p = pfind(vbl_sig->pid); 515 if (p != NULL) 516 psignal(p, vbl_sig->signo); 517 518 TAILQ_REMOVE(&dev->vbl_sig_list, vbl_sig, link); 519 DRM_FREE(vbl_sig,sizeof(*vbl_sig)); 520 } 521 vbl_sig = next; 522 } 523} 524#endif 525 526void drm_handle_vblank(struct drm_device *dev, int crtc) 527{ 528 atomic_inc(&dev->vblank[crtc].count); 529 DRM_WAKEUP(&dev->vblank[crtc].queue); 530 drm_vbl_send_signals(dev, crtc); 531} 532 533