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