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