drm_irq.c revision 1.18 1 1.18 riastrad /* $NetBSD: drm_irq.c,v 1.18 2021/12/19 12:05:08 riastradh Exp $ */
2 1.9 riastrad
3 1.9 riastrad /*
4 1.9 riastrad * drm_irq.c IRQ and vblank support
5 1.1 riastrad *
6 1.1 riastrad * \author Rickard E. (Rik) Faith <faith (at) valinux.com>
7 1.1 riastrad * \author Gareth Hughes <gareth (at) valinux.com>
8 1.17 riastrad *
9 1.17 riastrad * Permission is hereby granted, free of charge, to any person obtaining a
10 1.17 riastrad * copy of this software and associated documentation files (the "Software"),
11 1.17 riastrad * to deal in the Software without restriction, including without limitation
12 1.17 riastrad * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 1.17 riastrad * and/or sell copies of the Software, and to permit persons to whom the
14 1.17 riastrad * Software is furnished to do so, subject to the following conditions:
15 1.17 riastrad *
16 1.17 riastrad * The above copyright notice and this permission notice (including the next
17 1.17 riastrad * paragraph) shall be included in all copies or substantial portions of the
18 1.17 riastrad * Software.
19 1.17 riastrad *
20 1.17 riastrad * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 1.17 riastrad * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 1.17 riastrad * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 1.17 riastrad * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24 1.17 riastrad * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25 1.17 riastrad * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 1.17 riastrad * OTHER DEALINGS IN THE SOFTWARE.
27 1.1 riastrad */
28 1.1 riastrad
29 1.1 riastrad /*
30 1.1 riastrad * Created: Fri Mar 19 14:30:16 1999 by faith (at) valinux.com
31 1.1 riastrad *
32 1.1 riastrad * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
33 1.1 riastrad * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
34 1.1 riastrad * All Rights Reserved.
35 1.1 riastrad *
36 1.1 riastrad * Permission is hereby granted, free of charge, to any person obtaining a
37 1.1 riastrad * copy of this software and associated documentation files (the "Software"),
38 1.1 riastrad * to deal in the Software without restriction, including without limitation
39 1.1 riastrad * the rights to use, copy, modify, merge, publish, distribute, sublicense,
40 1.1 riastrad * and/or sell copies of the Software, and to permit persons to whom the
41 1.1 riastrad * Software is furnished to do so, subject to the following conditions:
42 1.1 riastrad *
43 1.1 riastrad * The above copyright notice and this permission notice (including the next
44 1.1 riastrad * paragraph) shall be included in all copies or substantial portions of the
45 1.1 riastrad * Software.
46 1.1 riastrad *
47 1.1 riastrad * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48 1.1 riastrad * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49 1.1 riastrad * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
50 1.1 riastrad * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
51 1.1 riastrad * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
52 1.1 riastrad * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
53 1.1 riastrad * OTHER DEALINGS IN THE SOFTWARE.
54 1.1 riastrad */
55 1.1 riastrad
56 1.17 riastrad
57 1.9 riastrad #include <sys/cdefs.h>
58 1.18 riastrad __KERNEL_RCSID(0, "$NetBSD: drm_irq.c,v 1.18 2021/12/19 12:05:08 riastradh Exp $");
59 1.9 riastrad
60 1.17 riastrad #include <linux/export.h>
61 1.17 riastrad #include <linux/interrupt.h> /* For task queue support */
62 1.17 riastrad #include <linux/pci.h>
63 1.17 riastrad #include <linux/vgaarb.h>
64 1.1 riastrad
65 1.17 riastrad #include <drm/drm.h>
66 1.17 riastrad #include <drm/drm_device.h>
67 1.17 riastrad #include <drm/drm_drv.h>
68 1.17 riastrad #include <drm/drm_irq.h>
69 1.17 riastrad #include <drm/drm_print.h>
70 1.17 riastrad #include <drm/drm_vblank.h>
71 1.1 riastrad
72 1.17 riastrad #include "drm_internal.h"
73 1.2 riastrad
74 1.2 riastrad #ifdef __NetBSD__ /* XXX hurk -- selnotify &c. */
75 1.2 riastrad #include <sys/poll.h>
76 1.2 riastrad #include <sys/select.h>
77 1.2 riastrad #endif
78 1.2 riastrad
79 1.1 riastrad
80 1.9 riastrad /**
81 1.17 riastrad * DOC: irq helpers
82 1.9 riastrad *
83 1.17 riastrad * The DRM core provides very simple support helpers to enable IRQ handling on a
84 1.17 riastrad * device through the drm_irq_install() and drm_irq_uninstall() functions. This
85 1.17 riastrad * only supports devices with a single interrupt on the main device stored in
86 1.17 riastrad * &drm_device.dev and set as the device paramter in drm_dev_alloc().
87 1.17 riastrad *
88 1.17 riastrad * These IRQ helpers are strictly optional. Drivers which roll their own only
89 1.17 riastrad * need to set &drm_device.irq_enabled to signal the DRM core that vblank
90 1.17 riastrad * interrupts are working. Since these helpers don't automatically clean up the
91 1.17 riastrad * requested interrupt like e.g. devm_request_irq() they're not really
92 1.17 riastrad * recommended.
93 1.9 riastrad */
94 1.1 riastrad
95 1.1 riastrad /**
96 1.9 riastrad * drm_irq_install - install IRQ handler
97 1.9 riastrad * @dev: DRM device
98 1.9 riastrad * @irq: IRQ number to install the handler for
99 1.9 riastrad *
100 1.9 riastrad * Initializes the IRQ related data. Installs the handler, calling the driver
101 1.17 riastrad * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before
102 1.17 riastrad * and after the installation.
103 1.1 riastrad *
104 1.9 riastrad * This is the simplified helper interface provided for drivers with no special
105 1.9 riastrad * needs. Drivers which need to install interrupt handlers for multiple
106 1.17 riastrad * interrupts must instead set &drm_device.irq_enabled to signal the DRM core
107 1.9 riastrad * that vblank interrupts are available.
108 1.1 riastrad *
109 1.17 riastrad * @irq must match the interrupt number that would be passed to request_irq(),
110 1.17 riastrad * if called directly instead of using this helper function.
111 1.17 riastrad *
112 1.17 riastrad * &drm_driver.irq_handler is called to handle the registered interrupt.
113 1.17 riastrad *
114 1.9 riastrad * Returns:
115 1.9 riastrad * Zero on success or a negative error code on failure.
116 1.1 riastrad */
117 1.11 riastrad #ifdef __NetBSD__
118 1.11 riastrad int drm_irq_install(struct drm_device *dev)
119 1.11 riastrad #else
120 1.9 riastrad int drm_irq_install(struct drm_device *dev, int irq)
121 1.11 riastrad #endif
122 1.1 riastrad {
123 1.1 riastrad int ret;
124 1.1 riastrad unsigned long sh_flags = 0;
125 1.1 riastrad
126 1.11 riastrad #ifndef __NetBSD__
127 1.9 riastrad if (irq == 0)
128 1.1 riastrad return -EINVAL;
129 1.11 riastrad #endif
130 1.1 riastrad
131 1.1 riastrad /* Driver must have been initialized */
132 1.9 riastrad if (!dev->dev_private)
133 1.1 riastrad return -EINVAL;
134 1.1 riastrad
135 1.9 riastrad if (dev->irq_enabled)
136 1.1 riastrad return -EBUSY;
137 1.4 riastrad dev->irq_enabled = true;
138 1.1 riastrad
139 1.11 riastrad #ifndef __NetBSD__
140 1.9 riastrad DRM_DEBUG("irq=%d\n", irq);
141 1.11 riastrad #endif
142 1.1 riastrad
143 1.1 riastrad /* Before installing handler */
144 1.1 riastrad if (dev->driver->irq_preinstall)
145 1.1 riastrad dev->driver->irq_preinstall(dev);
146 1.1 riastrad
147 1.17 riastrad /* PCI devices require shared interrupts. */
148 1.17 riastrad if (dev->pdev)
149 1.1 riastrad sh_flags = IRQF_SHARED;
150 1.1 riastrad
151 1.2 riastrad #ifdef __NetBSD__
152 1.11 riastrad ret = (*dev->driver->request_irq)(dev, sh_flags);
153 1.2 riastrad #else
154 1.9 riastrad ret = request_irq(irq, dev->driver->irq_handler,
155 1.9 riastrad sh_flags, dev->driver->name, dev);
156 1.2 riastrad #endif
157 1.1 riastrad
158 1.1 riastrad if (ret < 0) {
159 1.4 riastrad dev->irq_enabled = false;
160 1.1 riastrad return ret;
161 1.1 riastrad }
162 1.1 riastrad
163 1.1 riastrad /* After installing handler */
164 1.1 riastrad if (dev->driver->irq_postinstall)
165 1.1 riastrad ret = dev->driver->irq_postinstall(dev);
166 1.1 riastrad
167 1.1 riastrad if (ret < 0) {
168 1.4 riastrad dev->irq_enabled = false;
169 1.17 riastrad if (drm_core_check_feature(dev, DRIVER_LEGACY))
170 1.1 riastrad vga_client_register(dev->pdev, NULL, NULL, NULL);
171 1.2 riastrad #ifdef __NetBSD__
172 1.11 riastrad (*dev->driver->free_irq)(dev);
173 1.2 riastrad #else
174 1.9 riastrad free_irq(irq, dev);
175 1.2 riastrad #endif
176 1.9 riastrad } else {
177 1.11 riastrad #ifndef __NetBSD__
178 1.9 riastrad dev->irq = irq;
179 1.11 riastrad #endif
180 1.1 riastrad }
181 1.1 riastrad
182 1.1 riastrad return ret;
183 1.1 riastrad }
184 1.1 riastrad EXPORT_SYMBOL(drm_irq_install);
185 1.1 riastrad
186 1.1 riastrad /**
187 1.9 riastrad * drm_irq_uninstall - uninstall the IRQ handler
188 1.9 riastrad * @dev: DRM device
189 1.1 riastrad *
190 1.17 riastrad * Calls the driver's &drm_driver.irq_uninstall function and unregisters the IRQ
191 1.17 riastrad * handler. This should only be called by drivers which used drm_irq_install()
192 1.17 riastrad * to set up their interrupt handler. Other drivers must only reset
193 1.17 riastrad * &drm_device.irq_enabled to false.
194 1.9 riastrad *
195 1.9 riastrad * Note that for kernel modesetting drivers it is a bug if this function fails.
196 1.9 riastrad * The sanity checks are only to catch buggy user modesetting drivers which call
197 1.9 riastrad * the same function through an ioctl.
198 1.1 riastrad *
199 1.9 riastrad * Returns:
200 1.9 riastrad * Zero on success or a negative error code on failure.
201 1.1 riastrad */
202 1.1 riastrad int drm_irq_uninstall(struct drm_device *dev)
203 1.1 riastrad {
204 1.1 riastrad unsigned long irqflags;
205 1.4 riastrad bool irq_enabled;
206 1.4 riastrad int i;
207 1.1 riastrad
208 1.1 riastrad irq_enabled = dev->irq_enabled;
209 1.4 riastrad dev->irq_enabled = false;
210 1.1 riastrad
211 1.1 riastrad /*
212 1.9 riastrad * Wake up any waiters so they don't hang. This is just to paper over
213 1.17 riastrad * issues for UMS drivers which aren't in full control of their
214 1.9 riastrad * vblank/irq handling. KMS drivers must ensure that vblanks are all
215 1.9 riastrad * disabled when uninstalling the irq handler.
216 1.1 riastrad */
217 1.1 riastrad if (dev->num_crtcs) {
218 1.18 riastrad spin_lock_irqsave(&dev->event_lock, irqflags);
219 1.1 riastrad for (i = 0; i < dev->num_crtcs; i++) {
220 1.9 riastrad struct drm_vblank_crtc *vblank = &dev->vblank[i];
221 1.9 riastrad
222 1.9 riastrad if (!vblank->enabled)
223 1.9 riastrad continue;
224 1.9 riastrad
225 1.9 riastrad WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET));
226 1.9 riastrad
227 1.17 riastrad drm_vblank_disable_and_save(dev, i);
228 1.2 riastrad #ifdef __NetBSD__
229 1.18 riastrad DRM_SPIN_WAKEUP_ONE(&vblank->queue,
230 1.18 riastrad &dev->event_lock);
231 1.2 riastrad #else
232 1.9 riastrad wake_up(&vblank->queue);
233 1.2 riastrad #endif
234 1.1 riastrad }
235 1.18 riastrad spin_unlock_irqrestore(&dev->event_lock, irqflags);
236 1.1 riastrad }
237 1.1 riastrad
238 1.1 riastrad if (!irq_enabled)
239 1.1 riastrad return -EINVAL;
240 1.1 riastrad
241 1.9 riastrad DRM_DEBUG("irq=%d\n", dev->irq);
242 1.1 riastrad
243 1.17 riastrad if (drm_core_check_feature(dev, DRIVER_LEGACY))
244 1.1 riastrad vga_client_register(dev->pdev, NULL, NULL, NULL);
245 1.1 riastrad
246 1.1 riastrad if (dev->driver->irq_uninstall)
247 1.1 riastrad dev->driver->irq_uninstall(dev);
248 1.1 riastrad
249 1.2 riastrad #ifdef __NetBSD__
250 1.11 riastrad (*dev->driver->free_irq)(dev);
251 1.2 riastrad #else
252 1.9 riastrad free_irq(dev->irq, dev);
253 1.2 riastrad #endif
254 1.1 riastrad
255 1.1 riastrad return 0;
256 1.1 riastrad }
257 1.1 riastrad EXPORT_SYMBOL(drm_irq_uninstall);
258 1.1 riastrad
259 1.17 riastrad #if IS_ENABLED(CONFIG_DRM_LEGACY)
260 1.17 riastrad int drm_legacy_irq_control(struct drm_device *dev, void *data,
261 1.17 riastrad struct drm_file *file_priv)
262 1.1 riastrad {
263 1.1 riastrad struct drm_control *ctl = data;
264 1.9 riastrad int ret = 0, irq;
265 1.1 riastrad
266 1.1 riastrad /* if we haven't irq we fallback for compatibility reasons -
267 1.1 riastrad * this used to be a separate function in drm_dma.h
268 1.1 riastrad */
269 1.1 riastrad
270 1.9 riastrad if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
271 1.9 riastrad return 0;
272 1.17 riastrad if (!drm_core_check_feature(dev, DRIVER_LEGACY))
273 1.9 riastrad return 0;
274 1.17 riastrad /* UMS was only ever supported on pci devices. */
275 1.9 riastrad if (WARN_ON(!dev->pdev))
276 1.9 riastrad return -EINVAL;
277 1.1 riastrad
278 1.1 riastrad switch (ctl->func) {
279 1.1 riastrad case DRM_INST_HANDLER:
280 1.11 riastrad #ifdef __NetBSD__
281 1.11 riastrad irq = ctl->irq;
282 1.11 riastrad #else
283 1.9 riastrad irq = dev->pdev->irq;
284 1.11 riastrad #endif
285 1.9 riastrad
286 1.1 riastrad if (dev->if_version < DRM_IF_VERSION(1, 2) &&
287 1.9 riastrad ctl->irq != irq)
288 1.1 riastrad return -EINVAL;
289 1.9 riastrad mutex_lock(&dev->struct_mutex);
290 1.11 riastrad #ifdef __NetBSD__
291 1.11 riastrad ret = drm_irq_install(dev);
292 1.11 riastrad #else
293 1.9 riastrad ret = drm_irq_install(dev, irq);
294 1.11 riastrad #endif
295 1.9 riastrad mutex_unlock(&dev->struct_mutex);
296 1.9 riastrad
297 1.9 riastrad return ret;
298 1.1 riastrad case DRM_UNINST_HANDLER:
299 1.9 riastrad mutex_lock(&dev->struct_mutex);
300 1.9 riastrad ret = drm_irq_uninstall(dev);
301 1.9 riastrad mutex_unlock(&dev->struct_mutex);
302 1.9 riastrad
303 1.9 riastrad return ret;
304 1.1 riastrad default:
305 1.1 riastrad return -EINVAL;
306 1.1 riastrad }
307 1.1 riastrad }
308 1.17 riastrad #endif
309