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