xref: /openbsd-src/sys/dev/pci/drm/drm_irq.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /* $OpenBSD: drm_irq.c,v 1.43 2011/06/02 18:22:00 weerd Exp $ */
2 /*-
3  * Copyright 2003 Eric Anholt
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * ERIC ANHOLT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * Authors:
25  *    Eric Anholt <anholt@FreeBSD.org>
26  *
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 <sys/workq.h>
35 
36 #include "drmP.h"
37 #include "drm.h"
38 
39 void		drm_update_vblank_count(struct drm_device *, int);
40 void		vblank_disable(void *);
41 int		drm_queue_vblank_event(struct drm_device *, int,
42 		    union drm_wait_vblank *, struct drm_file *);
43 void		drm_handle_vblank_events(struct drm_device *, int);
44 
45 #ifdef DRM_VBLANK_DEBUG
46 #define DPRINTF(x...)	do { printf(x); } while(/* CONSTCOND */ 0)
47 #else
48 #define DPRINTF(x...)	do { } while(/* CONSTCOND */ 0)
49 #endif
50 
51 int
52 drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file_priv)
53 {
54 	struct drm_irq_busid	*irq = data;
55 
56 	/*
57 	 * This is only ever called by root as part of a stupid interface.
58 	 * just hand over the irq without checking the busid. If all clients
59 	 * can be forced to use interface 1.2 then this can die.
60 	 */
61 	irq->irq = dev->irq;
62 
63 	DRM_DEBUG("%d:%d:%d => IRQ %d\n", irq->busnum, irq->devnum,
64 	    irq->funcnum, irq->irq);
65 
66 	return 0;
67 }
68 
69 int
70 drm_irq_install(struct drm_device *dev)
71 {
72 	int	ret;
73 
74 	if (dev->irq == 0 || dev->dev_private == NULL)
75 		return (EINVAL);
76 
77 	DRM_DEBUG("irq=%d\n", dev->irq);
78 
79 	DRM_LOCK();
80 	if (dev->irq_enabled) {
81 		DRM_UNLOCK();
82 		return (EBUSY);
83 	}
84 	dev->irq_enabled = 1;
85 	DRM_UNLOCK();
86 
87 	if ((ret = dev->driver->irq_install(dev)) != 0)
88 		goto err;
89 
90 	return (0);
91 err:
92 	DRM_LOCK();
93 	dev->irq_enabled = 0;
94 	DRM_UNLOCK();
95 	return (ret);
96 }
97 
98 int
99 drm_irq_uninstall(struct drm_device *dev)
100 {
101 	int i;
102 
103 	DRM_LOCK();
104 	if (!dev->irq_enabled) {
105 		DRM_UNLOCK();
106 		return (EINVAL);
107 	}
108 
109 	dev->irq_enabled = 0;
110 	DRM_UNLOCK();
111 
112 	/*
113 	 * Ick. we're about to turn of vblanks, so make sure anyone waiting
114 	 * on them gets woken up. Also make sure we update state correctly
115 	 * so that we can continue refcounting correctly.
116 	 */
117 	if (dev->vblank != NULL) {
118 		mtx_enter(&dev->vblank->vb_lock);
119 		for (i = 0; i < dev->vblank->vb_num; i++) {
120 			wakeup(&dev->vblank->vb_crtcs[i]);
121 			dev->vblank->vb_crtcs[i].vbl_enabled = 0;
122 			dev->vblank->vb_crtcs[i].vbl_last =
123 			    dev->driver->get_vblank_counter(dev, i);
124 		}
125 		mtx_leave(&dev->vblank->vb_lock);
126 	}
127 
128 	DRM_DEBUG("irq=%d\n", dev->irq);
129 
130 	dev->driver->irq_uninstall(dev);
131 
132 	return (0);
133 }
134 
135 int
136 drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv)
137 {
138 	struct drm_control	*ctl = data;
139 
140 	/* Handle drivers who used to require IRQ setup no longer does. */
141 	if (!(dev->driver->flags & DRIVER_IRQ))
142 		return (0);
143 
144 	switch (ctl->func) {
145 	case DRM_INST_HANDLER:
146 		if (dev->if_version < DRM_IF_VERSION(1, 2) &&
147 		    ctl->irq != dev->irq)
148 			return (EINVAL);
149 		return (drm_irq_install(dev));
150 	case DRM_UNINST_HANDLER:
151 		return (drm_irq_uninstall(dev));
152 	default:
153 		return (EINVAL);
154 	}
155 }
156 
157 void
158 vblank_disable(void *arg)
159 {
160 	struct drm_device	*dev = (struct drm_device*)arg;
161 	struct drm_vblank_info	*vbl = dev->vblank;
162 	struct drm_vblank	*crtc;
163 	int			 i;
164 
165 	mtx_enter(&vbl->vb_lock);
166 	for (i = 0; i < vbl->vb_num; i++) {
167 		crtc = &vbl->vb_crtcs[i];
168 
169 		if (crtc->vbl_refs == 0 && crtc->vbl_enabled) {
170 			DPRINTF("%s: disabling crtc %d\n", __func__, i);
171 			crtc->vbl_last =
172 			    dev->driver->get_vblank_counter(dev, i);
173 			dev->driver->disable_vblank(dev, i);
174 			crtc->vbl_enabled = 0;
175 		}
176 	}
177 	mtx_leave(&vbl->vb_lock);
178 }
179 
180 void
181 drm_vblank_cleanup(struct drm_device *dev)
182 {
183 	if (dev->vblank == NULL)
184 		return; /* not initialised */
185 
186 	timeout_del(&dev->vblank->vb_disable_timer);
187 
188 	vblank_disable(dev);
189 
190 	drm_free(dev->vblank);
191 	dev->vblank = NULL;
192 }
193 
194 int
195 drm_vblank_init(struct drm_device *dev, int num_crtcs)
196 {
197 	int	i;
198 
199 	dev->vblank = malloc(sizeof(*dev->vblank) + (num_crtcs *
200 	    sizeof(struct drm_vblank)), M_DRM,  M_WAITOK | M_CANFAIL | M_ZERO);
201 	if (dev->vblank == NULL)
202 		return (ENOMEM);
203 
204 	dev->vblank->vb_num = num_crtcs;
205 	mtx_init(&dev->vblank->vb_lock, IPL_TTY);
206 	timeout_set(&dev->vblank->vb_disable_timer, vblank_disable, dev);
207 	for (i = 0; i < num_crtcs; i++)
208 		TAILQ_INIT(&dev->vblank->vb_crtcs[i].vbl_events);
209 
210 	return (0);
211 }
212 
213 u_int32_t
214 drm_vblank_count(struct drm_device *dev, int crtc)
215 {
216 	return (dev->vblank->vb_crtcs[crtc].vbl_count);
217 }
218 
219 void
220 drm_update_vblank_count(struct drm_device *dev, int crtc)
221 {
222 	u_int32_t	cur_vblank, diff;
223 
224 	/*
225 	 * Interrupt was disabled prior to this call, so deal with counter wrap
226 	 * note that we may have lost a full vb_max events if
227 	 * the register is small or the interrupts were off for a long time.
228 	 */
229 	cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
230 	diff = cur_vblank - dev->vblank->vb_crtcs[crtc].vbl_last;
231 	if (cur_vblank < dev->vblank->vb_crtcs[crtc].vbl_last)
232 		diff += dev->vblank->vb_max;
233 
234 	dev->vblank->vb_crtcs[crtc].vbl_count += diff;
235 }
236 
237 int
238 drm_vblank_get(struct drm_device *dev, int crtc)
239 {
240 	struct drm_vblank_info	*vbl = dev->vblank;
241 	int			 ret = 0;
242 
243 	if (dev->irq_enabled == 0)
244 		return (EINVAL);
245 
246 	mtx_enter(&vbl->vb_lock);
247 	DPRINTF("%s: %d refs = %d\n", __func__, crtc,
248 	    vbl->vb_crtcs[crtc].vbl_refs);
249 	vbl->vb_crtcs[crtc].vbl_refs++;
250 	if (vbl->vb_crtcs[crtc].vbl_refs == 1 &&
251 	    vbl->vb_crtcs[crtc].vbl_enabled == 0) {
252 		if ((ret = dev->driver->enable_vblank(dev, crtc)) == 0) {
253 			vbl->vb_crtcs[crtc].vbl_enabled = 1;
254 			drm_update_vblank_count(dev, crtc);
255 		} else {
256 			vbl->vb_crtcs[crtc].vbl_refs--;
257 		}
258 
259 	}
260 	mtx_leave(&vbl->vb_lock);
261 
262 	return (ret);
263 }
264 
265 void
266 drm_vblank_put(struct drm_device *dev, int crtc)
267 {
268 	mtx_enter(&dev->vblank->vb_lock);
269 	/* Last user schedules disable */
270 	DPRINTF("%s: %d  refs = %d\n", __func__, crtc,
271 	    dev->vblank->vb_crtcs[crtc].vbl_refs);
272 	KASSERT(dev->vblank->vb_crtcs[crtc].vbl_refs > 0);
273 	if (--dev->vblank->vb_crtcs[crtc].vbl_refs == 0)
274 		timeout_add_sec(&dev->vblank->vb_disable_timer, 5);
275 	mtx_leave(&dev->vblank->vb_lock);
276 }
277 
278 int
279 drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
280 {
281 	struct drm_modeset_ctl	*modeset = data;
282 	struct drm_vblank	*vbl;
283 	int			 crtc, ret = 0;
284 
285 	/* not initialised yet, just noop */
286 	if (dev->vblank == NULL)
287 		return (0);
288 
289 	crtc = modeset->crtc;
290 	if (crtc >= dev->vblank->vb_num || crtc < 0)
291 		return (EINVAL);
292 
293 	vbl = &dev->vblank->vb_crtcs[crtc];
294 
295 	/*
296 	 * If interrupts are enabled/disabled between calls to this ioctl then
297 	 * it can get nasty. So just grab a reference so that the interrupts
298 	 * keep going through the modeset
299 	 */
300 	switch (modeset->cmd) {
301 	case _DRM_PRE_MODESET:
302 		DPRINTF("%s: pre modeset on %d\n", __func__, crtc);
303 		if (vbl->vbl_inmodeset == 0) {
304 			mtx_enter(&dev->vblank->vb_lock);
305 			vbl->vbl_inmodeset = 0x1;
306 			mtx_leave(&dev->vblank->vb_lock);
307 			if (drm_vblank_get(dev, crtc) == 0)
308 				vbl->vbl_inmodeset |= 0x2;
309 		}
310 		break;
311 	case _DRM_POST_MODESET:
312 		DPRINTF("%s: post modeset on %d\n", __func__, crtc);
313 		if (vbl->vbl_inmodeset) {
314 			if (vbl->vbl_inmodeset & 0x2)
315 				drm_vblank_put(dev, crtc);
316 			vbl->vbl_inmodeset = 0;
317 		}
318 		break;
319 	default:
320 		ret = EINVAL;
321 		break;
322 	}
323 
324 	return (ret);
325 }
326 
327 int
328 drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv)
329 {
330 	struct timeval		 now;
331 	union drm_wait_vblank	*vblwait = data;
332 	int			 ret, flags, crtc, seq;
333 
334 	if (!dev->irq_enabled || dev->vblank == NULL ||
335 	    vblwait->request.type & _DRM_VBLANK_SIGNAL)
336 		return (EINVAL);
337 
338 	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
339 	crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
340 
341 	if (crtc >= dev->vblank->vb_num)
342 		return (EINVAL);
343 
344 	if ((ret = drm_vblank_get(dev, crtc)) != 0)
345 		return (ret);
346 	seq = drm_vblank_count(dev, crtc);
347 
348 	if (vblwait->request.type & _DRM_VBLANK_RELATIVE) {
349 		vblwait->request.sequence += seq;
350 		vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
351 	}
352 
353 	flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
354 	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
355 	    (seq - vblwait->request.sequence) <= (1<<23)) {
356 		vblwait->request.sequence = seq + 1;
357 	}
358 
359 	if (flags & _DRM_VBLANK_EVENT)
360 		return (drm_queue_vblank_event(dev, crtc, vblwait, file_priv));
361 
362 	DPRINTF("%s: %d waiting on %d, current %d\n", __func__, crtc,
363 	     vblwait->request.sequence, drm_vblank_count(dev, crtc));
364 	DRM_WAIT_ON(ret, &dev->vblank->vb_crtcs[crtc], &dev->vblank->vb_lock,
365 	    3 * hz, "drmvblq", ((drm_vblank_count(dev, crtc) -
366 	    vblwait->request.sequence) <= (1 << 23)) || dev->irq_enabled == 0);
367 
368 	microtime(&now);
369 	vblwait->reply.tval_sec = now.tv_sec;
370 	vblwait->reply.tval_usec = now.tv_usec;
371 	vblwait->reply.sequence = drm_vblank_count(dev, crtc);
372 	DPRINTF("%s: %d done waiting, seq = %d\n", __func__, crtc,
373 	    vblwait->reply.sequence);
374 
375 	drm_vblank_put(dev, crtc);
376 	return (ret);
377 }
378 
379 int
380 drm_queue_vblank_event(struct drm_device *dev, int crtc,
381     union drm_wait_vblank *vblwait, struct drm_file *file_priv)
382 {
383 	struct drm_pending_vblank_event	*vev;
384 	struct timeval			 now;
385 	u_int				 seq;
386 
387 
388 	vev = drm_calloc(1, sizeof(*vev));
389 	if (vev == NULL)
390 		return (ENOMEM);
391 
392 	vev->event.base.type = DRM_EVENT_VBLANK;
393 	vev->event.base.length = sizeof(vev->event);
394 	vev->event.user_data = vblwait->request.signal;
395 	vev->base.event = &vev->event.base;
396 	vev->base.file_priv = file_priv;
397 	vev->base.destroy = (void (*) (struct drm_pending_event *))drm_free;
398 
399 	microtime(&now);
400 
401 	mtx_enter(&dev->event_lock);
402 	if (file_priv->event_space < sizeof(vev->event)) {
403 		mtx_leave(&dev->event_lock);
404 		drm_free(vev);
405 		return (ENOMEM);
406 	}
407 
408 
409 	seq = drm_vblank_count(dev, crtc);
410 	file_priv->event_space -= sizeof(vev->event);
411 
412 	DPRINTF("%s: queueing event %d on crtc %d\n", __func__, seq, crtc);
413 
414 	if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
415 	    (seq - vblwait->request.sequence) <= (1 << 23)) {
416 		vblwait->request.sequence = seq + 1;
417 		vblwait->reply.sequence = vblwait->request.sequence;
418 	}
419 
420 	vev->event.sequence = vblwait->request.sequence;
421 	if ((seq - vblwait->request.sequence) <= (1 << 23)) {
422 		vev->event.tv_sec = now.tv_sec;
423 		vev->event.tv_usec = now.tv_usec;
424 		DPRINTF("%s: already passed, dequeuing: crtc %d, value %d\n",
425 		    __func__, crtc, seq);
426 		drm_vblank_put(dev, crtc);
427 		TAILQ_INSERT_TAIL(&file_priv->evlist, &vev->base, link);
428 		wakeup(&file_priv->evlist);
429 		selwakeup(&file_priv->rsel);
430 	} else {
431 		TAILQ_INSERT_TAIL(&dev->vblank->vb_crtcs[crtc].vbl_events,
432 		    &vev->base, link);
433 	}
434 	mtx_leave(&dev->event_lock);
435 
436 	return (0);
437 }
438 
439 void
440 drm_handle_vblank_events(struct drm_device *dev, int crtc)
441 {
442 	struct drmevlist		*list;
443 	struct drm_pending_event	*ev, *tmp;
444 	struct drm_pending_vblank_event	*vev;
445 	struct timeval			 now;
446 	u_int				 seq;
447 
448 	list = &dev->vblank->vb_crtcs[crtc].vbl_events;
449 	microtime(&now);
450 	seq = drm_vblank_count(dev, crtc);
451 
452 	mtx_enter(&dev->event_lock);
453 	for (ev = TAILQ_FIRST(list); ev != TAILQ_END(list); ev = tmp) {
454 		tmp = TAILQ_NEXT(ev, link);
455 
456 		vev = (struct drm_pending_vblank_event *)ev;
457 
458 		if ((seq - vev->event.sequence) > (1 << 23))
459 			continue;
460 		DPRINTF("%s: got vblank event on crtc %d, value %d\n",
461 		    __func__, crtc, seq);
462 
463 		vev->event.sequence = seq;
464 		vev->event.tv_sec = now.tv_sec;
465 		vev->event.tv_usec = now.tv_usec;
466 		drm_vblank_put(dev, crtc);
467 		TAILQ_REMOVE(list, ev, link);
468 		TAILQ_INSERT_TAIL(&ev->file_priv->evlist, ev, link);
469 		wakeup(&ev->file_priv->evlist);
470 		selwakeup(&ev->file_priv->rsel);
471 	}
472 	mtx_leave(&dev->event_lock);
473 }
474 
475 void
476 drm_handle_vblank(struct drm_device *dev, int crtc)
477 {
478 	/*
479 	 * XXX if we had proper atomic operations this mutex wouldn't
480 	 * XXX need to be held.
481 	 */
482 	mtx_enter(&dev->vblank->vb_lock);
483 	dev->vblank->vb_crtcs[crtc].vbl_count++;
484 	wakeup(&dev->vblank->vb_crtcs[crtc]);
485 	mtx_leave(&dev->vblank->vb_lock);
486 	drm_handle_vblank_events(dev, crtc);
487 }
488