xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/drm_irq.c (revision 638280b8fcfb7baa0a1b00f0be3a25d27d9b1b1f)
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__
drm_irq_install(struct drm_device * dev)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  */
drm_irq_uninstall(struct drm_device * dev)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)
drm_legacy_irq_control(struct drm_device * dev,void * data,struct drm_file * file_priv)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