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