1 1.18 riastrad /* $NetBSD: drm_irq.c,v 1.18 2021/12/19 12:05:08 riastradh Exp $ */ 2 1.9 riastrad 3 1.9 riastrad /* 4 1.9 riastrad * drm_irq.c IRQ and vblank support 5 1.1 riastrad * 6 1.1 riastrad * \author Rickard E. (Rik) Faith <faith (at) valinux.com> 7 1.1 riastrad * \author Gareth Hughes <gareth (at) valinux.com> 8 1.17 riastrad * 9 1.17 riastrad * Permission is hereby granted, free of charge, to any person obtaining a 10 1.17 riastrad * copy of this software and associated documentation files (the "Software"), 11 1.17 riastrad * to deal in the Software without restriction, including without limitation 12 1.17 riastrad * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 1.17 riastrad * and/or sell copies of the Software, and to permit persons to whom the 14 1.17 riastrad * Software is furnished to do so, subject to the following conditions: 15 1.17 riastrad * 16 1.17 riastrad * The above copyright notice and this permission notice (including the next 17 1.17 riastrad * paragraph) shall be included in all copies or substantial portions of the 18 1.17 riastrad * Software. 19 1.17 riastrad * 20 1.17 riastrad * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 1.17 riastrad * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 1.17 riastrad * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 1.17 riastrad * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 24 1.17 riastrad * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 25 1.17 riastrad * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 1.17 riastrad * OTHER DEALINGS IN THE SOFTWARE. 27 1.1 riastrad */ 28 1.1 riastrad 29 1.1 riastrad /* 30 1.1 riastrad * Created: Fri Mar 19 14:30:16 1999 by faith (at) valinux.com 31 1.1 riastrad * 32 1.1 riastrad * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. 33 1.1 riastrad * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 34 1.1 riastrad * All Rights Reserved. 35 1.1 riastrad * 36 1.1 riastrad * Permission is hereby granted, free of charge, to any person obtaining a 37 1.1 riastrad * copy of this software and associated documentation files (the "Software"), 38 1.1 riastrad * to deal in the Software without restriction, including without limitation 39 1.1 riastrad * the rights to use, copy, modify, merge, publish, distribute, sublicense, 40 1.1 riastrad * and/or sell copies of the Software, and to permit persons to whom the 41 1.1 riastrad * Software is furnished to do so, subject to the following conditions: 42 1.1 riastrad * 43 1.1 riastrad * The above copyright notice and this permission notice (including the next 44 1.1 riastrad * paragraph) shall be included in all copies or substantial portions of the 45 1.1 riastrad * Software. 46 1.1 riastrad * 47 1.1 riastrad * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 48 1.1 riastrad * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 49 1.1 riastrad * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 50 1.1 riastrad * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 51 1.1 riastrad * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 52 1.1 riastrad * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 53 1.1 riastrad * OTHER DEALINGS IN THE SOFTWARE. 54 1.1 riastrad */ 55 1.1 riastrad 56 1.17 riastrad 57 1.9 riastrad #include <sys/cdefs.h> 58 1.18 riastrad __KERNEL_RCSID(0, "$NetBSD: drm_irq.c,v 1.18 2021/12/19 12:05:08 riastradh Exp $"); 59 1.9 riastrad 60 1.17 riastrad #include <linux/export.h> 61 1.17 riastrad #include <linux/interrupt.h> /* For task queue support */ 62 1.17 riastrad #include <linux/pci.h> 63 1.17 riastrad #include <linux/vgaarb.h> 64 1.1 riastrad 65 1.17 riastrad #include <drm/drm.h> 66 1.17 riastrad #include <drm/drm_device.h> 67 1.17 riastrad #include <drm/drm_drv.h> 68 1.17 riastrad #include <drm/drm_irq.h> 69 1.17 riastrad #include <drm/drm_print.h> 70 1.17 riastrad #include <drm/drm_vblank.h> 71 1.1 riastrad 72 1.17 riastrad #include "drm_internal.h" 73 1.2 riastrad 74 1.2 riastrad #ifdef __NetBSD__ /* XXX hurk -- selnotify &c. */ 75 1.2 riastrad #include <sys/poll.h> 76 1.2 riastrad #include <sys/select.h> 77 1.2 riastrad #endif 78 1.2 riastrad 79 1.1 riastrad 80 1.9 riastrad /** 81 1.17 riastrad * DOC: irq helpers 82 1.9 riastrad * 83 1.17 riastrad * The DRM core provides very simple support helpers to enable IRQ handling on a 84 1.17 riastrad * device through the drm_irq_install() and drm_irq_uninstall() functions. This 85 1.17 riastrad * only supports devices with a single interrupt on the main device stored in 86 1.17 riastrad * &drm_device.dev and set as the device paramter in drm_dev_alloc(). 87 1.17 riastrad * 88 1.17 riastrad * These IRQ helpers are strictly optional. Drivers which roll their own only 89 1.17 riastrad * need to set &drm_device.irq_enabled to signal the DRM core that vblank 90 1.17 riastrad * interrupts are working. Since these helpers don't automatically clean up the 91 1.17 riastrad * requested interrupt like e.g. devm_request_irq() they're not really 92 1.17 riastrad * recommended. 93 1.9 riastrad */ 94 1.1 riastrad 95 1.1 riastrad /** 96 1.9 riastrad * drm_irq_install - install IRQ handler 97 1.9 riastrad * @dev: DRM device 98 1.9 riastrad * @irq: IRQ number to install the handler for 99 1.9 riastrad * 100 1.9 riastrad * Initializes the IRQ related data. Installs the handler, calling the driver 101 1.17 riastrad * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before 102 1.17 riastrad * and after the installation. 103 1.1 riastrad * 104 1.9 riastrad * This is the simplified helper interface provided for drivers with no special 105 1.9 riastrad * needs. Drivers which need to install interrupt handlers for multiple 106 1.17 riastrad * interrupts must instead set &drm_device.irq_enabled to signal the DRM core 107 1.9 riastrad * that vblank interrupts are available. 108 1.1 riastrad * 109 1.17 riastrad * @irq must match the interrupt number that would be passed to request_irq(), 110 1.17 riastrad * if called directly instead of using this helper function. 111 1.17 riastrad * 112 1.17 riastrad * &drm_driver.irq_handler is called to handle the registered interrupt. 113 1.17 riastrad * 114 1.9 riastrad * Returns: 115 1.9 riastrad * Zero on success or a negative error code on failure. 116 1.1 riastrad */ 117 1.11 riastrad #ifdef __NetBSD__ 118 1.11 riastrad int drm_irq_install(struct drm_device *dev) 119 1.11 riastrad #else 120 1.9 riastrad int drm_irq_install(struct drm_device *dev, int irq) 121 1.11 riastrad #endif 122 1.1 riastrad { 123 1.1 riastrad int ret; 124 1.1 riastrad unsigned long sh_flags = 0; 125 1.1 riastrad 126 1.11 riastrad #ifndef __NetBSD__ 127 1.9 riastrad if (irq == 0) 128 1.1 riastrad return -EINVAL; 129 1.11 riastrad #endif 130 1.1 riastrad 131 1.1 riastrad /* Driver must have been initialized */ 132 1.9 riastrad if (!dev->dev_private) 133 1.1 riastrad return -EINVAL; 134 1.1 riastrad 135 1.9 riastrad if (dev->irq_enabled) 136 1.1 riastrad return -EBUSY; 137 1.4 riastrad dev->irq_enabled = true; 138 1.1 riastrad 139 1.11 riastrad #ifndef __NetBSD__ 140 1.9 riastrad DRM_DEBUG("irq=%d\n", irq); 141 1.11 riastrad #endif 142 1.1 riastrad 143 1.1 riastrad /* Before installing handler */ 144 1.1 riastrad if (dev->driver->irq_preinstall) 145 1.1 riastrad dev->driver->irq_preinstall(dev); 146 1.1 riastrad 147 1.17 riastrad /* PCI devices require shared interrupts. */ 148 1.17 riastrad if (dev->pdev) 149 1.1 riastrad sh_flags = IRQF_SHARED; 150 1.1 riastrad 151 1.2 riastrad #ifdef __NetBSD__ 152 1.11 riastrad ret = (*dev->driver->request_irq)(dev, sh_flags); 153 1.2 riastrad #else 154 1.9 riastrad ret = request_irq(irq, dev->driver->irq_handler, 155 1.9 riastrad sh_flags, dev->driver->name, dev); 156 1.2 riastrad #endif 157 1.1 riastrad 158 1.1 riastrad if (ret < 0) { 159 1.4 riastrad dev->irq_enabled = false; 160 1.1 riastrad return ret; 161 1.1 riastrad } 162 1.1 riastrad 163 1.1 riastrad /* After installing handler */ 164 1.1 riastrad if (dev->driver->irq_postinstall) 165 1.1 riastrad ret = dev->driver->irq_postinstall(dev); 166 1.1 riastrad 167 1.1 riastrad if (ret < 0) { 168 1.4 riastrad dev->irq_enabled = false; 169 1.17 riastrad if (drm_core_check_feature(dev, DRIVER_LEGACY)) 170 1.1 riastrad vga_client_register(dev->pdev, NULL, NULL, NULL); 171 1.2 riastrad #ifdef __NetBSD__ 172 1.11 riastrad (*dev->driver->free_irq)(dev); 173 1.2 riastrad #else 174 1.9 riastrad free_irq(irq, dev); 175 1.2 riastrad #endif 176 1.9 riastrad } else { 177 1.11 riastrad #ifndef __NetBSD__ 178 1.9 riastrad dev->irq = irq; 179 1.11 riastrad #endif 180 1.1 riastrad } 181 1.1 riastrad 182 1.1 riastrad return ret; 183 1.1 riastrad } 184 1.1 riastrad EXPORT_SYMBOL(drm_irq_install); 185 1.1 riastrad 186 1.1 riastrad /** 187 1.9 riastrad * drm_irq_uninstall - uninstall the IRQ handler 188 1.9 riastrad * @dev: DRM device 189 1.1 riastrad * 190 1.17 riastrad * Calls the driver's &drm_driver.irq_uninstall function and unregisters the IRQ 191 1.17 riastrad * handler. This should only be called by drivers which used drm_irq_install() 192 1.17 riastrad * to set up their interrupt handler. Other drivers must only reset 193 1.17 riastrad * &drm_device.irq_enabled to false. 194 1.9 riastrad * 195 1.9 riastrad * Note that for kernel modesetting drivers it is a bug if this function fails. 196 1.9 riastrad * The sanity checks are only to catch buggy user modesetting drivers which call 197 1.9 riastrad * the same function through an ioctl. 198 1.1 riastrad * 199 1.9 riastrad * Returns: 200 1.9 riastrad * Zero on success or a negative error code on failure. 201 1.1 riastrad */ 202 1.1 riastrad int drm_irq_uninstall(struct drm_device *dev) 203 1.1 riastrad { 204 1.1 riastrad unsigned long irqflags; 205 1.4 riastrad bool irq_enabled; 206 1.4 riastrad int i; 207 1.1 riastrad 208 1.1 riastrad irq_enabled = dev->irq_enabled; 209 1.4 riastrad dev->irq_enabled = false; 210 1.1 riastrad 211 1.1 riastrad /* 212 1.9 riastrad * Wake up any waiters so they don't hang. This is just to paper over 213 1.17 riastrad * issues for UMS drivers which aren't in full control of their 214 1.9 riastrad * vblank/irq handling. KMS drivers must ensure that vblanks are all 215 1.9 riastrad * disabled when uninstalling the irq handler. 216 1.1 riastrad */ 217 1.1 riastrad if (dev->num_crtcs) { 218 1.18 riastrad spin_lock_irqsave(&dev->event_lock, irqflags); 219 1.1 riastrad for (i = 0; i < dev->num_crtcs; i++) { 220 1.9 riastrad struct drm_vblank_crtc *vblank = &dev->vblank[i]; 221 1.9 riastrad 222 1.9 riastrad if (!vblank->enabled) 223 1.9 riastrad continue; 224 1.9 riastrad 225 1.9 riastrad WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET)); 226 1.9 riastrad 227 1.17 riastrad drm_vblank_disable_and_save(dev, i); 228 1.2 riastrad #ifdef __NetBSD__ 229 1.18 riastrad DRM_SPIN_WAKEUP_ONE(&vblank->queue, 230 1.18 riastrad &dev->event_lock); 231 1.2 riastrad #else 232 1.9 riastrad wake_up(&vblank->queue); 233 1.2 riastrad #endif 234 1.1 riastrad } 235 1.18 riastrad spin_unlock_irqrestore(&dev->event_lock, irqflags); 236 1.1 riastrad } 237 1.1 riastrad 238 1.1 riastrad if (!irq_enabled) 239 1.1 riastrad return -EINVAL; 240 1.1 riastrad 241 1.9 riastrad DRM_DEBUG("irq=%d\n", dev->irq); 242 1.1 riastrad 243 1.17 riastrad if (drm_core_check_feature(dev, DRIVER_LEGACY)) 244 1.1 riastrad vga_client_register(dev->pdev, NULL, NULL, NULL); 245 1.1 riastrad 246 1.1 riastrad if (dev->driver->irq_uninstall) 247 1.1 riastrad dev->driver->irq_uninstall(dev); 248 1.1 riastrad 249 1.2 riastrad #ifdef __NetBSD__ 250 1.11 riastrad (*dev->driver->free_irq)(dev); 251 1.2 riastrad #else 252 1.9 riastrad free_irq(dev->irq, dev); 253 1.2 riastrad #endif 254 1.1 riastrad 255 1.1 riastrad return 0; 256 1.1 riastrad } 257 1.1 riastrad EXPORT_SYMBOL(drm_irq_uninstall); 258 1.1 riastrad 259 1.17 riastrad #if IS_ENABLED(CONFIG_DRM_LEGACY) 260 1.17 riastrad int drm_legacy_irq_control(struct drm_device *dev, void *data, 261 1.17 riastrad struct drm_file *file_priv) 262 1.1 riastrad { 263 1.1 riastrad struct drm_control *ctl = data; 264 1.9 riastrad int ret = 0, irq; 265 1.1 riastrad 266 1.1 riastrad /* if we haven't irq we fallback for compatibility reasons - 267 1.1 riastrad * this used to be a separate function in drm_dma.h 268 1.1 riastrad */ 269 1.1 riastrad 270 1.9 riastrad if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) 271 1.9 riastrad return 0; 272 1.17 riastrad if (!drm_core_check_feature(dev, DRIVER_LEGACY)) 273 1.9 riastrad return 0; 274 1.17 riastrad /* UMS was only ever supported on pci devices. */ 275 1.9 riastrad if (WARN_ON(!dev->pdev)) 276 1.9 riastrad return -EINVAL; 277 1.1 riastrad 278 1.1 riastrad switch (ctl->func) { 279 1.1 riastrad case DRM_INST_HANDLER: 280 1.11 riastrad #ifdef __NetBSD__ 281 1.11 riastrad irq = ctl->irq; 282 1.11 riastrad #else 283 1.9 riastrad irq = dev->pdev->irq; 284 1.11 riastrad #endif 285 1.9 riastrad 286 1.1 riastrad if (dev->if_version < DRM_IF_VERSION(1, 2) && 287 1.9 riastrad ctl->irq != irq) 288 1.1 riastrad return -EINVAL; 289 1.9 riastrad mutex_lock(&dev->struct_mutex); 290 1.11 riastrad #ifdef __NetBSD__ 291 1.11 riastrad ret = drm_irq_install(dev); 292 1.11 riastrad #else 293 1.9 riastrad ret = drm_irq_install(dev, irq); 294 1.11 riastrad #endif 295 1.9 riastrad mutex_unlock(&dev->struct_mutex); 296 1.9 riastrad 297 1.9 riastrad return ret; 298 1.1 riastrad case DRM_UNINST_HANDLER: 299 1.9 riastrad mutex_lock(&dev->struct_mutex); 300 1.9 riastrad ret = drm_irq_uninstall(dev); 301 1.9 riastrad mutex_unlock(&dev->struct_mutex); 302 1.9 riastrad 303 1.9 riastrad return ret; 304 1.1 riastrad default: 305 1.1 riastrad return -EINVAL; 306 1.1 riastrad } 307 1.1 riastrad } 308 1.17 riastrad #endif 309