Home | History | Annotate | Line # | Download | only in drm
      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 (at) valinux.com>
      7  * \author Gareth Hughes <gareth (at) 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 (at) 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__
    118 int drm_irq_install(struct drm_device *dev)
    119 #else
    120 int 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 }
    184 EXPORT_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  */
    202 int 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 }
    257 EXPORT_SYMBOL(drm_irq_uninstall);
    258 
    259 #if IS_ENABLED(CONFIG_DRM_LEGACY)
    260 int 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