Remove signals for vblank events code.
[dragonfly.git] / sys / dev / drm / drm_irq.c
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  */
27
28 /** @file drm_irq.c
29  * Support code for handling setup/teardown of interrupt handlers and
30  * handing interrupt handlers off to the drivers.
31  */
32
33 #include "dev/drm/drmP.h"
34 #include "dev/drm/drm.h"
35
36 int drm_irq_by_busid(struct drm_device *dev, void *data,
37                      struct drm_file *file_priv)
38 {
39         struct drm_irq_busid *irq = data;
40
41         if ((irq->busnum >> 8) != dev->pci_domain ||
42             (irq->busnum & 0xff) != dev->pci_bus ||
43             irq->devnum != dev->pci_slot ||
44             irq->funcnum != dev->pci_func)
45                 return EINVAL;
46
47         irq->irq = dev->irq;
48
49         DRM_DEBUG("%d:%d:%d => IRQ %d\n",
50             irq->busnum, irq->devnum, irq->funcnum, irq->irq);
51
52         return 0;
53 }
54
55 static void vblank_disable_fn(void *arg)
56 {
57         struct drm_device *dev = (struct drm_device *)arg;
58         int i;
59
60         if (callout_pending(&dev->vblank_disable_timer)) {
61                 /* callout was reset */
62                 return;
63         }
64         if (!callout_active(&dev->vblank_disable_timer)) {
65                 /* callout was stopped */
66                 return;
67         }
68         callout_deactivate(&dev->vblank_disable_timer);
69
70         DRM_DEBUG("vblank_disable: %s\n", dev->vblank_disable_allowed ?
71                 "allowed" : "denied");
72         if (!dev->vblank_disable_allowed)
73                 return;
74
75         for (i = 0; i < dev->num_crtcs; i++) {
76                 if (atomic_read(&dev->vblank[i].refcount) == 0 &&
77                     dev->vblank[i].enabled && !dev->vblank[i].inmodeset) {
78                         DRM_DEBUG("disabling vblank on crtc %d\n", i);
79                         dev->vblank[i].last =
80                             dev->driver->get_vblank_counter(dev, i);
81                         dev->driver->disable_vblank(dev, i);
82                         dev->vblank[i].enabled = 0;
83                 }
84         }
85 }
86
87 void drm_vblank_cleanup(struct drm_device *dev)
88 {
89         /* Bail if the driver didn't call drm_vblank_init() */
90         if (dev->num_crtcs == 0)
91                 return;
92
93         DRM_SPINLOCK(&dev->vbl_lock);
94         callout_stop(&dev->vblank_disable_timer);
95         DRM_SPINUNLOCK(&dev->vbl_lock);
96
97         vblank_disable_fn((void *)dev);
98
99         free(dev->vblank, DRM_MEM_DRIVER);
100
101         dev->num_crtcs = 0;
102 }
103
104 int drm_vblank_init(struct drm_device *dev, int num_crtcs)
105 {
106         int i, ret = ENOMEM;
107
108         callout_init(&dev->vblank_disable_timer);
109         dev->num_crtcs = num_crtcs;
110
111         dev->vblank = malloc(sizeof(struct drm_vblank_info) * num_crtcs,
112             DRM_MEM_DRIVER, M_NOWAIT | M_ZERO);
113         if (!dev->vblank)
114             goto err;
115
116         DRM_DEBUG("\n");
117
118         /* Zero per-crtc vblank stuff */
119         for (i = 0; i < num_crtcs; i++) {
120                 DRM_INIT_WAITQUEUE(&dev->vblank[i].queue);
121                 atomic_set(&dev->vblank[i].count, 0);
122                 atomic_set(&dev->vblank[i].refcount, 0);
123         }
124
125         dev->vblank_disable_allowed = 0;
126
127         return 0;
128
129 err:
130         drm_vblank_cleanup(dev);
131         return ret;
132 }
133
134 int drm_irq_install(struct drm_device *dev)
135 {
136         int crtc, retcode;
137
138         if (dev->irq == 0 || dev->dev_private == NULL)
139                 return EINVAL;
140
141         DRM_DEBUG("irq=%d\n", dev->irq);
142
143         DRM_LOCK();
144         if (dev->irq_enabled) {
145                 DRM_UNLOCK();
146                 return EBUSY;
147         }
148         dev->irq_enabled = 1;
149
150         dev->context_flag = 0;
151
152         /* Before installing handler */
153         dev->driver->irq_preinstall(dev);
154         DRM_UNLOCK();
155
156         /* Install handler */
157         retcode = bus_setup_intr(dev->device, dev->irqr, INTR_MPSAFE,
158                                  dev->driver->irq_handler, dev, &dev->irqh,
159                                  &dev->irq_lock);
160         if (retcode != 0)
161                 goto err;
162
163         /* After installing handler */
164         DRM_LOCK();
165         dev->driver->irq_postinstall(dev);
166         DRM_UNLOCK();
167         if (dev->driver->enable_vblank) {
168                 DRM_SPINLOCK(&dev->vbl_lock);
169                 for( crtc = 0 ; crtc < dev->num_crtcs ; crtc++) {
170                         if (dev->driver->enable_vblank(dev, crtc) == 0) {
171                                 dev->vblank[crtc].enabled = 1;
172                         }
173                 }
174                 callout_reset(&dev->vblank_disable_timer, 5 * DRM_HZ,
175                     (timeout_t *)vblank_disable_fn, (void *)dev);
176                 DRM_SPINUNLOCK(&dev->vbl_lock);
177         }
178
179         return 0;
180 err:
181         DRM_LOCK();
182         dev->irq_enabled = 0;
183         DRM_UNLOCK();
184
185         return retcode;
186 }
187
188 int drm_irq_uninstall(struct drm_device *dev)
189 {
190         int crtc;
191
192         if (!dev->irq_enabled)
193                 return EINVAL;
194
195         dev->irq_enabled = 0;
196
197         /*
198         * Wake up any waiters so they don't hang.
199         */
200         DRM_SPINLOCK(&dev->vbl_lock);
201         for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
202                 if (dev->vblank[crtc].enabled) {
203                         DRM_WAKEUP(&dev->vblank[crtc].queue);
204                         dev->vblank[crtc].last =
205                             dev->driver->get_vblank_counter(dev, crtc);
206                         dev->vblank[crtc].enabled = 0;
207                 }
208         }
209         DRM_SPINUNLOCK(&dev->vbl_lock);
210
211         DRM_DEBUG("irq=%d\n", dev->irq);
212
213         dev->driver->irq_uninstall(dev);
214
215         DRM_UNLOCK();
216         bus_teardown_intr(dev->device, dev->irqr, dev->irqh);
217         DRM_LOCK();
218
219         return 0;
220 }
221
222 int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv)
223 {
224         struct drm_control *ctl = data;
225         int err;
226
227         switch (ctl->func) {
228         case DRM_INST_HANDLER:
229                 /* Handle drivers whose DRM used to require IRQ setup but the
230                  * no longer does.
231                  */
232                 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
233                         return 0;
234                 if (dev->if_version < DRM_IF_VERSION(1, 2) &&
235                     ctl->irq != dev->irq)
236                         return EINVAL;
237                 return drm_irq_install(dev);
238         case DRM_UNINST_HANDLER:
239                 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
240                         return 0;
241                 DRM_LOCK();
242                 err = drm_irq_uninstall(dev);
243                 DRM_UNLOCK();
244                 return err;
245         default:
246                 return EINVAL;
247         }
248 }
249
250 u32 drm_vblank_count(struct drm_device *dev, int crtc)
251 {
252         return atomic_read(&dev->vblank[crtc].count);
253 }
254
255 static void drm_update_vblank_count(struct drm_device *dev, int crtc)
256 {
257         u32 cur_vblank, diff;
258
259         /*
260          * Interrupts were disabled prior to this call, so deal with counter
261          * wrap if needed.
262          * NOTE!  It's possible we lost a full dev->max_vblank_count events
263          * here if the register is small or we had vblank interrupts off for
264          * a long time.
265          */
266         cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
267         diff = cur_vblank - dev->vblank[crtc].last;
268         if (cur_vblank < dev->vblank[crtc].last) {
269                 diff += dev->max_vblank_count;
270
271                 DRM_DEBUG("vblank[%d].last=0x%x, cur_vblank=0x%x => diff=0x%x\n",
272                     crtc, dev->vblank[crtc].last, cur_vblank, diff);
273         }
274
275         DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n",
276             crtc, diff);
277
278         atomic_add(diff, &dev->vblank[crtc].count);
279 }
280
281 int drm_vblank_get(struct drm_device *dev, int crtc)
282 {
283         int ret = 0;
284
285         DRM_SPINLOCK(&dev->vbl_lock);
286         /* Going from 0->1 means we have to enable interrupts again */
287         atomic_add_acq_int(&dev->vblank[crtc].refcount, 1);
288         if (dev->vblank[crtc].refcount == 1 &&
289             !dev->vblank[crtc].enabled) {
290                 ret = dev->driver->enable_vblank(dev, crtc);
291                 DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
292                 if (ret)
293                         atomic_dec(&dev->vblank[crtc].refcount);
294                 else {
295                         dev->vblank[crtc].enabled = 1;
296                         drm_update_vblank_count(dev, crtc);
297                 }
298         }
299         DRM_SPINUNLOCK(&dev->vbl_lock);
300
301         return ret;
302 }
303
304 void drm_vblank_put(struct drm_device *dev, int crtc)
305 {
306         KASSERT(atomic_read(&dev->vblank[crtc].refcount) > 0,
307             ("invalid refcount"));
308
309         /* Last user schedules interrupt disable */
310         atomic_subtract_acq_int(&dev->vblank[crtc].refcount, 1);
311
312         DRM_SPINLOCK(&dev->vbl_lock);
313         if (dev->vblank[crtc].refcount == 0)
314             callout_reset(&dev->vblank_disable_timer, 5 * DRM_HZ,
315                 (timeout_t *)vblank_disable_fn, (void *)dev);
316         DRM_SPINUNLOCK(&dev->vbl_lock);
317 }
318
319 int drm_modeset_ctl(struct drm_device *dev, void *data,
320                     struct drm_file *file_priv)
321 {
322         struct drm_modeset_ctl *modeset = data;
323         int crtc, ret = 0;
324
325         DRM_DEBUG("num_crtcs=%d\n", dev->num_crtcs);
326         /* If drm_vblank_init() hasn't been called yet, just no-op */
327         if (!dev->num_crtcs)
328                 goto out;
329
330         crtc = modeset->crtc;
331         DRM_DEBUG("crtc=%d\n", crtc);
332         if (crtc >= dev->num_crtcs) {
333                 ret = EINVAL;
334                 goto out;
335         }
336
337         /*
338          * To avoid all the problems that might happen if interrupts
339          * were enabled/disabled around or between these calls, we just
340          * have the kernel take a reference on the CRTC (just once though
341          * to avoid corrupting the count if multiple, mismatch calls occur),
342          * so that interrupts remain enabled in the interim.
343          */
344         switch (modeset->cmd) {
345         case _DRM_PRE_MODESET:
346                 DRM_DEBUG("pre-modeset\n");
347                 if (!dev->vblank[crtc].inmodeset) {
348                         dev->vblank[crtc].inmodeset = 0x1;
349                         if (drm_vblank_get(dev, crtc) == 0)
350                                 dev->vblank[crtc].inmodeset |= 0x2;
351                 }
352                 break;
353         case _DRM_POST_MODESET:
354                 DRM_DEBUG("post-modeset\n");
355                 if (dev->vblank[crtc].inmodeset) {
356                         DRM_SPINLOCK(&dev->vbl_lock);
357                         dev->vblank_disable_allowed = 1;
358                         DRM_SPINUNLOCK(&dev->vbl_lock);
359
360                         if (dev->vblank[crtc].inmodeset & 0x2)
361                                 drm_vblank_put(dev, crtc);
362
363                         dev->vblank[crtc].inmodeset = 0;
364                 }
365                 break;
366         default:
367                 ret = EINVAL;
368                 break;
369         }
370
371 out:
372         return ret;
373 }
374
375 int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv)
376 {
377         union drm_wait_vblank *vblwait = data;
378         unsigned int flags, seq, crtc;
379         int ret = 0;
380
381         if (!dev->irq_enabled)
382                 return EINVAL;
383
384         if (vblwait->request.type &
385             ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) {
386                 DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
387                     vblwait->request.type,
388                     (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK));
389                 return EINVAL;
390         }
391
392         flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
393         crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
394
395         if (crtc >= dev->num_crtcs)
396                 return EINVAL;
397
398         ret = drm_vblank_get(dev, crtc);
399         if (ret) {
400                 DRM_ERROR("failed to acquire vblank counter, %d\n", ret);
401                 return ret;
402         }
403         seq = drm_vblank_count(dev, crtc);
404
405         switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
406         case _DRM_VBLANK_RELATIVE:
407                 vblwait->request.sequence += seq;
408                 vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
409         case _DRM_VBLANK_ABSOLUTE:
410                 break;
411         default:
412                 ret = EINVAL;
413                 goto done;
414         }
415
416         if ((flags & _DRM_VBLANK_NEXTONMISS) &&
417             (seq - vblwait->request.sequence) <= (1<<23)) {
418                 vblwait->request.sequence = seq + 1;
419         }
420
421         if (flags & _DRM_VBLANK_SIGNAL) {
422                 /* There have never been any consumers */
423                 ret = EINVAL;
424         } else {
425                 DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
426                     vblwait->request.sequence, crtc);
427                 for ( ret = 0 ; !ret && !(((drm_vblank_count(dev, crtc) -
428                     vblwait->request.sequence) <= (1 << 23)) ||
429                     !dev->irq_enabled) ; ) {
430                         lwkt_serialize_enter(&dev->irq_lock);
431                         if (!(((drm_vblank_count(dev, crtc) -
432                             vblwait->request.sequence) <= (1 << 23)) ||
433                             !dev->irq_enabled))
434                                 ret = serialize_sleep(&dev->vblank[crtc].queue,
435                                     &dev->irq_lock, PCATCH, "vblwtq",
436                                     DRM_HZ);
437                         lwkt_serialize_exit(&dev->irq_lock);
438                 }
439
440                 if (ret != EINTR && ret != ERESTART) {
441                         struct timeval now;
442
443                         microtime(&now);
444                         vblwait->reply.tval_sec = now.tv_sec;
445                         vblwait->reply.tval_usec = now.tv_usec;
446                         vblwait->reply.sequence = drm_vblank_count(dev, crtc);
447                         DRM_DEBUG("returning %d to client, irq_enabled %d\n",
448                             vblwait->reply.sequence, dev->irq_enabled);
449                 } else {
450                         DRM_DEBUG("vblank wait interrupted by signal\n");
451                 }
452         }
453
454 done:
455         drm_vblank_put(dev, crtc);
456         return ret;
457 }
458
459 void drm_handle_vblank(struct drm_device *dev, int crtc)
460 {
461         atomic_inc(&dev->vblank[crtc].count);
462         DRM_WAKEUP(&dev->vblank[crtc].queue);
463 }
464