xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/drm_irq.c (revision 638280b8fcfb7baa0a1b00f0be3a25d27d9b1b1f)
1*638280b8Sriastradh /*	$NetBSD: drm_irq.c,v 1.18 2021/12/19 12:05:08 riastradh Exp $	*/
2d01ac146Sriastradh 
3d01ac146Sriastradh /*
4d01ac146Sriastradh  * drm_irq.c IRQ and vblank support
5fcd0cb28Sriastradh  *
6fcd0cb28Sriastradh  * \author Rickard E. (Rik) Faith <faith@valinux.com>
7fcd0cb28Sriastradh  * \author Gareth Hughes <gareth@valinux.com>
841ec0267Sriastradh  *
941ec0267Sriastradh  * Permission is hereby granted, free of charge, to any person obtaining a
1041ec0267Sriastradh  * copy of this software and associated documentation files (the "Software"),
1141ec0267Sriastradh  * to deal in the Software without restriction, including without limitation
1241ec0267Sriastradh  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1341ec0267Sriastradh  * and/or sell copies of the Software, and to permit persons to whom the
1441ec0267Sriastradh  * Software is furnished to do so, subject to the following conditions:
1541ec0267Sriastradh  *
1641ec0267Sriastradh  * The above copyright notice and this permission notice (including the next
1741ec0267Sriastradh  * paragraph) shall be included in all copies or substantial portions of the
1841ec0267Sriastradh  * Software.
1941ec0267Sriastradh  *
2041ec0267Sriastradh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2141ec0267Sriastradh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2241ec0267Sriastradh  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2341ec0267Sriastradh  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2441ec0267Sriastradh  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2541ec0267Sriastradh  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2641ec0267Sriastradh  * OTHER DEALINGS IN THE SOFTWARE.
27fcd0cb28Sriastradh  */
28fcd0cb28Sriastradh 
29fcd0cb28Sriastradh /*
30fcd0cb28Sriastradh  * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com
31fcd0cb28Sriastradh  *
32fcd0cb28Sriastradh  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
33fcd0cb28Sriastradh  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
34fcd0cb28Sriastradh  * All Rights Reserved.
35fcd0cb28Sriastradh  *
36fcd0cb28Sriastradh  * Permission is hereby granted, free of charge, to any person obtaining a
37fcd0cb28Sriastradh  * copy of this software and associated documentation files (the "Software"),
38fcd0cb28Sriastradh  * to deal in the Software without restriction, including without limitation
39fcd0cb28Sriastradh  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
40fcd0cb28Sriastradh  * and/or sell copies of the Software, and to permit persons to whom the
41fcd0cb28Sriastradh  * Software is furnished to do so, subject to the following conditions:
42fcd0cb28Sriastradh  *
43fcd0cb28Sriastradh  * The above copyright notice and this permission notice (including the next
44fcd0cb28Sriastradh  * paragraph) shall be included in all copies or substantial portions of the
45fcd0cb28Sriastradh  * Software.
46fcd0cb28Sriastradh  *
47fcd0cb28Sriastradh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48fcd0cb28Sriastradh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49fcd0cb28Sriastradh  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
50fcd0cb28Sriastradh  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
51fcd0cb28Sriastradh  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
52fcd0cb28Sriastradh  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
53fcd0cb28Sriastradh  * OTHER DEALINGS IN THE SOFTWARE.
54fcd0cb28Sriastradh  */
55fcd0cb28Sriastradh 
5641ec0267Sriastradh 
57d01ac146Sriastradh #include <sys/cdefs.h>
58*638280b8Sriastradh __KERNEL_RCSID(0, "$NetBSD: drm_irq.c,v 1.18 2021/12/19 12:05:08 riastradh Exp $");
59d01ac146Sriastradh 
60fcd0cb28Sriastradh #include <linux/export.h>
6141ec0267Sriastradh #include <linux/interrupt.h>	/* For task queue support */
6241ec0267Sriastradh #include <linux/pci.h>
6341ec0267Sriastradh #include <linux/vgaarb.h>
6441ec0267Sriastradh 
6541ec0267Sriastradh #include <drm/drm.h>
6641ec0267Sriastradh #include <drm/drm_device.h>
6741ec0267Sriastradh #include <drm/drm_drv.h>
6841ec0267Sriastradh #include <drm/drm_irq.h>
6941ec0267Sriastradh #include <drm/drm_print.h>
7041ec0267Sriastradh #include <drm/drm_vblank.h>
7141ec0267Sriastradh 
7241ec0267Sriastradh #include "drm_internal.h"
736cb10275Sriastradh 
746cb10275Sriastradh #ifdef __NetBSD__		/* XXX hurk -- selnotify &c. */
756cb10275Sriastradh #include <sys/poll.h>
766cb10275Sriastradh #include <sys/select.h>
776cb10275Sriastradh #endif
786cb10275Sriastradh 
79d01ac146Sriastradh 
80d01ac146Sriastradh /**
8141ec0267Sriastradh  * DOC: irq helpers
82d01ac146Sriastradh  *
8341ec0267Sriastradh  * The DRM core provides very simple support helpers to enable IRQ handling on a
8441ec0267Sriastradh  * device through the drm_irq_install() and drm_irq_uninstall() functions. This
8541ec0267Sriastradh  * only supports devices with a single interrupt on the main device stored in
8641ec0267Sriastradh  * &drm_device.dev and set as the device paramter in drm_dev_alloc().
87d01ac146Sriastradh  *
8841ec0267Sriastradh  * These IRQ helpers are strictly optional. Drivers which roll their own only
8941ec0267Sriastradh  * need to set &drm_device.irq_enabled to signal the DRM core that vblank
9041ec0267Sriastradh  * interrupts are working. Since these helpers don't automatically clean up the
9141ec0267Sriastradh  * requested interrupt like e.g. devm_request_irq() they're not really
9241ec0267Sriastradh  * recommended.
93d01ac146Sriastradh  */
94fcd0cb28Sriastradh 
95fcd0cb28Sriastradh /**
96d01ac146Sriastradh  * drm_irq_install - install IRQ handler
97d01ac146Sriastradh  * @dev: DRM device
98d01ac146Sriastradh  * @irq: IRQ number to install the handler for
99fcd0cb28Sriastradh  *
100fcd0cb28Sriastradh  * Initializes the IRQ related data. Installs the handler, calling the driver
10141ec0267Sriastradh  * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before
10241ec0267Sriastradh  * and after the installation.
103d01ac146Sriastradh  *
104d01ac146Sriastradh  * This is the simplified helper interface provided for drivers with no special
105d01ac146Sriastradh  * needs. Drivers which need to install interrupt handlers for multiple
10641ec0267Sriastradh  * interrupts must instead set &drm_device.irq_enabled to signal the DRM core
107d01ac146Sriastradh  * that vblank interrupts are available.
108d01ac146Sriastradh  *
10941ec0267Sriastradh  * @irq must match the interrupt number that would be passed to request_irq(),
11041ec0267Sriastradh  * if called directly instead of using this helper function.
11141ec0267Sriastradh  *
11241ec0267Sriastradh  * &drm_driver.irq_handler is called to handle the registered interrupt.
11341ec0267Sriastradh  *
114d01ac146Sriastradh  * Returns:
115d01ac146Sriastradh  * Zero on success or a negative error code on failure.
116fcd0cb28Sriastradh  */
11737ac6a77Sriastradh #ifdef __NetBSD__
drm_irq_install(struct drm_device * dev)11837ac6a77Sriastradh int drm_irq_install(struct drm_device *dev)
11937ac6a77Sriastradh #else
120d01ac146Sriastradh int drm_irq_install(struct drm_device *dev, int irq)
12137ac6a77Sriastradh #endif
122fcd0cb28Sriastradh {
123fcd0cb28Sriastradh 	int ret;
124fcd0cb28Sriastradh 	unsigned long sh_flags = 0;
125fcd0cb28Sriastradh 
12637ac6a77Sriastradh #ifndef __NetBSD__
127d01ac146Sriastradh 	if (irq == 0)
128fcd0cb28Sriastradh 		return -EINVAL;
12937ac6a77Sriastradh #endif
130fcd0cb28Sriastradh 
131fcd0cb28Sriastradh 	/* Driver must have been initialized */
132d01ac146Sriastradh 	if (!dev->dev_private)
133fcd0cb28Sriastradh 		return -EINVAL;
134fcd0cb28Sriastradh 
135d01ac146Sriastradh 	if (dev->irq_enabled)
136fcd0cb28Sriastradh 		return -EBUSY;
1373c6dde82Sriastradh 	dev->irq_enabled = true;
138fcd0cb28Sriastradh 
13937ac6a77Sriastradh #ifndef __NetBSD__
140d01ac146Sriastradh 	DRM_DEBUG("irq=%d\n", irq);
14137ac6a77Sriastradh #endif
142fcd0cb28Sriastradh 
143fcd0cb28Sriastradh 	/* Before installing handler */
144fcd0cb28Sriastradh 	if (dev->driver->irq_preinstall)
145fcd0cb28Sriastradh 		dev->driver->irq_preinstall(dev);
146fcd0cb28Sriastradh 
14741ec0267Sriastradh 	/* PCI devices require shared interrupts. */
14841ec0267Sriastradh 	if (dev->pdev)
149fcd0cb28Sriastradh 		sh_flags = IRQF_SHARED;
150fcd0cb28Sriastradh 
1516cb10275Sriastradh #ifdef __NetBSD__
15237ac6a77Sriastradh 	ret = (*dev->driver->request_irq)(dev, sh_flags);
1536cb10275Sriastradh #else
154d01ac146Sriastradh 	ret = request_irq(irq, dev->driver->irq_handler,
155d01ac146Sriastradh 			  sh_flags, dev->driver->name, dev);
1566cb10275Sriastradh #endif
157fcd0cb28Sriastradh 
158fcd0cb28Sriastradh 	if (ret < 0) {
1593c6dde82Sriastradh 		dev->irq_enabled = false;
160fcd0cb28Sriastradh 		return ret;
161fcd0cb28Sriastradh 	}
162fcd0cb28Sriastradh 
163fcd0cb28Sriastradh 	/* After installing handler */
164fcd0cb28Sriastradh 	if (dev->driver->irq_postinstall)
165fcd0cb28Sriastradh 		ret = dev->driver->irq_postinstall(dev);
166fcd0cb28Sriastradh 
167fcd0cb28Sriastradh 	if (ret < 0) {
1683c6dde82Sriastradh 		dev->irq_enabled = false;
16941ec0267Sriastradh 		if (drm_core_check_feature(dev, DRIVER_LEGACY))
170fcd0cb28Sriastradh 			vga_client_register(dev->pdev, NULL, NULL, NULL);
1716cb10275Sriastradh #ifdef __NetBSD__
17237ac6a77Sriastradh 		(*dev->driver->free_irq)(dev);
1736cb10275Sriastradh #else
174d01ac146Sriastradh 		free_irq(irq, dev);
1756cb10275Sriastradh #endif
176d01ac146Sriastradh 	} else {
17737ac6a77Sriastradh #ifndef __NetBSD__
178d01ac146Sriastradh 		dev->irq = irq;
17937ac6a77Sriastradh #endif
180fcd0cb28Sriastradh 	}
181fcd0cb28Sriastradh 
182fcd0cb28Sriastradh 	return ret;
183fcd0cb28Sriastradh }
184fcd0cb28Sriastradh EXPORT_SYMBOL(drm_irq_install);
185fcd0cb28Sriastradh 
186fcd0cb28Sriastradh /**
187d01ac146Sriastradh  * drm_irq_uninstall - uninstall the IRQ handler
188d01ac146Sriastradh  * @dev: DRM device
189fcd0cb28Sriastradh  *
19041ec0267Sriastradh  * Calls the driver's &drm_driver.irq_uninstall function and unregisters the IRQ
19141ec0267Sriastradh  * handler.  This should only be called by drivers which used drm_irq_install()
19241ec0267Sriastradh  * to set up their interrupt handler. Other drivers must only reset
19341ec0267Sriastradh  * &drm_device.irq_enabled to false.
194fcd0cb28Sriastradh  *
195d01ac146Sriastradh  * Note that for kernel modesetting drivers it is a bug if this function fails.
196d01ac146Sriastradh  * The sanity checks are only to catch buggy user modesetting drivers which call
197d01ac146Sriastradh  * the same function through an ioctl.
198d01ac146Sriastradh  *
199d01ac146Sriastradh  * Returns:
200d01ac146Sriastradh  * Zero on success or a negative error code on failure.
201fcd0cb28Sriastradh  */
drm_irq_uninstall(struct drm_device * dev)202fcd0cb28Sriastradh int drm_irq_uninstall(struct drm_device *dev)
203fcd0cb28Sriastradh {
204fcd0cb28Sriastradh 	unsigned long irqflags;
2053c6dde82Sriastradh 	bool irq_enabled;
2063c6dde82Sriastradh 	int i;
207fcd0cb28Sriastradh 
208fcd0cb28Sriastradh 	irq_enabled = dev->irq_enabled;
2093c6dde82Sriastradh 	dev->irq_enabled = false;
210fcd0cb28Sriastradh 
211fcd0cb28Sriastradh 	/*
212d01ac146Sriastradh 	 * Wake up any waiters so they don't hang. This is just to paper over
21341ec0267Sriastradh 	 * issues for UMS drivers which aren't in full control of their
214d01ac146Sriastradh 	 * vblank/irq handling. KMS drivers must ensure that vblanks are all
215d01ac146Sriastradh 	 * disabled when uninstalling the irq handler.
216fcd0cb28Sriastradh 	 */
217fcd0cb28Sriastradh 	if (dev->num_crtcs) {
218*638280b8Sriastradh 		spin_lock_irqsave(&dev->event_lock, irqflags);
219fcd0cb28Sriastradh 		for (i = 0; i < dev->num_crtcs; i++) {
220d01ac146Sriastradh 			struct drm_vblank_crtc *vblank = &dev->vblank[i];
221d01ac146Sriastradh 
222d01ac146Sriastradh 			if (!vblank->enabled)
223d01ac146Sriastradh 				continue;
224d01ac146Sriastradh 
225d01ac146Sriastradh 			WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
226d01ac146Sriastradh 
22741ec0267Sriastradh 			drm_vblank_disable_and_save(dev, i);
2286cb10275Sriastradh #ifdef __NetBSD__
229*638280b8Sriastradh 			DRM_SPIN_WAKEUP_ONE(&vblank->queue,
230*638280b8Sriastradh 			    &dev->event_lock);
2316cb10275Sriastradh #else
232d01ac146Sriastradh 			wake_up(&vblank->queue);
2336cb10275Sriastradh #endif
234fcd0cb28Sriastradh 		}
235*638280b8Sriastradh 		spin_unlock_irqrestore(&dev->event_lock, irqflags);
236fcd0cb28Sriastradh 	}
237fcd0cb28Sriastradh 
238fcd0cb28Sriastradh 	if (!irq_enabled)
239fcd0cb28Sriastradh 		return -EINVAL;
240fcd0cb28Sriastradh 
241d01ac146Sriastradh 	DRM_DEBUG("irq=%d\n", dev->irq);
242fcd0cb28Sriastradh 
24341ec0267Sriastradh 	if (drm_core_check_feature(dev, DRIVER_LEGACY))
244fcd0cb28Sriastradh 		vga_client_register(dev->pdev, NULL, NULL, NULL);
245fcd0cb28Sriastradh 
246fcd0cb28Sriastradh 	if (dev->driver->irq_uninstall)
247fcd0cb28Sriastradh 		dev->driver->irq_uninstall(dev);
248fcd0cb28Sriastradh 
2496cb10275Sriastradh #ifdef __NetBSD__
25037ac6a77Sriastradh 	(*dev->driver->free_irq)(dev);
2516cb10275Sriastradh #else
252d01ac146Sriastradh 	free_irq(dev->irq, dev);
2536cb10275Sriastradh #endif
254fcd0cb28Sriastradh 
255fcd0cb28Sriastradh 	return 0;
256fcd0cb28Sriastradh }
257fcd0cb28Sriastradh EXPORT_SYMBOL(drm_irq_uninstall);
258fcd0cb28Sriastradh 
25941ec0267Sriastradh #if IS_ENABLED(CONFIG_DRM_LEGACY)
drm_legacy_irq_control(struct drm_device * dev,void * data,struct drm_file * file_priv)26041ec0267Sriastradh int drm_legacy_irq_control(struct drm_device *dev, void *data,
261fcd0cb28Sriastradh 			   struct drm_file *file_priv)
262fcd0cb28Sriastradh {
263fcd0cb28Sriastradh 	struct drm_control *ctl = data;
264d01ac146Sriastradh 	int ret = 0, irq;
265fcd0cb28Sriastradh 
266fcd0cb28Sriastradh 	/* if we haven't irq we fallback for compatibility reasons -
267fcd0cb28Sriastradh 	 * this used to be a separate function in drm_dma.h
268fcd0cb28Sriastradh 	 */
269fcd0cb28Sriastradh 
270d01ac146Sriastradh 	if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
271d01ac146Sriastradh 		return 0;
27241ec0267Sriastradh 	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
273d01ac146Sriastradh 		return 0;
27441ec0267Sriastradh 	/* UMS was only ever supported on pci devices. */
275d01ac146Sriastradh 	if (WARN_ON(!dev->pdev))
276d01ac146Sriastradh 		return -EINVAL;
277fcd0cb28Sriastradh 
278fcd0cb28Sriastradh 	switch (ctl->func) {
279fcd0cb28Sriastradh 	case DRM_INST_HANDLER:
28037ac6a77Sriastradh #ifdef __NetBSD__
28137ac6a77Sriastradh 		irq = ctl->irq;
28237ac6a77Sriastradh #else
283d01ac146Sriastradh 		irq = dev->pdev->irq;
28437ac6a77Sriastradh #endif
285d01ac146Sriastradh 
286fcd0cb28Sriastradh 		if (dev->if_version < DRM_IF_VERSION(1, 2) &&
287d01ac146Sriastradh 		    ctl->irq != irq)
288fcd0cb28Sriastradh 			return -EINVAL;
289d01ac146Sriastradh 		mutex_lock(&dev->struct_mutex);
29037ac6a77Sriastradh #ifdef __NetBSD__
29137ac6a77Sriastradh 		ret = drm_irq_install(dev);
29237ac6a77Sriastradh #else
293d01ac146Sriastradh 		ret = drm_irq_install(dev, irq);
29437ac6a77Sriastradh #endif
295d01ac146Sriastradh 		mutex_unlock(&dev->struct_mutex);
296d01ac146Sriastradh 
297d01ac146Sriastradh 		return ret;
298fcd0cb28Sriastradh 	case DRM_UNINST_HANDLER:
299d01ac146Sriastradh 		mutex_lock(&dev->struct_mutex);
300d01ac146Sriastradh 		ret = drm_irq_uninstall(dev);
301d01ac146Sriastradh 		mutex_unlock(&dev->struct_mutex);
302d01ac146Sriastradh 
303d01ac146Sriastradh 		return ret;
304fcd0cb28Sriastradh 	default:
305fcd0cb28Sriastradh 		return -EINVAL;
306fcd0cb28Sriastradh 	}
307fcd0cb28Sriastradh }
3086cb10275Sriastradh #endif
309