aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/drm/drm_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/drm/drm_irq.c')
-rw-r--r--drivers/char/drm/drm_irq.c155
1 files changed, 131 insertions, 24 deletions
diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c
index 4553a3a1e496..9d00c51fe2c4 100644
--- a/drivers/char/drm/drm_irq.c
+++ b/drivers/char/drm/drm_irq.c
@@ -121,6 +121,7 @@ static int drm_irq_install(drm_device_t * dev)
121 spin_lock_init(&dev->vbl_lock); 121 spin_lock_init(&dev->vbl_lock);
122 122
123 INIT_LIST_HEAD(&dev->vbl_sigs.head); 123 INIT_LIST_HEAD(&dev->vbl_sigs.head);
124 INIT_LIST_HEAD(&dev->vbl_sigs2.head);
124 125
125 dev->vbl_pending = 0; 126 dev->vbl_pending = 0;
126 } 127 }
@@ -175,6 +176,8 @@ int drm_irq_uninstall(drm_device_t * dev)
175 176
176 free_irq(dev->irq, dev); 177 free_irq(dev->irq, dev);
177 178
179 dev->locked_tasklet_func = NULL;
180
178 return 0; 181 return 0;
179} 182}
180 183
@@ -247,10 +250,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
247 drm_wait_vblank_t vblwait; 250 drm_wait_vblank_t vblwait;
248 struct timeval now; 251 struct timeval now;
249 int ret = 0; 252 int ret = 0;
250 unsigned int flags; 253 unsigned int flags, seq;
251
252 if (!drm_core_check_feature(dev, DRIVER_IRQ_VBL))
253 return -EINVAL;
254 254
255 if (!dev->irq) 255 if (!dev->irq)
256 return -EINVAL; 256 return -EINVAL;
@@ -258,9 +258,26 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
258 if (copy_from_user(&vblwait, argp, sizeof(vblwait))) 258 if (copy_from_user(&vblwait, argp, sizeof(vblwait)))
259 return -EFAULT; 259 return -EFAULT;
260 260
261 switch (vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK) { 261 if (vblwait.request.type &
262 ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) {
263 DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
264 vblwait.request.type,
265 (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK));
266 return -EINVAL;
267 }
268
269 flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK;
270
271 if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ?
272 DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL))
273 return -EINVAL;
274
275 seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2
276 : &dev->vbl_received);
277
278 switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) {
262 case _DRM_VBLANK_RELATIVE: 279 case _DRM_VBLANK_RELATIVE:
263 vblwait.request.sequence += atomic_read(&dev->vbl_received); 280 vblwait.request.sequence += seq;
264 vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; 281 vblwait.request.type &= ~_DRM_VBLANK_RELATIVE;
265 case _DRM_VBLANK_ABSOLUTE: 282 case _DRM_VBLANK_ABSOLUTE:
266 break; 283 break;
@@ -268,26 +285,30 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
268 return -EINVAL; 285 return -EINVAL;
269 } 286 }
270 287
271 flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; 288 if ((flags & _DRM_VBLANK_NEXTONMISS) &&
289 (seq - vblwait.request.sequence) <= (1<<23)) {
290 vblwait.request.sequence = seq + 1;
291 }
272 292
273 if (flags & _DRM_VBLANK_SIGNAL) { 293 if (flags & _DRM_VBLANK_SIGNAL) {
274 unsigned long irqflags; 294 unsigned long irqflags;
295 drm_vbl_sig_t *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY)
296 ? &dev->vbl_sigs2 : &dev->vbl_sigs;
275 drm_vbl_sig_t *vbl_sig; 297 drm_vbl_sig_t *vbl_sig;
276 298
277 vblwait.reply.sequence = atomic_read(&dev->vbl_received);
278
279 spin_lock_irqsave(&dev->vbl_lock, irqflags); 299 spin_lock_irqsave(&dev->vbl_lock, irqflags);
280 300
281 /* Check if this task has already scheduled the same signal 301 /* Check if this task has already scheduled the same signal
282 * for the same vblank sequence number; nothing to be done in 302 * for the same vblank sequence number; nothing to be done in
283 * that case 303 * that case
284 */ 304 */
285 list_for_each_entry(vbl_sig, &dev->vbl_sigs.head, head) { 305 list_for_each_entry(vbl_sig, &vbl_sigs->head, head) {
286 if (vbl_sig->sequence == vblwait.request.sequence 306 if (vbl_sig->sequence == vblwait.request.sequence
287 && vbl_sig->info.si_signo == vblwait.request.signal 307 && vbl_sig->info.si_signo == vblwait.request.signal
288 && vbl_sig->task == current) { 308 && vbl_sig->task == current) {
289 spin_unlock_irqrestore(&dev->vbl_lock, 309 spin_unlock_irqrestore(&dev->vbl_lock,
290 irqflags); 310 irqflags);
311 vblwait.reply.sequence = seq;
291 goto done; 312 goto done;
292 } 313 }
293 } 314 }
@@ -315,11 +336,16 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
315 336
316 spin_lock_irqsave(&dev->vbl_lock, irqflags); 337 spin_lock_irqsave(&dev->vbl_lock, irqflags);
317 338
318 list_add_tail((struct list_head *)vbl_sig, &dev->vbl_sigs.head); 339 list_add_tail((struct list_head *)vbl_sig, &vbl_sigs->head);
319 340
320 spin_unlock_irqrestore(&dev->vbl_lock, irqflags); 341 spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
342
343 vblwait.reply.sequence = seq;
321 } else { 344 } else {
322 if (dev->driver->vblank_wait) 345 if (flags & _DRM_VBLANK_SECONDARY) {
346 if (dev->driver->vblank_wait2)
347 ret = dev->driver->vblank_wait2(dev, &vblwait.request.sequence);
348 } else if (dev->driver->vblank_wait)
323 ret = 349 ret =
324 dev->driver->vblank_wait(dev, 350 dev->driver->vblank_wait(dev,
325 &vblwait.request.sequence); 351 &vblwait.request.sequence);
@@ -347,25 +373,32 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
347 */ 373 */
348void drm_vbl_send_signals(drm_device_t * dev) 374void drm_vbl_send_signals(drm_device_t * dev)
349{ 375{
350 struct list_head *list, *tmp;
351 drm_vbl_sig_t *vbl_sig;
352 unsigned int vbl_seq = atomic_read(&dev->vbl_received);
353 unsigned long flags; 376 unsigned long flags;
377 int i;
354 378
355 spin_lock_irqsave(&dev->vbl_lock, flags); 379 spin_lock_irqsave(&dev->vbl_lock, flags);
356 380
357 list_for_each_safe(list, tmp, &dev->vbl_sigs.head) { 381 for (i = 0; i < 2; i++) {
358 vbl_sig = list_entry(list, drm_vbl_sig_t, head); 382 struct list_head *list, *tmp;
359 if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { 383 drm_vbl_sig_t *vbl_sig;
360 vbl_sig->info.si_code = vbl_seq; 384 drm_vbl_sig_t *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs;
361 send_sig_info(vbl_sig->info.si_signo, &vbl_sig->info, 385 unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 :
362 vbl_sig->task); 386 &dev->vbl_received);
387
388 list_for_each_safe(list, tmp, &vbl_sigs->head) {
389 vbl_sig = list_entry(list, drm_vbl_sig_t, head);
390 if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
391 vbl_sig->info.si_code = vbl_seq;
392 send_sig_info(vbl_sig->info.si_signo,
393 &vbl_sig->info, vbl_sig->task);
363 394
364 list_del(list); 395 list_del(list);
365 396
366 drm_free(vbl_sig, sizeof(*vbl_sig), DRM_MEM_DRIVER); 397 drm_free(vbl_sig, sizeof(*vbl_sig),
398 DRM_MEM_DRIVER);
367 399
368 dev->vbl_pending--; 400 dev->vbl_pending--;
401 }
369 } 402 }
370 } 403 }
371 404
@@ -373,3 +406,77 @@ void drm_vbl_send_signals(drm_device_t * dev)
373} 406}
374 407
375EXPORT_SYMBOL(drm_vbl_send_signals); 408EXPORT_SYMBOL(drm_vbl_send_signals);
409
410/**
411 * Tasklet wrapper function.
412 *
413 * \param data DRM device in disguise.
414 *
415 * Attempts to grab the HW lock and calls the driver callback on success. On
416 * failure, leave the lock marked as contended so the callback can be called
417 * from drm_unlock().
418 */
419static void drm_locked_tasklet_func(unsigned long data)
420{
421 drm_device_t *dev = (drm_device_t*)data;
422 unsigned long irqflags;
423
424 spin_lock_irqsave(&dev->tasklet_lock, irqflags);
425
426 if (!dev->locked_tasklet_func ||
427 !drm_lock_take(&dev->lock.hw_lock->lock,
428 DRM_KERNEL_CONTEXT)) {
429 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
430 return;
431 }
432
433 dev->lock.lock_time = jiffies;
434 atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
435
436 dev->locked_tasklet_func(dev);
437
438 drm_lock_free(dev, &dev->lock.hw_lock->lock,
439 DRM_KERNEL_CONTEXT);
440
441 dev->locked_tasklet_func = NULL;
442
443 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
444}
445
446/**
447 * Schedule a tasklet to call back a driver hook with the HW lock held.
448 *
449 * \param dev DRM device.
450 * \param func Driver callback.
451 *
452 * This is intended for triggering actions that require the HW lock from an
453 * interrupt handler. The lock will be grabbed ASAP after the interrupt handler
454 * completes. Note that the callback may be called from interrupt or process
455 * context, it must not make any assumptions about this. Also, the HW lock will
456 * be held with the kernel context or any client context.
457 */
458void drm_locked_tasklet(drm_device_t *dev, void (*func)(drm_device_t*))
459{
460 unsigned long irqflags;
461 static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0);
462
463 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ) ||
464 test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state))
465 return;
466
467 spin_lock_irqsave(&dev->tasklet_lock, irqflags);
468
469 if (dev->locked_tasklet_func) {
470 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
471 return;
472 }
473
474 dev->locked_tasklet_func = func;
475
476 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
477
478 drm_tasklet.data = (unsigned long)dev;
479
480 tasklet_hi_schedule(&drm_tasklet);
481}
482EXPORT_SYMBOL(drm_locked_tasklet);