diff options
Diffstat (limited to 'drivers/char/drm/drm_irq.c')
-rw-r--r-- | drivers/char/drm/drm_irq.c | 155 |
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 | */ |
348 | void drm_vbl_send_signals(drm_device_t * dev) | 374 | void 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 | ||
375 | EXPORT_SYMBOL(drm_vbl_send_signals); | 408 | EXPORT_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 | */ | ||
419 | static 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 | */ | ||
458 | void 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 | } | ||
482 | EXPORT_SYMBOL(drm_locked_tasklet); | ||