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