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