drm_irq.c revision 145132
1/* drm_dma.c -- DMA IOCTL and function support
2 * Created: Fri Oct 18 2003 by anholt@FreeBSD.org
3 */
4/*-
5 * Copyright 2003 Eric Anholt
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * ERIC ANHOLT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * Authors:
27 *    Eric Anholt <anholt@FreeBSD.org>
28 *
29 * $FreeBSD: head/sys/dev/drm/drm_irq.c 145132 2005-04-16 03:44:47Z anholt $
30 */
31
32#include "dev/drm/drmP.h"
33#include "dev/drm/drm.h"
34
35int drm_irq_by_busid(DRM_IOCTL_ARGS)
36{
37	DRM_DEVICE;
38	drm_irq_busid_t irq;
39
40	DRM_COPY_FROM_USER_IOCTL(irq, (drm_irq_busid_t *)data, sizeof(irq));
41
42	if ((irq.busnum >> 8) != dev->pci_domain ||
43	    (irq.busnum & 0xff) != dev->pci_bus ||
44	    irq.devnum != dev->pci_slot ||
45	    irq.funcnum != dev->pci_func)
46		return EINVAL;
47
48	irq.irq = dev->irq;
49
50	DRM_DEBUG("%d:%d:%d => IRQ %d\n",
51		  irq.busnum, irq.devnum, irq.funcnum, irq.irq);
52
53	DRM_COPY_TO_USER_IOCTL( (drm_irq_busid_t *)data, irq, sizeof(irq) );
54
55	return 0;
56}
57
58#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
59static irqreturn_t
60drm_irq_handler_wrap(DRM_IRQ_ARGS)
61{
62	drm_device_t *dev = (drm_device_t *)arg;
63
64	DRM_SPINLOCK(&dev->irq_lock);
65	dev->irq_handler(arg);
66	DRM_SPINUNLOCK(&dev->irq_lock);
67}
68#endif
69
70int drm_irq_install(drm_device_t *dev)
71{
72	int retcode;
73
74	if (dev->irq == 0 || dev->dev_private == NULL)
75		return DRM_ERR(EINVAL);
76
77	DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq );
78
79	DRM_LOCK();
80	if (dev->irq_enabled) {
81		DRM_UNLOCK();
82		return DRM_ERR(EBUSY);
83	}
84	dev->irq_enabled = 1;
85
86	dev->context_flag = 0;
87
88	DRM_SPININIT(dev->irq_lock, "DRM IRQ lock");
89
90				/* Before installing handler */
91	dev->irq_preinstall(dev);
92	DRM_UNLOCK();
93
94				/* Install handler */
95#ifdef __FreeBSD__
96	dev->irqrid = 0;
97	dev->irqr = bus_alloc_resource_any(dev->device, SYS_RES_IRQ,
98				      &dev->irqrid, RF_SHAREABLE);
99	if (!dev->irqr) {
100		retcode = ENOENT;
101		goto err;
102	}
103#if __FreeBSD_version < 500000
104	retcode = bus_setup_intr(dev->device, dev->irqr, INTR_TYPE_TTY,
105				 dev->irq_handler, dev, &dev->irqh);
106#else
107	retcode = bus_setup_intr(dev->device, dev->irqr, INTR_TYPE_TTY | INTR_MPSAFE,
108				 drm_irq_handler_wrap, dev, &dev->irqh);
109#endif
110	if (retcode != 0)
111		goto err;
112#elif defined(__NetBSD__) || defined(__OpenBSD__)
113	if (pci_intr_map(&dev->pa, &dev->ih) != 0) {
114		retcode = ENOENT;
115		goto err;
116	}
117	dev->irqh = pci_intr_establish(&dev->pa.pa_pc, dev->ih, IPL_TTY,
118	    (irqreturn_t (*)(DRM_IRQ_ARGS))dev->irq_handler, dev);
119	if (!dev->irqh) {
120		retcode = ENOENT;
121		goto err;
122	}
123#endif
124
125				/* After installing handler */
126	DRM_LOCK();
127	dev->irq_postinstall(dev);
128	DRM_UNLOCK();
129
130	return 0;
131err:
132	DRM_LOCK();
133	dev->irq_enabled = 0;
134#ifdef ___FreeBSD__
135	if (dev->irqrid != 0) {
136		bus_release_resource(dev->device, SYS_RES_IRQ, dev->irqrid,
137		    dev->irqr);
138		dev->irqrid = 0;
139	}
140#endif
141	DRM_SPINUNINIT(dev->irq_lock);
142	DRM_UNLOCK();
143	return retcode;
144}
145
146int drm_irq_uninstall(drm_device_t *dev)
147{
148	int irqrid;
149
150	if (!dev->irq_enabled)
151		return DRM_ERR(EINVAL);
152
153	dev->irq_enabled = 0;
154	irqrid = dev->irqrid;
155	dev->irqrid = 0;
156
157	DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq );
158
159	dev->irq_uninstall(dev);
160
161#ifdef __FreeBSD__
162	DRM_UNLOCK();
163	bus_teardown_intr(dev->device, dev->irqr, dev->irqh);
164	bus_release_resource(dev->device, SYS_RES_IRQ, irqrid, dev->irqr);
165	DRM_LOCK();
166#elif defined(__NetBSD__) || defined(__OpenBSD__)
167	pci_intr_disestablish(&dev->pa.pa_pc, dev->irqh);
168#endif
169	DRM_SPINUNINIT(dev->irq_lock);
170
171	return 0;
172}
173
174int drm_control(DRM_IOCTL_ARGS)
175{
176	DRM_DEVICE;
177	drm_control_t ctl;
178	int err;
179
180	DRM_COPY_FROM_USER_IOCTL( ctl, (drm_control_t *) data, sizeof(ctl) );
181
182	switch ( ctl.func ) {
183	case DRM_INST_HANDLER:
184		/* Handle drivers whose DRM used to require IRQ setup but the
185		 * no longer does.
186		 */
187		if (!dev->use_irq)
188			return 0;
189		if (dev->if_version < DRM_IF_VERSION(1, 2) &&
190		    ctl.irq != dev->irq)
191			return DRM_ERR(EINVAL);
192		return drm_irq_install(dev);
193	case DRM_UNINST_HANDLER:
194		if (!dev->use_irq)
195			return 0;
196		DRM_LOCK();
197		err = drm_irq_uninstall(dev);
198		DRM_UNLOCK();
199		return err;
200	default:
201		return DRM_ERR(EINVAL);
202	}
203}
204
205int drm_wait_vblank(DRM_IOCTL_ARGS)
206{
207	DRM_DEVICE;
208	drm_wait_vblank_t vblwait;
209	struct timeval now;
210	int ret;
211
212	if (!dev->irq_enabled)
213		return DRM_ERR(EINVAL);
214
215	DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data,
216				  sizeof(vblwait) );
217
218	if (vblwait.request.type & _DRM_VBLANK_RELATIVE) {
219		vblwait.request.sequence += atomic_read(&dev->vbl_received);
220		vblwait.request.type &= ~_DRM_VBLANK_RELATIVE;
221	}
222
223	flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK;
224	if (flags & _DRM_VBLANK_SIGNAL) {
225#if 0 /* disabled */
226		drm_vbl_sig_t *vbl_sig = malloc(sizeof(drm_vbl_sig_t), M_DRM,
227		    M_NOWAIT | M_ZERO);
228		if (vbl_sig == NULL)
229			return ENOMEM;
230
231		vbl_sig->sequence = vblwait.request.sequence;
232		vbl_sig->signo = vblwait.request.signal;
233		vbl_sig->pid = DRM_CURRENTPID;
234
235		vblwait.reply.sequence = atomic_read(&dev->vbl_received);
236
237		DRM_SPINLOCK(&dev->irq_lock);
238		TAILQ_INSERT_HEAD(&dev->vbl_sig_list, vbl_sig, link);
239		DRM_SPINUNLOCK(&dev->irq_lock);
240		ret = 0;
241#endif
242		ret = EINVAL;
243	} else {
244		DRM_LOCK();
245		ret = dev->vblank_wait(dev, &vblwait.request.sequence);
246		DRM_UNLOCK();
247
248		microtime(&now);
249		vblwait.reply.tval_sec = now.tv_sec;
250		vblwait.reply.tval_usec = now.tv_usec;
251	}
252
253	DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait,
254				sizeof(vblwait) );
255
256	return ret;
257}
258
259void drm_vbl_send_signals(drm_device_t *dev)
260{
261}
262
263#if 0 /* disabled */
264void drm_vbl_send_signals( drm_device_t *dev )
265{
266	drm_vbl_sig_t *vbl_sig;
267	unsigned int vbl_seq = atomic_read( &dev->vbl_received );
268	struct proc *p;
269
270	vbl_sig = TAILQ_FIRST(&dev->vbl_sig_list);
271	while (vbl_sig != NULL) {
272		drm_vbl_sig_t *next = TAILQ_NEXT(vbl_sig, link);
273
274		if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) {
275			p = pfind(vbl_sig->pid);
276			if (p != NULL)
277				psignal(p, vbl_sig->signo);
278
279			TAILQ_REMOVE(&dev->vbl_sig_list, vbl_sig, link);
280			DRM_FREE(vbl_sig,sizeof(*vbl_sig));
281		}
282		vbl_sig = next;
283	}
284}
285#endif
286