Home | History | Annotate | Line # | Download | only in drm
drm_irq.c revision 1.1.1.4
      1 /*	$NetBSD: drm_irq.c,v 1.1.1.4 2021/12/18 20:11:02 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.1.1.4 2021/12/18 20:11:02 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 /**
     75  * DOC: irq helpers
     76  *
     77  * The DRM core provides very simple support helpers to enable IRQ handling on a
     78  * device through the drm_irq_install() and drm_irq_uninstall() functions. This
     79  * only supports devices with a single interrupt on the main device stored in
     80  * &drm_device.dev and set as the device paramter in drm_dev_alloc().
     81  *
     82  * These IRQ helpers are strictly optional. Drivers which roll their own only
     83  * need to set &drm_device.irq_enabled to signal the DRM core that vblank
     84  * interrupts are working. Since these helpers don't automatically clean up the
     85  * requested interrupt like e.g. devm_request_irq() they're not really
     86  * recommended.
     87  */
     88 
     89 /**
     90  * drm_irq_install - install IRQ handler
     91  * @dev: DRM device
     92  * @irq: IRQ number to install the handler for
     93  *
     94  * Initializes the IRQ related data. Installs the handler, calling the driver
     95  * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before
     96  * and after the installation.
     97  *
     98  * This is the simplified helper interface provided for drivers with no special
     99  * needs. Drivers which need to install interrupt handlers for multiple
    100  * interrupts must instead set &drm_device.irq_enabled to signal the DRM core
    101  * that vblank interrupts are available.
    102  *
    103  * @irq must match the interrupt number that would be passed to request_irq(),
    104  * if called directly instead of using this helper function.
    105  *
    106  * &drm_driver.irq_handler is called to handle the registered interrupt.
    107  *
    108  * Returns:
    109  * Zero on success or a negative error code on failure.
    110  */
    111 int drm_irq_install(struct drm_device *dev, int irq)
    112 {
    113 	int ret;
    114 	unsigned long sh_flags = 0;
    115 
    116 	if (irq == 0)
    117 		return -EINVAL;
    118 
    119 	/* Driver must have been initialized */
    120 	if (!dev->dev_private)
    121 		return -EINVAL;
    122 
    123 	if (dev->irq_enabled)
    124 		return -EBUSY;
    125 	dev->irq_enabled = true;
    126 
    127 	DRM_DEBUG("irq=%d\n", irq);
    128 
    129 	/* Before installing handler */
    130 	if (dev->driver->irq_preinstall)
    131 		dev->driver->irq_preinstall(dev);
    132 
    133 	/* PCI devices require shared interrupts. */
    134 	if (dev->pdev)
    135 		sh_flags = IRQF_SHARED;
    136 
    137 	ret = request_irq(irq, dev->driver->irq_handler,
    138 			  sh_flags, dev->driver->name, dev);
    139 
    140 	if (ret < 0) {
    141 		dev->irq_enabled = false;
    142 		return ret;
    143 	}
    144 
    145 	/* After installing handler */
    146 	if (dev->driver->irq_postinstall)
    147 		ret = dev->driver->irq_postinstall(dev);
    148 
    149 	if (ret < 0) {
    150 		dev->irq_enabled = false;
    151 		if (drm_core_check_feature(dev, DRIVER_LEGACY))
    152 			vga_client_register(dev->pdev, NULL, NULL, NULL);
    153 		free_irq(irq, dev);
    154 	} else {
    155 		dev->irq = irq;
    156 	}
    157 
    158 	return ret;
    159 }
    160 EXPORT_SYMBOL(drm_irq_install);
    161 
    162 /**
    163  * drm_irq_uninstall - uninstall the IRQ handler
    164  * @dev: DRM device
    165  *
    166  * Calls the driver's &drm_driver.irq_uninstall function and unregisters the IRQ
    167  * handler.  This should only be called by drivers which used drm_irq_install()
    168  * to set up their interrupt handler. Other drivers must only reset
    169  * &drm_device.irq_enabled to false.
    170  *
    171  * Note that for kernel modesetting drivers it is a bug if this function fails.
    172  * The sanity checks are only to catch buggy user modesetting drivers which call
    173  * the same function through an ioctl.
    174  *
    175  * Returns:
    176  * Zero on success or a negative error code on failure.
    177  */
    178 int drm_irq_uninstall(struct drm_device *dev)
    179 {
    180 	unsigned long irqflags;
    181 	bool irq_enabled;
    182 	int i;
    183 
    184 	irq_enabled = dev->irq_enabled;
    185 	dev->irq_enabled = false;
    186 
    187 	/*
    188 	 * Wake up any waiters so they don't hang. This is just to paper over
    189 	 * issues for UMS drivers which aren't in full control of their
    190 	 * vblank/irq handling. KMS drivers must ensure that vblanks are all
    191 	 * disabled when uninstalling the irq handler.
    192 	 */
    193 	if (dev->num_crtcs) {
    194 		spin_lock_irqsave(&dev->vbl_lock, irqflags);
    195 		for (i = 0; i < dev->num_crtcs; i++) {
    196 			struct drm_vblank_crtc *vblank = &dev->vblank[i];
    197 
    198 			if (!vblank->enabled)
    199 				continue;
    200 
    201 			WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
    202 
    203 			drm_vblank_disable_and_save(dev, i);
    204 			wake_up(&vblank->queue);
    205 		}
    206 		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
    207 	}
    208 
    209 	if (!irq_enabled)
    210 		return -EINVAL;
    211 
    212 	DRM_DEBUG("irq=%d\n", dev->irq);
    213 
    214 	if (drm_core_check_feature(dev, DRIVER_LEGACY))
    215 		vga_client_register(dev->pdev, NULL, NULL, NULL);
    216 
    217 	if (dev->driver->irq_uninstall)
    218 		dev->driver->irq_uninstall(dev);
    219 
    220 	free_irq(dev->irq, dev);
    221 
    222 	return 0;
    223 }
    224 EXPORT_SYMBOL(drm_irq_uninstall);
    225 
    226 #if IS_ENABLED(CONFIG_DRM_LEGACY)
    227 int drm_legacy_irq_control(struct drm_device *dev, void *data,
    228 			   struct drm_file *file_priv)
    229 {
    230 	struct drm_control *ctl = data;
    231 	int ret = 0, irq;
    232 
    233 	/* if we haven't irq we fallback for compatibility reasons -
    234 	 * this used to be a separate function in drm_dma.h
    235 	 */
    236 
    237 	if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
    238 		return 0;
    239 	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
    240 		return 0;
    241 	/* UMS was only ever supported on pci devices. */
    242 	if (WARN_ON(!dev->pdev))
    243 		return -EINVAL;
    244 
    245 	switch (ctl->func) {
    246 	case DRM_INST_HANDLER:
    247 		irq = dev->pdev->irq;
    248 
    249 		if (dev->if_version < DRM_IF_VERSION(1, 2) &&
    250 		    ctl->irq != irq)
    251 			return -EINVAL;
    252 		mutex_lock(&dev->struct_mutex);
    253 		ret = drm_irq_install(dev, irq);
    254 		mutex_unlock(&dev->struct_mutex);
    255 
    256 		return ret;
    257 	case DRM_UNINST_HANDLER:
    258 		mutex_lock(&dev->struct_mutex);
    259 		ret = drm_irq_uninstall(dev);
    260 		mutex_unlock(&dev->struct_mutex);
    261 
    262 		return ret;
    263 	default:
    264 		return -EINVAL;
    265 	}
    266 }
    267 #endif
    268