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 <sys/workq.h> 34 35 #include "drmP.h" 36 #include "drm.h" 37 38 void drm_update_vblank_count(struct drm_device *, int); 39 void vblank_disable(void *); 40 41 #ifdef DRM_VBLANK_DEBUG 42 #define DPRINTF(x...) do { printf(x); } while(/* CONSTCOND */ 0) 43 #else 44 #define DPRINTF(x...) do { } while(/* CONSTCOND */ 0) 45 #endif 46 47 int 48 drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file_priv) 49 { 50 struct drm_irq_busid *irq = data; 51 52 /* 53 * This is only ever called by root as part of a stupid interface. 54 * just hand over the irq without checking the busid. If all clients 55 * can be forced to use interface 1.2 then this can die. 56 */ 57 irq->irq = dev->irq; 58 59 DRM_DEBUG("%d:%d:%d => IRQ %d\n", irq->busnum, irq->devnum, 60 irq->funcnum, irq->irq); 61 62 return 0; 63 } 64 65 int 66 drm_irq_install(struct drm_device *dev) 67 { 68 int ret; 69 70 if (dev->irq == 0 || dev->dev_private == NULL) 71 return (EINVAL); 72 73 DRM_DEBUG("irq=%d\n", dev->irq); 74 75 DRM_LOCK(); 76 if (dev->irq_enabled) { 77 DRM_UNLOCK(); 78 return (EBUSY); 79 } 80 dev->irq_enabled = 1; 81 DRM_UNLOCK(); 82 83 if ((ret = dev->driver->irq_install(dev)) != 0) 84 goto err; 85 86 return (0); 87 err: 88 DRM_LOCK(); 89 dev->irq_enabled = 0; 90 DRM_UNLOCK(); 91 return (ret); 92 } 93 94 int 95 drm_irq_uninstall(struct drm_device *dev) 96 { 97 int i; 98 99 DRM_LOCK(); 100 if (!dev->irq_enabled) { 101 DRM_UNLOCK(); 102 return (EINVAL); 103 } 104 105 dev->irq_enabled = 0; 106 DRM_UNLOCK(); 107 108 /* 109 * Ick. we're about to turn of vblanks, so make sure anyone waiting 110 * on them gets woken up. Also make sure we update state correctly 111 * so that we can continue refcounting correctly. 112 */ 113 if (dev->vblank != NULL) { 114 mtx_enter(&dev->vblank->vb_lock); 115 for (i = 0; i < dev->vblank->vb_num; i++) { 116 wakeup(&dev->vblank->vb_crtcs[i]); 117 dev->vblank->vb_crtcs[i].vbl_enabled = 0; 118 dev->vblank->vb_crtcs[i].vbl_last = 119 dev->driver->get_vblank_counter(dev, i); 120 } 121 mtx_leave(&dev->vblank->vb_lock); 122 } 123 124 DRM_DEBUG("irq=%d\n", dev->irq); 125 126 dev->driver->irq_uninstall(dev); 127 128 return (0); 129 } 130 131 int 132 drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv) 133 { 134 struct drm_control *ctl = data; 135 136 /* Handle drivers who used to require IRQ setup no longer does. */ 137 if (!(dev->driver->flags & DRIVER_IRQ)) 138 return (0); 139 140 switch (ctl->func) { 141 case DRM_INST_HANDLER: 142 if (dev->if_version < DRM_IF_VERSION(1, 2) && 143 ctl->irq != dev->irq) 144 return (EINVAL); 145 return (drm_irq_install(dev)); 146 case DRM_UNINST_HANDLER: 147 return (drm_irq_uninstall(dev)); 148 default: 149 return (EINVAL); 150 } 151 } 152 153 void 154 vblank_disable(void *arg) 155 { 156 struct drm_device *dev = (struct drm_device*)arg; 157 struct drm_vblank_info *vbl = dev->vblank; 158 struct drm_vblank *crtc; 159 int i; 160 161 mtx_enter(&vbl->vb_lock); 162 for (i = 0; i < vbl->vb_num; i++) { 163 crtc = &vbl->vb_crtcs[i]; 164 165 if (crtc->vbl_refs == 0 && crtc->vbl_enabled) { 166 DPRINTF("%s: disabling crtc %d\n", __func__, i); 167 crtc->vbl_last = 168 dev->driver->get_vblank_counter(dev, i); 169 dev->driver->disable_vblank(dev, i); 170 crtc->vbl_enabled = 0; 171 } 172 } 173 mtx_leave(&vbl->vb_lock); 174 } 175 176 void 177 drm_vblank_cleanup(struct drm_device *dev) 178 { 179 if (dev->vblank == NULL) 180 return; /* not initialised */ 181 182 timeout_del(&dev->vblank->vb_disable_timer); 183 184 vblank_disable(dev); 185 186 drm_free(dev->vblank); 187 dev->vblank = NULL; 188 } 189 190 int 191 drm_vblank_init(struct drm_device *dev, int num_crtcs) 192 { 193 dev->vblank = malloc(sizeof(*dev->vblank) + (num_crtcs * 194 sizeof(struct drm_vblank)), M_DRM, M_WAITOK | M_CANFAIL | M_ZERO); 195 if (dev->vblank == NULL) 196 return (ENOMEM); 197 198 dev->vblank->vb_num = num_crtcs; 199 mtx_init(&dev->vblank->vb_lock, IPL_BIO); 200 timeout_set(&dev->vblank->vb_disable_timer, vblank_disable, dev); 201 202 return (0); 203 } 204 205 u_int32_t 206 drm_vblank_count(struct drm_device *dev, int crtc) 207 { 208 return (dev->vblank->vb_crtcs[crtc].vbl_count); 209 } 210 211 void 212 drm_update_vblank_count(struct drm_device *dev, int crtc) 213 { 214 u_int32_t cur_vblank, diff; 215 216 /* 217 * Interrupt was disabled prior to this call, so deal with counter wrap 218 * note that we may have lost a full vb_max events if 219 * the register is small or the interrupts were off for a long time. 220 */ 221 cur_vblank = dev->driver->get_vblank_counter(dev, crtc); 222 diff = cur_vblank - dev->vblank->vb_crtcs[crtc].vbl_last; 223 if (cur_vblank < dev->vblank->vb_crtcs[crtc].vbl_last) 224 diff += dev->vblank->vb_max; 225 226 dev->vblank->vb_crtcs[crtc].vbl_count += diff; 227 } 228 229 int 230 drm_vblank_get(struct drm_device *dev, int crtc) 231 { 232 struct drm_vblank_info *vbl = dev->vblank; 233 int ret = 0; 234 235 if (dev->irq_enabled == 0) 236 return (EINVAL); 237 238 mtx_enter(&vbl->vb_lock); 239 DPRINTF("%s: %d refs = %d\n", __func__, crtc, 240 vbl->vb_crtcs[crtc].vbl_refs); 241 vbl->vb_crtcs[crtc].vbl_refs++; 242 if (vbl->vb_crtcs[crtc].vbl_refs == 1 && 243 vbl->vb_crtcs[crtc].vbl_enabled == 0) { 244 if ((ret = dev->driver->enable_vblank(dev, crtc)) == 0) { 245 vbl->vb_crtcs[crtc].vbl_enabled = 1; 246 drm_update_vblank_count(dev, crtc); 247 } else { 248 vbl->vb_crtcs[crtc].vbl_refs--; 249 } 250 251 } 252 mtx_leave(&vbl->vb_lock); 253 254 return (ret); 255 } 256 257 void 258 drm_vblank_put(struct drm_device *dev, int crtc) 259 { 260 mtx_enter(&dev->vblank->vb_lock); 261 /* Last user schedules disable */ 262 DPRINTF("%s: %d refs = %d\n", __func__, crtc, 263 dev->vblank->vb_crtcs[crtc].vbl_refs); 264 KASSERT(dev->vblank->vb_crtcs[crtc].vbl_refs > 0); 265 if (--dev->vblank->vb_crtcs[crtc].vbl_refs == 0) 266 timeout_add_sec(&dev->vblank->vb_disable_timer, 5); 267 mtx_leave(&dev->vblank->vb_lock); 268 } 269 270 int 271 drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv) 272 { 273 struct drm_modeset_ctl *modeset = data; 274 struct drm_vblank *vbl; 275 int crtc, ret = 0; 276 277 /* not initialised yet, just noop */ 278 if (dev->vblank == NULL) 279 return (0); 280 281 crtc = modeset->crtc; 282 if (crtc >= dev->vblank->vb_num) 283 return (EINVAL); 284 285 vbl = &dev->vblank->vb_crtcs[crtc]; 286 287 /* 288 * If interrupts are enabled/disabled between calls to this ioctl then 289 * it can get nasty. So just grab a reference so that the interrupts 290 * keep going through the modeset 291 */ 292 switch (modeset->cmd) { 293 case _DRM_PRE_MODESET: 294 DPRINTF("%s: pre modeset on %d\n", __func__, crtc); 295 if (vbl->vbl_inmodeset == 0) { 296 mtx_enter(&dev->vblank->vb_lock); 297 vbl->vbl_inmodeset = 0x1; 298 mtx_leave(&dev->vblank->vb_lock); 299 if (drm_vblank_get(dev, crtc) == 0) 300 vbl->vbl_inmodeset |= 0x2; 301 } 302 break; 303 case _DRM_POST_MODESET: 304 DPRINTF("%s: post modeset on %d\n", __func__, crtc); 305 if (vbl->vbl_inmodeset) { 306 if (vbl->vbl_inmodeset & 0x2) 307 drm_vblank_put(dev, crtc); 308 vbl->vbl_inmodeset = 0; 309 } 310 break; 311 default: 312 ret = EINVAL; 313 break; 314 } 315 316 return (ret); 317 } 318 319 int 320 drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) 321 { 322 struct timeval now; 323 union drm_wait_vblank *vblwait = data; 324 int ret, flags, crtc, seq; 325 326 if (!dev->irq_enabled || dev->vblank == NULL || 327 vblwait->request.type & _DRM_VBLANK_SIGNAL) 328 return (EINVAL); 329 330 flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; 331 crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; 332 333 if (crtc >= dev->vblank->vb_num) 334 return (EINVAL); 335 336 if ((ret = drm_vblank_get(dev, crtc)) != 0) 337 return (ret); 338 seq = drm_vblank_count(dev,crtc); 339 340 if (vblwait->request.type & _DRM_VBLANK_RELATIVE) { 341 vblwait->request.sequence += seq; 342 vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; 343 } 344 345 flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; 346 if ((flags & _DRM_VBLANK_NEXTONMISS) && 347 (seq - vblwait->request.sequence) <= (1<<23)) { 348 vblwait->request.sequence = seq + 1; 349 } 350 351 DPRINTF("%s: %d waiting on %d, current %d\n", __func__, crtc, 352 vblwait->request.sequence, drm_vblank_count(dev, crtc)); 353 DRM_WAIT_ON(ret, &dev->vblank->vb_crtcs[crtc], &dev->vblank->vb_lock, 354 3 * hz, "drmvblq", ((drm_vblank_count(dev, crtc) - 355 vblwait->request.sequence) <= (1 << 23)) || dev->irq_enabled == 0); 356 357 microtime(&now); 358 vblwait->reply.tval_sec = now.tv_sec; 359 vblwait->reply.tval_usec = now.tv_usec; 360 vblwait->reply.sequence = drm_vblank_count(dev, crtc); 361 DPRINTF("%s: %d done waiting, seq = %d\n", __func__, crtc, 362 vblwait->reply.sequence); 363 364 drm_vblank_put(dev, crtc); 365 return (ret); 366 } 367 368 void 369 drm_handle_vblank(struct drm_device *dev, int crtc) 370 { 371 /* 372 * XXX if we had proper atomic operations this mutex wouldn't 373 * XXX need to be held. 374 */ 375 mtx_enter(&dev->vblank->vb_lock); 376 dev->vblank->vb_crtcs[crtc].vbl_count++; 377 wakeup(&dev->vblank->vb_crtcs[crtc]); 378 mtx_leave(&dev->vblank->vb_lock); 379 } 380