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 * $DragonFly: src/sys/dev/drm/drm_irq.c,v 1.1 2008/04/05 18:12:29 hasso Exp $ 27 */ 28 29 /** @file drm_irq.c 30 * Support code for handling setup/teardown of interrupt handlers and 31 * handing interrupt handlers off to the drivers. 32 */ 33 34 #include "drmP.h" 35 #include "drm.h" 36 37 static void drm_locked_task(void *context, int pending __unused); 38 39 int drm_irq_by_busid(drm_device_t *dev, void *data, struct drm_file *file_priv) 40 { 41 drm_irq_busid_t *irq = data; 42 43 if ((irq->busnum >> 8) != dev->pci_domain || 44 (irq->busnum & 0xff) != dev->pci_bus || 45 irq->devnum != dev->pci_slot || 46 irq->funcnum != dev->pci_func) 47 return EINVAL; 48 49 irq->irq = dev->irq; 50 51 DRM_DEBUG("%d:%d:%d => IRQ %d\n", 52 irq->busnum, irq->devnum, irq->funcnum, irq->irq); 53 54 return 0; 55 } 56 57 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 58 static irqreturn_t 59 drm_irq_handler_wrap(DRM_IRQ_ARGS) 60 { 61 drm_device_t *dev = (drm_device_t *)arg; 62 63 DRM_SPINLOCK(&dev->irq_lock); 64 dev->driver.irq_handler(arg); 65 DRM_SPINUNLOCK(&dev->irq_lock); 66 } 67 #endif 68 69 int drm_irq_install(drm_device_t *dev) 70 { 71 int retcode; 72 #ifdef __NetBSD__ 73 pci_intr_handle_t ih; 74 #endif 75 76 if (dev->irq == 0 || dev->dev_private == NULL) 77 return EINVAL; 78 79 DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); 80 81 DRM_LOCK(); 82 if (dev->irq_enabled) { 83 DRM_UNLOCK(); 84 return EBUSY; 85 } 86 dev->irq_enabled = 1; 87 88 dev->context_flag = 0; 89 90 #ifndef __DragonFly__ 91 DRM_SPININIT(&dev->irq_lock, "DRM IRQ lock"); 92 #else 93 lwkt_serialize_init(&dev->irq_lock); 94 #endif 95 96 /* Before installing handler */ 97 dev->driver.irq_preinstall(dev); 98 DRM_UNLOCK(); 99 100 /* Install handler */ 101 #if defined(__FreeBSD__) || defined(__DragonFly__) 102 dev->irqrid = 0; 103 dev->irqr = bus_alloc_resource_any(dev->device, SYS_RES_IRQ, 104 &dev->irqrid, RF_SHAREABLE); 105 if (!dev->irqr) { 106 retcode = ENOENT; 107 goto err; 108 } 109 #if __FreeBSD_version >= 700031 110 retcode = bus_setup_intr(dev->device, dev->irqr, 111 INTR_TYPE_TTY | INTR_MPSAFE, 112 NULL, drm_irq_handler_wrap, dev, &dev->irqh); 113 #elif defined(__DragonFly__) 114 retcode = bus_setup_intr(dev->device, dev->irqr, 115 INTR_MPSAFE, 116 dev->driver.irq_handler, dev, &dev->irqh, 117 &dev->irq_lock); 118 #else 119 retcode = bus_setup_intr(dev->device, dev->irqr, 120 INTR_TYPE_TTY | INTR_MPSAFE, 121 drm_irq_handler_wrap, dev, &dev->irqh); 122 #endif 123 if (retcode != 0) 124 goto err; 125 #elif defined(__NetBSD__) || defined(__OpenBSD__) 126 if (pci_intr_map(&dev->pa, &ih) != 0) { 127 retcode = ENOENT; 128 goto err; 129 } 130 dev->irqh = pci_intr_establish(&dev->pa.pa_pc, ih, IPL_TTY, 131 (irqreturn_t (*)(void *))dev->irq_handler, dev); 132 if (!dev->irqh) { 133 retcode = ENOENT; 134 goto err; 135 } 136 #endif 137 138 /* After installing handler */ 139 DRM_LOCK(); 140 dev->driver.irq_postinstall(dev); 141 DRM_UNLOCK(); 142 143 TASK_INIT(&dev->locked_task, 0, drm_locked_task, dev); 144 return 0; 145 err: 146 DRM_LOCK(); 147 dev->irq_enabled = 0; 148 #if defined(___FreeBSD__) || defined(__DragonFly__) 149 if (dev->irqrid != 0) { 150 bus_release_resource(dev->device, SYS_RES_IRQ, dev->irqrid, 151 dev->irqr); 152 dev->irqrid = 0; 153 } 154 #endif 155 #ifndef __DragonFly__ 156 DRM_SPINUNINIT(&dev->irq_lock); 157 #endif 158 DRM_UNLOCK(); 159 return retcode; 160 } 161 162 int drm_irq_uninstall(drm_device_t *dev) 163 { 164 #if defined(__FreeBSD__) || defined(__DragonFly__) 165 int irqrid; 166 #endif 167 168 if (!dev->irq_enabled) 169 return EINVAL; 170 171 dev->irq_enabled = 0; 172 #if defined(__FreeBSD__) || defined(__DragonFly__) 173 irqrid = dev->irqrid; 174 dev->irqrid = 0; 175 #endif 176 177 DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); 178 179 dev->driver.irq_uninstall(dev); 180 181 #if defined(__FreeBSD__) || defined(__DragonFly__) 182 DRM_UNLOCK(); 183 bus_teardown_intr(dev->device, dev->irqr, dev->irqh); 184 bus_release_resource(dev->device, SYS_RES_IRQ, irqrid, dev->irqr); 185 DRM_LOCK(); 186 #elif defined(__NetBSD__) || defined(__OpenBSD__) 187 pci_intr_disestablish(&dev->pa.pa_pc, dev->irqh); 188 #endif 189 #ifndef __DragonFly__ 190 DRM_SPINUNINIT(&dev->irq_lock); 191 #endif 192 193 return 0; 194 } 195 196 int drm_control(drm_device_t *dev, void *data, struct drm_file *file_priv) 197 { 198 drm_control_t *ctl = data; 199 int err; 200 201 switch ( ctl->func ) { 202 case DRM_INST_HANDLER: 203 /* Handle drivers whose DRM used to require IRQ setup but the 204 * no longer does. 205 */ 206 if (!dev->driver.use_irq) 207 return 0; 208 if (dev->if_version < DRM_IF_VERSION(1, 2) && 209 ctl->irq != dev->irq) 210 return EINVAL; 211 return drm_irq_install(dev); 212 case DRM_UNINST_HANDLER: 213 if (!dev->driver.use_irq) 214 return 0; 215 DRM_LOCK(); 216 err = drm_irq_uninstall(dev); 217 DRM_UNLOCK(); 218 return err; 219 default: 220 return EINVAL; 221 } 222 } 223 224 int drm_wait_vblank(drm_device_t *dev, void *data, struct drm_file *file_priv) 225 { 226 drm_wait_vblank_t *vblwait = data; 227 struct timeval now; 228 int ret = 0; 229 int flags, seq; 230 231 if (!dev->irq_enabled) 232 return EINVAL; 233 234 if (vblwait->request.type & 235 ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { 236 DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", 237 vblwait->request.type, 238 (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)); 239 return EINVAL; 240 } 241 242 flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; 243 244 if ((flags & _DRM_VBLANK_SECONDARY) && !dev->driver.use_vbl_irq2) 245 return EINVAL; 246 247 seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? 248 &dev->vbl_received2 : &dev->vbl_received); 249 250 switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { 251 case _DRM_VBLANK_RELATIVE: 252 vblwait->request.sequence += seq; 253 vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; 254 case _DRM_VBLANK_ABSOLUTE: 255 break; 256 default: 257 return EINVAL; 258 } 259 260 if ((flags & _DRM_VBLANK_NEXTONMISS) && 261 (seq - vblwait->request.sequence) <= (1<<23)) { 262 vblwait->request.sequence = seq + 1; 263 } 264 265 if (flags & _DRM_VBLANK_SIGNAL) { 266 #if 0 /* disabled */ 267 drm_vbl_sig_t *vbl_sig = malloc(sizeof(drm_vbl_sig_t), M_DRM, 268 M_NOWAIT | M_ZERO); 269 if (vbl_sig == NULL) 270 return ENOMEM; 271 272 vbl_sig->sequence = vblwait->request.sequence; 273 vbl_sig->signo = vblwait->request.signal; 274 vbl_sig->pid = DRM_CURRENTPID; 275 276 vblwait->reply.sequence = atomic_read(&dev->vbl_received); 277 278 DRM_SPINLOCK(&dev->irq_lock); 279 TAILQ_INSERT_HEAD(&dev->vbl_sig_list, vbl_sig, link); 280 DRM_SPINUNLOCK(&dev->irq_lock); 281 ret = 0; 282 #endif 283 ret = EINVAL; 284 } else { 285 DRM_LOCK(); 286 /* shared code returns -errno */ 287 if (flags & _DRM_VBLANK_SECONDARY) { 288 if (dev->driver.vblank_wait2) 289 ret = -dev->driver.vblank_wait2(dev, 290 &vblwait->request.sequence); 291 } else if (dev->driver.vblank_wait) 292 ret = -dev->driver.vblank_wait(dev, 293 &vblwait->request.sequence); 294 295 DRM_UNLOCK(); 296 297 microtime(&now); 298 vblwait->reply.tval_sec = now.tv_sec; 299 vblwait->reply.tval_usec = now.tv_usec; 300 } 301 302 return ret; 303 } 304 305 void drm_vbl_send_signals(drm_device_t *dev) 306 { 307 } 308 309 #if 0 /* disabled */ 310 void drm_vbl_send_signals( drm_device_t *dev ) 311 { 312 drm_vbl_sig_t *vbl_sig; 313 unsigned int vbl_seq = atomic_read( &dev->vbl_received ); 314 struct proc *p; 315 316 vbl_sig = TAILQ_FIRST(&dev->vbl_sig_list); 317 while (vbl_sig != NULL) { 318 drm_vbl_sig_t *next = TAILQ_NEXT(vbl_sig, link); 319 320 if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) { 321 p = pfind(vbl_sig->pid); 322 if (p != NULL) 323 psignal(p, vbl_sig->signo); 324 325 TAILQ_REMOVE(&dev->vbl_sig_list, vbl_sig, link); 326 DRM_FREE(vbl_sig,sizeof(*vbl_sig)); 327 } 328 vbl_sig = next; 329 } 330 } 331 #endif 332 333 static void drm_locked_task(void *context, int pending __unused) 334 { 335 drm_device_t *dev = context; 336 337 DRM_LOCK(); 338 for (;;) { 339 int ret; 340 341 if (drm_lock_take(&dev->lock.hw_lock->lock, 342 DRM_KERNEL_CONTEXT)) 343 { 344 dev->lock.file_priv = NULL; /* kernel owned */ 345 dev->lock.lock_time = jiffies; 346 atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); 347 break; /* Got lock */ 348 } 349 350 /* Contention */ 351 #if defined(__FreeBSD__) && __FreeBSD_version > 500000 352 ret = mtx_sleep((void *)&dev->lock.lock_queue, &dev->dev_lock, 353 PZERO | PCATCH, "drmlk2", 0); 354 #elif defined(__DragonFly__) 355 crit_enter(); 356 tsleep_interlock((void *)&dev->lock.lock_queue); 357 DRM_UNLOCK(); 358 ret = tsleep((void *)&dev->lock.lock_queue, PCATCH, 359 "drmlk2", 0); 360 crit_exit(); 361 DRM_LOCK(); 362 #else 363 ret = tsleep((void *)&dev->lock.lock_queue, PZERO | PCATCH, 364 "drmlk2", 0); 365 #endif 366 if (ret != 0) 367 return; 368 } 369 DRM_UNLOCK(); 370 371 dev->locked_task_call(dev); 372 373 drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); 374 } 375 376 void 377 drm_locked_tasklet(drm_device_t *dev, void (*tasklet)(drm_device_t *dev)) 378 { 379 dev->locked_task_call = tasklet; 380 taskqueue_enqueue(taskqueue_swi, &dev->locked_task); 381 } 382