drm_irq.c revision 166901
1/* drm_irq.c -- IRQ IOCTL and function support 2 * Created: Fri Oct 18 2003 by anholt@FreeBSD.org 3 */ 4/*- 5 * Copyright 2003 Eric Anholt 6 * All Rights Reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the "Software"), 10 * to deal in the Software without restriction, including without limitation 11 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 * and/or sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice (including the next 16 * paragraph) shall be included in all copies or substantial portions of the 17 * Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 * ERIC ANHOLT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 23 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 * Authors: 27 * Eric Anholt <anholt@FreeBSD.org> 28 * 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: head/sys/dev/drm/drm_irq.c 166901 2007-02-23 12:19:07Z piso $"); 33 34#include "dev/drm/drmP.h" 35#include "dev/drm/drm.h" 36 37int drm_irq_by_busid(DRM_IOCTL_ARGS) 38{ 39 DRM_DEVICE; 40 drm_irq_busid_t irq; 41 42 DRM_COPY_FROM_USER_IOCTL(irq, (drm_irq_busid_t *)data, sizeof(irq)); 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 DRM_COPY_TO_USER_IOCTL( (drm_irq_busid_t *)data, irq, sizeof(irq) ); 56 57 return 0; 58} 59 60#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 61static irqreturn_t 62drm_irq_handler_wrap(DRM_IRQ_ARGS) 63{ 64 drm_device_t *dev = (drm_device_t *)arg; 65 66 DRM_SPINLOCK(&dev->irq_lock); 67 dev->driver.irq_handler(arg); 68 DRM_SPINUNLOCK(&dev->irq_lock); 69} 70#endif 71 72int drm_irq_install(drm_device_t *dev) 73{ 74 int retcode; 75#ifdef __NetBSD__ 76 pci_intr_handle_t ih; 77#endif 78 79 if (dev->irq == 0 || dev->dev_private == NULL) 80 return DRM_ERR(EINVAL); 81 82 DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); 83 84 DRM_LOCK(); 85 if (dev->irq_enabled) { 86 DRM_UNLOCK(); 87 return DRM_ERR(EBUSY); 88 } 89 dev->irq_enabled = 1; 90 91 dev->context_flag = 0; 92 93 DRM_SPININIT(dev->irq_lock, "DRM IRQ lock"); 94 95 /* Before installing handler */ 96 dev->driver.irq_preinstall(dev); 97 DRM_UNLOCK(); 98 99 /* Install handler */ 100#ifdef __FreeBSD__ 101 dev->irqrid = 0; 102 dev->irqr = bus_alloc_resource_any(dev->device, SYS_RES_IRQ, 103 &dev->irqrid, RF_SHAREABLE); 104 if (!dev->irqr) { 105 retcode = ENOENT; 106 goto err; 107 } 108#if __FreeBSD_version < 500000 109 retcode = bus_setup_intr(dev->device, dev->irqr, INTR_TYPE_TTY, 110 dev->irq_handler, dev, &dev->irqh); 111#else 112 retcode = bus_setup_intr(dev->device, dev->irqr, INTR_TYPE_TTY | INTR_MPSAFE, 113 NULL, drm_irq_handler_wrap, dev, &dev->irqh); 114#endif 115 if (retcode != 0) 116 goto err; 117#elif defined(__NetBSD__) || defined(__OpenBSD__) 118 if (pci_intr_map(&dev->pa, &ih) != 0) { 119 retcode = ENOENT; 120 goto err; 121 } 122 dev->irqh = pci_intr_establish(&dev->pa.pa_pc, ih, IPL_TTY, 123 (irqreturn_t (*)(void *))dev->irq_handler, dev); 124 if (!dev->irqh) { 125 retcode = ENOENT; 126 goto err; 127 } 128#endif 129 130 /* After installing handler */ 131 DRM_LOCK(); 132 dev->driver.irq_postinstall(dev); 133 DRM_UNLOCK(); 134 135 return 0; 136err: 137 DRM_LOCK(); 138 dev->irq_enabled = 0; 139#ifdef ___FreeBSD__ 140 if (dev->irqrid != 0) { 141 bus_release_resource(dev->device, SYS_RES_IRQ, dev->irqrid, 142 dev->irqr); 143 dev->irqrid = 0; 144 } 145#endif 146 DRM_SPINUNINIT(dev->irq_lock); 147 DRM_UNLOCK(); 148 return retcode; 149} 150 151int drm_irq_uninstall(drm_device_t *dev) 152{ 153#ifdef __FreeBSD__ 154 int irqrid; 155#endif 156 157 if (!dev->irq_enabled) 158 return DRM_ERR(EINVAL); 159 160 dev->irq_enabled = 0; 161#ifdef __FreeBSD__ 162 irqrid = dev->irqrid; 163 dev->irqrid = 0; 164#endif 165 166 DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); 167 168 dev->driver.irq_uninstall(dev); 169 170#ifdef __FreeBSD__ 171 DRM_UNLOCK(); 172 bus_teardown_intr(dev->device, dev->irqr, dev->irqh); 173 bus_release_resource(dev->device, SYS_RES_IRQ, irqrid, dev->irqr); 174 DRM_LOCK(); 175#elif defined(__NetBSD__) || defined(__OpenBSD__) 176 pci_intr_disestablish(&dev->pa.pa_pc, dev->irqh); 177#endif 178 DRM_SPINUNINIT(dev->irq_lock); 179 180 return 0; 181} 182 183int drm_control(DRM_IOCTL_ARGS) 184{ 185 DRM_DEVICE; 186 drm_control_t ctl; 187 int err; 188 189 DRM_COPY_FROM_USER_IOCTL( ctl, (drm_control_t *) data, sizeof(ctl) ); 190 191 switch ( ctl.func ) { 192 case DRM_INST_HANDLER: 193 /* Handle drivers whose DRM used to require IRQ setup but the 194 * no longer does. 195 */ 196 if (!dev->driver.use_irq) 197 return 0; 198 if (dev->if_version < DRM_IF_VERSION(1, 2) && 199 ctl.irq != dev->irq) 200 return DRM_ERR(EINVAL); 201 return drm_irq_install(dev); 202 case DRM_UNINST_HANDLER: 203 if (!dev->driver.use_irq) 204 return 0; 205 DRM_LOCK(); 206 err = drm_irq_uninstall(dev); 207 DRM_UNLOCK(); 208 return err; 209 default: 210 return DRM_ERR(EINVAL); 211 } 212} 213 214int drm_wait_vblank(DRM_IOCTL_ARGS) 215{ 216 DRM_DEVICE; 217 drm_wait_vblank_t vblwait; 218 struct timeval now; 219 int ret; 220 221 if (!dev->irq_enabled) 222 return DRM_ERR(EINVAL); 223 224 DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data, 225 sizeof(vblwait) ); 226 227 if (vblwait.request.type & _DRM_VBLANK_RELATIVE) { 228 vblwait.request.sequence += atomic_read(&dev->vbl_received); 229 vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; 230 } 231 232 flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; 233 if (flags & _DRM_VBLANK_SIGNAL) { 234#if 0 /* disabled */ 235 drm_vbl_sig_t *vbl_sig = malloc(sizeof(drm_vbl_sig_t), M_DRM, 236 M_NOWAIT | M_ZERO); 237 if (vbl_sig == NULL) 238 return ENOMEM; 239 240 vbl_sig->sequence = vblwait.request.sequence; 241 vbl_sig->signo = vblwait.request.signal; 242 vbl_sig->pid = DRM_CURRENTPID; 243 244 vblwait.reply.sequence = atomic_read(&dev->vbl_received); 245 246 DRM_SPINLOCK(&dev->irq_lock); 247 TAILQ_INSERT_HEAD(&dev->vbl_sig_list, vbl_sig, link); 248 DRM_SPINUNLOCK(&dev->irq_lock); 249 ret = 0; 250#endif 251 ret = EINVAL; 252 } else { 253 DRM_LOCK(); 254 ret = dev->driver.vblank_wait(dev, &vblwait.request.sequence); 255 DRM_UNLOCK(); 256 257 microtime(&now); 258 vblwait.reply.tval_sec = now.tv_sec; 259 vblwait.reply.tval_usec = now.tv_usec; 260 } 261 262 DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait, 263 sizeof(vblwait) ); 264 265 return ret; 266} 267 268void drm_vbl_send_signals(drm_device_t *dev) 269{ 270} 271 272#if 0 /* disabled */ 273void drm_vbl_send_signals( drm_device_t *dev ) 274{ 275 drm_vbl_sig_t *vbl_sig; 276 unsigned int vbl_seq = atomic_read( &dev->vbl_received ); 277 struct proc *p; 278 279 vbl_sig = TAILQ_FIRST(&dev->vbl_sig_list); 280 while (vbl_sig != NULL) { 281 drm_vbl_sig_t *next = TAILQ_NEXT(vbl_sig, link); 282 283 if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) { 284 p = pfind(vbl_sig->pid); 285 if (p != NULL) 286 psignal(p, vbl_sig->signo); 287 288 TAILQ_REMOVE(&dev->vbl_sig_list, vbl_sig, link); 289 DRM_FREE(vbl_sig,sizeof(*vbl_sig)); 290 } 291 vbl_sig = next; 292 } 293} 294#endif 295