xref: /dragonfly/sys/dev/drm/drm_irq.c (revision 9a92bb4c)
1 /*-
2  * Copyright 2003 Eric Anholt
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * ERIC ANHOLT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *    Eric Anholt <anholt@FreeBSD.org>
25  *
26  * $DragonFly: src/sys/dev/drm/drm_irq.c,v 1.1 2008/04/05 18:12:29 hasso Exp $
27  */
28 
29 /** @file drm_irq.c
30  * Support code for handling setup/teardown of interrupt handlers and
31  * handing interrupt handlers off to the drivers.
32  */
33 
34 #include "drmP.h"
35 #include "drm.h"
36 
37 static void drm_locked_task(void *context, int pending __unused);
38 
39 int drm_irq_by_busid(drm_device_t *dev, void *data, struct drm_file *file_priv)
40 {
41 	drm_irq_busid_t *irq = data;
42 
43 	if ((irq->busnum >> 8) != dev->pci_domain ||
44 	    (irq->busnum & 0xff) != dev->pci_bus ||
45 	    irq->devnum != dev->pci_slot ||
46 	    irq->funcnum != dev->pci_func)
47 		return EINVAL;
48 
49 	irq->irq = dev->irq;
50 
51 	DRM_DEBUG("%d:%d:%d => IRQ %d\n",
52 		  irq->busnum, irq->devnum, irq->funcnum, irq->irq);
53 
54 	return 0;
55 }
56 
57 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
58 static irqreturn_t
59 drm_irq_handler_wrap(DRM_IRQ_ARGS)
60 {
61 	drm_device_t *dev = (drm_device_t *)arg;
62 
63 	DRM_SPINLOCK(&dev->irq_lock);
64 	dev->driver.irq_handler(arg);
65 	DRM_SPINUNLOCK(&dev->irq_lock);
66 }
67 #endif
68 
69 int drm_irq_install(drm_device_t *dev)
70 {
71 	int retcode;
72 #ifdef __NetBSD__
73 	pci_intr_handle_t ih;
74 #endif
75 
76 	if (dev->irq == 0 || dev->dev_private == NULL)
77 		return EINVAL;
78 
79 	DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq );
80 
81 	DRM_LOCK();
82 	if (dev->irq_enabled) {
83 		DRM_UNLOCK();
84 		return EBUSY;
85 	}
86 	dev->irq_enabled = 1;
87 
88 	dev->context_flag = 0;
89 
90 #ifndef __DragonFly__
91 	DRM_SPININIT(&dev->irq_lock, "DRM IRQ lock");
92 #else
93 	lwkt_serialize_init(&dev->irq_lock);
94 #endif
95 
96 				/* Before installing handler */
97 	dev->driver.irq_preinstall(dev);
98 	DRM_UNLOCK();
99 
100 				/* Install handler */
101 #if defined(__FreeBSD__) || defined(__DragonFly__)
102 	dev->irqrid = 0;
103 	dev->irqr = bus_alloc_resource_any(dev->device, SYS_RES_IRQ,
104 				      &dev->irqrid, RF_SHAREABLE);
105 	if (!dev->irqr) {
106 		retcode = ENOENT;
107 		goto err;
108 	}
109 #if __FreeBSD_version >= 700031
110 	retcode = bus_setup_intr(dev->device, dev->irqr,
111 				 INTR_TYPE_TTY | INTR_MPSAFE,
112 				 NULL, drm_irq_handler_wrap, dev, &dev->irqh);
113 #elif defined(__DragonFly__)
114 	retcode = bus_setup_intr(dev->device, dev->irqr,
115 				 INTR_MPSAFE,
116 				 dev->driver.irq_handler, dev, &dev->irqh,
117 				 &dev->irq_lock);
118 #else
119 	retcode = bus_setup_intr(dev->device, dev->irqr,
120 				 INTR_TYPE_TTY | INTR_MPSAFE,
121 				 drm_irq_handler_wrap, dev, &dev->irqh);
122 #endif
123 	if (retcode != 0)
124 		goto err;
125 #elif defined(__NetBSD__) || defined(__OpenBSD__)
126 	if (pci_intr_map(&dev->pa, &ih) != 0) {
127 		retcode = ENOENT;
128 		goto err;
129 	}
130 	dev->irqh = pci_intr_establish(&dev->pa.pa_pc, ih, IPL_TTY,
131 	    (irqreturn_t (*)(void *))dev->irq_handler, dev);
132 	if (!dev->irqh) {
133 		retcode = ENOENT;
134 		goto err;
135 	}
136 #endif
137 
138 				/* After installing handler */
139 	DRM_LOCK();
140 	dev->driver.irq_postinstall(dev);
141 	DRM_UNLOCK();
142 
143 	TASK_INIT(&dev->locked_task, 0, drm_locked_task, dev);
144 	return 0;
145 err:
146 	DRM_LOCK();
147 	dev->irq_enabled = 0;
148 #if defined(___FreeBSD__) || defined(__DragonFly__)
149 	if (dev->irqrid != 0) {
150 		bus_release_resource(dev->device, SYS_RES_IRQ, dev->irqrid,
151 		    dev->irqr);
152 		dev->irqrid = 0;
153 	}
154 #endif
155 #ifndef __DragonFly__
156 	DRM_SPINUNINIT(&dev->irq_lock);
157 #endif
158 	DRM_UNLOCK();
159 	return retcode;
160 }
161 
162 int drm_irq_uninstall(drm_device_t *dev)
163 {
164 #if defined(__FreeBSD__) || defined(__DragonFly__)
165 	int irqrid;
166 #endif
167 
168 	if (!dev->irq_enabled)
169 		return EINVAL;
170 
171 	dev->irq_enabled = 0;
172 #if defined(__FreeBSD__) || defined(__DragonFly__)
173 	irqrid = dev->irqrid;
174 	dev->irqrid = 0;
175 #endif
176 
177 	DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq );
178 
179 	dev->driver.irq_uninstall(dev);
180 
181 #if defined(__FreeBSD__) || defined(__DragonFly__)
182 	DRM_UNLOCK();
183 	bus_teardown_intr(dev->device, dev->irqr, dev->irqh);
184 	bus_release_resource(dev->device, SYS_RES_IRQ, irqrid, dev->irqr);
185 	DRM_LOCK();
186 #elif defined(__NetBSD__) || defined(__OpenBSD__)
187 	pci_intr_disestablish(&dev->pa.pa_pc, dev->irqh);
188 #endif
189 #ifndef __DragonFly__
190 	DRM_SPINUNINIT(&dev->irq_lock);
191 #endif
192 
193 	return 0;
194 }
195 
196 int drm_control(drm_device_t *dev, void *data, struct drm_file *file_priv)
197 {
198 	drm_control_t *ctl = data;
199 	int err;
200 
201 	switch ( ctl->func ) {
202 	case DRM_INST_HANDLER:
203 		/* Handle drivers whose DRM used to require IRQ setup but the
204 		 * no longer does.
205 		 */
206 		if (!dev->driver.use_irq)
207 			return 0;
208 		if (dev->if_version < DRM_IF_VERSION(1, 2) &&
209 		    ctl->irq != dev->irq)
210 			return EINVAL;
211 		return drm_irq_install(dev);
212 	case DRM_UNINST_HANDLER:
213 		if (!dev->driver.use_irq)
214 			return 0;
215 		DRM_LOCK();
216 		err = drm_irq_uninstall(dev);
217 		DRM_UNLOCK();
218 		return err;
219 	default:
220 		return EINVAL;
221 	}
222 }
223 
224 int drm_wait_vblank(drm_device_t *dev, void *data, struct drm_file *file_priv)
225 {
226 	drm_wait_vblank_t *vblwait = data;
227 	struct timeval now;
228 	int ret = 0;
229 	int flags, seq;
230 
231 	if (!dev->irq_enabled)
232 		return EINVAL;
233 
234 	if (vblwait->request.type &
235 	    ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) {
236 		DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
237 		    vblwait->request.type,
238 		    (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK));
239 		return EINVAL;
240 	}
241 
242 	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
243 
244 	if ((flags & _DRM_VBLANK_SECONDARY) && !dev->driver.use_vbl_irq2)
245 		return EINVAL;
246 
247 	seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ?
248 	    &dev->vbl_received2 : &dev->vbl_received);
249 
250 	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
251 	case _DRM_VBLANK_RELATIVE:
252 		vblwait->request.sequence += seq;
253 		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
254 	case _DRM_VBLANK_ABSOLUTE:
255 		break;
256 	default:
257 		return EINVAL;
258 	}
259 
260 	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
261 	    (seq - vblwait->request.sequence) <= (1<<23)) {
262 		vblwait->request.sequence = seq + 1;
263 	}
264 
265 	if (flags & _DRM_VBLANK_SIGNAL) {
266 #if 0 /* disabled */
267 		drm_vbl_sig_t *vbl_sig = malloc(sizeof(drm_vbl_sig_t), M_DRM,
268 		    M_NOWAIT | M_ZERO);
269 		if (vbl_sig == NULL)
270 			return ENOMEM;
271 
272 		vbl_sig->sequence = vblwait->request.sequence;
273 		vbl_sig->signo = vblwait->request.signal;
274 		vbl_sig->pid = DRM_CURRENTPID;
275 
276 		vblwait->reply.sequence = atomic_read(&dev->vbl_received);
277 
278 		DRM_SPINLOCK(&dev->irq_lock);
279 		TAILQ_INSERT_HEAD(&dev->vbl_sig_list, vbl_sig, link);
280 		DRM_SPINUNLOCK(&dev->irq_lock);
281 		ret = 0;
282 #endif
283 		ret = EINVAL;
284 	} else {
285 		DRM_LOCK();
286 		/* shared code returns -errno */
287 		if (flags & _DRM_VBLANK_SECONDARY) {
288 			if (dev->driver.vblank_wait2)
289 				ret = -dev->driver.vblank_wait2(dev,
290 				    &vblwait->request.sequence);
291 		} else if (dev->driver.vblank_wait)
292 			ret = -dev->driver.vblank_wait(dev,
293 			    &vblwait->request.sequence);
294 
295 		DRM_UNLOCK();
296 
297 		microtime(&now);
298 		vblwait->reply.tval_sec = now.tv_sec;
299 		vblwait->reply.tval_usec = now.tv_usec;
300 	}
301 
302 	return ret;
303 }
304 
305 void drm_vbl_send_signals(drm_device_t *dev)
306 {
307 }
308 
309 #if 0 /* disabled */
310 void drm_vbl_send_signals( drm_device_t *dev )
311 {
312 	drm_vbl_sig_t *vbl_sig;
313 	unsigned int vbl_seq = atomic_read( &dev->vbl_received );
314 	struct proc *p;
315 
316 	vbl_sig = TAILQ_FIRST(&dev->vbl_sig_list);
317 	while (vbl_sig != NULL) {
318 		drm_vbl_sig_t *next = TAILQ_NEXT(vbl_sig, link);
319 
320 		if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) {
321 			p = pfind(vbl_sig->pid);
322 			if (p != NULL)
323 				psignal(p, vbl_sig->signo);
324 
325 			TAILQ_REMOVE(&dev->vbl_sig_list, vbl_sig, link);
326 			DRM_FREE(vbl_sig,sizeof(*vbl_sig));
327 		}
328 		vbl_sig = next;
329 	}
330 }
331 #endif
332 
333 static void drm_locked_task(void *context, int pending __unused)
334 {
335 	drm_device_t *dev = context;
336 
337 	DRM_LOCK();
338 	for (;;) {
339 		int ret;
340 
341 		if (drm_lock_take(&dev->lock.hw_lock->lock,
342 		    DRM_KERNEL_CONTEXT))
343 		{
344 			dev->lock.file_priv = NULL; /* kernel owned */
345 			dev->lock.lock_time = jiffies;
346 			atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
347 			break;  /* Got lock */
348 		}
349 
350 		/* Contention */
351 #if defined(__FreeBSD__) && __FreeBSD_version > 500000
352 		ret = mtx_sleep((void *)&dev->lock.lock_queue, &dev->dev_lock,
353 		    PZERO | PCATCH, "drmlk2", 0);
354 #elif defined(__DragonFly__)
355 		crit_enter();
356 		tsleep_interlock((void *)&dev->lock.lock_queue);
357 		DRM_UNLOCK();
358 		ret = tsleep((void *)&dev->lock.lock_queue, PCATCH,
359 		    "drmlk2", 0);
360 		crit_exit();
361 		DRM_LOCK();
362 #else
363 		ret = tsleep((void *)&dev->lock.lock_queue, PZERO | PCATCH,
364 		    "drmlk2", 0);
365 #endif
366 		if (ret != 0)
367 			return;
368 	}
369 	DRM_UNLOCK();
370 
371 	dev->locked_task_call(dev);
372 
373 	drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
374 }
375 
376 void
377 drm_locked_tasklet(drm_device_t *dev, void (*tasklet)(drm_device_t *dev))
378 {
379 	dev->locked_task_call = tasklet;
380 	taskqueue_enqueue(taskqueue_swi, &dev->locked_task);
381 }
382